Skip to content

Commit 230de97

Browse files
committed
fix: cancellation date locale-driven order/clock + splash gate requires non-empty cache
1 parent dff6379 commit 230de97

3 files changed

Lines changed: 15 additions & 6 deletions

File tree

src/languages/IntlStore.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ class IntlStore {
295295
Log.warn('[IntlStore] locale chunk failed to load', {locale, error});
296296
})
297297
.finally(() => {
298-
if (IntlStore.loadToken === token) {
298+
// Non-empty cache required — else a rejected first-load would open the splash to raw path strings.
299+
if (IntlStore.loadToken === token && IntlStore.cache.size > 0) {
299300
setAreTranslationsLoading(false);
300301
}
301302
if (!localeSpan) {

src/libs/DateUtils.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -966,13 +966,11 @@ function getFormattedCancellationDate(isoDateString: string, locale: Locale): st
966966
if (Number.isNaN(instant.getTime())) {
967967
return '';
968968
}
969-
// Manual venue-wall-clock shift + format-in-UTC — sidesteps `formatInTimeZone`'s raw-offset rejection AND guarantees the rendered time can't contradict `venueTimezoneLabel`.
969+
// Pre-shifted venue instant + Intl in UTC — sidesteps raw-offset rejection, can't contradict `venueTimezoneLabel`, and lets locale drive order + clock.
970970
const venueInstant = new Date(instant.getTime() + offsetMinutes * 60_000);
971971
const nowInVenue = new Date(Date.now() + offsetMinutes * 60_000);
972-
const pattern = venueInstant.getUTCFullYear() === nowInVenue.getUTCFullYear() ? 'EEEE, MMM d h:mm a' : 'EEEE, MMM d, yyyy h:mm a';
973-
// Explicit `enUS` (never `undefined`) so a chunk-load race can't silently leak through to date-fns's global default.
974-
const dateFnsLocale = IntlStore.getDateFnsLocale(locale) ?? enUS;
975-
return `${formatInTimeZoneWithFallback(venueInstant, 'UTC', pattern, {locale: dateFnsLocale})}, ${venueTimezoneLabel}`;
972+
const datePreset = venueInstant.getUTCFullYear() === nowInVenue.getUTCFullYear() ? 'WEEKDAY_MONTH_DAY' : 'WEEKDAY_MONTH_DAY_YEAR';
973+
return `${formatIntl(locale, datePreset, venueInstant, 'UTC')} ${formatIntl(locale, 'SHORT_TIME', venueInstant, 'UTC')}, ${venueTimezoneLabel}`;
976974
}
977975

978976
/**

tests/unit/DateUtilsTest.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,16 @@ describe('DateUtils', () => {
933933
const result = DateUtils.getFormattedCancellationDate('2026-04-19', CONST.LOCALES.EN);
934934
expect(result).toBe('Sunday, Apr 19, 2026 12:00 AM, UTC');
935935
});
936+
937+
it('renders non-English locales with locale-driven field order + clock (not just token translation)', () => {
938+
jest.useFakeTimers();
939+
jest.setSystemTime(new Date('2025-01-01T00:00:00Z'));
940+
const es = DateUtils.getFormattedCancellationDate('2026-04-19T15:00:00+07:00', CONST.LOCALES.ES);
941+
// Spanish convention is day-before-month + 24h clock — a date-fns pattern would keep the English order and 12h "3:00 PM".
942+
expect(es).not.toMatch(/AM|PM/);
943+
expect(es).toContain('15:00');
944+
expect(es).toContain('GMT+7');
945+
});
936946
});
937947

938948
// CI's TZ=UTC hides "forgot the timeZone arg" regressions from output-based tests; isolate the module for a fresh memoize cache so the spy actually sees the constructor call.

0 commit comments

Comments
 (0)