Skip to content

feat(i18n): multi-language support (en/ja/de/es) foundation#1147

Open
JamieRuderman wants to merge 10 commits into
mainfrom
feature/i18n
Open

feat(i18n): multi-language support (en/ja/de/es) foundation#1147
JamieRuderman wants to merge 10 commits into
mainfrom
feature/i18n

Conversation

@JamieRuderman

Copy link
Copy Markdown
Member

Summary

Adds a working multi-language (i18n) foundation across all three platforms (web portal, Electron desktop, Capacitor mobile) plus the Electron main process. Supports English (source/fallback) + Japanese, German, Spanish, with a language picker and OS-based auto-detection.

Because untranslated strings fall back to English, the app is shippable at every step — this PR lands the full infrastructure and a proven extraction template; the remaining bulk string extraction can follow incrementally.

What's included

  • Core infra — app-wide i18next instance (frontend/src/i18n/) with app/notices/cognito namespaces. English bundled eagerly; ja/de/es lazy-loaded and code-split per locale. The existing Cognito auth module is folded into the shared instance.
  • Language preferencelanguage state in models/ui.ts mirroring the existing themeMode pattern (localStorage, persists across logout). Default system follows the OS/browser/device language via navigator.language, re-resolved each launch.
  • Auto-detectionsystem (the default) matches the OS language on all three platforms; region tags degrade gracefully (ja-JPja, unknown→en). Standard "follow OS + in-app override" convention.
  • Picker UI — selector in OptionsPage.tsx with native endonyms (日本語, Deutsch, Español).
  • Electron main — tray, menus, and native dialogs localized (electron/src/i18n.ts + electron/src/locales/*.json); language synced from the renderer through the existing preferences channel (no new IPC).
  • Dates/durationshumanizeDuration() + getLocale() in dateHelper.ts; luxon locale follows the app language.
  • Toolingnpm run i18n:extract (i18next-parser) and npm run i18n:check (CI key-parity / empty-English gate).

Cognito auth and OptionsPage are fully translated in all four languages (machine-seeded, pending professional review before production promotion).

Verified

  • Type-check clean (frontend + electron), production build passes with per-locale code-splitting.
  • Runtime: live language switching confirmed in a browser harness — interpolation ({{device}}, {{version}}) renders correctly in Japanese with adapted word order; unseeded keys fall back to English (returnEmptyString: false).
  • i18n:check passes across all 4 locales / 3 namespaces.

Remaining (follow-up work)

  • Bulk extraction of ~570 more .tsx files (order: shell/nav → core surfaces → ~41 redux-model notice messages with plural fixes → long tail). Each batch: extract → parser → Sonnet-seed → parity gate.
  • Pseudo-locale + key-parity CI job; full manual matrix (Electron tray, iOS simulator).

Decisions worth noting

  • Strict TS key-typing rejected — it forces t() return types into a union that breaks existing string usages and dynamic keys; the CI parity check covers correctness instead.
  • Parser set to never auto-prune — some cognito keys are built dynamically and would otherwise be silently deleted.

🤖 Generated with Claude Code

@aws-amplify-us-west-1

Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-1147.d20k671nqqv4kl.amplifyapp.com

@JamieRuderman

Copy link
Copy Markdown
Member Author

@codex review

@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: ed012bfcdc

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

.init({
// The resolved language is driven by redux (ui.setLanguage); 'en' here is a
// safe boot value overwritten before first paint.
lng: 'en',

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 Initialize the locale before rendering signed-out screens

For signed-out or first-time users, this hard-coded English locale is never replaced: ui.setLanguage is reached through ui.init, but auth.ts dispatches that only from signedIn, and the language picker is also behind authentication. Consequently the newly translated Cognito sign-in, signup, password-reset, and MFA screens always render in English even when the system locale is Japanese, German, or Spanish. Resolve the persisted/system language during application startup, before the authentication UI is rendered.

Useful? React with 👍 / 👎.

import { useInterval } from '../../hooks/useInterval'
import humanize, { HumanizerOptions } from 'humanize-duration'
import { HumanizerOptions } from 'humanize-duration'
import { humanizeDuration as humanize } from '../../helpers/dateHelper'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor the selected locale in every Duration branch

When the selected app language differs from navigator.language, this migration localizes only the humanized branches; the tiered month/year branches and the non-tiered date branch still pass navigator.language directly at lines 52 and 70. For example, selecting Japanese on an English OS produces Japanese short durations but English long dates in the same component. Use the new app locale helper for those native date-formatting branches as well.

Useful? React with 👍 / 👎.

import { useInterval } from '../../hooks/useInterval'
import humanize, { HumanizerOptions } from 'humanize-duration'
import { HumanizerOptions } from 'humanize-duration'
import { humanizeDuration as humanize } from '../../helpers/dateHelper'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Translate the complete relative-duration phrase

For every Duration caller using ago under a non-English app language, the new wrapper translates the duration units but lines 56, 59, and 71 still append the literal English suffix " ago", producing output such as 3 Tage ago; German and Japanese also require different relative-word ordering. Format or translate the complete relative phrase instead of appending an English fragment to the localized duration.

Useful? React with 👍 / 👎.

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