diff options
Diffstat (limited to 'packages/main/src/i18n')
-rw-r--r-- | packages/main/src/i18n/loadLocalization.ts | 11 | ||||
-rw-r--r-- | packages/main/src/i18n/synchronizeLocalizationSettings.ts | 99 |
2 files changed, 104 insertions, 6 deletions
diff --git a/packages/main/src/i18n/loadLocalization.ts b/packages/main/src/i18n/loadLocalization.ts index ec3cf84..0413373 100644 --- a/packages/main/src/i18n/loadLocalization.ts +++ b/packages/main/src/i18n/loadLocalization.ts | |||
@@ -18,33 +18,31 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { fallbackLng } from '@sophie/shared'; | 21 | import { FALLBACK_LOCALE } from '@sophie/shared'; |
22 | import i18next from 'i18next'; | 22 | import i18next from 'i18next'; |
23 | import { autorun } from 'mobx'; | 23 | import { autorun } from 'mobx'; |
24 | import { addDisposer } from 'mobx-state-tree'; | 24 | import { addDisposer } from 'mobx-state-tree'; |
25 | 25 | ||
26 | import type Resources from '../infrastructure/resources/Resources'; | ||
27 | import type MainStore from '../stores/MainStore'; | 26 | import type MainStore from '../stores/MainStore'; |
28 | import { getLogger } from '../utils/log'; | 27 | import { getLogger } from '../utils/log'; |
29 | 28 | ||
30 | import I18nStore from './I18nStore'; | 29 | import I18nStore from './I18nStore'; |
30 | import LocatlizationRepository from './LocalizationRepository'; | ||
31 | import RepositoryBasedI18nBackend from './RepositoryBasedI18nBackend'; | 31 | import RepositoryBasedI18nBackend from './RepositoryBasedI18nBackend'; |
32 | import i18nLog from './i18nLog'; | 32 | import i18nLog from './i18nLog'; |
33 | import LocalizationFiles from './impl/LocaltizationFiles'; | ||
34 | 33 | ||
35 | const log = getLogger('loadLocationzation'); | 34 | const log = getLogger('loadLocationzation'); |
36 | 35 | ||
37 | export default async function loadLocalization( | 36 | export default async function loadLocalization( |
38 | store: MainStore, | 37 | store: MainStore, |
39 | resources: Resources, | 38 | repository: LocatlizationRepository, |
40 | devMode: boolean, | 39 | devMode: boolean, |
41 | ): Promise<void> { | 40 | ): Promise<void> { |
42 | const repository = new LocalizationFiles(resources); | ||
43 | const backend = new RepositoryBasedI18nBackend(repository, devMode); | 41 | const backend = new RepositoryBasedI18nBackend(repository, devMode); |
44 | const i18n = i18next | 42 | const i18n = i18next |
45 | .createInstance({ | 43 | .createInstance({ |
46 | lng: store.shared.language, | 44 | lng: store.shared.language, |
47 | fallbackLng, | 45 | fallbackLng: [FALLBACK_LOCALE], |
48 | debug: devMode, | 46 | debug: devMode, |
49 | saveMissing: devMode, | 47 | saveMissing: devMode, |
50 | }) | 48 | }) |
@@ -58,6 +56,7 @@ export default async function loadLocalization( | |||
58 | shared: { language }, | 56 | shared: { language }, |
59 | } = store; | 57 | } = store; |
60 | if (i18n.language !== language) { | 58 | if (i18n.language !== language) { |
59 | log.debug('Setting language', language); | ||
61 | i18n.changeLanguage(language).catch((error) => { | 60 | i18n.changeLanguage(language).catch((error) => { |
62 | log.error('Failed to change language', error); | 61 | log.error('Failed to change language', error); |
63 | }); | 62 | }); |
diff --git a/packages/main/src/i18n/synchronizeLocalizationSettings.ts b/packages/main/src/i18n/synchronizeLocalizationSettings.ts new file mode 100644 index 0000000..971a593 --- /dev/null +++ b/packages/main/src/i18n/synchronizeLocalizationSettings.ts | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com> | ||
3 | * | ||
4 | * This file is part of Sophie. | ||
5 | * | ||
6 | * Sophie is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Affero General Public License as | ||
8 | * published by the Free Software Foundation, version 3. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Affero General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Affero General Public License | ||
16 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | * SPDX-License-Identifier: AGPL-3.0-only | ||
19 | */ | ||
20 | |||
21 | import { FALLBACK_LOCALE, SYSTEM_LOCALE } from '@sophie/shared'; | ||
22 | import { reaction } from 'mobx'; | ||
23 | import { addDisposer } from 'mobx-state-tree'; | ||
24 | |||
25 | import type MainStore from '../stores/MainStore'; | ||
26 | import { getLogger } from '../utils/log'; | ||
27 | |||
28 | const log = getLogger('synchronizeLocalizationSettings'); | ||
29 | |||
30 | export const TEST_LOCALE = 'cimode'; | ||
31 | |||
32 | /** | ||
33 | * Finds the closes requested supported language for the requested one. | ||
34 | * | ||
35 | * If `language` is supported, this function will return it. | ||
36 | * Otherwise, it returns a supported language with the same ISO639 code but | ||
37 | * no country code (e.g., `en` for `en-GB`) if it exists. | ||
38 | * If no supported language matches, `FALLBACK_LOCALE` will be returned. | ||
39 | * | ||
40 | * @param language The requested language. | ||
41 | * @param supportedLanguages The set of supported languages. | ||
42 | * @returns The language to load. | ||
43 | */ | ||
44 | function getMatchingLocale( | ||
45 | language: string, | ||
46 | supportedLanguages: Set<string>, | ||
47 | ): string { | ||
48 | // Also let the test locale (i.e., show localization keys directly) through. | ||
49 | if (language === TEST_LOCALE || supportedLanguages.has(language)) { | ||
50 | return language; | ||
51 | } | ||
52 | const separatorIndex = language.indexOf('-'); | ||
53 | if (separatorIndex < 0) { | ||
54 | return FALLBACK_LOCALE; | ||
55 | } | ||
56 | const iso639 = language.slice(0, Math.max(0, separatorIndex)); | ||
57 | if (supportedLanguages.has(iso639)) { | ||
58 | return iso639; | ||
59 | } | ||
60 | return FALLBACK_LOCALE; | ||
61 | } | ||
62 | |||
63 | export async function synchronizeLocalizationSettings( | ||
64 | store: MainStore, | ||
65 | supportedLanguages: string[], | ||
66 | osLocale: () => Promise<string>, | ||
67 | ): Promise<void> { | ||
68 | const { settings, shared } = store; | ||
69 | const supportedLangaugesSet = new Set(supportedLanguages); | ||
70 | const setLanguageAsync = async (languageSetting: string) => { | ||
71 | const requestedLanguage = | ||
72 | languageSetting === SYSTEM_LOCALE ? await osLocale() : languageSetting; | ||
73 | const matchingLanguage = getMatchingLocale( | ||
74 | requestedLanguage, | ||
75 | supportedLangaugesSet, | ||
76 | ); | ||
77 | log.debug( | ||
78 | 'Setting language', | ||
79 | matchingLanguage, | ||
80 | 'for requested language', | ||
81 | requestedLanguage, | ||
82 | ); | ||
83 | shared.setLanguage(matchingLanguage); | ||
84 | }; | ||
85 | const disposer = reaction( | ||
86 | () => settings.language, | ||
87 | (languageSetting) => { | ||
88 | setLanguageAsync(languageSetting).catch((error) => { | ||
89 | log.error('Failed to update language', error); | ||
90 | }); | ||
91 | }, | ||
92 | { | ||
93 | fireImmediately: false, | ||
94 | }, | ||
95 | ); | ||
96 | addDisposer(store, disposer); | ||
97 | // Make sure that the language is already set when we resolve. | ||
98 | await setLanguageAsync(settings.language); | ||
99 | } | ||