dynamically load translation files

This commit is contained in:
Henry Dollman
2024-10-30 11:40:17 -04:00
parent 3505b215a2
commit 2c4ea6f52a
5 changed files with 48 additions and 42 deletions

View File

@@ -6,6 +6,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import languages from "../lib/languages.json" import languages from "../lib/languages.json"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { setLang } from "@/lib/i18n"
export function LangToggle() { export function LangToggle() {
const { i18n } = useTranslation() const { i18n } = useTranslation()
@@ -27,7 +28,7 @@ export function LangToggle() {
<DropdownMenuItem <DropdownMenuItem
key={lang} key={lang}
className={cn("pl-4", lang === i18n.language ? "font-bold" : "")} className={cn("pl-4", lang === i18n.language ? "font-bold" : "")}
onClick={() => i18n.changeLanguage(lang)} onClick={() => setLang(lang)}
> >
{label} {label}
</DropdownMenuItem> </DropdownMenuItem>

View File

@@ -29,7 +29,7 @@ import {
import { AddSystemButton } from "./add-system" import { AddSystemButton } from "./add-system"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
const CommandPalette = lazy(() => import("./command-palette.tsx")) const CommandPalette = lazy(() => import("./command-palette"))
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0 const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0
@@ -38,7 +38,7 @@ export default function Navbar() {
return ( return (
<div className="flex items-center h-14 md:h-16 bg-card px-4 pr-3 sm:px-6 border bt-0 rounded-md my-4"> <div className="flex items-center h-14 md:h-16 bg-card px-4 pr-3 sm:px-6 border bt-0 rounded-md my-4">
<Link href="/" aria-label="Home" className={"p-2 pl-0"}> <Link href="/" aria-label="Home" className={"p-2 pl-0"}>
<Logo className="h-[1.3em] fill-foreground" /> <Logo className="h-[1.15rem] md:h-[1.3em] fill-foreground" />
</Link> </Link>
<SearchButton /> <SearchButton />

View File

@@ -10,6 +10,7 @@ import { useState, useEffect } from "react"
// import { Input } from '@/components/ui/input' // import { Input } from '@/components/ui/input'
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import languages from "../../../lib/languages.json" import languages from "../../../lib/languages.json"
import { setLang } from "@/lib/i18n"
export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) { export default function SettingsProfilePage({ userSettings }: { userSettings: UserSettings }) {
const { t, i18n } = useTranslation() const { t, i18n } = useTranslation()
@@ -54,7 +55,7 @@ export default function SettingsProfilePage({ userSettings }: { userSettings: Us
<Label className="block" htmlFor="lang"> <Label className="block" htmlFor="lang">
{t("settings.general.language.preferred_language")} {t("settings.general.language.preferred_language")}
</Label> </Label>
<Select value={i18n.language} onValueChange={(lang: string) => i18n.changeLanguage(lang)}> <Select value={i18n.language} onValueChange={(lang: string) => setLang(lang)}>
<SelectTrigger id="lang"> <SelectTrigger id="lang">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>

View File

@@ -1,37 +1,24 @@
import i18n from "i18next" import i18n from "i18next"
import { initReactI18next } from "react-i18next" import { initReactI18next } from "react-i18next"
import en from "../locales/en/translation.json"
import es from "../locales/es/translation.json"
import fr from "../locales/fr/translation.json"
import de from "../locales/de/translation.json"
import ru from "../locales/ru/translation.json"
import zhHans from "../locales/zh-CN/translation.json"
import zhHant from "../locales/zh-HK/translation.json"
// Custom language detector to use localStorage // Custom language detector to use localStorage
const languageDetector: any = { const languageDetector: any = {
type: "languageDetector", type: "languageDetector",
async: true, async: true,
detect: (callback: (lng: string) => void) => { detect: (callback: (lng: string) => void) => {
const savedLanguage = localStorage.getItem("i18nextLng") const savedLanguage = localStorage.getItem("i18nextLng")
const fallbackLanguage = (() => { const zhVariantMap: Record<string, string> = {
switch (navigator.language) { "zh-CN": "zh-CN",
case "zh-CN": "zh-SG": "zh-CN",
case "zh-SG": "zh-MY": "zh-CN",
case "zh-MY": zh: "zh-CN",
case "zh": "zh-Hans": "zh-CN",
case "zh-Hans": "zh-HK": "zh-HK",
return "zh-CN" "zh-TW": "zh-HK",
case "zh-HK": "zh-MO": "zh-HK",
case "zh-TW": "zh-Hant": "zh-HK",
case "zh-MO": }
case "zh-Hant": const fallbackLanguage = zhVariantMap[navigator.language] || navigator.language
return "zh-HK"
default:
return navigator.language
}
})()
callback(savedLanguage || fallbackLanguage) callback(savedLanguage || fallbackLanguage)
}, },
init: () => {}, init: () => {},
@@ -40,25 +27,39 @@ const languageDetector: any = {
}, },
} }
// Function to dynamically load translation files
async function loadMessages(locale: string) {
try {
const translation = await import(`../locales/${locale}/translation.json`)
return translation.default
} catch (error) {
console.error(`Error loading ${locale}`, error)
// Fallback to English if translation fails to load
const enTranslation = await import(`../locales/en/translation.json`)
return enTranslation.default
}
}
i18n i18n
.use(languageDetector) .use(languageDetector)
.use(initReactI18next) .use(initReactI18next)
.init({ .init({
resources: { resources: {}, // Start with empty resources
en: { translation: en },
es: { translation: es },
fr: { translation: fr },
de: { translation: de },
ru: { translation: ru },
// Chinese (Simplified)
"zh-CN": { translation: zhHans },
// Chinese (Traditional)
"zh-HK": { translation: zhHant },
},
fallbackLng: "en", fallbackLng: "en",
interpolation: { interpolation: {
escapeValue: false, escapeValue: false,
}, },
}) })
// Function to dynamically activate a language
export async function setLang(locale: string) {
const messages = await loadMessages(locale)
i18n.addResourceBundle(locale, "translation", messages)
await i18n.changeLanguage(locale)
}
// Initialize with detected/saved language
const initialLanguage = localStorage.getItem("i18nextLng") || navigator.language
setLang(initialLanguage)
export { i18n } export { i18n }

View File

@@ -1,5 +1,6 @@
import "./index.css" import "./index.css"
import { Suspense, lazy, useEffect, StrictMode } from "react" // import { Suspense, lazy, useEffect, StrictMode } from "react"
import { Suspense, lazy, useEffect } from "react"
import ReactDOM from "react-dom/client" import ReactDOM from "react-dom/client"
import Home from "./components/routes/home.tsx" import Home from "./components/routes/home.tsx"
import { ThemeProvider } from "./components/theme-provider.tsx" import { ThemeProvider } from "./components/theme-provider.tsx"
@@ -87,7 +88,9 @@ const Layout = () => {
return ( return (
<> <>
<div className="container">{Navbar()}</div> <div className="container">
<Navbar />
</div>
<div className="container mb-14 relative"> <div className="container mb-14 relative">
<App /> <App />
{copyContent && ( {copyContent && (