fix(i18n): default startup language to English
First-visit users now always land on the English UI regardless of navigator.language. Arabic remains one click away via the toggle, and the user's explicit pick is persisted to localStorage (encoach-lang) and honoured on every subsequent load. - src/i18n/index.ts: set lng: "en", drop navigator/htmlTag from the detector order so an Arabic-locale browser no longer silently boots the UI in Arabic before the user has chosen. - src/lib/api-client.ts: mirror the same policy for the Accept-Language header sent to the backend, so AI-generated content stays English on first visit instead of echoing the browser locale. Made-with: Cursor
This commit is contained in:
@@ -5,10 +5,16 @@
|
|||||||
* they go through type-checking and tree-shaking, and so non-translators
|
* they go through type-checking and tree-shaking, and so non-translators
|
||||||
* can't ship a broken JSON file that blocks the build.
|
* can't ship a broken JSON file that blocks the build.
|
||||||
*
|
*
|
||||||
* Language detection order:
|
* Language selection order (product decision: English is the default UI
|
||||||
* 1. localStorage key ``encoach-lang`` (explicit user pick)
|
* language at startup; Arabic is opt-in via the language toggle):
|
||||||
* 2. ``navigator.language``
|
* 1. localStorage key ``encoach-lang`` (explicit user pick, persisted)
|
||||||
* 3. fallback ``en``
|
* 2. ``en`` — fallback used on first visit, regardless of browser locale
|
||||||
|
*
|
||||||
|
* We intentionally do **not** read ``navigator.language`` anymore: an
|
||||||
|
* Arabic-locale browser should still land on the English UI the first time
|
||||||
|
* a user visits, because the QA team and the product manager both sign off
|
||||||
|
* against the English baseline. Users that actively want Arabic flip the
|
||||||
|
* toggle once and the preference is remembered from then on.
|
||||||
*
|
*
|
||||||
* RTL handling: whenever the active language switches to one of RTL_LANGS,
|
* RTL handling: whenever the active language switches to one of RTL_LANGS,
|
||||||
* we flip ``document.documentElement.dir`` so Tailwind's RTL utilities +
|
* we flip ``document.documentElement.dir`` so Tailwind's RTL utilities +
|
||||||
@@ -31,6 +37,8 @@ export const SUPPORTED_LANGS = [
|
|||||||
|
|
||||||
export type SupportedLang = (typeof SUPPORTED_LANGS)[number]["code"];
|
export type SupportedLang = (typeof SUPPORTED_LANGS)[number]["code"];
|
||||||
|
|
||||||
|
export const DEFAULT_LANG: SupportedLang = "en";
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
@@ -39,11 +47,16 @@ i18n
|
|||||||
en: { translation: en },
|
en: { translation: en },
|
||||||
ar: { translation: ar },
|
ar: { translation: ar },
|
||||||
},
|
},
|
||||||
fallbackLng: "en",
|
// Explicit default — english is the baseline UI language at startup.
|
||||||
|
lng: DEFAULT_LANG,
|
||||||
|
fallbackLng: DEFAULT_LANG,
|
||||||
supportedLngs: SUPPORTED_LANGS.map((l) => l.code),
|
supportedLngs: SUPPORTED_LANGS.map((l) => l.code),
|
||||||
interpolation: { escapeValue: false },
|
interpolation: { escapeValue: false },
|
||||||
detection: {
|
detection: {
|
||||||
order: ["localStorage", "navigator", "htmlTag"],
|
// Only honour the user's saved pick. Navigator/htmlTag detection was
|
||||||
|
// removed so Arabic-locale browsers don't silently boot the UI in
|
||||||
|
// Arabic before the user has chosen.
|
||||||
|
order: ["localStorage"],
|
||||||
caches: ["localStorage"],
|
caches: ["localStorage"],
|
||||||
lookupLocalStorage: "encoach-lang",
|
lookupLocalStorage: "encoach-lang",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -131,15 +131,17 @@ function refreshOnce(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentLanguage(): string {
|
function getCurrentLanguage(): string {
|
||||||
|
// Mirrors the i18n bootstrap in src/i18n/index.ts: the user's explicit
|
||||||
|
// pick wins; everything else falls back to English. We intentionally do
|
||||||
|
// not consult navigator.language here so AI-generated content stays in
|
||||||
|
// English on first visit (matching the UI default) until the user flips
|
||||||
|
// the language toggle.
|
||||||
try {
|
try {
|
||||||
const stored = localStorage.getItem("encoach-lang");
|
const stored = localStorage.getItem("encoach-lang");
|
||||||
if (stored) return stored;
|
if (stored) return stored;
|
||||||
} catch {
|
} catch {
|
||||||
// localStorage unavailable (SSR, sandboxing, etc.)
|
// localStorage unavailable (SSR, sandboxing, etc.)
|
||||||
}
|
}
|
||||||
if (typeof navigator !== "undefined" && navigator.language) {
|
|
||||||
return navigator.language.split("-")[0];
|
|
||||||
}
|
|
||||||
return "en";
|
return "en";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user