Skip to content

add i18n support - #6796

Open
benedikt-bartscher wants to merge 18 commits into
reflex-dev:mainfrom
benedikt-bartscher:add-i18n
Open

add i18n support#6796
benedikt-bartscher wants to merge 18 commits into
reflex-dev:mainfrom
benedikt-bartscher:add-i18n

Conversation

@benedikt-bartscher

@benedikt-bartscher benedikt-bartscher commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

closes #2339

@codspeed-hq

codspeed-hq Bot commented Jul 17, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing benedikt-bartscher:add-i18n (af6e1b3) with main (7e365ce)2

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (69ef304) during the generation of this report, so 7e365ce was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a full internationalization (i18n) system for Reflex as a new reflex-i18n package, closing issue #2339. It adds both client-side (rx.t()) and server-side (gettext/ngettext/pgettext) translation, locale-aware number/date/currency formatting, optional URL-based routing (PathPrefixRouting), and a reflex i18n CLI for catalog extraction and validation.

Key additions:

  • reflex-i18n package: I18nPlugin compiles .po → JS catalog modules, fans pages into per-locale routes, and registers the I18nProvider into the React tree. rx.t() collects static messages at compile time; gettext() translates server-side using a contextvar set by the new event_scope mechanism.
  • reflex-base changes: A new event_scope hook lets plugins set per-event ambient context (locale ContextVar) around both handler execution and delta resolution; dep_tracking.py gains bytecode-level implicit dependency detection so computed vars that call gettext() automatically depend on I18nState.locale.
  • reflex core changes: reload_state_module is refactored to purge dangling dependency-graph edges when states are removed; plugin CLI commands are loaded via entry points with proper type-checking and error isolation.

Confidence Score: 5/5

Safe to merge; all previously raised issues have been addressed and no new blocking defects were found.

The core mechanics — event-scope locale injection, implicit dependency tracking via bytecode scanning, catalog compilation, and client-side provider — are all well-designed with solid unit and integration test coverage. The two remaining findings are quality/developer-experience concerns that do not affect production correctness.

Files Needing Attention: runtime.py (stale translation cache on hot-reload) and state.py (set_locale no-op with URL routing) are worth a second look before GA, but neither blocks production correctness.

Important Files Changed

Filename Overview
packages/reflex-i18n/src/reflex_i18n/plugin.py New plugin wiring i18n into compilation: catalog emission, route expansion per locale, hreflang injection. Well-structured with clean separation of static/dynamic catalog concerns.
packages/reflex-i18n/src/reflex_i18n/runtime.py Server-side gettext helpers and locale formatting via Babel. Module-level _translations cache is not cleared on hot-reload, causing server/client translation divergence during development.
packages/reflex-i18n/src/reflex_i18n/state.py I18nState holds locale cookie; _locale_scope registers per-event locale context. set_locale() is silently a no-op with URL routing due to _switchLocale=null in that mode.
packages/reflex-i18n/src/reflex_i18n/_web/i18n.js Client runtime: I18nProvider, lazy catalog loading, Intl formatter caching, URL routing helpers. Secure cookie attribute conditionally added. Promise rejection caught. RTL detection included.
packages/reflex-base/src/reflex_base/event/processor/scope.py New per-event ambient context mechanism with ExitStack-based cleanup. Mid-loop exception safety correctly handled with try/except in aenter.
packages/reflex-base/src/reflex_base/vars/dep_tracking.py Adds implicit dependency tracking for LOAD_GLOBAL/LOAD_DEREF bytecode instructions, enabling gettext-family calls to imply a dep on I18nState.locale without explicit annotation.
packages/reflex-base/src/reflex_base/event/processor/base_state_processor.py Wraps both handler execution and delta resolution in event_scope so locale contextvar is active during computed-var recomputation. No-op fast path when no providers are registered.
packages/reflex-i18n/src/reflex_i18n/catalog.py Compiles .po catalogs to JS modules. Plural expression whitelisted via regex before embedding in generated code. Missing translations warned with truncation at 10.
packages/reflex-i18n/src/reflex_i18n/routing.py Locale routing strategy abstraction with PathPrefixRouting implementation. Correctly handles default-at-root, delocalization, and locale_of path detection.
reflex/state.py Refactors reload_state_module to accumulate removed state names and purge dangling dep-graph edges, fixing stale dependency crashes on hot-reload.
reflex/reflex.py Adds _add_plugin_cli_commands() to load click.Command entry points from installed packages. Correctly guards against non-Command objects and failed loads.
packages/reflex-i18n/pyproject.toml New package manifest with a TODO noting the reflex-base/reflex dependencies must be pinned to released versions before publishing.
packages/reflex-i18n/src/reflex_i18n/vars.py rx.t() translation var: validates literal message at call site, registers to compile-time message registry, and emits JS calls to useTranslation hook.
reflex/init.py Adds lazy-loaded i18n symbols (I18nConfig, I18nPlugin, I18nState, gettext, ngettext, pgettext, set_locale, t) to the rx.* namespace via _MAPPING.

Reviews (13): Last reviewed commit: "Merge remote-tracking branch 'upstream/m..." | Re-trigger Greptile

Comment thread packages/reflex-base/src/reflex_base/event/processor/scope.py
Comment thread packages/reflex-i18n/src/reflex_i18n/runtime.py
Comment thread packages/reflex-i18n/src/reflex_i18n/_web/i18n.js
Comment thread packages/reflex-i18n/src/reflex_i18n/_web/i18n.js
Comment thread reflex/__init__.py
@benedikt-bartscher
benedikt-bartscher marked this pull request as ready for review July 18, 2026 11:09
@benedikt-bartscher
benedikt-bartscher requested review from a team and Alek99 as code owners July 18, 2026 11:09
@benedikt-bartscher benedikt-bartscher changed the title first i18n wip add basic i18n support Jul 18, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b9a5f5342

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +12 to +13
"reflex-base >= 0.9.7.dev0",
"reflex >= 0.9.7.dev0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove development pins before publishing reflex-i18n

With the new dispatch-release option, selecting reflex-i18n will create a release and trigger .github/workflows/publish.yml, whose publish job runs scripts/check_min_deps.py --check-dev-pins "$PACKAGE" and explicitly rejects published dependencies containing *.dev pins. Because these two requirements are part of reflex-i18n's published metadata, the publish workflow for any reflex-i18n release will fail at that gate until they are pinned to released versions.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll leave this open for the maintainers

Comment thread reflex/reflex.py
@benedikt-bartscher benedikt-bartscher changed the title add basic i18n support add i18n support Jul 18, 2026
@benedikt-bartscher

Copy link
Copy Markdown
Contributor Author

@masenf could you please take a brief look at this from an architectural design and DX perspective? it's totally okay if you have smth completly different in mind

@masenf masenf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@benedikt-bartscher thanks for adding this and sorry for the review back-up. i did an initial look and i think the added extension points make sense and the API feels nice.

one potential issue i see affects SEO performance of the SSG static versions of the site -- since the translation depends on state, other languages might not be indexable by crawlers.

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.

Internationalization (I18N) and localization (L10N) (Feature Request)

2 participants