- Full 8,703-key Hindi dictionary in src/i18n/hi (7 namespace batches),
placeholders verified, only 2% identical strings (brand/protocol names).
- Register hi in Language/LANGUAGE_LOADERS, detectLanguage (hi-IN),
LanguageToggle (🇮🇳, हिन्दी) and dateFnsI18n (hi-IN).
- Lazy-load Noto Sans Devanagari for hi via lib/localeFonts.ts (renamed from
cjkFonts.ts) and add it to the CSS font stack; other locales don't download it.
- check-i18n covers hi: 24 dictionaries / 25 codes at 100%; tsc/tests/build green.
172 lines
5.2 KiB
TypeScript
172 lines
5.2 KiB
TypeScript
/**
|
|
* Locale-aware date/number formatting.
|
|
*
|
|
* The app ships English + Spanish, but `date-fns` calls `formatDistanceToNow`
|
|
* with the default (English) locale and native `Date.toLocaleDateString()`
|
|
* falls back to the *runtime* locale, not the app language. This module keeps a
|
|
* single module-level locale in sync with `LanguageProvider` and re-exports all
|
|
* of `date-fns` with `formatDistanceToNow` / `formatDistance` / `format`
|
|
* overridden to apply it. Import from here instead of `date-fns` directly.
|
|
*
|
|
* `dateLocaleCode()` feeds native `toLocaleDateString/TimeString` calls so a
|
|
* Spanish user sees "5 ene" rather than "Jan 5".
|
|
*/
|
|
export * from 'date-fns';
|
|
|
|
import {
|
|
formatDistanceToNow as _formatDistanceToNow,
|
|
formatDistanceToNowStrict as _formatDistanceToNowStrict,
|
|
formatDistance as _formatDistance,
|
|
format as _format,
|
|
} from 'date-fns';
|
|
import {
|
|
enUS,
|
|
es as esLocale,
|
|
ca as caLocale,
|
|
eu as euLocale,
|
|
gl as glLocale,
|
|
hi as hiLocale,
|
|
th as thLocale,
|
|
ja as jaLocale,
|
|
ptBR as ptLocale,
|
|
pt as ptPTLocale,
|
|
fr as frLocale,
|
|
de as deLocale,
|
|
it as itLocale,
|
|
nl as nlLocale,
|
|
pl as plLocale,
|
|
ru as ruLocale,
|
|
ko as koLocale,
|
|
zhCN as zhCNLocale,
|
|
zhTW as zhTWLocale,
|
|
uk as ukLocale,
|
|
tr as trLocale,
|
|
vi as viLocale,
|
|
id as idLocale,
|
|
sv as svLocale,
|
|
cs as csLocale,
|
|
ro as roLocale,
|
|
el as elLocale,
|
|
} from 'date-fns/locale';
|
|
import type { Locale } from 'date-fns';
|
|
import type { Language } from '@/contexts/language';
|
|
|
|
let activeLocale: Locale = enUS;
|
|
let activeCode = 'en-US';
|
|
|
|
/** Called by LanguageProvider (during render) so children format in-language. */
|
|
export function setDateLocale(language: Language): void {
|
|
if (language === 'es') {
|
|
activeLocale = esLocale;
|
|
activeCode = 'es-ES';
|
|
} else if (language === 'ca') {
|
|
activeLocale = caLocale;
|
|
activeCode = 'ca-ES';
|
|
} else if (language === 'eu') {
|
|
activeLocale = euLocale;
|
|
activeCode = 'eu-ES';
|
|
} else if (language === 'gl') {
|
|
activeLocale = glLocale;
|
|
activeCode = 'gl-ES';
|
|
} else if (language === 'hi') {
|
|
activeLocale = hiLocale;
|
|
activeCode = 'hi-IN';
|
|
} else if (language === 'th') {
|
|
activeLocale = thLocale;
|
|
activeCode = 'th-TH';
|
|
} else if (language === 'ja') {
|
|
activeLocale = jaLocale;
|
|
activeCode = 'ja-JP';
|
|
} else if (language === 'pt') {
|
|
activeLocale = ptLocale;
|
|
activeCode = 'pt-BR';
|
|
} else if (language === 'pt-PT') {
|
|
activeLocale = ptPTLocale;
|
|
activeCode = 'pt-PT';
|
|
} else if (language === 'fr') {
|
|
activeLocale = frLocale;
|
|
activeCode = 'fr-FR';
|
|
} else if (language === 'de') {
|
|
activeLocale = deLocale;
|
|
activeCode = 'de-DE';
|
|
} else if (language === 'it') {
|
|
activeLocale = itLocale;
|
|
activeCode = 'it-IT';
|
|
} else if (language === 'nl') {
|
|
activeLocale = nlLocale;
|
|
activeCode = 'nl-NL';
|
|
} else if (language === 'pl') {
|
|
activeLocale = plLocale;
|
|
activeCode = 'pl-PL';
|
|
} else if (language === 'ru') {
|
|
activeLocale = ruLocale;
|
|
activeCode = 'ru-RU';
|
|
} else if (language === 'ko' || language === 'ko-KP') {
|
|
activeLocale = koLocale;
|
|
activeCode = 'ko-KR';
|
|
} else if (language === 'zh-CN') {
|
|
activeLocale = zhCNLocale;
|
|
activeCode = 'zh-CN';
|
|
} else if (language === 'zh-TW') {
|
|
activeLocale = zhTWLocale;
|
|
activeCode = 'zh-TW';
|
|
} else if (language === 'uk') {
|
|
activeLocale = ukLocale;
|
|
activeCode = 'uk-UA';
|
|
} else if (language === 'tr') {
|
|
activeLocale = trLocale;
|
|
activeCode = 'tr-TR';
|
|
} else if (language === 'vi') {
|
|
activeLocale = viLocale;
|
|
activeCode = 'vi-VN';
|
|
} else if (language === 'id') {
|
|
activeLocale = idLocale;
|
|
activeCode = 'id-ID';
|
|
} else if (language === 'sv') {
|
|
activeLocale = svLocale;
|
|
activeCode = 'sv-SE';
|
|
} else if (language === 'cs') {
|
|
activeLocale = csLocale;
|
|
activeCode = 'cs-CZ';
|
|
} else if (language === 'ro') {
|
|
activeLocale = roLocale;
|
|
activeCode = 'ro-RO';
|
|
} else if (language === 'el') {
|
|
activeLocale = elLocale;
|
|
activeCode = 'el-GR';
|
|
} else {
|
|
activeLocale = enUS;
|
|
activeCode = 'en-US';
|
|
}
|
|
}
|
|
|
|
/** The active date-fns locale. */
|
|
export function dateLocale(): Locale {
|
|
return activeLocale;
|
|
}
|
|
|
|
/** BCP-47 code for native `toLocale*` / `Intl` APIs. */
|
|
export function dateLocaleCode(): string {
|
|
return activeCode;
|
|
}
|
|
|
|
type DistOptions = Parameters<typeof _formatDistanceToNow>[1];
|
|
type DistStrictOptions = Parameters<typeof _formatDistanceToNowStrict>[1];
|
|
type FormatDistanceOptions = Parameters<typeof _formatDistance>[2];
|
|
type FormatOptions = Parameters<typeof _format>[2];
|
|
|
|
export function formatDistanceToNow(date: Date | number, options?: DistOptions) {
|
|
return _formatDistanceToNow(date, { ...options, locale: options?.locale ?? activeLocale });
|
|
}
|
|
|
|
export function formatDistanceToNowStrict(date: Date | number, options?: DistStrictOptions) {
|
|
return _formatDistanceToNowStrict(date, { ...options, locale: options?.locale ?? activeLocale });
|
|
}
|
|
|
|
export function formatDistance(date: Date | number, baseDate: Date | number, options?: FormatDistanceOptions) {
|
|
return _formatDistance(date, baseDate, { ...options, locale: options?.locale ?? activeLocale });
|
|
}
|
|
|
|
export function format(date: Date | number, formatStr: string, options?: FormatOptions) {
|
|
return _format(date, formatStr, { ...options, locale: options?.locale ?? activeLocale });
|
|
}
|