diff options
-rw-r--r-- | packages/main/package.json | 1 | ||||
-rw-r--r-- | packages/main/src/i18n/loadLocalization.ts | 93 | ||||
-rw-r--r-- | packages/main/src/i18n/synchronizeLocalizationSettings.ts | 99 | ||||
-rw-r--r-- | packages/main/src/initReactions.ts | 21 | ||||
-rw-r--r-- | packages/main/src/stores/SharedStore.ts | 5 | ||||
-rw-r--r-- | packages/renderer/src/components/ThemeProvider.tsx | 3 | ||||
-rw-r--r-- | packages/renderer/src/i18n/loadRendererLoalization.ts | 27 | ||||
-rw-r--r-- | packages/renderer/src/index.tsx | 9 | ||||
-rw-r--r-- | packages/shared/src/index.ts | 2 | ||||
-rw-r--r-- | packages/shared/src/stores/SharedStoreBase.ts | 2 | ||||
-rw-r--r-- | packages/shared/src/stores/WritingDirection.ts | 32 | ||||
-rw-r--r-- | yarn.lock | 26 |
12 files changed, 150 insertions, 170 deletions
diff --git a/packages/main/package.json b/packages/main/package.json index c84816d..491e83a 100644 --- a/packages/main/package.json +++ b/packages/main/package.json | |||
@@ -23,7 +23,6 @@ | |||
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", | ||
27 | "os-name": "^5.0.1", | 26 | "os-name": "^5.0.1", |
28 | "slug": "^5.3.0" | 27 | "slug": "^5.3.0" |
29 | }, | 28 | }, |
diff --git a/packages/main/src/i18n/loadLocalization.ts b/packages/main/src/i18n/loadLocalization.ts index 0413373..507075d 100644 --- a/packages/main/src/i18n/loadLocalization.ts +++ b/packages/main/src/i18n/loadLocalization.ts | |||
@@ -18,49 +18,108 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { FALLBACK_LOCALE } from '@sophie/shared'; | 21 | import { FALLBACK_LOCALE, SYSTEM_LOCALE } from '@sophie/shared'; |
22 | import i18next from 'i18next'; | 22 | import i18next, { i18n } from 'i18next'; |
23 | import { autorun } from 'mobx'; | 23 | import { reaction } from 'mobx'; |
24 | import { addDisposer } from 'mobx-state-tree'; | 24 | import { addDisposer } from 'mobx-state-tree'; |
25 | 25 | ||
26 | import type MainStore from '../stores/MainStore'; | 26 | import type MainStore from '../stores/MainStore'; |
27 | import { getLogger } from '../utils/log'; | 27 | import { getLogger } from '../utils/log'; |
28 | 28 | ||
29 | import I18nStore from './I18nStore'; | 29 | import I18nStore from './I18nStore'; |
30 | import LocatlizationRepository from './LocalizationRepository'; | 30 | import type LocatlizationRepository from './LocalizationRepository'; |
31 | import RepositoryBasedI18nBackend from './RepositoryBasedI18nBackend'; | 31 | import RepositoryBasedI18nBackend from './RepositoryBasedI18nBackend'; |
32 | import i18nLog from './i18nLog'; | 32 | import i18nLog from './i18nLog'; |
33 | 33 | ||
34 | const log = getLogger('loadLocationzation'); | 34 | const log = getLogger('loadLocationzation'); |
35 | 35 | ||
36 | const TEST_LOCALE = 'cimode'; | ||
37 | |||
38 | function getLanguage( | ||
39 | language: string, | ||
40 | systemLocale: string, | ||
41 | supportedLanguages: string[], | ||
42 | ): string { | ||
43 | const selectedLanguage = language === SYSTEM_LOCALE ? systemLocale : language; | ||
44 | if (selectedLanguage === TEST_LOCALE) { | ||
45 | return selectedLanguage; | ||
46 | } | ||
47 | // Even though i18next has a `supportedLngs` array from which it can pick a supported language, | ||
48 | // we still have to do this ourselves to avoid spurious warnings like | ||
49 | // https://github.com/i18next/i18next/issues/1564 | ||
50 | if (supportedLanguages.includes(selectedLanguage)) { | ||
51 | return selectedLanguage; | ||
52 | } | ||
53 | if (selectedLanguage.includes('-')) { | ||
54 | const iso639 = selectedLanguage.split('-')[0]; | ||
55 | if (supportedLanguages.includes(iso639)) { | ||
56 | return iso639; | ||
57 | } | ||
58 | } | ||
59 | return FALLBACK_LOCALE; | ||
60 | } | ||
61 | |||
62 | function updateSharedStoreLanguage(store: MainStore, i18nInstance: i18n): void { | ||
63 | const resolvedLanguage = | ||
64 | i18nInstance.language === TEST_LOCALE | ||
65 | ? TEST_LOCALE | ||
66 | : i18nInstance.resolvedLanguage; | ||
67 | const dir = i18nInstance.dir(); | ||
68 | // We do not want to pass the list of supported languages to the renderer process, | ||
69 | // so we extract the resolved languages from `i18n` as pass only that to the renderer. | ||
70 | // Thus, the renderer always selects a language that is actually supported. | ||
71 | store.shared.setLanguage(resolvedLanguage, dir); | ||
72 | log.debug('Loaded language', resolvedLanguage, 'with direction', dir); | ||
73 | } | ||
74 | |||
36 | export default async function loadLocalization( | 75 | export default async function loadLocalization( |
37 | store: MainStore, | 76 | store: MainStore, |
77 | systemLocale: string, | ||
78 | supportedLanguages: string[], | ||
38 | repository: LocatlizationRepository, | 79 | repository: LocatlizationRepository, |
39 | devMode: boolean, | 80 | devMode: boolean, |
40 | ): Promise<void> { | 81 | ): Promise<void> { |
41 | const backend = new RepositoryBasedI18nBackend(repository, devMode); | 82 | const backend = new RepositoryBasedI18nBackend(repository, devMode); |
42 | const i18n = i18next | 83 | const i18nInstance = i18next |
43 | .createInstance({ | 84 | .createInstance({ |
44 | lng: store.shared.language, | 85 | lng: getLanguage( |
86 | store.settings.language, | ||
87 | systemLocale, | ||
88 | supportedLanguages, | ||
89 | ), | ||
90 | supportedLngs: supportedLanguages, | ||
45 | fallbackLng: [FALLBACK_LOCALE], | 91 | fallbackLng: [FALLBACK_LOCALE], |
46 | debug: devMode, | 92 | debug: devMode, |
47 | saveMissing: devMode, | 93 | saveMissing: devMode, |
48 | }) | 94 | }) |
49 | .use(backend) | 95 | .use(backend) |
50 | .use(i18nLog); | 96 | .use(i18nLog); |
51 | const i18nStore = new I18nStore(i18n); | 97 | const i18nStore = new I18nStore(i18nInstance); |
52 | store.setI18n(i18nStore); | 98 | store.setI18n(i18nStore); |
53 | await i18n.init(); | 99 | |
54 | const disposeChangeLanguage = autorun(() => { | 100 | await i18nInstance.init(); |
55 | const { | 101 | updateSharedStoreLanguage(store, i18nInstance); |
56 | shared: { language }, | 102 | |
57 | } = store; | 103 | const disposeChangeLanguage = reaction( |
58 | if (i18n.language !== language) { | 104 | () => store.settings.language, |
59 | log.debug('Setting language', language); | 105 | (languageSetting) => { |
60 | i18n.changeLanguage(language).catch((error) => { | 106 | (async () => { |
107 | const languageToSet = getLanguage( | ||
108 | languageSetting, | ||
109 | systemLocale, | ||
110 | supportedLanguages, | ||
111 | ); | ||
112 | if (i18nInstance.language !== languageToSet) { | ||
113 | await i18nInstance.changeLanguage(languageToSet); | ||
114 | updateSharedStoreLanguage(store, i18nInstance); | ||
115 | } | ||
116 | })().catch((error) => { | ||
61 | log.error('Failed to change language', error); | 117 | log.error('Failed to change language', error); |
62 | }); | 118 | }); |
63 | } | 119 | }, |
64 | }); | 120 | { |
121 | fireImmediately: true, | ||
122 | }, | ||
123 | ); | ||
65 | addDisposer(store, disposeChangeLanguage); | 124 | addDisposer(store, disposeChangeLanguage); |
66 | } | 125 | } |
diff --git a/packages/main/src/i18n/synchronizeLocalizationSettings.ts b/packages/main/src/i18n/synchronizeLocalizationSettings.ts deleted file mode 100644 index 971a593..0000000 --- a/packages/main/src/i18n/synchronizeLocalizationSettings.ts +++ /dev/null | |||
@@ -1,99 +0,0 @@ | |||
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 b383c8f..94b1f06 100644 --- a/packages/main/src/initReactions.ts +++ b/packages/main/src/initReactions.ts | |||
@@ -19,11 +19,9 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { app, session } from 'electron'; | 21 | import { app, session } from 'electron'; |
22 | import { osLocale } from 'os-locale'; | ||
23 | 22 | ||
24 | import LocalizationFiles from './i18n/impl/LocaltizationFiles'; | 23 | import LocalizationFiles from './i18n/impl/LocaltizationFiles'; |
25 | import loadLocalization from './i18n/loadLocalization'; | 24 | import loadLocalization from './i18n/loadLocalization'; |
26 | import { synchronizeLocalizationSettings } from './i18n/synchronizeLocalizationSettings'; | ||
27 | import ConfigFile from './infrastructure/config/impl/ConfigFile'; | 25 | import ConfigFile from './infrastructure/config/impl/ConfigFile'; |
28 | import UserAgents from './infrastructure/electron/UserAgents'; | 26 | import UserAgents from './infrastructure/electron/UserAgents'; |
29 | import ElectronViewFactory from './infrastructure/electron/impl/ElectronViewFactory'; | 27 | import ElectronViewFactory from './infrastructure/electron/impl/ElectronViewFactory'; |
@@ -48,18 +46,17 @@ export default async function initReactions( | |||
48 | configRepository, | 46 | configRepository, |
49 | ); | 47 | ); |
50 | const resources = getDistResources(devMode); | 48 | const resources = getDistResources(devMode); |
51 | const localizationLoaded = (async () => { | 49 | // Ideally, we would the the chromium `--lang` according to the settings store here, |
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. | 50 | // but `app.isReady()` is often already `true`, so we're too late to do that. |
62 | await app.whenReady(); | 51 | await app.whenReady(); |
52 | const localizationFiles = new LocalizationFiles(resources); | ||
53 | const localizationLoaded = loadLocalization( | ||
54 | store, | ||
55 | app.getLocale(), | ||
56 | import.meta.env.SUPPORTED_LOCALES, | ||
57 | localizationFiles, | ||
58 | devMode, | ||
59 | ); | ||
63 | const disposeNativeThemeController = synchronizeNativeTheme(store.shared); | 60 | const disposeNativeThemeController = synchronizeNativeTheme(store.shared); |
64 | hardenSession(resources, devMode, session.defaultSession); | 61 | hardenSession(resources, devMode, session.defaultSession); |
65 | const userAgents = new UserAgents(app.userAgentFallback); | 62 | const userAgents = new UserAgents(app.userAgentFallback); |
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts index 960b65e..3aa8c73 100644 --- a/packages/main/src/stores/SharedStore.ts +++ b/packages/main/src/stores/SharedStore.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 { defineSharedStoreModel } from '@sophie/shared'; | 21 | import { defineSharedStoreModel, WritingDirection } from '@sophie/shared'; |
22 | import { getSnapshot, Instance } from 'mobx-state-tree'; | 22 | import { getSnapshot, Instance } from 'mobx-state-tree'; |
23 | 23 | ||
24 | import { getLogger } from '../utils/log'; | 24 | import { getLogger } from '../utils/log'; |
@@ -64,8 +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 { | 67 | setLanguage(langauge: string, writingDirection: WritingDirection): void { |
68 | self.language = langauge; | 68 | self.language = langauge; |
69 | self.writingDirection = writingDirection; | ||
69 | }, | 70 | }, |
70 | setShouldUseDarkColors(shouldUseDarkColors: boolean): void { | 71 | setShouldUseDarkColors(shouldUseDarkColors: boolean): void { |
71 | self.shouldUseDarkColors = shouldUseDarkColors; | 72 | self.shouldUseDarkColors = shouldUseDarkColors; |
diff --git a/packages/renderer/src/components/ThemeProvider.tsx b/packages/renderer/src/components/ThemeProvider.tsx index 18dc2a8..581563b 100644 --- a/packages/renderer/src/components/ThemeProvider.tsx +++ b/packages/renderer/src/components/ThemeProvider.tsx | |||
@@ -30,10 +30,11 @@ import { useStore } from './StoreProvider'; | |||
30 | export default observer( | 30 | export default observer( |
31 | ({ children }: { children: JSX.Element | JSX.Element[] }) => { | 31 | ({ children }: { children: JSX.Element | JSX.Element[] }) => { |
32 | const { | 32 | const { |
33 | shared: { shouldUseDarkColors }, | 33 | shared: { shouldUseDarkColors, writingDirection }, |
34 | } = useStore(); | 34 | } = useStore(); |
35 | 35 | ||
36 | const theme = createTheme({ | 36 | const theme = createTheme({ |
37 | direction: writingDirection, | ||
37 | palette: { | 38 | palette: { |
38 | mode: shouldUseDarkColors ? 'dark' : 'light', | 39 | mode: shouldUseDarkColors ? 'dark' : 'light', |
39 | }, | 40 | }, |
diff --git a/packages/renderer/src/i18n/loadRendererLoalization.ts b/packages/renderer/src/i18n/loadRendererLoalization.ts index b078aeb..4d7b84a 100644 --- a/packages/renderer/src/i18n/loadRendererLoalization.ts +++ b/packages/renderer/src/i18n/loadRendererLoalization.ts | |||
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | import { FALLBACK_LOCALE, 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 { reaction } from 'mobx'; |
24 | import { addDisposer } from 'mobx-state-tree'; | 24 | import { addDisposer } from 'mobx-state-tree'; |
25 | import { initReactI18next } from 'react-i18next'; | 25 | import { initReactI18next } from 'react-i18next'; |
26 | 26 | ||
@@ -68,17 +68,20 @@ export default function loadRendererLocalization( | |||
68 | } | 68 | } |
69 | 69 | ||
70 | await i18n.init(); | 70 | await i18n.init(); |
71 | const disposeChangeLanguage = autorun(() => { | 71 | |
72 | const { | 72 | const disposeChangeLanguage = reaction( |
73 | shared: { language }, | 73 | () => store.shared.language, |
74 | } = store; | 74 | (languageToSet) => { |
75 | if (i18n.language !== language) { | 75 | if (i18n.language !== languageToSet) { |
76 | log.debug('Setting language', language); | 76 | i18n.changeLanguage(languageToSet).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 | }); | 79 | } |
80 | } | 80 | }, |
81 | }); | 81 | { |
82 | fireImmediately: true, | ||
83 | }, | ||
84 | ); | ||
82 | addDisposer(store, disposeChangeLanguage); | 85 | addDisposer(store, disposeChangeLanguage); |
83 | }; | 86 | }; |
84 | 87 | ||
diff --git a/packages/renderer/src/index.tsx b/packages/renderer/src/index.tsx index 37116db..60ef714 100644 --- a/packages/renderer/src/index.tsx +++ b/packages/renderer/src/index.tsx | |||
@@ -56,6 +56,15 @@ if (isDevelopment) { | |||
56 | 56 | ||
57 | loadRendererLocalization(store, ipc, isDevelopment); | 57 | loadRendererLocalization(store, ipc, isDevelopment); |
58 | 58 | ||
59 | const disposeSetHtmlLang = autorun(() => { | ||
60 | const { | ||
61 | shared: { language, writingDirection }, | ||
62 | } = store; | ||
63 | document.documentElement.lang = language; | ||
64 | document.documentElement.dir = writingDirection; | ||
65 | }); | ||
66 | addDisposer(store, disposeSetHtmlLang); | ||
67 | |||
59 | const disposeSetTitle = autorun(() => { | 68 | const disposeSetTitle = autorun(() => { |
60 | const titlePrefix = isDevelopment ? '[dev] ' : ''; | 69 | const titlePrefix = isDevelopment ? '[dev] ' : ''; |
61 | const serviceTitle = store.settings.selectedService?.title; | 70 | const serviceTitle = store.settings.selectedService?.title; |
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index c4de885..95af73a 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts | |||
@@ -80,3 +80,5 @@ export { | |||
80 | defineSharedStoreModel, | 80 | defineSharedStoreModel, |
81 | FALLBACK_LOCALE, | 81 | FALLBACK_LOCALE, |
82 | } from './stores/SharedStoreBase'; | 82 | } from './stores/SharedStoreBase'; |
83 | |||
84 | export { default as WritingDirection } from './stores/WritingDirection'; | ||
diff --git a/packages/shared/src/stores/SharedStoreBase.ts b/packages/shared/src/stores/SharedStoreBase.ts index a576a0e..bd71cea 100644 --- a/packages/shared/src/stores/SharedStoreBase.ts +++ b/packages/shared/src/stores/SharedStoreBase.ts | |||
@@ -30,6 +30,7 @@ import { | |||
30 | import GlobalSettingsBase from './GlobalSettingsBase'; | 30 | 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 | import WritingDirection from './WritingDirection'; | ||
33 | 34 | ||
34 | export const FALLBACK_LOCALE = 'en'; | 35 | export const FALLBACK_LOCALE = 'en'; |
35 | 36 | ||
@@ -46,6 +47,7 @@ export function defineSharedStoreModel< | |||
46 | services: types.array(types.reference(service)), | 47 | services: types.array(types.reference(service)), |
47 | shouldUseDarkColors: false, | 48 | shouldUseDarkColors: false, |
48 | language: FALLBACK_LOCALE, | 49 | language: FALLBACK_LOCALE, |
50 | writingDirection: types.optional(WritingDirection, 'ltr'), | ||
49 | }); | 51 | }); |
50 | } | 52 | } |
51 | 53 | ||
diff --git a/packages/shared/src/stores/WritingDirection.ts b/packages/shared/src/stores/WritingDirection.ts new file mode 100644 index 0000000..561a86b --- /dev/null +++ b/packages/shared/src/stores/WritingDirection.ts | |||
@@ -0,0 +1,32 @@ | |||
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 { Instance, types } from 'mobx-state-tree'; | ||
22 | |||
23 | const WritingDirection = /* @__PURE__ */ (() => | ||
24 | types.enumeration('WritingDirection', ['ltr', 'rtl']))(); | ||
25 | |||
26 | /* | ||
27 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | ||
28 | Intentionally naming the type the same as the store definition. | ||
29 | */ | ||
30 | type WritingDirection = Instance<typeof WritingDirection>; | ||
31 | |||
32 | export default WritingDirection; | ||
@@ -1297,7 +1297,6 @@ __metadata: | |||
1297 | mobx-state-tree: ^5.1.3 | 1297 | mobx-state-tree: ^5.1.3 |
1298 | ms: ^2.1.3 | 1298 | ms: ^2.1.3 |
1299 | nanoid: ^3.3.2 | 1299 | nanoid: ^3.3.2 |
1300 | os-locale: ^6.0.2 | ||
1301 | os-name: ^5.0.1 | 1300 | os-name: ^5.0.1 |
1302 | slug: ^5.3.0 | 1301 | slug: ^5.3.0 |
1303 | source-map-support: ^0.5.21 | 1302 | source-map-support: ^0.5.21 |
@@ -5220,13 +5219,6 @@ __metadata: | |||
5220 | languageName: node | 5219 | languageName: node |
5221 | linkType: hard | 5220 | linkType: hard |
5222 | 5221 | ||
5223 | "invert-kv@npm:^3.0.0": | ||
5224 | version: 3.0.1 | ||
5225 | resolution: "invert-kv@npm:3.0.1" | ||
5226 | checksum: 782c44c97f8b693006f5ba0995301754bf68d2160ec98fc34d96b266e2c28cc0c91d86c341ca058fe993bc3dd91f104f776a40f04b6c75254a9a1a0d716ac814 | ||
5227 | languageName: node | ||
5228 | linkType: hard | ||
5229 | |||
5230 | "ip@npm:^1.1.5": | 5222 | "ip@npm:^1.1.5": |
5231 | version: 1.1.5 | 5223 | version: 1.1.5 |
5232 | resolution: "ip@npm:1.1.5" | 5224 | resolution: "ip@npm:1.1.5" |
@@ -6429,15 +6421,6 @@ __metadata: | |||
6429 | languageName: node | 6421 | languageName: node |
6430 | linkType: hard | 6422 | linkType: hard |
6431 | 6423 | ||
6432 | "lcid@npm:^3.1.1": | ||
6433 | version: 3.1.1 | ||
6434 | resolution: "lcid@npm:3.1.1" | ||
6435 | dependencies: | ||
6436 | invert-kv: ^3.0.0 | ||
6437 | checksum: 7ebab7a2696a3cc6c6c9f25d957ef81dd2a8a2f48b7e2a9185e4bbcfc36d70cb633acf5fa5c9508f3d30badf23a303b1b6afe0bba8f0bb7d353d0f5d59c9ec1b | ||
6438 | languageName: node | ||
6439 | linkType: hard | ||
6440 | |||
6441 | "leven@npm:^3.1.0": | 6424 | "leven@npm:^3.1.0": |
6442 | version: 3.1.0 | 6425 | version: 3.1.0 |
6443 | resolution: "leven@npm:3.1.0" | 6426 | resolution: "leven@npm:3.1.0" |
@@ -7164,15 +7147,6 @@ __metadata: | |||
7164 | languageName: node | 7147 | languageName: node |
7165 | linkType: hard | 7148 | linkType: hard |
7166 | 7149 | ||
7167 | "os-locale@npm:^6.0.2": | ||
7168 | version: 6.0.2 | ||
7169 | resolution: "os-locale@npm:6.0.2" | ||
7170 | dependencies: | ||
7171 | lcid: ^3.1.1 | ||
7172 | checksum: 812d73334c8773b971bf7fd257b84d2ce7b85d5d2184370f2875fe0e51451f530d6f7c272de1faa0b9ff02d0d10dafd665b6425ed85489271705ab5738691a43 | ||
7173 | languageName: node | ||
7174 | linkType: hard | ||
7175 | |||
7176 | "os-name@npm:^5.0.1": | 7150 | "os-name@npm:^5.0.1": |
7177 | version: 5.0.1 | 7151 | version: 5.0.1 |
7178 | resolution: "os-name@npm:5.0.1" | 7152 | resolution: "os-name@npm:5.0.1" |