Skip to content

build(deps): DEP-9 — i18n + formatting stack (i18next 26, react-i18next 17, @formatjs/intl 4) (WALLET-1334)#1386

Merged
Comp0te merged 11 commits into
release/2.6.0from
WALLET-1334-dep-9-i-18-n-formatting-stack
Jul 2, 2026
Merged

build(deps): DEP-9 — i18n + formatting stack (i18next 26, react-i18next 17, @formatjs/intl 4) (WALLET-1334)#1386
Comp0te merged 11 commits into
release/2.6.0from
WALLET-1334-dep-9-i-18-n-formatting-stack

Conversation

@ost-ptk

@ost-ptk ost-ptk commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

  • Bump i18next-http-backend 3.0.5 → 4.0.0, finishing the security fix left open from DEP-2 (drops the bundled cross-fetch; MV3 service workers use native fetch)
  • Bump i18next 23 → 26, react-i18next 14 → 17, i18next-browser-languagedetector 7 → 8, @formatjs/intl 2 → 4 (pure ESM), and the dev-only i18next-conv 15 → 17
  • i18next-parser stays on hold as an independent extractor
  • Extend jest.config.js transformIgnorePatterns to transpile @formatjs/intl v4's ESM dependency tree, including the unscoped intl-messageformat transitive
  • Convert scripts/locale_extract_pot.js / locale_update_translations.js to .mjs (ESM), fixing a latent ERR_REQUIRE_ESM from i18next-conv already being ESM-only
  • No changes needed in src/libs/i18n/i18n.ts or language-detector.ts — runtime config and custom detector shape are forward-compatible with all 4 runtime bumps

Jira: https://make-software.atlassian.net/browse/WALLET-1334

Test plan

  • npm run tsc — 0 errors across the 186-file useTranslation/Trans surface
  • npm test — 12/12 suites, 32/32 tests
  • npm run lint / npm run format:check — clean (pre-existing import/no-cycle warnings only)
  • npm run build:chrome / build:firefox — all 10 locale files copied, no cross-fetch bundled (native fetch confirmed)
  • npm run build:safari — webpack bundling succeeds; fails at the pre-existing Xcode/pbxproj step (unrelated, tracked separately)
  • npm run locale:extract_pot / locale:update_translations on Node 22 — run without ERR_REQUIRE_ESM after the ESM conversion
  • npm auditi18next-http-backend/cross-fetch CVE no longer present
  • npm run ci-check — green
  • Manual smoke: switch UI language across all 10 locales in a loaded popup build

ost-ptk and others added 11 commits July 2, 2026 09:49
…a 1.5 (WALLET-1333)

Bump redux 4.2.1 -> 5.0.1, react-redux 8.1.3 -> 9.3.0, reselect 4.1.7 -> 5.2.0,
and redux-saga 1.2.3 -> 1.5.0.

Fix a dead deep-import (`react-redux/es/exports`) left over from react-redux 8,
which no longer resolves under react-redux 9's ESM `exports` map.

Wrap four previously-unmemoized selectors (selectVaultAccountsPublicKeys,
selectVaultAccountsExceptLedgersAccounts, selectAllContactsNames,
selectAllContactsPublicKeys) in createSelector, and split
selectIsAccountConnected's tuple-returning input selector into two scalar
input selectors — both were returning a new reference on every call, which
reselect 5's new default inputStabilityCheck/react-redux 9's stabilityCheck
now surface as dev-mode warnings.
…react-hook-form 7.80 (WALLET-1335)

Atomic bump of 4 coupled packages: @hookform/resolvers@5 is typed against
yup@1 and peers on react-hook-form@^7.55, so all three must move together;
react-router-dom@7 tags along since it needs the same Node 20 baseline.

Mechanical fallout fixed: 12 deep imports of
@hookform/resolvers/yup/dist/yup -> @hookform/resolvers/yup (v5 dist
layout changed), 2 deep imports of yup/es/types -> yup (v1 dropped es/),
and use-typed-location.ts's Location import moved from the now-removed
@remix-run/router package to react-router-dom's re-export.

Type reconciliation for yup 1's stricter inference (yupResolver vs
FormValues) is deliberately out of scope here and follows in later
commits.
…-cspr forms (WALLET-1335)

yup 1 infers Yup.string() without .required()/.defined() as an
optional/undefinable output, which broke yupResolver's strict typing
against useForm<FormValues>() wherever the *FormValues interface
declared the field as a plain required string.

- form-validation-rules.ts: add .required() to useCreatePasswordRule,
  useVerifyPasswordAgainstHashRule, useRepeatPasswordRule, and
  useValidSecretPhraseRule — all four back genuinely required fields
  (password, confirm password, secret phrase), so this is also a type
  fix and a validation-completeness fix.
- buy-cspr.ts: casperAmount is an unvalidated derived/display field
  (set via setValue, never user-required), so relax
  BuyCSPRFormValues.casperAmount to string | undefined instead of
  forcing .required(). yup's inferred schema type is structurally
  asymmetric between Input and Output for non-required fields, so no
  single FormValues shape satisfies Resolver<F, any, F> here; cast the
  resolver to Resolver<BuyCSPRFormValues> to bridge that gap.
…on schema (WALLET-1335)

useTransferAmountForm picks between two differently-shaped yup schemas
(erc20AmountFormSchema has paymentAmount, csprAmountFormSchema doesn't),
making amountFormSchema's type a TS union. keyof of a union is the
intersection of keys, so the resolver's inferred `names` type silently
dropped paymentAmount, breaking assignability against
Resolver<TransferAmountFormValues, ...>.

Add an unvalidated paymentAmount field to csprAmountFormSchema so both
union branches share the same key set (paymentAmount stays ERC-20-only
in practice — this is purely to align the inferred type, not a
validation change for CSPR transfers). That alone doesn't fully close
the gap because of the same Input/Output asymmetry yup produces for
unvalidated fields (as in buy-cspr.ts), so cast the resolver to
Resolver<TransferAmountFormValues> as well.
…gSchema cleanup (WALLET-1335)

Yup.mixed() with no generic now infers its .test() callback argument
as yup 1's narrow AnyPresentValue type, breaking .length/index access
on secretKeyFile's file-type validators. Give it an explicit
Yup.mixed<FileList>() — the field is bound to a native
<input type="file"> and was always a FileList at runtime, so also fix
ImportAccountFormValues.secretKeyFile from the pre-existing (wrong)
string | undefined to FileList | undefined. Same Input/Output
asymmetry as buy-cspr.ts/transfer.ts requires the same
yupResolver(...) as Resolver<FormValues> cast.

Also fix an unrelated pre-existing type bug surfaced during review:
unlock-vault/index.tsx and password-protection-page/index.tsx typed
their Worker message payload's isPasswordCorrect as
Yup.StringSchema<...> instead of boolean, which is what
verify-password-worker.ts actually posts back
(verifyPasswordAgainstHash returns Promise<boolean>). The wrong type
never caused a runtime issue (TS can't check cross-Worker message
shapes, and both call sites only did truthy/falsy checks) but was
worth correcting while touching yup types in this area.

This closes out the type-reconciliation phase of the yup 1 /
resolvers 5 / react-hook-form 7.80 bump — npm run tsc is now clean
project-wide.
…w-flow missing awaits (WALLET-1335)

rename-account.spec.ts: react-router 7 no longer unmounts the outgoing
route synchronously with the incoming one (v6 did), so after
navigate() there can be a brief window (confirmed via reproduction:
under ~10ms) where the account-settings page's stale heading and the
Home header banner both render the same account name. The
unscoped getByText(name) assertion hit a Playwright strict-mode
violation deterministically. Scope both assertions to the header
banner, which is what the test actually intends to verify post-close.

review-flow.spec.ts: 8 popupExpect(...).toBeVisible() calls across all
three tests were missing await (pre-existing since #1151, unrelated
to this bump), so the test function could return before the assertion
resolved. Add the missing awaits.
…x-saga-1-5' into WALLET-1335-dep-8-router-forms-react-router-7-yup-1-resolvers-5-react-hook-form-7-80
…dep-8-router-forms-react-router-7-yup-1-resolvers-5-react-hook-form-7-80

# Conflicts:
#	package-lock.json
#	package.json
…xt 17, @formatjs/intl 4) (WALLET-1334)

Bumps i18next-http-backend 3.0.5 -> 4.0.0 (finishes the DEP-2 security
fix, drops the bundled cross-fetch since MV3 service workers have
native fetch), i18next 23 -> 26, react-i18next 14 -> 17,
i18next-browser-languagedetector 7 -> 8, @formatjs/intl 2 -> 4
(pure ESM), and the i18next-conv dev tool 15 -> 17.
i18next-parser stays on hold as an independent extractor.

Extends jest.config.js transformIgnorePatterns to transpile
@formatjs/intl v4's ESM dependency tree, including the unscoped
intl-messageformat transitive that isn't covered by the @formatjs
scope alone.

Converts the two locale dev scripts to ESM to fix a latent
ERR_REQUIRE_ESM from i18next-conv already being ESM-only.

No changes needed in src/libs/i18n/i18n.ts or language-detector.ts —
the runtime config and custom detector shape are forward-compatible.
@ost-ptk ost-ptk self-assigned this Jul 2, 2026
@ost-ptk ost-ptk requested a review from Comp0te July 2, 2026 12:25
@Comp0te Comp0te merged commit 68525e3 into release/2.6.0 Jul 2, 2026
1 check passed
@Comp0te Comp0te deleted the WALLET-1334-dep-9-i-18-n-formatting-stack branch July 2, 2026 20:16
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