Skip to content

feat(composables/i18n): add signal-based translation library#4

Open
homj wants to merge 6 commits intomainfrom
claude/add-translation-library-BhG28
Open

feat(composables/i18n): add signal-based translation library#4
homj wants to merge 6 commits intomainfrom
claude/add-translation-library-BhG28

Conversation

@homj
Copy link
Owner

@homj homj commented Feb 22, 2026

Adds a new @homj/composables/i18n secondary entry point that provides
a signal-based i18n system backed by Angular's resource API.

Key features:

  • provideTranslation(loader?) — registers the global translation store
    and optional global loader at environment level
  • provideTranslationScope(scope, loader) — registers a local scope loader
    at component level; the resource is created lazily in the store
  • useTranslation() — returns a reactive TranslateFn that reads from
    signal-backed resources; falls back to the key while loading
  • Translation keys follow the format 'key' (global) or 'scope:key'
    (scoped, e.g. 'my-component:foo')
  • {{ paramName }} placeholder interpolation via optional params argument
  • Each scope is backed by a single resource() instance; subsequent calls
    to ensureScope with the same scope are no-ops

https://claude.ai/code/session_017w3dyuKpzdFHjg7ng5SeJD

claude and others added 6 commits February 21, 2026 22:49
Adds a new `@homj/composables/i18n` secondary entry point that provides
a signal-based i18n system backed by Angular's resource API.

Key features:
- `provideTranslation(loader?)` — registers the global translation store
  and optional global loader at environment level
- `provideTranslationScope(scope, loader)` — registers a local scope loader
  at component level; the resource is created lazily in the store
- `useTranslation()` — returns a reactive `TranslateFn` that reads from
  signal-backed resources; falls back to the key while loading
- Translation keys follow the format `'key'` (global) or `'scope:key'`
  (scoped, e.g. `'my-component:foo'`)
- `{{ paramName }}` placeholder interpolation via optional params argument
- Each scope is backed by a single `resource()` instance; subsequent calls
  to `ensureScope` with the same scope are no-ops

https://claude.ai/code/session_017w3dyuKpzdFHjg7ng5SeJD
- Extend @homj/composables/i18n with CURRENT_LANGUAGE signal token so
  any component can switch the active language reactively; all resource
  scopes reload automatically via the resource request signal
- Change TranslationLoader signature to (lang: string) => Promise<TranslationData>
  so loaders can serve the correct language file
- Add defaultLang parameter to provideTranslation (defaults to 'en')
- Export CURRENT_LANGUAGE from public API
- Update specs to provide CURRENT_LANGUAGE in TestBed and use the new
  loader signature; add a language-switching integration test
- Refactor AppComponent to a shell (header nav + router-outlet); extract
  ButtonsDemoComponent for the existing button demo
- Add /i18n route with I18nDemoComponent that demonstrates:
  * Global scope translations (EN/DE, with interpolation)
  * Custom scope via ScopedDemoComponent (user-card scope)
  * Lazy-loaded scope via LazyDemoComponent inside @defer (on interaction)
  * Error / fallback cases for missing key and unknown scope
  * Language switcher button (EN / DE) wired to CURRENT_LANGUAGE

https://claude.ai/code/session_017w3dyuKpzdFHjg7ng5SeJD
Introduce a TranslationParser type and TRANSLATION_PARSER injection token
so the string-formatting step can be swapped out per application.

Built-in parsers
- interpolationParser (default, no extra deps) — replaces {{ name }}
  placeholders; extracted from the store so it is independently testable
- withMessageFormat(MessageFormatCtor) — factory that wraps any
  MessageFormat-compatible library (e.g. @messageformat/core) with
  per-language compiler instances and a lang+pattern cache

API changes
- provideTranslation() gains an optional third parameter `parser`
  (defaults to interpolationParser, fully backward-compatible)
- TRANSLATION_PARSER token is exported so advanced setups can provide
  a parser directly via DI
- TranslationParser and MessageFormatLike types are exported from the
  public API

TranslationStore now injects TRANSLATION_PARSER (optional, falls back to
interpolationParser) and passes (pattern, lang, params) to the parser on
every translate() call when params are present.

https://claude.ai/code/session_017w3dyuKpzdFHjg7ng5SeJD
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