Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

15 — Flutter Implementation Guide (i18n Module)

Maps the backend i18n contract onto the Flutter app. Architecture baseline per 00-shared/11_Flutter_App_Architecture.md; state per 13_State_Management.md.


1. Contract recap (from source)

  • Catalog endpoint: GET /api/v1/i18n/messages?locale= → flat Map<String,String> (i18n.controller.ts:13-17); fallback English (i18n.service.ts:47-49).
  • Locales: en, hi, ta, te, kn, ml, gu, mr, bn (i18n.service.ts:11-21); only en+hi shipped today.
  • Interpolation: {name} regex \{(\w+)\} (i18n.service.ts:42-44); missing params stay literal {name}.
  • Fallback chain on server: locale catalog → en → key.

2. Integration strategy: ARB + remote overlay

flowchart LR
    A[flutter_localizations] --> B[Localizations app]
    B --> C[ARB assets<br/>en.arb, hi.arb]
    C --> D[AppStrings<br/>generated: .tr / .of context]
    E[GET /i18n/messages?locale] --> F[RemoteCatalog<br/>Map<String,String>]
    F --> G[MessageLookup override]
    G --> B
  • Ship ARB files for offline + hot-reload dev (flutter gen-l10n).
  • Add a RemoteLookup implementing GlobalWidgetsLocalizations / AppLocalizationsDelegate fallback chain: ARB first, remote overlay second, key literal last — mirroring server order (i18n.service.ts:40: key literal).
  • Fetch via CatalogCubit (13 §2): fetch on launch after locale resolve; cache per locale with shared_preferences; version hash (planned) header triggers refetch.
  • Locale resolution: WidgetsBinding.instance.platformDispatcher.locale → match against SUPPORTED_LOCALES-equivalent constant; fallback en.

Key → lookup mapping

Backend keys are dotted (errors.resourceNotFound). ARB keys must be camelCase — map once in a generated KeyMap: errors.resourceNotFounderrorsResourceNotFound. Never duplicate strings; the ARB file holds source values and the remote catalog overrides at runtime.

Interpolation mapping

String tr(String key, [Map<String, Object?>? params]) {
  var t = remote[key] ?? arb(key) ?? key;            // order mirrors server
  params?.forEach((k, v) => t = t.replaceAll('{$k}', v.toString()));
  return t;
}
  • Regex parity: server uses \{(\w+)\} (i18n.service.ts:42); implement the same on the client so {days} behaves identically offline and online.
  • Missing param → literal {days} (matches server i18n.service.ts:43).

3. Locale specifics

ItemGuide
intl setupIntl.defaultLocale = resolved locale; MaterialApp(localizationsDelegates: ...)
Pluralsleave.requested uses {days} with English "(s)" hack — replace with proper pluralization via ARB plural when the key is re-worked (planned)
Numbers/datesuse NumberFormat/DateFormat per locale, never hardcode digits in translation values
DirectiontextDirection from locale tag (all current locales LTR; RTL (forward-looking))
Fontsno forced fontFamily for Indic text; system fallback (07 §8); test Devanagari/Tamil at 2.0 scale
IMEtranslation-adjacent inputs: TextCapitalization.none, autocorrect off

4. Cache & refresh

final CatalogCubit catalogCubit;   // 13 §2
// launch:
final locale = await resolveLocale();            // platform → whitelist → en
await catalogCubit.fetch(locale);                // offline: cached map
// locale switch (forward-looking):
onLocaleChanged(catalogCubit.setLocale);         // rebuild Localizations
  • Cache key: catalog_<locale>; TTL 24 h + version-hash refetch (planned); delta push (forward-looking).

5. Notifications copy (planned wiring)

Server notification titles are hardcoded English today (notifications.handler.ts:16-31); planned: move to keys, e.g. notification.passwordReset, interpolated server-side per recipient locale (12_API_Mapping.md §3). Client just renders title/body from the Notification document (schemas/notification.schema.ts:22-26).

6. Testing

  • Golden: catalog parity test — for every key in en.arb, assert remoteCatalog contains it (guards missing-key drift; CI script (planned) per IMPLEMENTATION_PLAN.md:845).
  • Widget test: tr('leave.requested', {'days': 3}) renders both en/hi.
  • flutter test + npm run typecheck + npm run lint before merge.

7. File checklist

FilePurpose
l10n.yamlgen-l10n config, en/hi templates
lib/l10n/app_en.arb, app_hi.arbshipped locales
lib/l10n/app_ta.arbbn(planned) as catalogs ship server-side
lib/i18n/key_map.dartdotted → camelCase map
lib/i18n/catalog_cubit.dartfetch/cache/refresh (13 §2)
lib/i18n/remote_lookup.dartARB→remote→key fallback