Skip to content

ops: prepare for internationalization without shipping translations (… - #97

Open
Josetic224 wants to merge 5 commits into
FinesseStudioLab:mainfrom
Josetic224:ops/i18n-preparation
Open

ops: prepare for internationalization without shipping translations (…#97
Josetic224 wants to merge 5 commits into
FinesseStudioLab:mainfrom
Josetic224:ops/i18n-preparation

Conversation

@Josetic224

Copy link
Copy Markdown

Closes #80

What this does

Preparation for i18n — no translations are shipped, English is the only active locale. The goal is to make adding a second locale a well-scoped task rather than a cross-codebase retrofit.

Changes

Message catalogue (lib/i18n/messages/en.ts)

Every user-facing string is extracted into a single file with stable, namespaced keys. Keys are a contract — they can be added freely but renaming requires a deprecation period so translation tooling does not break.

Locale infrastructure (lib/i18n/index.ts)

  • getMessages() for Server Components and API routes
  • useMessages() for Client Components
  • resolveLocale() — returns "en" now, reads from request headers/middleware when a second locale lands. No call sites change.
  • LOCALE_META — maps locale → { dir, label } for <html lang dir> and RTL support

Intl formatters (lib/i18n/formatters.ts)

Replaces every hardcoded locale format call. No locale tag is ever hardcoded at a call site.

Formatter Replaces
formatNumber(n) n.toLocaleString(), new Intl.NumberFormat("en-US")
formatCompact(n) new Intl.NumberFormat("en-US", { notation: "compact" })
formatTime(date) new Date().toLocaleTimeString()
formatDate(date) bare date strings
formatCurrency(n, currency) hand-formatted amounts
interpolate(template, values) template literals in summary strings

The currency formatter is included specifically because 1,000.50 vs 1.000,50 is a correctness problem in a billing product, not just a cosmetic one.

HTML shell (app/layout.tsx)

<html lang={locale} dir={dir}> — both attributes derived from LOCALE_META. RTL locales work by adding one entry to the map.

Layout hardening

Audited all layouts against 30–40% longer strings (German, Finnish worst-case):

  • SettlementsPanel stat grid: repeat(3, 1fr)repeat(3, minmax(0, 1fr)) — prevents label overflow in narrow columns
  • Chart data table (charts.module.css): white-space: nowrap scoped to value cells only; row-header cells (which carry translated labels) now wrap
  • Nav bar (globals.css): brand gets flex-shrink: 0; links gets min-width: 0 so the link list wraps before overflowing

All pages and components updated

All JSX strings replaced with catalogue lookups. No user-visible string change in English.

How to add a new locale later

  1. Create lib/i18n/messages/<locale>.ts with the same shape as en.ts
  2. Add the locale to the Locale union and LOCALE_META in lib/i18n/index.ts
  3. Update resolveLocale() to read from middleware/request headers

That is the complete addition path. No call sites in components or pages change.

Acceptance criteria from #80

  • Strings extracted to a catalogue with stable keys
  • i18n routing configured, English-only for now
  • All dates, numbers, and currency formatted via Intl
  • Layouts tested against significantly longer strings

Testing

npm run build passes with zero TypeScript errors and all 14 pages generated statically.
EOF
)"

@joelpeace48-cell

Copy link
Copy Markdown
Contributor

@Josetic224, ci pipeline failed

@Josetic224

Copy link
Copy Markdown
Author

I'll work on it now

@joelpeace48-cell

Copy link
Copy Markdown
Contributor

@Josetic224, ci failed

@joelpeace48-cell

Copy link
Copy Markdown
Contributor

Also having some conflicts

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.

ops: prepare for internationalization without shipping translations yet

2 participants