@@ -33,7 +33,7 @@
-
+
{{ t('userProfile.actions.changeLanguage.entry.browser') }}
@@ -76,7 +76,7 @@ import backend, { UserDto, VersionDto } from '../common/backend';
import config from '../common/config';
import userdata from '../common/userdata';
-import { Locale } from '../i18n';
+import { Locale, detectBrowserLocale } from '../i18n';
import DeviceList from './DeviceList.vue';
import FetchError from './FetchError.vue';
import LegacyDeviceList from './LegacyDeviceList.vue';
@@ -85,16 +85,25 @@ import UserkeyFingerprint from './UserkeyFingerprint.vue';
const { locale, t } = useI18n({ useScope: 'global' });
+type LanguagePreference = Locale | 'browser';
+
+const languagePreference = ref('browser');
+
const me = ref();
const keycloakUserAccountURL = ref();
const version = ref();
const onFetchError = ref();
-const browserLocale = ref(navigator.language);
+const browserLocale = ref(detectBrowserLocale());
onMounted(async () => {
const cfg = config.get();
keycloakUserAccountURL.value = `${cfg.keycloakUrl}/realms/${cfg.keycloakRealm}/account`;
await fetchData();
+ if (me.value?.language) {
+ languagePreference.value = me.value.language as Locale;
+ } else {
+ languagePreference.value = 'browser';
+ }
});
async function fetchData() {
diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts
index a93068045..6a04260fd 100644
--- a/frontend/src/i18n/index.ts
+++ b/frontend/src/i18n/index.ts
@@ -102,13 +102,56 @@ export const numberFormats: I18nOptions['numberFormats'] = {
[Locale.ZH_TW]: defaultNumberFormat
};
-export const mapToLocale = (local: string): Locale =>
- (Object.values(Locale) as string[]).includes(local)
- ? (local as Locale)
- : Locale.EN_US;
+export const mapToLocale = (local: string): Locale => {
+ if (!local) {
+ return Locale.EN_US;
+ }
+
+ const normalized = local.replace('_', '-');
+
+ if ((Object.values(Locale) as string[]).includes(normalized)) {
+ return normalized as Locale;
+ }
+
+ const base = normalized.split('-')[0];
+
+ switch (base) {
+ case 'de': return Locale.DE_DE;
+ case 'en': return Locale.EN_US;
+ case 'fr': return Locale.FR_FR;
+ case 'it': return Locale.IT_IT;
+ case 'ko': return Locale.KO_KR;
+ case 'lv': return Locale.LV_LV;
+ case 'nl': return Locale.NL_NL;
+ case 'pt': return Locale.PT_PT;
+ case 'ru': return Locale.RU_RU;
+ case 'tr': return Locale.TR_TR;
+ case 'uk': return Locale.UK_UA;
+ case 'zh': return Locale.ZH_TW;
+ default: return Locale.EN_US;
+ }
+};
+
+export const detectBrowserLocale = (): Locale => {
+ const raw = getBrowserLocale();
+
+ return mapToLocale(raw);
+};
+
+function getBrowserLocale(): string {
+ if (typeof navigator === 'undefined') {
+ return Locale.EN_US;
+ } else if (navigator.languages && navigator.languages.length > 0) {
+ return navigator.languages[0];
+ } else if (navigator.language) {
+ return navigator.language;
+ } else {
+ return Locale.EN_US;
+ }
+}
const i18n = createI18n({
- locale: navigator.language,
+ locale: detectBrowserLocale(),
fallbackLocale: Locale.EN_US,
messages,
datetimeFormats,