Skip to content

fix(widget): re-render i18n consumers when strings are added (JUM-1733) - #918

Merged
chybisov merged 4 commits into
mainfrom
fix/jum-1733-default-language-strings
Oct 29, 2026
Merged

chybisov merged 4 commits into
mainfrom
fix/jum-1733-default-language-strings

Conversation

@chybisov

@chybisov chybisov commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Which Linear task is linked to this PR?

JUM-1733

Why was it implemented this way?

The problem

With languages.default: 'fr' and nothing saved, the widget tabs and often Send, Receive and Select can stay in English. The rest of the widget is French. We saw this on jumper.xyz/fr.

The cause

  1. With languages.default: 'fr' and nothing saved, I18nProvider creates the i18n instance with lng: 'fr' but without French strings. The first render shows the English fallback.
  2. When fr.json arrives, the widget adds it with addResourceBundle. The language does not change, so no languageChanged event fires.
  3. react-i18next re-renders useTranslation consumers only on languageChanged, because bindI18nStore is '' by default. A memoized component, such as the navigation tabs, keeps the English text.

The same thing occurs for a returning user with old saved strings. The widget loads the new strings and adds them, but memoized components keep the old text. The result depends on timing.

The fix

  • The i18n instance sets react: { bindI18nStore: 'added' }. All useTranslation consumers now re-render when the instance adds strings.
  • I18nProvider adds a changed language cache in an effect, not in useMemo. The added event then fires after the commit and does not update other components during a render. A ref skips the cache that the instance already has at creation.
  • I18nProvider creates the i18n instance one time with useState. Before, a useMemo with a ref did the same job.

Alternatives that I did not use

  • Call changeLanguage again after the widget adds a bundle. This was the first version of this PR. It fixes the fresh load, but not the old saved strings. It also fails when the bundle arrives through the store rehydration before the hook runs.

How I checked it

  • I18nProvider.test.tsx has 3 tests: a fresh load, and old saved strings with and without languages.default. All 3 fail on main and pass with the fix.
  • Jumper frontend with the same fix in the Jumper fork (next dev) with a proxy that delays the widget's French chunk by 3 s:
    • Jumper widget 4.20.0 (the same i18n code as main here): 3 of 6 fresh loads of /fr showed English tabs and an English Send / Receive / Select card.
    • This branch: 10 of 10 fresh loads showed French. 3 of 3 loads with old saved strings showed the new strings.
    • A language switch from Spanish to French in the site menu changed the widget at once.
  • Upstream playground (widget-playground-vite) with the navigation tabs and a proxy that delays fr.json and es.json by 3 s:
    • main: 2 of 4 fresh loads with languages.default: 'fr' stayed English (tabs and the From / To / Send labels).
    • This branch: 10 of 10 fresh loads showed French. 4 of 4 loads with old saved strings showed the new strings.
    • Custom languageResources for fr and en showed in all 4 loads.
    • With languages.allow: ['fr', 'es'], the menu showed only these 2 languages. A switch to Spanish changed the widget at once.
    • The fix adds 3 to 4 widget commits on a fresh load. The render time stayed in the same range as main (about 450 ms in dev mode).

Known items that this PR does not change

  • Each language switch adds the same strings two times: once in useLanguages and once in the provider effect. This gives one extra re-render for each switch.
  • availableLanguages in useLanguages does not update when a bundle loads later, because i18n.store.data keeps the same identity.
  • On reload, languages.default replaces a language that the user saved.

Visual showcase (Screenshots or Videos)

Not applicable. The Linear task has screenshots of the bug.

Checklist before requesting a review

  • I have performed a self-review and testing of my code.
  • This pull request is focused and addresses a single problem.
  • If this PR modifies the Widget API or adds new features that require documentation, I have updated the documentation in the public-docs repository. (Not applicable: no API change.)

@changeset-bot

changeset-bot Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c927d15

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@lifi/widget Patch
@lifi/widget-checkout Patch
nft-checkout Patch
tanstack-router-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle size

✅ No significant changes. Every check is within budget.

@lifi/widget

Import Base PR Change Budget used
⚪ LiFiWidget (without translations) 273.8 kB 273.8 kB 🔻 −0.02 kB (−0.0%) ▰▰▰▰▰▰▰▰▰▱ 85%
⚪ English translations (always loaded) 7.44 kB 7.44 kB — ▰▰▰▰▰▰▰▱▱▱ 74%
⚪ /shared (all exports) 173.1 kB 173.2 kB 🔺 +0.13 kB (+0.1%) ▰▰▰▰▰▰▰▰▱▱ 84%
⚪ /queries (all exports) 2.14 kB 2.14 kB — ▰▰▰▰▰▰▰▱▱▱ 71%
⚪ /skeleton (WidgetSkeleton) 44.0 kB 44.0 kB — ▰▰▰▰▰▰▰▰▱▱ 83%

@lifi/wallet-management

Import Base PR Change Budget used
⚪ all exports (without translations) 71.2 kB 71.2 kB — ▰▰▰▰▰▰▰▰▱▱ 81%

@lifi/widget-light

Import Base PR Change Budget used
⚪ LiFiWidgetLight 1.21 kB 1.21 kB — ▰▰▰▰▰▰▱▱▱▱ 60%

Providers

Import Base PR Change Budget used
⚪ ethereum 98.7 kB 98.7 kB — ▰▰▰▰▰▰▰▰▱▱ 84%
⚪ solana 31.8 kB 31.8 kB — ▰▰▰▰▰▰▰▰▱▱ 82%
⚪ bitcoin 89.0 kB 89.0 kB — ▰▰▰▰▰▰▰▰▱▱ 83%
⚪ sui 99.2 kB 99.2 kB — ▰▰▰▰▰▰▰▰▰▱ 89%
⚪ tron (incl. WalletConnect loaded on demand) 746.3 kB 746.3 kB — ▰▰▰▰▰▰▰▰▰▱ 85%
⚪ stellar 176.9 kB 176.9 kB — ▰▰▰▰▰▰▰▰▱▱ 83%
⚪ zcash 6.59 kB 6.59 kB — ▰▰▰▰▰▰▰▰▱▱ 82%

Full integration

Import Base PR Change Budget used
⚪ LiFiWidget + all providers + their peers (incl. on-demand code) 2022.8 kB 2021.9 kB 🔻 −0.84 kB (−0.0%) ▰▰▰▰▰▰▰▰▰▱ 88%
How to read this
  • Sizes are minified + brotli, measured with size-limit the way an app bundles the import: tree-shaken, with all dependencies included except the ones in the check's ignore list (packages the app already has, such as peers).
  • Code behind a dynamic import() is counted too, unless the check's name says otherwise.
  • 🟠 / 🟢 mark a change of at least 512 B and at least 1% or 5.00 kB. 🔴 means the check is over its budget.
  • Budgets live in .size-limit.json. The base commit is measured with the same checks as the PR.

c927d15 merged into b1dd29c · workflow run

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

✅ E2E Dev Smoke — passing

Check Result
Dev server start (pnpm dev) ✅ started
Smoke tests ✅ passed

4 passed · 0 failed · 0 skipped · 23s

View run

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

E2E Playground results

passed  164 passed

Details

stats  164 tests across 11 suites
duration  2 minutes, 31 seconds
commit  c927d15

📥 Download full HTML report (open the run → Artifacts → playwright-report)

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

E2E Examples — all passed

All examples passed in the latest run.

@chybisov chybisov changed the title fix(widget): show the default language strings on a fresh load (JUM-1733) fix(widget): re-render i18n consumers when strings are added (JUM-1733) Oct 28, 2026
@chybisov
chybisov merged commit 7167584 into main Oct 29, 2026
35 checks passed
@chybisov
chybisov deleted the fix/jum-1733-default-language-strings branch October 29, 2026 15:18
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.

1 participant