Skip to content

feat(i18n): try browser language before English fallback#9060

Closed
emsplendit wants to merge 2 commits into
makeplane:previewfrom
emsplendit:i18n/auto-detect-browser-language
Closed

feat(i18n): try browser language before English fallback#9060
emsplendit wants to merge 2 commits into
makeplane:previewfrom
emsplendit:i18n/auto-detect-browser-language

Conversation

@emsplendit
Copy link
Copy Markdown

@emsplendit emsplendit commented May 12, 2026

Description

Self-hosted Plane instances currently always boot every user into English on first visit. This PR changes the i18n initialization so that, when no saved userLanguage preference exists in localStorage, the store consults navigator.languages and selects the first supported match before falling back to English.

Selection priority (new behavior in bold):

  1. localStorage["userLanguage"] if it holds a supported code (unchanged)
  2. First supported entry in navigator.languages (or navigator.language as fallback), with regional fallthrough — e.g. pt-BR matches exactly, es-AR falls through to es
  3. FALLBACK_LANGUAGE (English) (unchanged)

Once a user explicitly picks a language, the choice is persisted to localStorage as before and takes precedence on future visits. Behavior for users with an existing saved preference is therefore unchanged.

Scope of the change

  • Single file: packages/i18n/src/store/index.ts
  • New private method detectBrowserLanguage() (~25 lines)
  • One added call site inside initializeLanguage(), between the localStorage check and the English fallback
  • One docstring updated to reflect the new behavior
  • No API changes, no new dependencies, no migrations
  • SSR-safe (guards on typeof navigator === "undefined")

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

N/A — the change is invisible to anyone who already has a saved language preference, and new visitors simply land in their browser's locale instead of English.

Test Scenarios

Manually verified the four meaningful cases. The @plane/i18n package has no test infrastructure (no jest/vitest in devDependencies, no test script), so this PR is code-only; happy to bootstrap a test setup if reviewers want one, but that's arguably a separate concern.

  • Browser navigator.languages = ["es-AR", "es", "en"], no localStorage entry → loads in Spanish
  • Browser navigator.languages = ["fr-CA", "fr"], no localStorage entry → loads in French
  • Browser navigator.languages = ["xx-YY"] (unsupported), no localStorage entry → falls through to English
  • localStorage has userLanguage=ja already → loads Japanese, browser preference ignored (unchanged behavior)

References

No existing open issue matches exactly. Closest tangentially-related is #8581 ("DateTime Format is not taking effect"), about date/time format persistence rather than UI language. Happy to file a tracking issue first if maintainers prefer that workflow.

Summary by CodeRabbit

  • New Features

    • App now detects and applies the browser's language preferences on startup, with intelligent fallback to related language variants when no exact match is supported.
    • Detected languages are applied for the session (not persisted) by default when derived from the browser.
  • Behavior Change

    • You can now choose to change the app language without saving the preference; language attribute and in-memory cache still update immediately.

Review Change Stack

When localStorage has no valid 'userLanguage' entry, the i18n store
now consults navigator.languages and selects the first supported
match instead of going straight to the English fallback. Region
subtags fall through (e.g. es-AR -> es). Behavior for users with an
existing localStorage preference is unchanged.
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 12, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a5c74812-61bb-4e0e-bfc8-4c5c70a2f222

📥 Commits

Reviewing files that changed from the base of the PR and between 166f0fd and e91670a.

📒 Files selected for processing (1)
  • packages/i18n/src/store/index.ts

📝 Walkthrough

Walkthrough

initializeLanguage() now falls back to browser-preference detection when localStorage contains no valid locale; a new detectBrowserLanguage() scans navigator.languages (then navigator.language) for supported exact matches and base-language fallbacks. setLanguage(lng, persistPreference = true) can now skip persisting the choice to localStorage.

Changes

Browser Language Detection Fallback

Layer / File(s) Summary
Language initialization flow with browser fallback
packages/i18n/src/store/index.ts
Updated initializeLanguage() documentation and control flow to attempt browser language detection after rejecting invalid stored locales and before FALLBACK_LANGUAGE.
Browser language detection implementation
packages/i18n/src/store/index.ts
New private detectBrowserLanguage() helper scans navigator.languages (or navigator.language fallback) for the first supported language using exact-match validation, then base-language subtag fallback.
Optional persistence in setLanguage
packages/i18n/src/store/index.ts
Public setLanguage signature now accepts persistPreference (default true); when false, the store updates runtime state and document lang without writing to localStorage.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Soft paws tap the browser's tongue,
I listen to the languages sung.
If storage sleeps or flags are shy,
I follow whispers from the sky.
A session choice, not scribed in stone.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: implementing browser language detection as a fallback before English, which is the primary feature of this PR.
Description check ✅ Passed The description comprehensively covers all required template sections with clear detail about the feature, testing approach, and scope, despite the package lacking automated test infrastructure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@packages/i18n/src/store/index.ts`:
- Around line 64-71: The initialization currently calls setLanguage(detected)
and setLanguage(FALLBACK_LANGUAGE) which persist to localStorage; change this so
auto-detected or fallback locales are applied without persisting. Add an
optional persist flag to setLanguage (e.g., setLanguage(lang: string, persist =
true)) or add a helper applyLanguageWithoutPersist, update calls from
detectBrowserLanguage and the fallback path to call with persist = false (or use
the new helper), and ensure setLanguage still persists when called by explicit
user preference flows.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 190429f9-b35f-4eee-aec1-b41104f0354e

📥 Commits

Reviewing files that changed from the base of the PR and between 4225bc5 and 166f0fd.

📒 Files selected for processing (1)
  • packages/i18n/src/store/index.ts

Comment thread packages/i18n/src/store/index.ts Outdated
setLanguage() previously always wrote to localStorage, turning the
auto-detect and fallback paths into a one-shot stamp. Add a
persistPreference flag (default true) and pass false from
initializeLanguage so the browser preference is re-evaluated on each
visit until the user makes an explicit choice.

Per CodeRabbit review.
@emsplendit
Copy link
Copy Markdown
Author

Closing this PR.

#8898 migrated packages/i18n from the MobX TranslationStore to react-i18next, which removed the file this PR modified. Rebasing on top of preview would essentially be a rewrite, and the new architecture allows a smaller, cleaner change (the init path no longer routes through setLanguage, so no persistPreference flag is needed).

I'll open a fresh PR against the post-#8898 codebase that does the same thing — try navigator.languages (exact match, then base subtag) before falling back to English — as a single-file diff in packages/i18n/src/core/instance.ts.

Thanks for the reviews so far.

@emsplendit emsplendit closed this May 14, 2026
@emsplendit emsplendit deleted the i18n/auto-detect-browser-language branch May 14, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants