diff options
author | Kristóf Marussy <kristof@marussy.com> | 2022-04-03 02:05:40 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2022-05-16 00:54:59 +0200 |
commit | 49c622189cf1d2c489b963d9be2f7493543afa3a (patch) | |
tree | 87636e0c08ce6ee2258566e3e0879707c9a51ea7 /packages | |
parent | build: Allow command line arguments to watch (diff) | |
download | sophie-49c622189cf1d2c489b963d9be2f7493543afa3a.tar.gz sophie-49c622189cf1d2c489b963d9be2f7493543afa3a.tar.zst sophie-49c622189cf1d2c489b963d9be2f7493543afa3a.zip |
feat(main): Language setting in config file
Load localization according to either the environment or the
configuration file from the list of supported locales.
Ideally, we would also set the chromium locale with --lang, but by the
time we have read the config file (to known which locale to set),
electron has already initialized the chromium resource bundle.
So the chromium localization will always be auto-detected by chromium.
Also makes startup hopefully a bit faster by doing more things
concurrently while the localization and the main window is being loaded.
Signed-off-by: Kristóf Marussy <kristof@marussy.com>
Diffstat (limited to 'packages')
-rw-r--r-- | packages/main/esbuild.config.js | 12 | ||||
-rw-r--r-- | packages/main/package.json | 1 | ||||
-rw-r--r-- | packages/main/src/i18n/loadLocalization.ts | 11 | ||||
-rw-r--r-- | packages/main/src/i18n/synchronizeLocalizationSettings.ts | 99 | ||||
-rw-r--r-- | packages/main/src/initReactions.ts | 41 | ||||
-rw-r--r-- | packages/main/src/stores/SharedStore.ts | 3 | ||||
-rw-r--r-- | packages/main/types/importMeta.d.ts | 1 | ||||
-rw-r--r-- | packages/renderer/src/i18n/loadRendererLoalization.ts | 5 | ||||
-rw-r--r-- | packages/shared/src/index.ts | 4 | ||||
-rw-r--r-- | packages/shared/src/stores/GlobalSettingsBase.ts | 3 | ||||
-rw-r--r-- | packages/shared/src/stores/SharedStoreBase.ts | 4 |
11 files changed, 157 insertions, 27 deletions
diff --git a/packages/main/esbuild.config.js b/packages/main/esbuild.config.js index 996ec5a..ae8565d 100644 --- a/packages/main/esbuild.config.js +++ b/packages/main/esbuild.config.js | |||
@@ -1,9 +1,15 @@ | |||
1 | import { readdir } from 'node:fs/promises'; | ||
2 | import path from 'node:path'; | ||
3 | |||
1 | import getRepoInfo from 'git-repo-info'; | 4 | import getRepoInfo from 'git-repo-info'; |
2 | 5 | ||
3 | import { node } from '../../config/buildConstants.js'; | 6 | import { node } from '../../config/buildConstants.js'; |
4 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; | 7 | import fileUrlToDirname from '../../config/fileUrlToDirname.js'; |
5 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; | 8 | import getEsbuildConfig from '../../config/getEsbuildConfig.js'; |
6 | 9 | ||
10 | /** @type {string} */ | ||
11 | const thisDir = fileUrlToDirname(import.meta.url); | ||
12 | |||
7 | const externalPackages = ['electron']; | 13 | const externalPackages = ['electron']; |
8 | 14 | ||
9 | if (process.env.MODE !== 'development') { | 15 | if (process.env.MODE !== 'development') { |
@@ -12,9 +18,12 @@ if (process.env.MODE !== 'development') { | |||
12 | 18 | ||
13 | const gitInfo = getRepoInfo(); | 19 | const gitInfo = getRepoInfo(); |
14 | 20 | ||
21 | /** @type {string[]} */ | ||
22 | const locales = await readdir(path.join(thisDir, '../../locales')); | ||
23 | |||
15 | export default getEsbuildConfig( | 24 | export default getEsbuildConfig( |
16 | { | 25 | { |
17 | absWorkingDir: fileUrlToDirname(import.meta.url), | 26 | absWorkingDir: thisDir, |
18 | entryPoints: ['src/index.ts'], | 27 | entryPoints: ['src/index.ts'], |
19 | outfile: 'dist/index.cjs', | 28 | outfile: 'dist/index.cjs', |
20 | format: 'cjs', | 29 | format: 'cjs', |
@@ -27,5 +36,6 @@ export default getEsbuildConfig( | |||
27 | GIT_SHA: gitInfo.abbreviatedSha, | 36 | GIT_SHA: gitInfo.abbreviatedSha, |
28 | GIT_BRANCH: gitInfo.branch, | 37 | GIT_BRANCH: gitInfo.branch, |
29 | BUILD_DATE: Date.now(), | 38 | BUILD_DATE: Date.now(), |
39 | SUPPORTED_LOCALES: locales, | ||
30 | }, | 40 | }, |
31 | ); | 41 | ); |
diff --git a/packages/main/package.json b/packages/main/package.json index 491e83a..c84816d 100644 --- a/packages/main/package.json +++ b/packages/main/package.json | |||
@@ -23,6 +23,7 @@ | |||
23 | "mobx-state-tree": "^5.1.3", | 23 | "mobx-state-tree": "^5.1.3", |
24 | "ms": "^2.1.3", | 24 | "ms": "^2.1.3", |
25 | "nanoid": "^3.3.2", | 25 | "nanoid": "^3.3.2", |
26 | "os-locale": "^6.0.2", | ||
26 | "os-name": "^5.0.1", | 27 | "os-name": "^5.0.1", |
27 | "slug": "^5.3.0" | 28 | "slug": "^5.3.0" |
28 | }, | 29 | }, |
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 | } | ||
diff --git a/packages/main/src/initReactions.ts b/packages/main/src/initReactions.ts index cdff551..b383c8f 100644 --- a/packages/main/src/initReactions.ts +++ b/packages/main/src/initReactions.ts | |||
@@ -19,8 +19,11 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { app, session } from 'electron'; | 21 | import { app, session } from 'electron'; |
22 | import { osLocale } from 'os-locale'; | ||
22 | 23 | ||
24 | import LocalizationFiles from './i18n/impl/LocaltizationFiles'; | ||
23 | import loadLocalization from './i18n/loadLocalization'; | 25 | import loadLocalization from './i18n/loadLocalization'; |
26 | import { synchronizeLocalizationSettings } from './i18n/synchronizeLocalizationSettings'; | ||
24 | import ConfigFile from './infrastructure/config/impl/ConfigFile'; | 27 | import ConfigFile from './infrastructure/config/impl/ConfigFile'; |
25 | import UserAgents from './infrastructure/electron/UserAgents'; | 28 | import UserAgents from './infrastructure/electron/UserAgents'; |
26 | import ElectronViewFactory from './infrastructure/electron/impl/ElectronViewFactory'; | 29 | import ElectronViewFactory from './infrastructure/electron/impl/ElectronViewFactory'; |
@@ -44,28 +47,36 @@ export default async function initReactions( | |||
44 | store.shared, | 47 | store.shared, |
45 | configRepository, | 48 | configRepository, |
46 | ); | 49 | ); |
50 | const resources = getDistResources(devMode); | ||
51 | const localizationLoaded = (async () => { | ||
52 | const localizationFiles = new LocalizationFiles(resources); | ||
53 | await synchronizeLocalizationSettings( | ||
54 | store, | ||
55 | import.meta.env.SUPPORTED_LOCALES, | ||
56 | osLocale, | ||
57 | ); | ||
58 | await loadLocalization(store, localizationFiles, devMode); | ||
59 | })(); | ||
60 | // Ideally, we would the the chromium `--lang` here, | ||
61 | // but `app.isReady()` is often already `true`, so we're too late to do that. | ||
47 | await app.whenReady(); | 62 | await app.whenReady(); |
48 | const disposeNativeThemeController = synchronizeNativeTheme(store.shared); | 63 | const disposeNativeThemeController = synchronizeNativeTheme(store.shared); |
49 | const resources = getDistResources(devMode); | ||
50 | hardenSession(resources, devMode, session.defaultSession); | 64 | hardenSession(resources, devMode, session.defaultSession); |
51 | if (devMode) { | ||
52 | await installDevToolsExtensions(); | ||
53 | } | ||
54 | const userAgents = new UserAgents(app.userAgentFallback); | 65 | const userAgents = new UserAgents(app.userAgentFallback); |
55 | app.userAgentFallback = userAgents.fallbackUserAgent(devMode); | 66 | app.userAgentFallback = userAgents.fallbackUserAgent(devMode); |
56 | const localizeInterface = async () => { | 67 | const devToolsLoaded = devMode |
57 | await loadLocalization(store, resources, devMode); | 68 | ? installDevToolsExtensions() |
58 | setApplicationMenu(store, devMode, isMac); | 69 | : Promise.resolve(); |
59 | }; | ||
60 | const localization = localizeInterface(); | ||
61 | const viewFactory = new ElectronViewFactory(userAgents, resources, devMode); | 70 | const viewFactory = new ElectronViewFactory(userAgents, resources, devMode); |
62 | const [mainWindow] = await Promise.all([ | 71 | const mainWindow = (async () => { |
63 | viewFactory.createMainWindow(store), | 72 | await localizationLoaded; |
64 | viewFactory.loadServiceInject(), | 73 | setApplicationMenu(store, devMode, isMac); |
65 | ]); | 74 | await devToolsLoaded; |
66 | store.setMainWindow(mainWindow); | 75 | return viewFactory.createMainWindow(store); |
76 | })(); | ||
77 | await viewFactory.loadServiceInject(); | ||
67 | loadServices(store, viewFactory); | 78 | loadServices(store, viewFactory); |
68 | await localization; | 79 | store.setMainWindow(await mainWindow); |
69 | return () => { | 80 | return () => { |
70 | disposeNativeThemeController(); | 81 | disposeNativeThemeController(); |
71 | disposeConfigController(); | 82 | disposeConfigController(); |
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts index 67d58d6..960b65e 100644 --- a/packages/main/src/stores/SharedStore.ts +++ b/packages/main/src/stores/SharedStore.ts | |||
@@ -64,6 +64,9 @@ const SharedStore = defineSharedStoreModel(GlobalSettings, Profile, Service) | |||
64 | loadConfig(config: Config): void { | 64 | loadConfig(config: Config): void { |
65 | loadConfig(self, config); | 65 | loadConfig(self, config); |
66 | }, | 66 | }, |
67 | setLanguage(langauge: string): void { | ||
68 | self.language = langauge; | ||
69 | }, | ||
67 | setShouldUseDarkColors(shouldUseDarkColors: boolean): void { | 70 | setShouldUseDarkColors(shouldUseDarkColors: boolean): void { |
68 | self.shouldUseDarkColors = shouldUseDarkColors; | 71 | self.shouldUseDarkColors = shouldUseDarkColors; |
69 | }, | 72 | }, |
diff --git a/packages/main/types/importMeta.d.ts b/packages/main/types/importMeta.d.ts index 7426961..9818ca1 100644 --- a/packages/main/types/importMeta.d.ts +++ b/packages/main/types/importMeta.d.ts | |||
@@ -7,5 +7,6 @@ interface ImportMeta { | |||
7 | GIT_SHA: string; | 7 | GIT_SHA: string; |
8 | GIT_BRANCH: string; | 8 | GIT_BRANCH: string; |
9 | BUILD_DATE: number; | 9 | BUILD_DATE: number; |
10 | SUPPORTED_LOCALES: string[]; | ||
10 | }; | 11 | }; |
11 | } | 12 | } |
diff --git a/packages/renderer/src/i18n/loadRendererLoalization.ts b/packages/renderer/src/i18n/loadRendererLoalization.ts index 19d1e2d..b078aeb 100644 --- a/packages/renderer/src/i18n/loadRendererLoalization.ts +++ b/packages/renderer/src/i18n/loadRendererLoalization.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 | ||
21 | import { fallbackLng, SophieRenderer } from '@sophie/shared'; | 21 | import { FALLBACK_LOCALE, SophieRenderer } 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'; |
@@ -40,7 +40,7 @@ export default function loadRendererLocalization( | |||
40 | const i18n = i18next | 40 | const i18n = i18next |
41 | .createInstance({ | 41 | .createInstance({ |
42 | lng: store.shared.language, | 42 | lng: store.shared.language, |
43 | fallbackLng, | 43 | fallbackLng: [FALLBACK_LOCALE], |
44 | interpolation: { | 44 | interpolation: { |
45 | escapeValue: false, // Not needed for react | 45 | escapeValue: false, // Not needed for react |
46 | }, | 46 | }, |
@@ -73,6 +73,7 @@ export default function loadRendererLocalization( | |||
73 | shared: { language }, | 73 | shared: { language }, |
74 | } = store; | 74 | } = store; |
75 | if (i18n.language !== language) { | 75 | if (i18n.language !== language) { |
76 | log.debug('Setting language', language); | ||
76 | i18n.changeLanguage(language).catch((error) => { | 77 | i18n.changeLanguage(language).catch((error) => { |
77 | log.error('Failed to change language', error); | 78 | log.error('Failed to change language', error); |
78 | }); | 79 | }); |
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 51f9f06..c4de885 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts | |||
@@ -18,8 +18,6 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | export const fallbackLng = ['en']; | ||
22 | |||
23 | export type { default as SophieRenderer } from './contextBridge/SophieRenderer'; | 21 | export type { default as SophieRenderer } from './contextBridge/SophieRenderer'; |
24 | 22 | ||
25 | export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc'; | 23 | export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc'; |
@@ -44,6 +42,7 @@ export type { | |||
44 | export { | 42 | export { |
45 | default as GlobalSettingsBase, | 43 | default as GlobalSettingsBase, |
46 | defineGlobalSettingsModel, | 44 | defineGlobalSettingsModel, |
45 | SYSTEM_LOCALE, | ||
47 | } from './stores/GlobalSettingsBase'; | 46 | } from './stores/GlobalSettingsBase'; |
48 | 47 | ||
49 | export { default as Profile } from './stores/Profile'; | 48 | export { default as Profile } from './stores/Profile'; |
@@ -79,4 +78,5 @@ export type { | |||
79 | export { | 78 | export { |
80 | default as SharedStoreBase, | 79 | default as SharedStoreBase, |
81 | defineSharedStoreModel, | 80 | defineSharedStoreModel, |
81 | FALLBACK_LOCALE, | ||
82 | } from './stores/SharedStoreBase'; | 82 | } from './stores/SharedStoreBase'; |
diff --git a/packages/shared/src/stores/GlobalSettingsBase.ts b/packages/shared/src/stores/GlobalSettingsBase.ts index 1bd0628..c74c822 100644 --- a/packages/shared/src/stores/GlobalSettingsBase.ts +++ b/packages/shared/src/stores/GlobalSettingsBase.ts | |||
@@ -30,10 +30,13 @@ import { ThemeSource } from '../schemas/ThemeSource'; | |||
30 | 30 | ||
31 | import ServiceBase from './ServiceBase'; | 31 | import ServiceBase from './ServiceBase'; |
32 | 32 | ||
33 | export const SYSTEM_LOCALE = 'system'; | ||
34 | |||
33 | export function defineGlobalSettingsModel<TS extends IAnyModelType>( | 35 | export function defineGlobalSettingsModel<TS extends IAnyModelType>( |
34 | service: TS, | 36 | service: TS, |
35 | ) { | 37 | ) { |
36 | return types.model('GlobalSettings', { | 38 | return types.model('GlobalSettings', { |
39 | language: SYSTEM_LOCALE, | ||
37 | themeSource: types.optional( | 40 | themeSource: types.optional( |
38 | types.enumeration(ThemeSource.options), | 41 | types.enumeration(ThemeSource.options), |
39 | 'system', | 42 | 'system', |
diff --git a/packages/shared/src/stores/SharedStoreBase.ts b/packages/shared/src/stores/SharedStoreBase.ts index 86bd0fc..a576a0e 100644 --- a/packages/shared/src/stores/SharedStoreBase.ts +++ b/packages/shared/src/stores/SharedStoreBase.ts | |||
@@ -31,6 +31,8 @@ import GlobalSettingsBase from './GlobalSettingsBase'; | |||
31 | import ProfileBase from './Profile'; | 31 | import ProfileBase from './Profile'; |
32 | import ServiceBase from './ServiceBase'; | 32 | import ServiceBase from './ServiceBase'; |
33 | 33 | ||
34 | export const FALLBACK_LOCALE = 'en'; | ||
35 | |||
34 | export function defineSharedStoreModel< | 36 | export function defineSharedStoreModel< |
35 | TG extends IAnyModelType, | 37 | TG extends IAnyModelType, |
36 | TP extends IAnyModelType, | 38 | TP extends IAnyModelType, |
@@ -43,7 +45,7 @@ export function defineSharedStoreModel< | |||
43 | servicesById: types.map(service), | 45 | servicesById: types.map(service), |
44 | services: types.array(types.reference(service)), | 46 | services: types.array(types.reference(service)), |
45 | shouldUseDarkColors: false, | 47 | shouldUseDarkColors: false, |
46 | language: 'en', | 48 | language: FALLBACK_LOCALE, |
47 | }); | 49 | }); |
48 | } | 50 | } |
49 | 51 | ||