fix: normalize "original" locale to "en" for datetime formatting#4472
fix: normalize "original" locale to "en" for datetime formatting#4472leonardthethird wants to merge 4 commits into
Conversation
When users select "Untranslated" mode, the locale is set to "original". This is not a valid BCP 47 locale tag, so browser Intl APIs fall back to the OS language, causing mixed-language strings like "Revealed el próximo mes". Add a normalizeIntlLocale() utility that maps "original" to "en" and apply it consistently across all components that pass locale to intlFormat, intlFormatDistance, or the <relative-time> element. Fixes #4122 Co-authored-by: leonardthethird <leonardthethird@users.noreply.github.com>
📝 WalkthroughWalkthroughCentralizes locale normalization for Intl date formatting, updates several UI components to use the shared helpers, and adds an ESLint rule preventing direct use of the restricted date-fns Intl imports. ChangesLocale-normalized formatting
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
front_end/src/utils/formatters/date.ts (1)
192-198: Add a small regression test aroundnormalizeIntlLocale.This helper is now the single guard for Untranslated mode, so a focused test for
"original" -> "en"plus one passthrough locale would make the bugfix harder to regress.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@front_end/src/utils/formatters/date.ts` around lines 192 - 198, Add a small unit test suite for normalizeIntlLocale: import normalizeIntlLocale from its module, then add two tests — one asserting normalizeIntlLocale("original") returns "en" and another asserting normalizeIntlLocale("fr") (or "en-US") returns the same input (passthrough). Use the project's test framework (e.g., Jest) and place the test alongside other utils tests so it runs in CI; name the tests clearly like "returns en for 'original' (Untranslated mode)" and "passes through valid locales".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@front_end/src/utils/formatters/date.ts`:
- Around line 192-198: Add a small unit test suite for normalizeIntlLocale:
import normalizeIntlLocale from its module, then add two tests — one asserting
normalizeIntlLocale("original") returns "en" and another asserting
normalizeIntlLocale("fr") (or "en-US") returns the same input (passthrough). Use
the project's test framework (e.g., Jest) and place the test alongside other
utils tests so it runs in CI; name the tests clearly like "returns en for
'original' (Untranslated mode)" and "passes through valid locales".
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7c986d69-6129-4fac-b08b-7fe3c66e37ad
📒 Files selected for processing (5)
front_end/src/app/(main)/(home)/components/research_and_updates.tsxfront_end/src/components/consumer_post_card/upcoming_cp.tsxfront_end/src/components/cp_reveal_time/cp_reveal_time.tsxfront_end/src/components/ui/local_daytime.tsxfront_end/src/utils/formatters/date.ts
🚀 Preview EnvironmentYour preview environment is ready!
Details
ℹ️ Preview Environment InfoIsolation:
Limitations:
Cleanup:
|
|
@claude deal with failing unit tests / frontend checks. |
|
Claude finished @leonardthethird's task in 1m 35s —— View job Fixed failing frontend lint check
Root cause: The |
…ates Co-authored-by: leonardthethird <leonardthethird@users.noreply.github.com>
|
@claude If we introduce a new place where Intl formatting is applied, developers will need to manually add |
|
Claude finished @ncarazon's task in 1m 42s —— View job Analysis: centralizing
|
|
@claude let's proceed with option A |
|
Claude finished @ncarazon's task in 5m 44s —— View job Implementing option A: wrap direct
|
…ization Adds `formatIntlDate` / `formatIntlDistance` in @/utils/formatters/date so `normalizeIntlLocale` is applied inside the wrapper, and callers stop needing to normalize the "original" locale at every site. Also normalizes the `lang` prop inside RelativeTime, since the underlying <relative-time> web component uses it for Intl formatting and would otherwise hit the same OS-language-fallback bug. An ESLint `no-restricted-imports` rule now blocks direct imports of `intlFormat` / `intlFormatDistance` from date-fns so a future contributor gets a lint error instead of a subtle locale bug in Untranslated mode. Co-authored-by: Nikita <93587872+ncarazon@users.noreply.github.com>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
front_end/src/utils/formatters/date.ts (1)
96-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider having
formatDate/formatDatetime/formatRelativeDatedelegate to the new wrappers.
formatIntlDate/formatIntlDistancewere added specifically to centralize thenormalizeIntlLocale+intlFormat/intlFormatDistancecall pattern, butformatDate,formatDatetime, andformatRelativeDatestill callintlFormat/intlFormatDistancedirectly with an inlinenormalizeIntlLocale(locale)rather than reusing the new wrappers. This leaves two parallel implementations of the same call pattern in the same file, so any future change to the wrapping logic (e.g. adding a fallback, logging, or additional normalization) won't automatically propagate to these three functions.♻️ Example refactor for formatRelativeDate
- if (Math.abs(delta) < relCutoff) { - dateStr = intlFormatDistance(date, now, { - locale: normalizeIntlLocale(locale), - numeric: "always", - style: short ? "short" : "long", - }); + if (Math.abs(delta) < relCutoff) { + dateStr = formatIntlDistance(locale, date, now, { + numeric: "always", + style: short ? "short" : "long", + });A similar change applies to
formatDate/formatDatetimeusingformatIntlDate.Also applies to: 110-110, 134-140, 198-235
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@front_end/src/utils/formatters/date.ts` at line 96, `formatDate`, `formatDatetime`, and `formatRelativeDate` still duplicate the `normalizeIntlLocale` + `intlFormat`/`intlFormatDistance` pattern instead of using the new wrappers. Update these functions to delegate to `formatIntlDate` and `formatIntlDistance` so the shared locale normalization and formatting logic stays centralized in `formatIntlDate`/`formatIntlDistance`, and remove the inline `normalizeIntlLocale(locale)` calls from the direct formatter invocations.front_end/.eslintrc.json (1)
43-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winESLint rule also needs the
date-fnssubpath entrypoints
no-restricted-importsonly matches the baredate-fnsspecifier, sodate-fns/intlFormatanddate-fns/intlFormatDistancewould still bypass this guard. Add explicit restrictions for both subpaths too.Suggested addition
"paths": [ { "name": "date-fns", "importNames": ["intlFormat", "intlFormatDistance"], "message": "Use formatIntlDate / formatIntlDistance from `@/utils/formatters/date` so the 'original' locale (Untranslated mode) is normalized for Intl APIs." + }, + { + "name": "date-fns/intlFormat", + "message": "Use formatIntlDate from `@/utils/formatters/date` so the 'original' locale (Untranslated mode) is normalized for Intl APIs." + }, + { + "name": "date-fns/intlFormatDistance", + "message": "Use formatIntlDistance from `@/utils/formatters/date` so the 'original' locale (Untranslated mode) is normalized for Intl APIs." } ]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@front_end/.eslintrc.json` around lines 43 - 54, The no-restricted-imports rule only blocks the bare date-fns import, so date-fns/intlFormat and date-fns/intlFormatDistance can still slip through. Update the ESLint config in the no-restricted-imports paths list to explicitly restrict both subpath entrypoints as well, alongside the existing date-fns restriction. Keep the existing message pointing developers to formatIntlDate and formatIntlDistance from `@/utils/formatters/date`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@front_end/.eslintrc.json`:
- Around line 43-54: The no-restricted-imports rule only blocks the bare
date-fns import, so date-fns/intlFormat and date-fns/intlFormatDistance can
still slip through. Update the ESLint config in the no-restricted-imports paths
list to explicitly restrict both subpath entrypoints as well, alongside the
existing date-fns restriction. Keep the existing message pointing developers to
formatIntlDate and formatIntlDistance from `@/utils/formatters/date`.
In `@front_end/src/utils/formatters/date.ts`:
- Line 96: `formatDate`, `formatDatetime`, and `formatRelativeDate` still
duplicate the `normalizeIntlLocale` + `intlFormat`/`intlFormatDistance` pattern
instead of using the new wrappers. Update these functions to delegate to
`formatIntlDate` and `formatIntlDistance` so the shared locale normalization and
formatting logic stays centralized in `formatIntlDate`/`formatIntlDistance`, and
remove the inline `normalizeIntlLocale(locale)` calls from the direct formatter
invocations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 444f4c6a-4a60-4dce-9f30-d633a3984272
📒 Files selected for processing (7)
front_end/.eslintrc.jsonfront_end/src/app/(main)/(home)/components/research_and_updates.tsxfront_end/src/components/consumer_post_card/upcoming_cp.tsxfront_end/src/components/cp_reveal_time/cp_reveal_time.tsxfront_end/src/components/ui/local_daytime.tsxfront_end/src/components/ui/relative_time.tsxfront_end/src/utils/formatters/date.ts
|
You removed fallback to I think we should add it back and keep the comment. |
|
Claude finished @ncarazon's task in 1m 3s —— View job Restored the "original" → "en" fallback in
|
Restore the pre-existing workaround (and its comment) that defaults the locale to "en" when it is "original", to avoid the server/client hydration mismatch on `localValue`. The centralized `normalizeIntlLocale` covers Intl call sites, but this component-level guard is what keeps SSR and CSR producing the same string here. Co-authored-by: Nikita <93587872+ncarazon@users.noreply.github.com>
|
@claude can you resolve merge conflicts with |
|
Claude finished @ncarazon's task in 10m 5s —— View job Merge conflict analysisI can't perform the actual merge in this environment (both Conflict inventoryOnly three files really matter — the rest of main's ~964 changed files are additions/edits our branch never touched, so git will auto-take main's version there. 1. Main removed the legacy JSON config and switched to flat config ( 2. Main deleted the whole file (the homepage was restructured — a batch of 3. Main changed The other four PR files ( The
|
Fix datetime strings being partially localized based on OS language when site is in "Untranslated" mode.
When users select "Untranslated" mode, the locale is set to "original" which is not a valid BCP 47 tag. Browser Intl APIs fall back to OS language, causing mixed-language strings like "Revealed el próximo mes".
Adds a
normalizeIntlLocale()utility that maps "original" to "en" and applies it consistently across all components using Intl-based date formatting.Fixes #4122
Generated with Claude Code
Summary by CodeRabbit
"original"locale.date-fnsIntl formatting.