mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
Merge pull request #71 from storytold/echelon-i18n-refresh
Echelon i18n refresh
This commit is contained in:
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 }
|
||||
@@ -6,6 +6,11 @@ enum Language {
|
||||
Indonesian,
|
||||
German,
|
||||
Japanese,
|
||||
French,
|
||||
Vietnamese,
|
||||
Hindi,
|
||||
ChineseSimplified,
|
||||
Korean,
|
||||
}
|
||||
|
||||
export { Language }
|
||||
@@ -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} = 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
|
||||
<span className="fw-semibold">{{ userName }}</span>
|
||||
</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>
|
||||
|
||||
Reference in new issue
Block a user