chore: tweak i18n locale

This commit is contained in:
Steven 2024-03-04 23:23:14 +08:00
parent 342f341b3d
commit ec206104e5
6 changed files with 21 additions and 38 deletions

View File

@ -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",

9
web/pnpm-lock.yaml generated
View File

@ -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:

View File

@ -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: 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) {

View File

@ -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];

View File

@ -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));

View File

@ -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<typeof locales>;
export type Translations = NestedKeyOf<typeof enTranslation>;
// Represents a typed translation function.
type TypedT = (key: Translations, params?: Record<string, any>) => string;