aboutsummaryrefslogtreecommitdiffstats
path: root/packages/main/src/i18n
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-03-30 21:47:45 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-05-16 00:54:57 +0200
commit85d91c64b5b3ec31df8acecd68a1fa6a68d57ff9 (patch)
tree277ab45a66a1c74e2d0a885c8a354aea27128d12 /packages/main/src/i18n
parentfeat(main): Translation hot reloading during development (diff)
downloadsophie-85d91c64b5b3ec31df8acecd68a1fa6a68d57ff9.tar.gz
sophie-85d91c64b5b3ec31df8acecd68a1fa6a68d57ff9.tar.zst
sophie-85d91c64b5b3ec31df8acecd68a1fa6a68d57ff9.zip
feat(renderer): Renderer translations
Add react-i18n to make us able to use i18next translations in the renderer process just like we do in the main process. Translations are hot-reloaded automatically. Signed-off-by: Kristóf Marussy <kristof@marussy.com>
Diffstat (limited to 'packages/main/src/i18n')
-rw-r--r--packages/main/src/i18n/I18nStore.ts31
-rw-r--r--packages/main/src/i18n/loadLocalization.ts23
2 files changed, 50 insertions, 4 deletions
diff --git a/packages/main/src/i18n/I18nStore.ts b/packages/main/src/i18n/I18nStore.ts
index c364f0e..54c3d20 100644
--- a/packages/main/src/i18n/I18nStore.ts
+++ b/packages/main/src/i18n/I18nStore.ts
@@ -18,7 +18,7 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import type { i18n, TFunction } from 'i18next'; 21import type { i18n, ResourceKey, TFunction } from 'i18next';
22import { IAtom, createAtom } from 'mobx'; 22import { IAtom, createAtom } from 'mobx';
23 23
24import { getLogger } from '../utils/log'; 24import { getLogger } from '../utils/log';
@@ -113,4 +113,33 @@ export default class I18nStore {
113 }); 113 });
114 log.debug('Reloaded translations'); 114 log.debug('Reloaded translations');
115 } 115 }
116
117 async getTranslation(
118 language: string,
119 namespace: string,
120 ): Promise<ResourceKey> {
121 if (!this.i18next.hasResourceBundle(language, namespace)) {
122 await this.i18next.loadLanguages([language]);
123 await this.i18next.loadNamespaces([namespace]);
124 }
125 const bundle = this.i18next.getResourceBundle(
126 language,
127 namespace,
128 ) as unknown;
129 if (typeof bundle !== 'object' || bundle === null) {
130 throw new Error(
131 `Failed to load ${namespace} resource bundle for language ${language}`,
132 );
133 }
134 return bundle as ResourceKey;
135 }
136
137 addMissingTranslation(
138 languages: string[],
139 namespace: string,
140 key: string,
141 value: string,
142 ): void {
143 this.i18next.modules.backend?.create?.(languages, namespace, key, value);
144 }
116} 145}
diff --git a/packages/main/src/i18n/loadLocalization.ts b/packages/main/src/i18n/loadLocalization.ts
index 1408a30..ec3cf84 100644
--- a/packages/main/src/i18n/loadLocalization.ts
+++ b/packages/main/src/i18n/loadLocalization.ts
@@ -18,16 +18,22 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { fallbackLng } from '@sophie/shared';
21import i18next from 'i18next'; 22import i18next from 'i18next';
23import { autorun } from 'mobx';
24import { addDisposer } from 'mobx-state-tree';
22 25
23import type Resources from '../infrastructure/resources/Resources'; 26import type Resources from '../infrastructure/resources/Resources';
24import type MainStore from '../stores/MainStore'; 27import type MainStore from '../stores/MainStore';
28import { getLogger } from '../utils/log';
25 29
26import I18nStore from './I18nStore'; 30import I18nStore from './I18nStore';
27import RepositoryBasedI18nBackend from './RepositoryBasedI18nBackend'; 31import RepositoryBasedI18nBackend from './RepositoryBasedI18nBackend';
28import i18nLog from './i18nLog'; 32import i18nLog from './i18nLog';
29import LocalizationFiles from './impl/LocaltizationFiles'; 33import LocalizationFiles from './impl/LocaltizationFiles';
30 34
35const log = getLogger('loadLocationzation');
36
31export default async function loadLocalization( 37export default async function loadLocalization(
32 store: MainStore, 38 store: MainStore,
33 resources: Resources, 39 resources: Resources,
@@ -37,14 +43,25 @@ export default async function loadLocalization(
37 const backend = new RepositoryBasedI18nBackend(repository, devMode); 43 const backend = new RepositoryBasedI18nBackend(repository, devMode);
38 const i18n = i18next 44 const i18n = i18next
39 .createInstance({ 45 .createInstance({
40 lng: 'en', 46 lng: store.shared.language,
41 fallbackLng: ['en'], 47 fallbackLng,
42 debug: devMode, 48 debug: devMode,
43 saveMissing: devMode, 49 saveMissing: devMode,
44 }) 50 })
45 .use(backend) 51 .use(backend)
46 .use(i18nLog); 52 .use(i18nLog);
47 await i18n.init();
48 const i18nStore = new I18nStore(i18n); 53 const i18nStore = new I18nStore(i18n);
49 store.setI18n(i18nStore); 54 store.setI18n(i18nStore);
55 await i18n.init();
56 const disposeChangeLanguage = autorun(() => {
57 const {
58 shared: { language },
59 } = store;
60 if (i18n.language !== language) {
61 i18n.changeLanguage(language).catch((error) => {
62 log.error('Failed to change language', error);
63 });
64 }
65 });
66 addDisposer(store, disposeChangeLanguage);
50} 67}