From 8b1224a15459204a8c0089f3c1c6a00e7ba554f4 Mon Sep 17 00:00:00 2001 From: Yxq Date: Tue, 20 Oct 2026 16:34:46 +0800 Subject: [PATCH] fix: read the date locale when formatting instead of at import time `const local = getLocale()` runs once, when the module is first imported, and the result is frozen for the lifetime of the bundle. Switching the UI language afterwards leaves every "2 months ago" style label in the previous language until the page is reloaded. Resolve the locale through a `currentLocale()` helper at call time instead; both formatToNow and formatToNowStrict use it and the formatting itself is untouched. The new test mocks getLocale, formats once with en, switches the mock to zh and formats again in the same module instance: 1 failed before the change, 4 passed after it. Signed-off-by: Yxq --- apps/web/src/common/utils/time.test.ts | 18 ++++++++++++++++++ apps/web/src/common/utils/time.ts | 8 +++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/web/src/common/utils/time.test.ts b/apps/web/src/common/utils/time.test.ts index 8479d46476..53af55310c 100644 --- a/apps/web/src/common/utils/time.test.ts +++ b/apps/web/src/common/utils/time.test.ts @@ -21,3 +21,21 @@ describe('time', () => { expect(formatToNowStrict('2022-09-28T02:11:54Z')).toBe('2 months'); }); }); + +describe('time locale', () => { + it('follows the locale that is active at call time', () => { + let locale = 'en'; + jest.isolateModules(() => { + jest.doMock('@common/utils/getLocale', () => ({ + __esModule: true, + default: () => locale, + })); + const mod = require('./time'); + const en = mod.formatToNow('2022-09-28T02:11:54Z'); + locale = 'zh'; + const zh = mod.formatToNow('2022-09-28T02:11:54Z'); + expect(en).toContain('about'); + expect(zh).not.toEqual(en); + }); + }); +}); diff --git a/apps/web/src/common/utils/time.ts b/apps/web/src/common/utils/time.ts index fce7917ae1..69a4a3d10e 100644 --- a/apps/web/src/common/utils/time.ts +++ b/apps/web/src/common/utils/time.ts @@ -8,8 +8,10 @@ import { enGB, zhCN } from 'date-fns/locale'; import getLocale from '@common/utils/getLocale'; const FORMAT_YMD = 'yyyy-MM-dd'; -const local = getLocale(); const languages = { en: enGB, zh: zhCN }; +// Read the locale at call time: capturing it in a module-level constant froze the +// date language for the lifetime of the bundle. +const currentLocale = () => languages[getLocale()]; export const formatISO = (s: string, layout?: string) => { if (!s) return ''; @@ -20,13 +22,13 @@ export const formatISO = (s: string, layout?: string) => { export const formatToNow = (s: string) => { if (!s) return ''; const p = parseJSON(s); - return formatDistanceToNow(p, { locale: languages[local] }); + return formatDistanceToNow(p, { locale: currentLocale() }); }; export const formatToNowStrict = (s: string) => { if (!s) return ''; const p = parseJSON(s); - return formatDistanceToNowStrict(p, { locale: languages[local] }); + return formatDistanceToNowStrict(p, { locale: currentLocale() }); }; export const isTimestampWithinSec = (ts: string, sec: number): boolean => {