From ec206104e5fa4e0177ba53f99faf681b5caf686c Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 4 Mar 2024 23:23:14 +0800 Subject: [PATCH] chore: tweak i18n locale --- web/package.json | 1 - web/pnpm-lock.yaml | 9 --------- web/src/components/LocaleSelect.tsx | 4 ++-- web/src/i18n.ts | 13 +++++-------- web/src/store/module/global.ts | 4 ++-- web/src/utils/i18n.ts | 28 ++++++++++++---------------- 6 files changed, 21 insertions(+), 38 deletions(-) diff --git a/web/package.json b/web/package.json index 0f04e656..e6ebbcd5 100644 --- a/web/package.json +++ b/web/package.json @@ -20,7 +20,6 @@ "fuse.js": "^7.0.0", "highlight.js": "^11.9.0", "i18next": "^21.10.0", - "i18next-browser-languagedetector": "^7.2.0", "katex": "^0.16.9", "lodash-es": "^4.17.21", "lucide-react": "^0.309.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index a0943e9c..28c927f9 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -41,9 +41,6 @@ dependencies: i18next: specifier: ^21.10.0 version: 21.10.0 - i18next-browser-languagedetector: - specifier: ^7.2.0 - version: 7.2.0 katex: specifier: ^0.16.9 version: 0.16.9 @@ -3304,12 +3301,6 @@ packages: resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} dev: false - /i18next-browser-languagedetector@7.2.0: - resolution: {integrity: sha512-U00DbDtFIYD3wkWsr2aVGfXGAj2TgnELzOX9qv8bT0aJtvPV9CRO77h+vgmHFBMe7LAxdwvT/7VkCWGya6L3tA==} - dependencies: - '@babel/runtime': 7.23.9 - dev: false - /i18next@21.10.0: resolution: {integrity: sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg==} dependencies: diff --git a/web/src/components/LocaleSelect.tsx b/web/src/components/LocaleSelect.tsx index 8c19149d..bf760a66 100644 --- a/web/src/components/LocaleSelect.tsx +++ b/web/src/components/LocaleSelect.tsx @@ -1,6 +1,6 @@ import { Option, Select } from "@mui/joy"; import { FC } from "react"; -import { availableLocales } from "@/i18n"; +import { locales } from "@/i18n"; import Icon from "./Icon"; interface Props { @@ -23,7 +23,7 @@ const LocaleSelect: FC = (props: Props) => { value={value} onChange={(_, value) => handleSelectChange(value as Locale)} > - {availableLocales.map((locale) => { + {locales.map((locale) => { try { const languageName = new Intl.DisplayNames([locale], { type: "language" }).of(locale); if (languageName) { diff --git a/web/src/i18n.ts b/web/src/i18n.ts index e5366dd7..377edb06 100644 --- a/web/src/i18n.ts +++ b/web/src/i18n.ts @@ -1,8 +1,8 @@ import i18n, { BackendModule, FallbackLng, FallbackLngObjList } from "i18next"; -import LanguageDetector from "i18next-browser-languagedetector"; import { initReactI18next } from "react-i18next"; +import { findNearestMatchedLanguage } from "./utils/i18n"; -export const availableLocales = [ +export const locales = [ "ar", "de", "en", @@ -38,10 +38,8 @@ const LazyImportPlugin: BackendModule = { type: "backend", init: function () {}, read: function (language, _, callback) { - if (fallbacks[language]) { - language = fallbacks[language][0]; - } - import(`./locales/${language}.json`) + const matchedLanguage = findNearestMatchedLanguage(language); + import(`./locales/${matchedLanguage}.json`) .then((translation: any) => { callback(null, translation); }) @@ -53,7 +51,6 @@ const LazyImportPlugin: BackendModule = { i18n .use(LazyImportPlugin) - .use(LanguageDetector) .use(initReactI18next) .init({ detection: { @@ -66,4 +63,4 @@ i18n }); export default i18n; -export type TLocale = (typeof availableLocales)[number]; +export type TLocale = (typeof locales)[number]; diff --git a/web/src/store/module/global.ts b/web/src/store/module/global.ts index 97119f9c..0f20e18b 100644 --- a/web/src/store/module/global.ts +++ b/web/src/store/module/global.ts @@ -1,7 +1,7 @@ import * as api from "@/helpers/api"; import storage from "@/helpers/storage"; import i18n from "@/i18n"; -import { findNearestLanguageMatch } from "@/utils/i18n"; +import { findNearestMatchedLanguage } from "@/utils/i18n"; import store, { useAppSelector } from "../"; import { setAppearance, setGlobalState, setLocale } from "../reducer/global"; @@ -44,7 +44,7 @@ export const initialGlobalState = async () => { // Otherwise, use server's default locale, set to storageLocale. const { locale: storageLocale, appearance: storageAppearance } = storage.get(["locale", "appearance"]); defaultGlobalState.locale = - storageLocale || defaultGlobalState.systemStatus.customizedProfile.locale || findNearestLanguageMatch(i18n.language); + storageLocale || defaultGlobalState.systemStatus.customizedProfile.locale || findNearestMatchedLanguage(i18n.language); defaultGlobalState.appearance = storageAppearance || defaultGlobalState.systemStatus.customizedProfile.appearance; } store.dispatch(setGlobalState(defaultGlobalState)); diff --git a/web/src/utils/i18n.ts b/web/src/utils/i18n.ts index 9395f782..29020c2c 100644 --- a/web/src/utils/i18n.ts +++ b/web/src/utils/i18n.ts @@ -1,33 +1,29 @@ import { FallbackLngObjList } from "i18next"; import { useTranslation } from "react-i18next"; -import i18n, { availableLocales, TLocale } from "@/i18n"; -import locales from "@/locales/en.json"; +import i18n, { locales, TLocale } from "@/i18n"; +import enTranslation from "@/locales/en.json"; import type { NestedKeyOf } from "@/types/utils/nestedKeyOf.types"; -export const findNearestLanguageMatch = (codename: string): Locale => { - // Find existing translations for full codes (e.g. "en-US", "zh-Hant") - if (codename.length > 2 && availableLocales.includes(codename as TLocale)) { - return codename as Locale; +export const findNearestMatchedLanguage = (language: string): Locale => { + if (locales.includes(language as TLocale)) { + return language as Locale; } - // Find fallback in src/i18n.ts - const i18nfallbacks = Object.entries(i18n.store.options.fallbackLng as FallbackLngObjList); - for (const [main, fallbacks] of i18nfallbacks) { - if (codename === main) { + const i18nFallbacks = Object.entries(i18n.store.options.fallbackLng as FallbackLngObjList); + for (const [main, fallbacks] of i18nFallbacks) { + if (language === main) { return fallbacks[0] as Locale; } } - const shortCode = codename.substring(0, 2); - - // Match existing short code translation - if (availableLocales.includes(shortCode as TLocale)) { + const shortCode = language.substring(0, 2); + if (locales.includes(shortCode as TLocale)) { return shortCode as Locale; } // Try to match "xx-YY" to existing translation for "xx-ZZ" as a last resort // If some match is undesired, it can be overriden in src/i18n.ts `fallbacks` option - for (const existing of availableLocales) { + for (const existing of locales) { if (shortCode == existing.substring(0, 2)) { return existing as Locale; } @@ -38,7 +34,7 @@ export const findNearestLanguageMatch = (codename: string): Locale => { }; // Represents the keys of nested translation objects. -export type Translations = NestedKeyOf; +export type Translations = NestedKeyOf; // Represents a typed translation function. type TypedT = (key: Translations, params?: Record) => string;