import type { i18n } from "i18next"; import type { locales } from "@shared/utils/date"; import { unicodeCLDRtoBCP47 } from "@shared/utils/date"; import { isRTLLanguage } from "@shared/utils/rtl"; import Desktop from "./Desktop"; /** * Detects the user's language based on the browser's language settings. A * language without a region, such as "ja", resolves to its most likely region. * * @returns The user's language in CLDR format (en_US) */ export function detectLanguage() { try { const { language, region } = new Intl.Locale(navigator.language).maximize(); if (region) { return `${language}_${region}` as keyof typeof locales; } } catch (_err) { // invalid language tag, fall back to splitting the string } const [ln, r] = navigator.language.split("-"); const region = (r || ln).toUpperCase(); return `${ln}_${region}` as keyof typeof locales; } /** * Changes the language of the app, and updates the spellchecker language * if running in the desktop shell. * * @param locale The locale to change to, in CLDR format (en_US) * @param instance The i18n instance to use */ export async function changeLanguage( locale: string | null | undefined, instance: i18n ) { // Languages are stored in en_US format in the database, however the // frontend translation framework (i18next) expects en-US const localeBCP = locale ? unicodeCLDRtoBCP47(locale) : undefined; if (localeBCP && instance.languages?.[0] !== localeBCP) { await instance.changeLanguage(localeBCP); await Desktop.bridge?.setSpellCheckerLanguages(["en-US", localeBCP]); } if (typeof document !== "undefined") { document.documentElement.dir = isRTLLanguage(locale) ? "rtl" : "ltr"; } }