···1-{
2- "app-version": "Streamplace v{ $version }",
3- "download-new-update": "Télécharger la nouvelle mise à jour",
4- "check-for-updates": "Vérifier les mises à jour",
5- "bundled-runtype": "Inclus",
6- "ota-runtype": "Over-the-Air (OTA)",
7- "recovery-runtype": "Mode de récupération",
8- "modal-latest-version": "Vous utilisez la dernière version.",
9- "modal-no-update-available": "Vous avez la dernière version de Streamplace, génial !",
10- "modal-update-available-title": "Mise à jour disponible",
11- "modal-update-available-description": "Une nouvelle version de Streamplace est prête à télécharger",
12- "modal-update-failed": "La vérification des mises à jour a échoué. Vous devrez peut-être mettre à jour l'application via { $store }.",
13- "modal-update-failed-title": "Échec de la mise à jour",
14- "modal-update-failed-description": "La vérification des mises à jour a échoué. Vous devrez peut-être mettre à jour l'application via { $store }.",
15- "button-reload-app-on-update": "Appliquer la mise à jour (l'application va se recharger)",
16- "use-custom-node": "Utiliser un nœud personnalisé",
17- "default-url": "Par défaut : { $url }",
18- "enter-custom-node-url": "Saisir l'URL du nœud personnalisé",
19- "save-button": "ENREGISTRER",
20- "language-selection": "Langue",
21- "language-selection-description": "Choisissez votre langue préférée",
22- "debug-recording-title": "Autoriser { $host } à enregistrer votre diffusion en direct pour le débogage et l'amélioration du service",
23- "debug-recording-description": "Optionnel",
24- "input-search-languages": "Rechercher des langues...",
25- "manage-keys": "Gérer les clés",
26- "settings-title": "Paramètres",
27- "loading": "Chargement...",
28- "error": "Erreur",
29- "cancel": "Annuler",
30- "confirm": "Confirmer",
31- "welcome-user": "Bienvenue, { $username } !",
32- "notification-count": "{ $count ->\n [0] Aucune notification\n [1] Une notification\n *[other] { $count } notifications\n}",
33- "search-placeholder": "Rechercher...",
34- "message-input": "Saisissez votre message...",
35- "success": "Succès",
36- "warning": "Attention",
37- "info": "Information",
38- "close": "Fermer",
39- "open": "Ouvrir",
40- "delete": "Supprimer",
41- "edit": "Modifier",
42- "create": "Créer",
43- "update": "Mettre à jour",
44- "refresh": "Actualiser",
45- "save": "Enregistrer",
46- "cancel-button": "Annuler",
47- "ok": "OK",
48- "yes": "Oui",
49- "no": "Non",
50- "continue": "Continuer",
51- "back": "Retour",
52- "next": "Suivant",
53- "finish": "Terminer"
54-}
···1{
2 "app-version": "Streamplace v{ $version }",
00000000000003 "use-custom-node": "Use Custom Node",
4 "default-url": "Default: { $url }",
5 "enter-custom-node-url": "Enter custom node URL",
6 "save-button": "SAVE",
7 "language-selection": "Language",
8 "language-selection-description": "Choose your preferred language",
09 "debug-recording-title": "Allow { $host } to record your livestream for debugging and improving the service",
10 "debug-recording-description": "Optional",
11 "manage-keys": "Manage Keys",
···1{
2 "app-version": "Streamplace v{ $version }",
3+ "download-new-update": "Download New Update",
4+ "check-for-updates": "Check for Updates",
5+ "bundled-runtype": "Bundled",
6+ "ota-runtype": "Over-the-Air (OTA)",
7+ "recovery-runtype": "Recovery Mode",
8+ "modal-latest-version": "You are using the latest version.",
9+ "modal-no-update-available": "You are on the latest version of Streamplace, hooray!",
10+ "modal-update-available-title": "Update Available",
11+ "modal-update-available-description": "A new version of Streamplace is ready to download",
12+ "modal-update-failed": "Update check failed. You may need to update the app through the { $store }.",
13+ "modal-update-failed-title": "Update Failed",
14+ "modal-update-failed-description": "Update check failed. You may need to update the app through the { $store }.",
15+ "button-reload-app-on-update": "Apply Update (will reload app)",
16 "use-custom-node": "Use Custom Node",
17 "default-url": "Default: { $url }",
18 "enter-custom-node-url": "Enter custom node URL",
19 "save-button": "SAVE",
20 "language-selection": "Language",
21 "language-selection-description": "Choose your preferred language",
22+ "input-search-languages": "Search languages...",
23 "debug-recording-title": "Allow { $host } to record your livestream for debugging and improving the service",
24 "debug-recording-description": "Optional",
25 "manage-keys": "Manage Keys",
+14
js/components/public/locales/es-ES/messages.json
···1{
2 "app-version": "Streamplace v{ $version }",
00000000000003 "use-custom-node": "Usar Nodo Personalizado",
4 "default-url": "Predeterminado: { $url }",
5 "enter-custom-node-url": "Introduce la URL del nodo personalizado",
···8 "language-selection-description": "Elige tu idioma preferido",
9 "debug-recording-title": "Permitir que { $host } grabe tu retransmisión en directo para depuración y mejora del servicio",
10 "debug-recording-description": "Opcional",
011 "manage-keys": "Gestionar Claves",
12 "settings-title": "Configuración",
13 "loading": "Cargando...",
···1{
2 "app-version": "Streamplace v{ $version }",
3+ "download-new-update": "Descargar Nueva Actualización",
4+ "check-for-updates": "Buscar Actualizaciones",
5+ "bundled-runtype": "Empaquetado",
6+ "ota-runtype": "Over-the-Air (OTA)",
7+ "recovery-runtype": "Modo de Recuperación",
8+ "modal-latest-version": "Estás usando la última versión.",
9+ "modal-no-update-available": "¡Tienes la versión más reciente de Streamplace, genial!",
10+ "modal-update-available-title": "Actualización Disponible",
11+ "modal-update-available-description": "Una nueva versión de Streamplace está lista para descargar",
12+ "modal-update-failed": "La búsqueda de actualizaciones falló. Es posible que necesites actualizar",
13+ "modal-update-failed-title": "Actualización Falló",
14+ "modal-update-failed-description": "La búsqueda de actualizaciones falló. Es posible que necesites actualizar",
15+ "button-reload-app-on-update": "Aplicar Actualización (recargará la aplicación)",
16 "use-custom-node": "Usar Nodo Personalizado",
17 "default-url": "Predeterminado: { $url }",
18 "enter-custom-node-url": "Introduce la URL del nodo personalizado",
···21 "language-selection-description": "Elige tu idioma preferido",
22 "debug-recording-title": "Permitir que { $host } grabe tu retransmisión en directo para depuración y mejora del servicio",
23 "debug-recording-description": "Opcional",
24+ "input-search-languages": "Buscar idiomas...",
25 "manage-keys": "Gestionar Claves",
26 "settings-title": "Configuración",
27 "loading": "Cargando...",
+14
js/components/public/locales/fr-FR/messages.json
···1{
2 "app-version": "Streamplace v{ $version }",
00000000000003 "use-custom-node": "Utiliser un nœud personnalisé",
4 "default-url": "Par défaut : { $url }",
5 "enter-custom-node-url": "Saisir l'URL du nœud personnalisé",
···8 "language-selection-description": "Choisissez votre langue préférée",
9 "debug-recording-title": "Autoriser { $host } à enregistrer votre diffusion en direct pour le débogage et l'amélioration du service",
10 "debug-recording-description": "Optionnel",
011 "manage-keys": "Gérer les clés",
12 "settings-title": "Paramètres",
13 "loading": "Chargement...",
···1{
2 "app-version": "Streamplace v{ $version }",
3+ "download-new-update": "Télécharger la nouvelle mise à jour",
4+ "check-for-updates": "Vérifier les mises à jour",
5+ "bundled-runtype": "Inclus",
6+ "ota-runtype": "Over-the-Air (OTA)",
7+ "recovery-runtype": "Mode de récupération",
8+ "modal-latest-version": "Vous utilisez la dernière version.",
9+ "modal-no-update-available": "Vous avez la dernière version de Streamplace, génial !",
10+ "modal-update-available-title": "Mise à jour disponible",
11+ "modal-update-available-description": "Une nouvelle version de Streamplace est prête à télécharger",
12+ "modal-update-failed": "La vérification des mises à jour a échoué. Vous devrez peut-être mettre à jour l'application via { $store }.",
13+ "modal-update-failed-title": "Échec de la mise à jour",
14+ "modal-update-failed-description": "La vérification des mises à jour a échoué. Vous devrez peut-être mettre à jour l'application via { $store }.",
15+ "button-reload-app-on-update": "Appliquer la mise à jour (l'application va se recharger)",
16 "use-custom-node": "Utiliser un nœud personnalisé",
17 "default-url": "Par défaut : { $url }",
18 "enter-custom-node-url": "Saisir l'URL du nœud personnalisé",
···21 "language-selection-description": "Choisissez votre langue préférée",
22 "debug-recording-title": "Autoriser { $host } à enregistrer votre diffusion en direct pour le débogage et l'amélioration du service",
23 "debug-recording-description": "Optionnel",
24+ "input-search-languages": "Rechercher des langues...",
25 "manage-keys": "Gérer les clés",
26 "settings-title": "Paramètres",
27 "loading": "Chargement...",
+14
js/components/public/locales/pt-BR/messages.json
···1{
2 "app-version": "Streamplace v{ $version }",
00000000000003 "use-custom-node": "Usar Nó Personalizado",
4 "default-url": "Padrão: { $url }",
5 "enter-custom-node-url": "Digite a URL do nó personalizado",
···8 "language-selection-description": "Escolha seu idioma preferido",
9 "debug-recording-title": "Permitir que { $host } grave sua transmissão ao vivo para depuração e melhoria do serviço",
10 "debug-recording-description": "Opcional",
011 "manage-keys": "Gerenciar Chaves",
12 "settings-title": "Configurações",
13 "loading": "Carregando...",
···1{
2 "app-version": "Streamplace v{ $version }",
3+ "download-new-update": "Baixar Nova Atualização",
4+ "check-for-updates": "Verificar Atualizações",
5+ "bundled-runtype": "Empacotado",
6+ "ota-runtype": "Over-the-Air (OTA)",
7+ "recovery-runtype": "Modo de Recuperação",
8+ "modal-latest-version": "Você está usando a versão mais recente.",
9+ "modal-no-update-available": "Você está na versão mais recente do Streamplace, eba!",
10+ "modal-update-available-title": "Atualização Disponível",
11+ "modal-update-available-description": "Uma nova versão do Streamplace está pronta para download",
12+ "modal-update-failed": "A verificação de atualizações falhou. Você pode precisar atualizar o aplicativo através do { $store }.",
13+ "modal-update-failed-title": "Atualização Falhou",
14+ "modal-update-failed-description": "A verificação de atualizações falhou. Você pode precisar atualizar o aplicativo através do { $store }.",
15+ "button-reload-app-on-update": "Aplicar Atualização (o aplicativo será recarregado)",
16 "use-custom-node": "Usar Nó Personalizado",
17 "default-url": "Padrão: { $url }",
18 "enter-custom-node-url": "Digite a URL do nó personalizado",
···21 "language-selection-description": "Escolha seu idioma preferido",
22 "debug-recording-title": "Permitir que { $host } grave sua transmissão ao vivo para depuração e melhoria do serviço",
23 "debug-recording-description": "Opcional",
24+ "input-search-languages": "Pesquisar idiomas...",
25 "manage-keys": "Gerenciar Chaves",
26 "settings-title": "Configurações",
27 "loading": "Carregando...",
···92<p>{t('message-count', { count: 5 })}</p>
93```
9495-2. Add English translations (or others if you're proficient) to the .ftl files.
09697-````fluent
98-# Edit: js/app/src/i18n/locales/data/en-US/settings.ftl
99settings-title = Settings
100message-count = { $count ->
101 [0] No messages
102 [1] You have one new message
103 *[other] You have { $count } new messages
104-}```
000105106-3. Compile and build the i18n files:
107```bash
108-pnpm run i18n:build
109-````
0000000000000000000000000000000000000000000000000000110111## Keep in mind...
112···122button-save-changes = Save Changes
123form-validation-email-invalid = Please enter a valid email address
124```
000000000000000000000000000000000000000000000000125126You can also
127[view the official Fluent docs](https://github.com/projectfluent/fluent/wiki/Good-Practices-for-Developers)
···92<p>{t('message-count', { count: 5 })}</p>
93```
9495+2. Add English translations (or others if you're proficient) to the .ftl files
96+ in `js/components/locales/`:
9798+```fluent
99+# Edit: js/components/locales/en-US/settings.ftl
100settings-title = Settings
101message-count = { $count ->
102 [0] No messages
103 [1] You have one new message
104 *[other] You have { $count } new messages
105+}
106+```
107+108+3. Compile the translations to JSON:
1090110```bash
111+cd js/components
112+pnpm i18n:compile
113+```
114+115+This reads the `.ftl` files and outputs compiled JSON to
116+`js/components/public/locales/{locale}/messages.json`.
117+118+4. For web: the compiled files in `public/locales/` are served as static assets
119+ and loaded on demand.
120+121+For native: the compiled files are bundled with the app via static `require()`
122+calls in the components package.
123+124+## Project structure
125+126+The i18n system is centralized in `@streamplace/components`:
127+128+```
129+js/components/
130+├── locales/ # Source .ftl files
131+│ ├── en-US/
132+│ │ └── settings.ftl
133+│ ├── pt-BR/
134+│ ├── es-ES/
135+│ ├── zh-TW/
136+│ └── fr-FR/
137+├── src/i18n/
138+│ ├── manifest.json # Supported locales and metadata
139+│ ├── i18next-config.ts # Bootstrap configuration
140+│ ├── provider.tsx # React provider components
141+│ └── index.ts # Public exports
142+├── public/locales/ # Compiled JSON output
143+│ ├── en-US/
144+│ │ └── messages.json
145+│ └── ...
146+└── scripts/
147+ ├── compile-translations.js
148+ └── extract-i18n.js
149+```
150+151+The app imports i18n from `@streamplace/components`:
152+153+```ts
154+import { i18next, useTranslation } from "@streamplace/components";
155+```
156+157+## Available scripts
158+159+In `js/components`:
160+161+- `pnpm i18n:compile` - Compile .ftl files to JSON
162+- `pnpm i18n:watch` - Watch .ftl files and recompile on changes
163+- `pnpm i18n:extract` - Extract translation keys from source code (TODO: needs
164+ path updates)
165166## Keep in mind...
167···177button-save-changes = Save Changes
178form-validation-email-invalid = Please enter a valid email address
179```
180+181+### Platform differences
182+183+The system handles both web and React Native:
184+185+- **Web**: loads translations via HTTP from `/locales/{locale}/messages.json`
186+- **React Native**: bundles translations via static `require()` calls
187+188+The bootstrap code in `@streamplace/components/i18n` automatically detects the
189+platform and uses the appropriate loading strategy.
190+191+### Adding new locales
192+193+1. Add the locale to `js/components/src/i18n/manifest.json`:
194+195+```json
196+{
197+ "supportedLocales": [
198+ "en-US",
199+ "pt-BR",
200+ "es-ES",
201+ "zh-TW",
202+ "fr-FR",
203+ "new-LOCALE"
204+ ],
205+ "languages": {
206+ "new-LOCALE": {
207+ "code": "new-LOCALE",
208+ "name": "Language Name",
209+ "nativeName": "Native Name",
210+ "flag": "🏁"
211+ }
212+ }
213+}
214+```
215+216+2. Create the locale directory and .ftl files in
217+ `js/components/locales/new-LOCALE/`
218+219+3. Add a static `require()` case in `js/components/src/i18n/i18next-config.ts`:
220+221+```ts
222+case "new-LOCALE":
223+ translations = require("../../public/locales/new-LOCALE/messages.json");
224+ break;
225+```
226+227+4. Run `pnpm i18n:compile` to generate the JSON file
228229You can also
230[view the official Fluent docs](https://github.com/projectfluent/fluent/wiki/Good-Practices-for-Developers)