import type { I18n, MessageDescriptor } from '@lingui/core'; import { i18n } from '@lingui/core'; import type { I18nLocaleData, SupportedLanguageCodes } from '../constants/i18n'; import { APP_I18N_OPTIONS } from '../constants/i18n'; import { env } from './env'; export async function dynamicActivate(locale: string) { const extension = env('NODE_ENV') === 'development' ? 'po' : 'mjs'; const { messages } = await import(`../translations/${locale}/web.${extension}`); i18n.loadAndActivate({ locale, messages }); } const parseLanguageFromLocale = (locale: string): SupportedLanguageCodes | null => { const [language, _country] = locale.split('-'); const foundSupportedLanguage = APP_I18N_OPTIONS.supportedLangs.find( (lang): lang is SupportedLanguageCodes => lang === language, ); if (!foundSupportedLanguage) { return null; } return foundSupportedLanguage; }; /** * Extracts the language from the `accept-language` header. */ export const extractLocaleDataFromHeaders = ( headers: Headers, ): { lang: SupportedLanguageCodes | null; locales: string[] } => { const headerLocales = (headers.get('accept-language') ?? '').split(','); const language = parseLanguageFromLocale(headerLocales[0]); return { lang: language, locales: [headerLocales[0]], }; }; type ExtractLocaleDataOptions = { headers: Headers; }; /** * Extract the supported language from the header. * * Will return the default fallback language if not found. */ export const extractLocaleData = ({ headers }: ExtractLocaleDataOptions): I18nLocaleData => { const headerLocales = (headers.get('accept-language') ?? '').split(','); const unknownLanguages = headerLocales .map((locale) => parseLanguageFromLocale(locale)) .filter((value): value is SupportedLanguageCodes => value !== null); // Filter out locales that are not valid. const languages = (unknownLanguages ?? []).filter((language) => { try { new Intl.Locale(language); return true; } catch { return false; } }); return { lang: languages[0] || APP_I18N_OPTIONS.sourceLang, locales: headerLocales, }; }; export const parseMessageDescriptor = (_: I18n['_'], value: string | MessageDescriptor) => { return typeof value === 'string' ? value : _(value); };