d40bb10876
Implemented localization foundations across backend and frontend (locale settings/middleware, preferred language, i18n wiring, RTL support, minimal Arabic UI strings, Accept-Language). Added targeted backend and frontend tests plus a risks note for pending full translation coverage.
92 lines
2.0 KiB
JavaScript
92 lines
2.0 KiB
JavaScript
import i18n from "i18next";
|
|
import { initReactI18next } from "react-i18next";
|
|
import en from "./en.json";
|
|
import arSa from "./ar-sa.json";
|
|
|
|
const STORAGE_KEY = "locale";
|
|
const DEFAULT_LOCALE = "ar-sa";
|
|
const SUPPORTED_LOCALES = ["ar-sa", "en"];
|
|
|
|
function normalizeLocale(value) {
|
|
if (!value) {
|
|
return DEFAULT_LOCALE;
|
|
}
|
|
const lowered = value.toLowerCase();
|
|
if (lowered.startsWith("ar")) {
|
|
return "ar-sa";
|
|
}
|
|
return "en";
|
|
}
|
|
|
|
function readStoredLocale() {
|
|
if (typeof window === "undefined") {
|
|
return null;
|
|
}
|
|
try {
|
|
return window.localStorage.getItem(STORAGE_KEY);
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function writeStoredLocale(locale) {
|
|
if (typeof window === "undefined") {
|
|
return;
|
|
}
|
|
try {
|
|
window.localStorage.setItem(STORAGE_KEY, locale);
|
|
} catch (error) {
|
|
// Ignore storage errors (private mode, blocked storage, etc.).
|
|
}
|
|
}
|
|
|
|
function getInitialLocale() {
|
|
const stored = readStoredLocale();
|
|
if (stored) {
|
|
return normalizeLocale(stored);
|
|
}
|
|
if (typeof navigator !== "undefined") {
|
|
return normalizeLocale(navigator.language);
|
|
}
|
|
return DEFAULT_LOCALE;
|
|
}
|
|
|
|
function isRtl(locale) {
|
|
return normalizeLocale(locale) === "ar-sa";
|
|
}
|
|
|
|
function applyDocumentLocale(locale) {
|
|
if (typeof document === "undefined") {
|
|
return;
|
|
}
|
|
const normalized = normalizeLocale(locale);
|
|
document.documentElement.lang = normalized;
|
|
document.documentElement.dir = isRtl(normalized) ? "rtl" : "ltr";
|
|
}
|
|
|
|
i18n.use(initReactI18next).init({
|
|
resources: {
|
|
en: { translation: en },
|
|
"ar-sa": { translation: arSa },
|
|
},
|
|
lng: getInitialLocale(),
|
|
fallbackLng: "en",
|
|
interpolation: { escapeValue: false },
|
|
});
|
|
|
|
applyDocumentLocale(i18n.language);
|
|
i18n.on("languageChanged", applyDocumentLocale);
|
|
|
|
export function setLocale(locale) {
|
|
const normalized = normalizeLocale(locale);
|
|
writeStoredLocale(normalized);
|
|
return i18n.changeLanguage(normalized);
|
|
}
|
|
|
|
export function getActiveLocale() {
|
|
return i18n.language || DEFAULT_LOCALE;
|
|
}
|
|
|
|
export { DEFAULT_LOCALE, SUPPORTED_LOCALES, STORAGE_KEY };
|
|
export default i18n;
|