mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
fix(i18n): форматировать даты и числа локалью выбранного языка, а не браузера
This commit is contained in:
@@ -11,6 +11,7 @@ export function formatUptime(seconds: number): string {
|
||||
|
||||
import i18next from 'i18next';
|
||||
import { currencyApi, type ExchangeRates } from '../api/currency';
|
||||
import { uiLocale } from './uiLocale';
|
||||
|
||||
const LANG_CURRENCY_MAP: Record<
|
||||
string,
|
||||
@@ -60,18 +61,10 @@ export function formatPrice(kopeks: number, lang?: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
const SHORT_DATE_LOCALE_MAP: Record<string, string> = {
|
||||
ru: 'ru-RU',
|
||||
en: 'en-US',
|
||||
zh: 'zh-CN',
|
||||
fa: 'fa-IR',
|
||||
};
|
||||
|
||||
/** Date-only (dd.mm.yyyy) in the active UI locale; '-' for a null date. */
|
||||
export function formatShortDate(date: string | null): string {
|
||||
if (!date) return '-';
|
||||
const locale = SHORT_DATE_LOCALE_MAP[i18next.language] || 'ru-RU';
|
||||
return new Date(date).toLocaleDateString(locale, {
|
||||
return new Date(date).toLocaleDateString(uiLocale(), {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
|
||||
36
src/utils/uiLocale.test.ts
Normal file
36
src/utils/uiLocale.test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import i18next from 'i18next';
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
import { uiLocale } from './uiLocale';
|
||||
|
||||
describe('uiLocale', () => {
|
||||
beforeAll(async () => {
|
||||
await i18next.init({ lng: 'ru', resources: {} });
|
||||
});
|
||||
|
||||
it('маппит язык интерфейса в BCP-47 тег', async () => {
|
||||
await i18next.changeLanguage('en');
|
||||
expect(uiLocale()).toBe('en-US');
|
||||
await i18next.changeLanguage('zh');
|
||||
expect(uiLocale()).toBe('zh-CN');
|
||||
await i18next.changeLanguage('fa');
|
||||
expect(uiLocale()).toBe('fa-IR');
|
||||
});
|
||||
|
||||
it('режет региональный суффикс языка', async () => {
|
||||
await i18next.changeLanguage('en-GB');
|
||||
expect(uiLocale()).toBe('en-US');
|
||||
});
|
||||
|
||||
it('падает в ru-RU для неизвестного языка', async () => {
|
||||
await i18next.changeLanguage('xx');
|
||||
expect(uiLocale()).toBe('ru-RU');
|
||||
});
|
||||
|
||||
it('дата форматируется по языку интерфейса, а не браузера', async () => {
|
||||
await i18next.changeLanguage('en');
|
||||
const date = new Date('2026-07-13T12:00:00Z');
|
||||
expect(date.toLocaleDateString(uiLocale(), { timeZone: 'UTC' })).toBe('7/13/2026');
|
||||
await i18next.changeLanguage('ru');
|
||||
expect(date.toLocaleDateString(uiLocale(), { timeZone: 'UTC' })).toBe('13.07.2026');
|
||||
});
|
||||
});
|
||||
18
src/utils/uiLocale.ts
Normal file
18
src/utils/uiLocale.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import i18next from 'i18next';
|
||||
|
||||
const LOCALE_MAP: Record<string, string> = {
|
||||
ru: 'ru-RU',
|
||||
en: 'en-US',
|
||||
zh: 'zh-CN',
|
||||
fa: 'fa-IR',
|
||||
};
|
||||
|
||||
/**
|
||||
* BCP-47 тег активного языка интерфейса для toLocale*-методов и Intl-API.
|
||||
* Без него даты/числа форматируются локалью браузера (или жёстким 'ru-RU')
|
||||
* и не реагируют на смену языка в приложении.
|
||||
*/
|
||||
export function uiLocale(): string {
|
||||
const lang = (i18next.language || '').split('-')[0];
|
||||
return LOCALE_MAP[lang] || 'ru-RU';
|
||||
}
|
||||
Reference in New Issue
Block a user