Merge pull request #71 from storytold/echelon-i18n-refresh

Echelon i18n refresh
This commit is contained in:
Brandon Thomas authored and GitHub committed 2022-07-29 04:40:10 -04:00
commit eb002fe8e5
43 files changed
+558 -286

No files matched your search

@@ -110,5 +110,22 @@ function MergeDeepDictionary(dictA: DeepDictionary, dictB: DeepDictionary) : Dee
return output;
}
// Modify all the string leaves of a dictionary to include a prefix string
// This is useful to test if translations were updated using i18next.
function DebugPrefixLeaves(obj: DeepDictionary, prefix: string) : DeepDictionary {
let output : DeepDictionary = {};
export { COMMON_TRANSLATIONS, MergeDeepDictionary }
Object.keys(obj).forEach((key : string) => {
const value = obj[key];
if (typeof value === 'string') {
output[key] = `${prefix}${value}`;
} else {
output[key] = DebugPrefixLeaves(value, prefix);
}
});
return output;
}
export { COMMON_TRANSLATIONS, MergeDeepDictionary, DebugPrefixLeaves }
+5
View File
@@ -6,6 +6,11 @@ enum Language {
Indonesian,
German,
Japanese,
French,
Vietnamese,
Hindi,
ChineseSimplified,
Korean,
}
export { Language }
+20
View File
@@ -219,6 +219,11 @@ class App extends React.Component<Props, State> {
const hasIndonesian = locale.language_codes.indexOf("id") > -1;
const hasGerman = locale.language_codes.indexOf("de") > -1;
const hasJapanese = locale.language_codes.indexOf("ja") > -1;
const hasFrench = locale.language_codes.indexOf("fr") > -1;
const hasVietnamese = locale.language_codes.indexOf("vi") > -1;
const hasHindi= locale.language_codes.indexOf("hi") > -1;
const hasChineseSimplified = locale.language_codes.indexOf("zh") > -1;
const hasKorean = locale.language_codes.indexOf("ko") > -1;
const showNotice = hasSpanish || hasPortuguese || hasTurkish; //|| hasIndonesian || hasGerman || hasJapanese;
let displayLanguage = Language.English;
@@ -246,6 +251,21 @@ class App extends React.Component<Props, State> {
} else if (hasJapanese) {
displayLanguage = Language.Japanese;
languageCode = "ja";
} else if (hasKorean) {
displayLanguage = Language.Korean;
languageCode = "ko";
} else if (hasFrench) {
displayLanguage = Language.French;
languageCode = "fr";
} else if (hasVietnamese) {
displayLanguage = Language.Vietnamese;
languageCode = "vi";
} else if (hasHindi) {
displayLanguage = Language.Hindi;
languageCode = "hi";
} else if (hasChineseSimplified) {
displayLanguage = Language.ChineseSimplified;
languageCode = "zh";
}
this.setState({
@@ -1,7 +1,41 @@
import { COMMON_TRANSLATIONS, MergeDeepDictionary } from '@storyteller/components/src/_i18n/CommonTranslations';
import { COMMON_TRANSLATIONS, DebugPrefixLeaves, MergeDeepDictionary } from '@storyteller/components/src/_i18n/CommonTranslations';
// Use \u{00a0} = &nbsp; character literal
// NB: This is the new translation system design. Multiple, per-page or per-module files are easier
// to navigate and maintain than singular monolithic files. We'll gradually phase out the old system.
import * as de from './locales/de';
import * as en from './locales/en';
import * as es from './locales/es';
import * as fr from './locales/fr';
import * as hi from './locales/hi';
import * as id from './locales/id';
import * as it from './locales/it';
import * as ja from './locales/ja';
import * as ko from './locales/ko';
import * as pt from './locales/pt';
import * as vi from './locales/vi';
import * as zh from './locales/zh';
const NEW_TRANSLATIONS: any = {
de: { translation: de },
en: { translation: en },
es: { translation: es },
fr: { translation: fr },
hi: { translation: hi },
id: { translation: id },
it: { translation: it },
ja: { translation: ja },
ko: { translation: ko },
pt: { translation: pt },
vi: { translation: vi },
zh: { translation: zh },
}
// Make it easy to "see" new translations. Comment out when not developing.
// NEW_TRANSLATIONS = DebugPrefixLeaves(NEW_TRANSLATIONS, '\u{2705} ');
// NB: These are the old translations. They should be phased out.
const FAKEYOU_TRANSLATIONS: any = {
// English: 46.6% Twitch (#1), 30+% FakeYou (#1)
en: {
@@ -270,6 +304,9 @@ const FAKEYOU_TRANSLATIONS: any = {
}
// TODO: Type these as i18next dictionaries
const FAKEYOU_MERGED_TRANSLATIONS: any = MergeDeepDictionary(FAKEYOU_TRANSLATIONS, COMMON_TRANSLATIONS);
const FAKEYOU_MERGED_TRANSLATIONS: any = MergeDeepDictionary(
NEW_TRANSLATIONS,
MergeDeepDictionary(FAKEYOU_TRANSLATIONS, COMMON_TRANSLATIONS)
);
export { FAKEYOU_MERGED_TRANSLATIONS }
@@ -0,0 +1,8 @@
// "de" - German
// 1.1 billion speakers (worldwide #1)
// Twitch #4 (150k concurrent viewers, 4.5k concurrent streamers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Initialisierung...",
"buttons": {
"clear": "Klar",
"speak": "Sprechen"
},
"by": "von",
"categoryFiltersLabel": "Kategorie / Spracvon",
"modelSeeMoreLink": "Weitere Details zum Modell \"<1>{{modelName}}</1>\" von <3>{{userName}}</3>",
"searchHint": "Suchbegriff",
"searchLabel": "Suche",
"textInputHint": "Eingang...",
"voicesLabel": "Stimme ({{count}} zur Auswahl)"
},
"formTitle": "TTS erstellen",
"heroSection": {
"buttons": {
"cloneVoice": "Klonen Sie meine Stimme!",
"signUp": "Anmelden"
},
"subtitle": "Verwenden Sie die Deep-Fake-Technologie von <strong>FakeYou</strong>, um Dinge mit Ihren Lieblingsfiguren zu sagen",
"title": "Text zu Sprache"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "en" - English
// 1.1 billion speakers (worldwide #1)
// Twitch #1 (1.1m concurrent viewers, 52k concurrent streamers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Loading...",
"buttons": {
"clear": "Clear",
"speak": "Speak"
},
"by": "by",
"categoryFiltersLabel": "Category / Langauge",
"modelSeeMoreLink": "See more details about the \"<1>{{modelName}}</1>\" model by <3>{{userName}}</3>",
"searchHint": "Search Term",
"searchLabel": "Search",
"textInputHint": "Textual shenanigans go here...",
"voicesLabel": "Voice ({{count}} to choose from)"
},
"formTitle": "Create TTS",
"heroSection": {
"buttons": {
"cloneVoice": "Clone My Voice!",
"signUp": "Sign Up"
},
"subtitle": "Use <strong>FakeYou</strong> deep fake tech to say stuff with your favorite characters",
"title": "Text to Speech"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "es" - Spanish
// 534 million speakers (worldwide #4)
// Twitch #2 (288k concurrent viewers, 10k concurrent streamers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Cargando...",
"buttons": {
"clear": "Claro",
"speak": "Hablar"
},
"by": "por",
"categoryFiltersLabel": "Categoría / Idioma",
"modelSeeMoreLink": "Ver más detalles sobre el modelo \"<1>{{modelName}}</1>\" de <3>{{userName}}</3>",
"searchHint": "Término de búsqueda",
"searchLabel": "Búsqueda",
"textInputHint": "Las travesuras de texto van aquí...",
"voicesLabel": "Voz ({{count}} para elegir)"
},
"formTitle": "Crear mensaje de texto",
"heroSection": {
"buttons": {
"cloneVoice": "Clonar mi Voz!",
"signUp": "Inscribirse"
},
"subtitle": "Usa <strong>FakeYou</strong> tecnología falsa profunda para decir cosas con tus personajes favoritos",
"title": "Texto a Voz"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "fr" - French
// 280 million speakers (worldwide #5)
// Twitch #5 (140k concurrent viewers, 4.4k concurrent streamers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Initialisation...",
"buttons": {
"clear": "Évacuer",
"speak": "Parler"
},
"by": "par",
"categoryFiltersLabel": "Catégorie / Langue",
"modelSeeMoreLink": "Voir plus de détails sur le modèle \"<1>{{modelName}}</1>\" par <3>{{userName}}</3>",
"searchHint": "Terme de recherche",
"searchLabel": "Chercher",
"textInputHint": "Texte de saisie...",
"voicesLabel": "Voix ({{count}} au choix)"
},
"formTitle": "Créer une synthèse vocale",
"heroSection": {
"buttons": {
"cloneVoice": "Clonez ma voix!",
"signUp": "S'inscrire"
},
"subtitle": "Utilisez <strong>FakeYou</strong> deep fake tech pour dire des trucs avec vos personnages préférés",
"title": "Texte pour parler"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "hi" - Hindi
// 615 million speakers (worldwide #3)
// Twitch negligable (<50 streamers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "शुरु कर रहा है",
"buttons": {
"clear": "रीसेट",
"speak": "बोलना"
},
"by": "द्वारा",
"categoryFiltersLabel": "श्रेणी / भाषा",
"modelSeeMoreLink": "<3>{{userName}}</3> के \"<1>{{modelName}}</1>\" मॉडल के बारे में अधिक विवरण देखें",
"searchHint": "शब्द को खोजें",
"searchLabel": "तलाशी",
"textInputHint": "यहां टेक्स्ट इनपुट करें",
"voicesLabel": "आवाज ({{count}} चुनने के लिए)"
},
"formTitle": "Create TTS",
"heroSection": {
"buttons": {
"cloneVoice": "क्लोन माय वॉयस",
"signUp": "साइन अप करें"
},
"subtitle": "अपने पसंदीदा पात्रों के साथ बातें कहने के लिए <strong>FakeYou</strong> गहरी नकली तकनीक का उपयोग करें",
"title": "लिखे हुए को बोलने में बदलना"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "id" - Indonesian
// 200 million speakers (worldwide #10)
// Twitch #23 (600 concurrent viewers, 97 concurrent streamers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Inisialisasi...",
"buttons": {
"clear": "Jernih",
"speak": "Berbicara"
},
"by": "oleh",
"categoryFiltersLabel": "Kategori / Bahasa",
"modelSeeMoreLink": "Lihat detail selengkapnya tentang model \"<1>{{modelName}}</1>\" oleh <3>{{userName}}</3>",
"searchHint": "Istilah Pencarian",
"searchLabel": "Mencari",
"textInputHint": "Masukan teks",
"voicesLabel": "Suara ({{count}} untuk dipilih)"
},
"formTitle": "Buat TTS",
"heroSection": {
"buttons": {
"cloneVoice": "Klon suara saya",
"signUp": "Daftar"
},
"subtitle": "Gunakan teknologi palsu mendalam <strong>FakeYou</strong> untuk mengatakan hal-hal dengan karakter favorit Anda",
"title": "Teks pidato"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "it" - Italian
// 67 million speakers (worldwide #?)
// Twitch #10 (41k concurrent viewers, 1.2k concurrent viewers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Loading...",
"buttons": {
"clear": "Clear",
"speak": "Speak"
},
"by": "by",
"categoryFiltersLabel": "Category / Langauge",
"modelSeeMoreLink": "See more details about the \"<1>{{modelName}}</1>\" model by <3>{{userName}}</3>",
"searchHint": "Search Term",
"searchLabel": "Search",
"textInputHint": "Textual shenanigans go here...",
"voicesLabel": "Voice ({{count}} to choose from)"
},
"formTitle": "Create TTS",
"heroSection": {
"buttons": {
"cloneVoice": "Clone My Voice!",
"signUp": "Sign Up"
},
"subtitle": "Use <strong>FakeYou</strong> deep fake tech to say stuff with your favorite characters",
"title": "Text to Speech"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "ja" - Japanese
// 128 million speakers (worldwide #13)
// Twitch #7 (87k concurrent viewers, 2.6k concurrent streams)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "初期化中...",
"buttons": {
"clear": "クリア",
"speak": "話す"
},
"by": "の",
"categoryFiltersLabel": "カテゴリ/言語",
"modelSeeMoreLink": "<3>{{userName}}</3>による\"<1>{{modelName}}</1>\"モデルの詳細をご覧ください",
"searchHint": "検索語",
"searchLabel": "探す",
"textInputHint": "ここに言うテキストを入力してください",
"voicesLabel": "音声({{count}}から選択)"
},
"formTitle": "TTSを作成する",
"heroSection": {
"buttons": {
"cloneVoice": "私の声を複製する",
"signUp": "サインアップ"
},
"subtitle": "<strong>FakeYou</ strong>ディープフェイクテックを使用して、お気に入りのキャラクターと話します",
"title": "テキスト読み上げ"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "ko" - Korean
// 75 million speakers (worldwide #?)
// Twitch #8 (144k concurrent viewers, 2.5k concurrent streamers)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "초기화 중",
"buttons": {
"clear": "초기화",
"speak": "말하다"
},
"by": "의",
"categoryFiltersLabel": "카테고리 / 언어",
"modelSeeMoreLink": "<3>{{userName}}</3>의 \"<1>{{modelName}}</1>\" 모델에 대한 자세한 내용 보기",
"searchHint": "검색어",
"searchLabel": "검색",
"textInputHint": "여기에 텍스트를 입력하세요",
"voicesLabel": "음성({{count}}개 중에서 선택)"
},
"formTitle": "TTS 생성",
"heroSection": {
"buttons": {
"cloneVoice": "내 목소리를 복제하십시오!",
"signUp": "가입하기"
},
"subtitle": "<strong>FakeYou</strong> 딥 페이크 기술을 사용하여 좋아하는 캐릭터와 함께 말하세요.",
"title": "텍스트 음성 변환"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "pt" - Portuguese
// 200 million speakers (worldwide #9)
// Twitch #3 (200k concurrent viewers!, 5.5k concurrent streams)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Inicializando",
"buttons": {
"clear": "Redefinir",
"speak": "Falar"
},
"by": "de",
"categoryFiltersLabel": "Categoria / Idioma",
"modelSeeMoreLink": "Veja mais detalhes sobre o modelo \"<1>{{modelName}}</1>\" de <3>{{userName}}</3>",
"searchHint": "Termo de pesquisa",
"searchLabel": "Procurar",
"textInputHint": "Insira o texto aqui",
"voicesLabel": "Voz ({{count}} para escolher)"
},
"formTitle": "Criar TTS",
"heroSection": {
"buttons": {
"cloneVoice": "Clonar minha voz!",
"signUp": "Inscrever-se"
},
"subtitle": "Use a tecnologia deepfake do <strong>FakeYou</strong> para dizer coisas com seus personagens favoritos",
"title": "Texto para fala"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "vi" - Vietnamese
// 87 million speakers (worldwide #21)
// Twitch #30 (a paltry 170 concurrent viewers, 70 concurrent channels)
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "Khởi tạo...",
"buttons": {
"clear": "Cài lại",
"speak": "Nói"
},
"by": "của",
"categoryFiltersLabel": "Thể loại / Ngôn ngữ",
"modelSeeMoreLink": "Xem thêm chi tiết về mô hình \"<1>{{modelName}}</1>\" của <3>{{userName}}</3>",
"searchHint": "Cụm từ tìm kiếm",
"searchLabel": "Tìm kiếm",
"textInputHint": "Nhập văn bản tại đây",
"voicesLabel": "Giọng nói ({{count}} để chọn)"
},
"formTitle": "Tạo TTS",
"heroSection": {
"buttons": {
"cloneVoice": "Sao chép giọng nói của tôi!",
"signUp": "Đăng ký"
},
"subtitle": "Sử dụng công nghệ giả sâu <strong>FakeYou</strong> để nói nội dung với các nhân vật yêu thích của bạn",
"title": "Chuyển văn bản thành giọng nói"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -0,0 +1,8 @@
// "zh" - Mandarin
// 1.1 billion speakers (worldwide #2)
// Twitch #9
import * as tts from './tts';
export {
tts,
}
@@ -0,0 +1,25 @@
{
"form": {
"asyncLoadingDropdownValue": "初始化",
"buttons": {
"clear": "重置",
"speak": "说话"
},
"by": "的",
"categoryFiltersLabel": "类别/语言",
"modelSeeMoreLink": "查看 <3>{{userName}}</3> 的\"<1>{{modelName}}</1>\"模型的更多详细信息",
"searchHint": "搜索词",
"searchLabel": "搜索",
"textInputHint": "此处输入文字",
"voicesLabel": "语音({{count}} 个可供选择)"
},
"formTitle": "创建 TTS",
"heroSection": {
"buttons": {
"cloneVoice": "克隆我的声音!",
"signUp": "注册"
},
"subtitle": "使用 <strong>FakeYou</strong> 深度伪造技术与您最喜欢的角色说话",
"title": "文字转语音"
}
}
@@ -0,0 +1,5 @@
import * as TtsModelListPage from './TtsModelListPage.json';
export {
TtsModelListPage,
}
@@ -5,7 +5,6 @@ import Autocomplete from "react-autocomplete";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import { t } from "i18next";
import { USE_REFRESH } from "../../../../Refresh";
// NB: This probably is not the best autocomplete library in the world
// A lot of the libraries are really old and depend on jQuery (gross).
@@ -34,91 +33,10 @@ export function AutocompleteSearch(props: Props) {
// It would be nice if the library stopped searching.
const maxMenuItems = 15;
if (!USE_REFRESH) {
return (
<>
<div className="field">
<strong style={{ display: "block", margin: "7px 0 0 0" }}>
{t("ttsListPage.search")}
</strong>
<div className="control has-icons-left">
{/* NB: See note above about this library. */}
<Autocomplete
getItemValue={(item: TtsModelListItem) => item.title}
items={props.allTtsModels}
renderInput={(props) => (
<input
className="input"
type="text"
placeholder={t("ttsListPage.searchTerm")}
value={searchValue}
{...props}
/>
)}
renderMenu={(children) => (
<div className="menu">{children.slice(0, maxMenuItems)}</div>
)}
renderItem={(item: TtsModelListItem, isHighlighted: boolean) => (
<div
style={{ background: isHighlighted ? "lightgray" : "white" }}
>
{item.title}
</div>
)}
value={searchValue}
onChange={(e: any) => setSearchValue(e.target.value)}
onSelect={(val: string, item: TtsModelListItem) => {
// Nothing selected.
props.setSelectedCategories([]);
// And the dropdowns themselves
// Shouldn't need to rebuild this...
let newDropdownCategories = props.dropdownCategories.slice(
0,
1
);
if (newDropdownCategories.length === 0) {
// ...but just in case.
newDropdownCategories.push(
props.allTtsCategories.filter((category) => {
return category.maybe_super_category_token === undefined;
})
);
}
props.setDropdownCategories(newDropdownCategories);
props.setMaybeSelectedTtsModel(item);
}}
shouldItemRender={(item: TtsModelListItem, value) => {
// TODO: A trie would be so much better. Ugh, this is so bad.
let test = value.toLocaleLowerCase().trim();
if (test.length === 0) {
return false;
}
return item.title.toLocaleLowerCase().includes(test);
}}
inputProps={{
id: "states-autocomplete",
}}
wrapperStyle={{
position: "relative",
display: "inline-block",
}}
/>
<span className="icon is-small is-left">
<FontAwesomeIcon icon={faSearch} />
</span>
</div>
</div>
</>
);
}
return (
<>
<div>
<label className="sub-title">{t("ttsListPage.search")}</label>
<label className="sub-title">{t("tts.TtsModelListPage.form.searchLabel")}</label>
<div className="form-group input-icon">
{/* NB: See note above about this library. */}
<span className="form-control-feedback">
@@ -132,7 +50,7 @@ export function AutocompleteSearch(props: Props) {
<input
className="form-control"
type="text"
placeholder={t("ttsListPage.searchTerm")}
placeholder={t("tts.TtsModelListPage.form.searchHint")}
value={searchValue}
{...props}
/>
@@ -2,7 +2,6 @@ import React, { useEffect } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faCaretRight,
faHeadphonesAlt,
faMicrophone,
faTags,
faTimes,
@@ -10,7 +9,6 @@ import {
import { TtsModelListItem } from "@storyteller/components/src/api/tts/ListTtsModels";
import { TtsCategoryType } from "../../../../AppWrapper";
import { Trans, useTranslation } from "react-i18next";
import { USE_REFRESH } from "../../../../Refresh";
interface Props {
allTtsCategories: TtsCategoryType[];
@@ -196,14 +194,8 @@ export function MultiDropdownSearch(props: Props) {
break;
}
let selectCssClasses = "category-dropdown"; // NB: 'category-dropdown' is important for function.
let xButtonCssClasses = "button is-rounded is-outlined";
if (USE_REFRESH) {
// Redesign and Bootstrap CSS
selectCssClasses = "category-dropdown form-select"; // NB: 'category-dropdown' is important for function.
xButtonCssClasses = "btn btn-destructive btn-inform";
}
const selectCssClasses = "category-dropdown form-select"; // NB: 'category-dropdown' is important for function.
const xButtonCssClasses = "btn btn-destructive btn-inform";
categoryDropdowns.push(
<React.Fragment key={`categoryDropdown-${i}`}>
@@ -286,69 +278,27 @@ export function MultiDropdownSearch(props: Props) {
const voiceCount = leafiestCategoryModels.size;
let selectClasses = USE_REFRESH ? "form-select" : "select is-normal";
let selectClasses = "form-select";
let loadingOption = undefined;
if (isLoading) {
selectClasses = USE_REFRESH ? "form-select" : "select is-normal is-loading";
selectClasses = "form-select";
loadingOption = (
<option key="waiting" value="" disabled={true}>
{t("ttsListPage.loading")}
{t("tts.TtsModelListPage.form.asyncLoadingDropdownValue")}
</option>
);
}
if (!USE_REFRESH) {
return (
<div>
{/* Category Dropdowns */}
<strong>{t("ttsListPage.categoryFilters")}</strong>
<br />
{categoryFieldGroups}
{/* Model Dropdown */}
<strong>
<Trans i18nKey="ttsListPage.voiceCount" count={voiceCount}>
Voice ({voiceCount} to choose from)
</Trans>
</strong>
<br />
<div className="control has-icons-left">
<div className={selectClasses}>
<select
name="tts-model-select"
onChange={handleChangeVoice}
disabled={isLoading}
>
{isLoading
? loadingOption
: Array.from(leafiestCategoryModels).map((model) => {
return (
<option key={model.model_token} value={model.model_token}>
{model.title} ({t("ttsListPage.by")}{" "}
{model.creator_display_name})
</option>
);
})}
</select>
</div>
<span className="icon is-small is-left">
<FontAwesomeIcon icon={faHeadphonesAlt} />
</span>
</div>
</div>
);
}
return (
<div>
{/* Category Dropdowns */}
<label className="sub-title">{t("ttsListPage.categoryFilters")}</label>
<label className="sub-title">{t("tts.TtsModelListPage.form.categoryFiltersLabel")}</label>
{categoryFieldGroups}
{/* Model Dropdown */}
<label className="sub-title">
<Trans i18nKey="ttsListPage.voiceCount" count={voiceCount}>
<Trans i18nKey="tts.TtsModelListPage.form.voicesLabel" count={voiceCount}>
Voice ({voiceCount} to choose from)
</Trans>
</label>
@@ -367,7 +317,7 @@ export function MultiDropdownSearch(props: Props) {
: Array.from(leafiestCategoryModels).map((model) => {
return (
<option key={model.model_token} value={model.model_token}>
{model.title} ({t("ttsListPage.by")}{" "}
{model.title} ({t("tts.TtsModelListPage.form.by")}{" "}
{model.creator_display_name})
</option>
);
@@ -43,7 +43,6 @@ import {
faVolumeHigh,
faVolumeUp,
} from "@fortawesome/free-solid-svg-icons";
import { USE_REFRESH } from "../../../../Refresh";
const Fade = require("react-reveal/Fade");
@@ -213,11 +212,12 @@ function TtsModelListPage(props: Props) {
let userName = props.maybeSelectedTtsModel.creator_display_name;
directViewLink = (
<Link to={modelLink} className="py-2">
<Trans i18nKey="ttsListPage.seeModelDetails">
<Trans i18nKey="tts.TtsModelListPage.form.modelSeeMoreLink">
See more details about the "
<span className="fw-semibold">{{ modelName }}</span>" model by&nbsp;
<span className="fw-semibold">{{ userName }}</span>&nbsp;
</Trans>
{" "}
<Gravatar
size={15}
username={props.maybeSelectedTtsModel.creator_display_name}
@@ -288,137 +288,6 @@ function TtsModelListPage(props: Props) {
}
}
if (!USE_REFRESH) {
return (
<div>
<section className="hero is-small">
<div className="hero-body">
<div className="columns is-vcentered">
<div className="column is-one-third">
<div className="mascot">
<img
src="/mascot/kitsune_pose2_black_2000.webp"
alt="FakeYou's mascot!"
/>
</div>
</div>
<div className="column">
<p className="title">{t("pages.ttsList.heroTitle")}</p>
<p className="subtitle">
<Trans i18nKey="pages.ttsList.heroSubtitle">
Use <strong>FakeYou</strong> deep fake tech to say stuff
with your favorite characters.
</Trans>
</p>
</div>
</div>
</div>
</section>
<br />
{pleaseFollowNotice}
{languageNotice}
{vocodesNotice}
{twitchTtsNotice}
<form onSubmit={handleFormSubmit} className="main-form">
<MultiDropdownSearch
allTtsCategories={props.allTtsCategories}
allTtsModels={props.ttsModels}
allTtsCategoriesByTokenMap={props.allTtsCategoriesByTokenMap}
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
ttsModelsByCategoryToken={props.ttsModelsByCategoryToken}
dropdownCategories={props.dropdownCategories}
setDropdownCategories={props.setDropdownCategories}
selectedCategories={props.selectedCategories}
setSelectedCategories={props.setSelectedCategories}
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
/>
<AutocompleteSearch
allTtsCategories={props.allTtsCategories}
allTtsModels={props.ttsModels}
allTtsModelsByTokenMap={props.allTtsModelsByTokenMap}
dropdownCategories={props.dropdownCategories}
setDropdownCategories={props.setDropdownCategories}
selectedCategories={props.selectedCategories}
setSelectedCategories={props.setSelectedCategories}
maybeSelectedTtsModel={props.maybeSelectedTtsModel}
setMaybeSelectedTtsModel={props.setMaybeSelectedTtsModel}
/>
<div>{directViewLink}</div>
<div className="field">
<div className="control">
<textarea
onChange={handleChangeText}
className="textarea is-large"
value={props.textBuffer}
placeholder={t("pages.ttsList.placeholderTextGoesHere")}
></textarea>
</div>
</div>
{maybeError}
<div className="button-group">
<div className="columns is-mobile">
<div className="column has-text-centered">
<button
className="button is-danger is-large"
disabled={remainingCharactersButtonDisabled}
>
{t("pages.ttsList.buttonSpeak")}
</button>
</div>
<div className="column has-text-centered">
<button
className="button is-danger is-light is-large"
onClick={handleClearClick}
>
{t("pages.ttsList.buttonClear")}
</button>
</div>
</div>
</div>
</form>
<br />
<br />
<SessionTtsInferenceResultListFc
ttsInferenceJobs={props.ttsInferenceJobs}
/>
<br />
<br />
<SessionW2lInferenceResultListFc
w2lInferenceJobs={props.w2lInferenceJobs}
/>
<br />
<br />
<SessionW2lTemplateUploadResultListFc
w2lTemplateUploadJobs={props.w2lTemplateUploadJobs}
/>
<br />
<br />
<SessionTtsModelUploadResultListFc
modelUploadJobs={props.ttsModelUploadJobs}
/>
</div>
);
}
let signUpButton = <></>;
if (!props.sessionWrapper.isLoggedIn()) {
@@ -426,7 +295,7 @@ function TtsModelListPage(props: Props) {
<>
<Link to="/signup">
<button type="button" className="btn btn-primary w-100">
Sign Up
{t("tts.TtsModelListPage.heroSection.buttons.signUp")}
</button>
</Link>
</>
@@ -461,10 +330,10 @@ function TtsModelListPage(props: Props) {
<Fade bottom cascade duration={duration} distance={distance}>
<div>
<h1 className="display-3 fw-bold lh-1 mb-3 text-center text-lg-start">
Text to Speech
{t('tts.TtsModelListPage.heroSection.title')}
</h1>
<p className="lead mb-5 text-center text-lg-start pe-xl-2">
<Trans i18nKey="pages.ttsList.heroSubtitle">
<Trans i18nKey="tts.TtsModelListPage.heroSection.subtitle">
Use FakeYou's deepfake tech to say stuff with your favorite
characters.
</Trans>
@@ -477,7 +346,7 @@ function TtsModelListPage(props: Props) {
{signUpButton}
<Link to="/clone">
<button type="button" className="btn btn-secondary w-100">
Clone My Voice!
{t("tts.TtsModelListPage.heroSection.buttons.cloneVoice")}
</button>
</Link>
</div>
@@ -492,7 +361,7 @@ function TtsModelListPage(props: Props) {
<i className="fas fa-volume-high"></i>
<h1 className="panel-title fw-bold">
<FontAwesomeIcon icon={faVolumeUp} className="me-3" />
Create TTS
{t("tts.TtsModelListPage.formTitle")}
</h1>
<div className="py-6">
<div className="d-flex gap-4">
@@ -536,7 +405,7 @@ function TtsModelListPage(props: Props) {
className="form-control fs-5"
style={{ minHeight: "200px" }}
value={props.textBuffer}
placeholder={t("pages.ttsList.placeholderTextGoesHere")}
placeholder={t("tts.TtsModelListPage.form.textInputHint")}
></textarea>
</div>
@@ -548,14 +417,14 @@ function TtsModelListPage(props: Props) {
disabled={remainingCharactersButtonDisabled}
>
<FontAwesomeIcon icon={faVolumeHigh} className="me-2" />
{t("pages.ttsList.buttonSpeak")}
{t("tts.TtsModelListPage.form.buttons.speak")}
</button>
<button
className="btn btn-destructive w-100"
onClick={handleClearClick}
>
<FontAwesomeIcon icon={faTrash} className="me-2" />
{t("pages.ttsList.buttonClear")}
{t("tts.TtsModelListPage.form.buttons.clear")}
</button>
</div>
</form>