@mmstack/translate 21.0.2 → 21.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  `@mmstack/translate` is an opinionated internationalization (i18n) library for Angular applications built with three core priorities:
10
10
 
11
11
  1. **Maximum Type Safety:** Catch errors related to missing keys or incorrect/missing parameters at compile time.
12
- 2. **Simplified Build Process:** Lazily load translations at runtime, requiring only a **single application build** regardless of the number of supported locales.
12
+ 2. **Flexible Build Process:** Works as a traditional multi-build solution (like `@angular/localize`) **OR** as a single-build runtime solution.
13
13
  3. **Scalable Modularity:** Organize translations into **namespaces** (typically aligned with feature libraries) and load them on demand.
14
14
 
15
15
  It uses the robust **FormatJS** Intl runtime (`@formatjs/intl`) for ICU message formatting, and integrates with Angular's dependency injection and routing.
@@ -21,23 +21,24 @@ It uses the robust **FormatJS** Intl runtime (`@formatjs/intl`) for ICU message
21
21
  - Correct parameter names and types.
22
22
  - Required vs. optional parameters based on ICU message.
23
23
  - Structural consistency check when defining non-default locales.
24
- - 🚀 **Single Build Artifact:** Runtime translation loading.
24
+ - 🚀 **Flexible Deployment:** Support both multi-build (traditional) and single-build (runtime) scenarios.
25
25
  - 📦 **Namespacing:** Organize translations by feature/library (e.g., 'quotes', 'userProfile', 'common').
26
- - 🔄 **Dynamic Language Switching:** Change locales at runtime with automatic translation loading.
26
+ - 🔄 **Dynamic Language Switching (Optional):** Change locales at runtime with automatic translation loading.
27
+ - 🛣️ **Route-Based Locale Support (Optional):** Automatic locale detection and switching based on route parameters.
27
28
  - ⏳ **Lazy Loading:** Load namespaced translations on demand using Route Resolvers.
28
29
  - ✨ **Reactive API:** Includes `t.asSignal()` for creating computed translation signals based on signal parameters.
29
- - 🌍 **ICU Message Syntax:** Uses FormatJS runtime for robust support of variables (`{name}`), `plural`, `select`, and `selectordinal`. (Note: Complex inline date/number formats are not the focus; use Angular's built in Pipes/format functions & use the result as variables in your translation.)
30
+ - 🌍 **ICU Message Syntax:** Uses FormatJS runtime for robust support of variables (`{name}`), `plural`, `select`, and `selectordinal`. (Note: Complex inline date/number formats are not the focus; use Angular's built-in Pipes/format functions & use the result as variables in your translation.)
30
31
  - 🔗 **Shared Namespace Support:** Define common translations (e.g., 'Save', 'Cancel') in one namespace and make them type-safely accessible from others.
31
32
  - 🛠️ **Template Helpers:** Includes abstract `Translator` pipe and `Translate` directive for easy, type-safe templating.
32
33
 
33
34
  ### Comparison
34
35
 
35
- While Angular offers excellent i18n solutions like `@angular/localize` and `transloco`, `@mmstack/translate` aims to fill a specific niche.
36
+ While Angular offers excellent i18n solutions like `@angular/localize` and `transloco`, `@mmstack/translate` aims to fill a specific niche by supporting **both** traditional multi-build and modern single-build approaches.
36
37
 
37
38
  | Feature | `@mmstack/translate` | `@angular/localize` | `transloco` | `ngx-translate` |
38
39
  | :----------------------- | :----------------------------------: | :----------------------: | :---------------------------: | :------------------------: |
39
- | **Build Process** | ✅ Single Build | ❌ Multi-Build (Typical) | ✅ Single Build | ✅ Single Build |
40
- | **Translation Timing** | Runtime | Compile Time | Runtime | Runtime |
40
+ | **Build Process** | ✅ Single or Multi-Build | ❌ Multi-Build (Typical) | ✅ Single Build | ✅ Single Build |
41
+ | **Translation Timing** | Runtime or Build Time | Compile Time | Runtime | Runtime |
41
42
  | **Type Safety (Keys)** | ✅ Strong (Inferred from structure) | 🟡 via extraction | 🟡 Tooling/TS Files | 🟡 OK Manual/Tooling |
42
43
  | **Type Safety (Params)** | ✅ Strong (Inferred from ICU) | ❌ None | 🟡 Manual | 🟡 Manual |
43
44
  | **Locale Switching** | ✅ Dynamic (Runtime) or Page refresh | 🔄 Page Refresh Required | ✅ Dynamic (Runtime) | ✅ Dynamic (Runtime) |
@@ -47,7 +48,7 @@ While Angular offers excellent i18n solutions like `@angular/localize` and `tran
47
48
  | **Signal Integration** | ✅ Good (`t.asSignal()`) | N/A | ✅ Good (`translateSignal()`) | ❌ Minimal/None |
48
49
  | **Maturity / Community** | ✨ New | Core Angular | ✅ Mature / Active | ✅ Mature |
49
50
 
50
- ## Installation & Configuration
51
+ ## Installation
51
52
 
52
53
  Install the library & its peer dependency, `@formatjs/intl`.
53
54
 
@@ -55,69 +56,63 @@ Install the library & its peer dependency, `@formatjs/intl`.
55
56
  npm install @mmstack/translate @formatjs/intl
56
57
  ```
57
58
 
58
- If you'd like to setup @formatjs with custom properties & setup a different default locale then 'en-US' you can define these options at the root of your application.
59
+ ## Configuration
59
60
 
60
- ```typescript
61
- import { provideIntlConfig } from '@mmstack/translate';
61
+ ### Default: Multi-Build Scenario (like @angular/localize)
62
62
 
63
- const appConfig: Providers = [
64
- provideIntlConfig({
65
- defaultLocale: 'en-US', // defaults to 'en-US' if nothing is provided
66
- }),
67
- ];
68
- ```
63
+ By default, `@mmstack/translate` works like `@angular/localize` - it uses Angular's `LOCALE_ID` token and expects a page refresh for locale changes. This is ideal for traditional multi-build deployments where each locale has its own build artifact.
69
64
 
70
- ```typescript
71
- // DEMO impl, how you actually provide LOCALE_ID & what it's based on is up to you, it just has to be available when the resolvers are called
65
+ **No special configuration needed!** Just provide `LOCALE_ID`:
72
66
 
73
- import { Component, LOCALE_ID } from '@angular/core';
67
+ ```typescript
68
+ // app.config.ts
69
+ import { ApplicationConfig, LOCALE_ID } from '@angular/core';
70
+
71
+ export const appConfig: ApplicationConfig = {
72
+ providers: [
73
+ { provide: LOCALE_ID, useValue: 'en-US' }, // Set your locale
74
+ // ... other providers
75
+ ],
76
+ };
77
+ ```
74
78
 
75
- @Component({
76
- selector: 'app-locale-shell',
77
- template: `<router-outlet />`,
78
- })
79
- export class LocaleShellComponent {}
79
+ The library will use this `LOCALE_ID` value and work exactly like `@angular/localize` - requiring a full page refresh to change locales.
80
80
 
81
- // app.routes.ts
82
- import { Routes, ActivatedRouteSnapshot } from '@angular/router';
83
- import { Injectable, LOCALE_ID } from '@angular/core';
84
- import { QuoteComponent } from './quote.component';
81
+ ### Single-Build with Runtime Translation Loading
85
82
 
86
- @Injectable({
87
- providedIn: 'root',
88
- })
89
- export class LocaleStore {
90
- locale = 'en-US';
91
- }
83
+ If you want a **single build** that loads translations at runtime, use `provideIntlConfig()`:
92
84
 
93
- export const routes: Routes = [
94
- {
95
- path: ':locale',
96
- component: LocaleShellComponent,
97
- resolve: {
98
- localeId: (route: ActivatedRouteSnapshot) => {
99
- const locale = route.params['locale'] || 'en-US';
85
+ ```typescript
86
+ // app.config.ts
87
+ import { ApplicationConfig, LOCALE_ID } from '@angular/core';
88
+ import { provideIntlConfig } from '@mmstack/translate';
100
89
 
101
- return route.params['locale'] || 'en-US';
102
- },
103
- },
104
- providers: [
105
- {
106
- provide: LOCALE_ID,
107
- useFactory: (store: LocaleStore) => {
108
- return store.locale;
109
- },
110
- deps: [LocaleStore],
111
- },
112
- ],
113
- loadChildren: () => import('./quote.routes').then((m) => m.QUOTE_ROUTES),
114
- },
115
- ];
90
+ export const appConfig: ApplicationConfig = {
91
+ providers: [
92
+ { provide: LOCALE_ID, useValue: 'en-US' }, // Initial/fallback locale
93
+ provideIntlConfig({
94
+ defaultLocale: 'en-US',
95
+ supportedLocales: ['en-US', 'sl-SI', 'de-DE', 'fr-FR'], // Validates locale switches
96
+ }),
97
+ // ... other providers
98
+ ],
99
+ };
116
100
  ```
117
101
 
118
- _Note:_ `@mmstack/translate` relies on Angular's `LOCALE_ID` provider for its default. You should provide a value for it. How you determine this value (e.g., hardcoded, from server config, from URL segment via a factory provider) is up to your application's architecture.
102
+ **Optional Configuration:**
119
103
 
120
- By default the library assumes this `LOCALE_ID` is static for the duration of the application session and requires a page refresh to change. You can however opt in to dynamic locale switching via `injectDynamicLocale`. see usage note 5 for details :)
104
+ ```typescript
105
+ provideIntlConfig({
106
+ defaultLocale: 'en-US',
107
+ supportedLocales: ['en-US', 'sl-SI', 'de-DE'],
108
+
109
+ // Automatically detect and respond to locale route parameter changes, should correspond with actual param name example bellow
110
+ localeParamName: 'locale',
111
+
112
+ // Preload default locale for synchronous fallback (rarely needed)
113
+ preloadDefaultLocale: true,
114
+ });
115
+ ```
121
116
 
122
117
  ## Usage
123
118
 
@@ -125,7 +120,7 @@ The core workflow involves defining namespaces, registering them (often via lazy
125
120
 
126
121
  ### 1. Define Namespace & Translations
127
122
 
128
- Define your default locale translations (e.g., 'en-US') as a `const` TypeScript object. Use createNamespace to process it and generate helpers.
123
+ Define your default locale translations (e.g., 'en-US') as a `const` TypeScript object. Use `createNamespace` to process it and generate helpers.
129
124
 
130
125
  ```typescript
131
126
  // Example: packages/quote/src/lib/quote.namespace.ts
@@ -149,13 +144,15 @@ export default ns.translation;
149
144
  export type QuoteLocale = (typeof ns)['translation'];
150
145
 
151
146
  export const createQuoteTranslation = ns.createTranslation;
147
+ ```
152
148
 
153
- // Note the translations should be in separate files, if you are using import() to lazy load them.
154
- // packages/quote/src/lib/quote-sl.translation.ts
149
+ **Define other locales in separate files** (for lazy loading):
155
150
 
151
+ ```typescript
152
+ // packages/quote/src/lib/quote-sl.translation.ts
156
153
  import { createQuoteTranslation } from './quote.namespace';
157
154
 
158
- // shape is typesafe (errors if you have missing or additional keys)
155
+ // Shape is type-safe (errors if you have missing or additional keys)
159
156
  export default createQuoteTranslation('sl-SI', {
160
157
  pageTitle: 'Znani Citati',
161
158
  greeting: 'Zdravo {name}!',
@@ -163,50 +160,53 @@ export default createQuoteTranslation('sl-SI', {
163
160
  authorLabel: 'Avtor',
164
161
  },
165
162
  errors: {
166
- minLength: 'Citat mora imeti vsaj {min} znakov.', // If original has variables, the translation must contain a subset of used variables (min 1)
163
+ minLength: 'Citat mora imeti vsaj {min} znakov.', // Variables must match original
167
164
  },
168
- stats: '{count, plural, =1 {# citat} =2 {# citata} few {# citati} other {# citatov}} na voljo', // also guarenteed for "complex" variables, so {count} must be used in this translation
165
+ stats: '{count, plural, =1 {# citat} =2 {# citata} few {# citati} other {# citatov}} na voljo',
169
166
  });
170
167
  ```
171
168
 
172
- ### 2. Register the namespace & load
169
+ ### 2. Register the Namespace & Load Translations
173
170
 
174
- Use registerNamespace to prepare your namespace definition and obtain the injectNamespaceT function and the resolveNamespaceTranslation resolver function. Use the resolver in your Angular routes.
171
+ Use `registerNamespace` to prepare your namespace definition and obtain the `injectNamespaceT` function and the `resolveNamespaceTranslation` resolver function.
175
172
 
176
173
  ```typescript
174
+ // Example: packages/quote/src/lib/quote.t.ts
177
175
  import { registerNamespace } from '@mmstack/translate';
178
176
 
179
- // Register the namespace
180
- // Example: packages/quote/src/lib/quote.t.ts
181
177
  const r = registerNamespace(
182
- // you can also fetch from a server if you prefer, as long as the shape is the same
183
- () => import('./quote.namespace').then((m) => m.default), // Default locale's compiled translation (functions as fallback if no locale of type provided)
178
+ // Default locale's compiled translation (functions as fallback)
179
+ () => import('./quote.namespace').then((m) => m.default),
184
180
  {
185
181
  // Map other locales to promise factories (dynamic imports)
186
182
  'sl-SI': () => import('./quote-sl.translation').then((m) => m.default),
183
+ // Add more locales as needed...
187
184
  },
188
185
  );
189
186
 
190
187
  export const injectQuoteT = r.injectNamespaceT;
191
188
  export const resolveQuoteTranslations = r.resolveNamespaceTranslation;
189
+ ```
192
190
 
193
- // in the main quote route add the provided resolver
191
+ **Add the resolver to your routes:**
194
192
 
193
+ ```typescript
194
+ // quote.routes.ts
195
195
  import { type Routes } from '@angular/router';
196
196
  import { resolveQuoteTranslations } from './quote.t';
197
197
 
198
- // quote.routes.ts
199
198
  export const QUOTE_ROUTES: Routes = [
200
199
  {
201
- // ... component at or above where the translations need to be available
200
+ path: '',
201
+ component: QuoteComponent,
202
202
  resolve: {
203
- resolveQuoteTranslations,
203
+ translations: resolveQuoteTranslations, // Loads translations before component
204
204
  },
205
205
  },
206
206
  ];
207
207
  ```
208
208
 
209
- #### 2b. [OPTIONAL] Configure the translation pipe and/or directive
209
+ #### 2b. [OPTIONAL] Configure Type-Safe Pipe and/or Directive
210
210
 
211
211
  ```typescript
212
212
  import { Pipe, Directive } from '@angular/core';
@@ -215,29 +215,35 @@ import { type QuoteLocale } from './quote.namespace';
215
215
 
216
216
  @Pipe({
217
217
  name: 'translate',
218
+ standalone: true,
218
219
  })
219
220
  export class QuoteTranslator extends Translator<QuoteLocale> {}
220
221
 
221
222
  @Directive({
222
- selector: '[translate]', // input in Translate is named 'translate'
223
+ selector: '[translate]', // Input in Translate is named 'translate'
224
+ standalone: true,
223
225
  })
224
- // TInput is necessary to correctly infer the variables to the key
225
226
  export class QuoteTranslate<TInput extends string> extends Translate<TInput, QuoteLocale> {}
226
227
  ```
227
228
 
228
- ### 3. Have fun :)
229
+ ### 3. Use Translations in Components
229
230
 
230
231
  ```typescript
232
+ import { Component, signal } from '@angular/core';
233
+ import { injectQuoteT } from './quote.t';
234
+ import { QuoteTranslator, QuoteTranslate } from './quote.helpers';
235
+
231
236
  @Component({
232
237
  selector: 'app-quote',
238
+ standalone: true,
233
239
  imports: [QuoteTranslator, QuoteTranslate],
234
240
  template: `
235
241
  <!-- Pipe validates key & variables match -->
236
242
  <h1>{{ 'quote.pageTitle' | translate }}</h1>
237
- <!-- Non pluralized params must be string -->
243
+ <!-- Non-pluralized params must be string -->
238
244
  <span>{{ 'quote.errors.minLength' | translate: { min: '5' } }}</span>
239
245
 
240
- <!-- Directive replaces innerHTML of el -->
246
+ <!-- Directive replaces textContent of element -->
241
247
  <h1 translate="quote.pageTitle"></h1>
242
248
  <span [translate]="['quote.errors.minLength', { min: '5' }]"></span>
243
249
  `,
@@ -246,104 +252,296 @@ export class QuoteComponent {
246
252
  protected readonly count = signal(0);
247
253
  private readonly t = injectQuoteT();
248
254
 
249
- private readonly author = this.t('quote.detail.authorLabel'); // static translation
255
+ // Static translation
256
+ private readonly author = this.t('quote.detail.authorLabel');
250
257
 
258
+ // Reactive translation with signal parameters
251
259
  private readonly stats = this.t.asSignal('quote.stats', () => ({
252
- count: this.count(), // must be object with count parameter & type number
260
+ count: this.count(), // Must match ICU parameter (type: number)
253
261
  }));
254
262
  }
255
263
  ```
256
264
 
257
- ### 4. [OPTIONAL] - Creating a shared/common namespace
265
+ ### 4. [OPTIONAL] Route-Based Locale Detection
266
+
267
+ For applications with locale-based routing (e.g., `/en-US/quotes`, `/sl-SI/quotes`), the library can automatically detect and switch locales.
268
+
269
+ **Step 1: Configure locale parameter name**
270
+
271
+ ```typescript
272
+ // app.config.ts
273
+ import { provideIntlConfig } from '@mmstack/translate';
274
+
275
+ export const appConfig: ApplicationConfig = {
276
+ providers: [
277
+ provideIntlConfig({
278
+ defaultLocale: 'en-US',
279
+ supportedLocales: ['en-US', 'sl-SI', 'de-DE'],
280
+ localeParamName: 'locale', // Track this route parameter automatically
281
+ }),
282
+ ],
283
+ };
284
+ ```
285
+
286
+ **Step 2: Add route guard for validation**
287
+
288
+ ```typescript
289
+ // app.routes.ts
290
+ import { Routes } from '@angular/router';
291
+ import { canMatchLocale } from '@mmstack/translate';
292
+
293
+ export const routes: Routes = [
294
+ {
295
+ path: ':locale',
296
+ canMatch: [canMatchLocale()], // Validates & redirects invalid locales
297
+ children: [
298
+ {
299
+ path: 'quotes',
300
+ loadChildren: () => import('./quote/quote.routes').then((m) => m.QUOTE_ROUTES),
301
+ },
302
+ // ... other routes
303
+ ],
304
+ },
305
+ ];
306
+ ```
307
+
308
+ **That's it!** The library will:
309
+
310
+ - Detect locale changes from route parameters
311
+ - Load translations on demand for the new locale
312
+ - Update all translation outputs reactively
313
+ - Redirect invalid locales to the default
314
+
315
+ **With prefix segments:**
316
+
317
+ If your locale parameter isn't the first segment (e.g., `/app/:locale/...`):
318
+
319
+ ```typescript
320
+ {
321
+ path: 'app/:locale',
322
+ canMatch: [canMatchLocale(['app'])], // Validates second segment
323
+ children: [...]
324
+ }
325
+ ```
326
+
327
+ ### 5. [OPTIONAL] Dynamic Language Switching
328
+
329
+ For applications that need runtime language switching without page refreshes (e.g., language selector in header), use `injectDynamicLocale()`:
330
+
331
+ ```typescript
332
+ import { Component } from '@angular/core';
333
+ import { injectDynamicLocale } from '@mmstack/translate';
334
+
335
+ @Component({
336
+ selector: 'app-language-switcher',
337
+ template: `
338
+ <select [value]="locale()" (change)="changeLanguage($event)">
339
+ <option value="en-US">English</option>
340
+ <option value="sl-SI">Slovenščina</option>
341
+ <option value="de-DE">Deutsch</option>
342
+ </select>
343
+
344
+ @if (locale.isLoading()) {
345
+ <div class="spinner">Loading translations...</div>
346
+ }
347
+ `,
348
+ })
349
+ export class LanguageSwitcherComponent {
350
+ protected readonly locale = injectDynamicLocale();
351
+
352
+ changeLanguage(event: Event) {
353
+ const target = event.target as HTMLSelectElement;
354
+ this.locale.set(target.value); // Automatically loads missing translations
355
+ }
356
+ }
357
+ ```
358
+
359
+ **Features:**
360
+
361
+ - Validates against `supportedLocales` (if configured)
362
+ - Automatically loads missing namespace translations
363
+ - Provides `isLoading()` signal for UI feedback
364
+ - Works with route-based locales
365
+
366
+ **Important Note for Pure Pipes:**
367
+
368
+ Due to Angular's memoization, pure pipes don't automatically react to locale changes. Solutions:
369
+
370
+ ```typescript
371
+ // Option 1: Pass locale as parameter (recommended)
372
+ {{ 'common.yes' | translate : locale() }}
373
+
374
+ // Option 2: Make pipe impure (not recommended for performance)
375
+ @Pipe({
376
+ name: 'translate',
377
+ pure: false,
378
+ })
379
+ export class QuoteTranslator extends Translator<QuoteLocale> {}
380
+ ```
258
381
 
259
- _Note:_ A shared namespace allows you to use it within other `t` functions. You are however responsible for loading it before those `t` functions initialize, usually for a shared namespace that would be at the top route.
382
+ ### 6. [OPTIONAL] Creating a Shared/Common Namespace
260
383
 
261
- #### 4a. Define a shared namespace
384
+ A shared namespace allows you to define common translations (e.g., 'Save', 'Cancel', 'Yes', 'No') once and use them type-safely across all other namespaces.
262
385
 
263
- Same as quote example, but this time also export the `createMergedNamespace` function. This will be your new factory for other namespaces.
386
+ **Step 1: Define a shared namespace**
264
387
 
265
388
  ```typescript
266
- // Example: packages/common-locales/src/lib/common.namespace.ts
389
+ // packages/common/src/lib/common.namespace.ts
267
390
  import { createNamespace } from '@mmstack/translate';
268
391
 
269
392
  const ns = createNamespace('common', {
270
393
  yes: 'Yes',
271
394
  no: 'No',
395
+ save: 'Save',
396
+ cancel: 'Cancel',
397
+ delete: 'Delete',
272
398
  });
273
399
 
274
- // ... rest
400
+ export default ns.translation;
401
+ export type CommonLocale = (typeof ns)['translation'];
402
+ export const createCommonTranslation = ns.createTranslation;
275
403
 
404
+ // Export this for other namespaces to use
276
405
  export const createAppNamespace = ns.createMergedNamespace;
277
406
  ```
278
407
 
279
- ### 4b. Use the common namespace factory instead of the library one
408
+ **Step 2: Register the common namespace at the top level**
280
409
 
281
410
  ```typescript
282
- // Example: packages/quote/src/lib/quote.namespace.ts
283
- import { createAppNamespace } from '@org/common/locale'; // replace with your library import path
411
+ // common.t.ts
412
+ import { registerNamespace } from '@mmstack/translate';
413
+
414
+ const r = registerNamespace(() => import('./common.namespace').then((m) => m.default), {
415
+ 'sl-SI': () => import('./common-sl.translation').then((m) => m.default),
416
+ });
417
+
418
+ export const injectCommonT = r.injectNamespaceT;
419
+ export const resolveCommonTranslations = r.resolveNamespaceTranslation;
420
+ ```
421
+
422
+ ```typescript
423
+ // app.routes.ts - resolve at top level
424
+ export const routes: Routes = [
425
+ {
426
+ path: '',
427
+ resolve: {
428
+ common: resolveCommonTranslations, // Load common translations first
429
+ },
430
+ children: [
431
+ // ... other routes
432
+ ],
433
+ },
434
+ ];
435
+ ```
436
+
437
+ **Step 3: Use the shared namespace factory in other namespaces**
438
+
439
+ ```typescript
440
+ // packages/quote/src/lib/quote.namespace.ts
441
+ import { createAppNamespace } from '@org/common'; // Your import path
284
442
 
285
- // Create the namespace definition object
286
443
  const ns = createAppNamespace('quote', {
287
444
  pageTitle: 'Famous Quotes',
445
+ // ... other translations
288
446
  });
289
447
 
290
- // registration & other stuff remains the same
448
+ export default ns.translation;
449
+ // ... rest remains the same
291
450
  ```
292
451
 
293
- ### 4c. Have even more fun!
452
+ **Step 4: Access both namespaces in components**
294
453
 
295
454
  ```typescript
296
- @Component({
297
- //...
298
- })
455
+ @Component({...})
299
456
  export class QuoteComponent {
300
457
  private readonly t = injectQuoteT();
301
458
 
302
- // t function is now 'aware' of the common namespace & its translations
303
- private readonly yes = this.t('common.yes');
459
+ // Access common namespace translations
460
+ private readonly yesLabel = this.t('common.yes');
461
+ private readonly saveLabel = this.t('common.save');
304
462
 
305
- // quote translations also work the same
306
- private readonly author = this.t('quote.detail.authorLabel');
463
+ // Access quote namespace translations
464
+ private readonly title = this.t('quote.pageTitle');
307
465
  }
308
466
  ```
309
467
 
310
- ### 5 [OPTIONAl] - Add dynamic language switching
468
+ ## Helper Functions
311
469
 
312
- `LOCALE_ID` is always used as the initial value, but you can dynamically switch that & `@mmstack/translate` will load the new translations on demand, updating as necessary.
470
+ ### Core Injection Functions
313
471
 
314
- ```typescript
315
- import { injectDynamicLocale } from '@mmstack/translate';
472
+ **`injectDefaultLocale(): string`**
473
+ Returns the configured default locale or falls back to `LOCALE_ID`.
316
474
 
317
- @Component({...})
318
- export class LanguageSwitcher {
319
- private readonly locale = injectDynamicLocale();
475
+ **`injectSupportedLocales(): string[]`**
476
+ Returns the array of supported locales or defaults to `[defaultLocale]`.
320
477
 
321
- switchToSlovene() {
322
- this.locale.set('sl-SI'); // Automatically loads missing translations
323
- }
478
+ **`injectIntl(): Signal<IntlShape>`**
479
+ Directly access the FormatJS `Intl` instance for advanced formatting needs.
324
480
 
325
- // Template can react to loading state
326
- @if (locale.isLoading()) {
327
- <div>Loading translations...</div>
328
- }
329
- }
481
+ ```typescript
482
+ import { injectIntl } from '@mmstack/translate';
483
+
484
+ const intl = injectIntl();
485
+ const formatted = intl().formatNumber(1234.56, {
486
+ style: 'currency',
487
+ currency: 'EUR',
488
+ });
330
489
  ```
331
490
 
332
- _Note:_ Due to Angular memo-izing the transform function, custom Pipe's used will not update on dynamic local switches automatically. To resolve this you can:
491
+ **`injectDynamicLocale(): WritableSignal<string> & { isLoading: Signal<boolean> }`**
492
+ Inject a dynamic locale signal for runtime language switching.
333
493
 
334
- ```typescript
494
+ ### Route Utilities
335
495
 
336
- // Add the locale as the last parameter when calling the pipe
337
- {{'common.yes' | translate : locale()}}
496
+ **`canMatchLocale(prefixSegments?: string[]): CanMatchFn`**
497
+ Route guard that validates locale parameters against `supportedLocales` and redirects invalid locales to the default.
338
498
 
339
- // or set pure to false (not recommended)
340
- @Pipe({
341
- name: 'translate',
342
- pure: false
343
- })
344
- export class QuoteTranslator extends Translator<QuoteLocale> {}
345
- ```
499
+ ## Advanced: Architecture & Performance
500
+
501
+ ### Resource-Based Translation Loading
502
+
503
+ The library uses Angular's `resource()` API for efficient, reactive translation loading:
504
+
505
+ - Automatic request deduplication
506
+ - Built-in loading states
507
+ - Cancellation support via `AbortSignal`
508
+ - Better error handling
509
+
510
+ ### On-Demand Translation Loading
511
+
512
+ When switching locales dynamically, the library:
513
+
514
+ 1. Checks which namespaces need translations for the new locale
515
+ 2. Loads only the missing translations in parallel
516
+ 3. Updates all reactive outputs automatically
517
+ 4. Falls back to the default locale if unavailable
518
+
519
+ ## Migration from Other Libraries
520
+
521
+ ### From @angular/localize
522
+
523
+ `@mmstack/translate` can work exactly like `@angular/localize` by default - no migration needed for the build process! Simply:
524
+
525
+ 1. Define your translations using `createNamespace`
526
+ 2. Register namespaces with resolvers
527
+ 3. Use the translation functions/pipes/directives
528
+
529
+ The main difference is the namespace organization and type safety.
530
+
531
+ ### From transloco/ngx-translate
532
+
533
+ If you're migrating from a runtime-only solution:
534
+
535
+ 1. Configure `provideIntlConfig()` with your supported locales
536
+ 2. Use `localeParamName` if you have route-based locales
537
+ 3. Use `injectDynamicLocale()` for programmatic locale switching
538
+ 4. Convert your translation JSON files to TypeScript using `createNamespace`
539
+ 5. Update component/template usage to use the type-safe APIs
346
540
 
347
541
  ## Contributing
348
542
 
349
- Contributions, issues, and feature requests are welcome!
543
+ Contributions, issues, and feature requests are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
544
+
545
+ ## License
546
+
547
+ MIT © [Mihael Mulec](https://github.com/mihajm)
@@ -1,6 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, signal, LOCALE_ID, computed, resource, untracked, isDevMode, effect, Injectable, isSignal, input, Renderer2, ElementRef, afterRenderEffect, Directive, ChangeDetectorRef } from '@angular/core';
2
+ import { inject, computed, InjectionToken, LOCALE_ID, signal, resource, untracked, isDevMode, effect, Injectable, isSignal, input, Renderer2, ElementRef, afterRenderEffect, Directive, ChangeDetectorRef } from '@angular/core';
3
+ import { Router, ActivatedRoute } from '@angular/router';
3
4
  import { createIntlCache, createIntl } from '@formatjs/intl';
5
+ import { toSignal } from '@angular/core/rxjs-interop';
4
6
 
5
7
  const KEY_DELIM = '::MMT_DELIM::';
6
8
  function prependDelim(prefix, key) {
@@ -53,28 +55,88 @@ function createNamespace(ns, translation) {
53
55
  return namespace;
54
56
  }
55
57
 
58
+ function pathParam(key, route = inject(ActivatedRoute)) {
59
+ const keySignal = typeof key === 'string' ? computed(() => key) : computed(key);
60
+ const routerOptions = inject(Router)['options'];
61
+ if (routerOptions &&
62
+ typeof routerOptions === 'object' &&
63
+ routerOptions.paramsInheritanceStrategy === 'always') {
64
+ const params = toSignal(route.paramMap, {
65
+ initialValue: route.snapshot.paramMap,
66
+ });
67
+ return computed(() => params().get(keySignal()));
68
+ }
69
+ const paramMapSignals = [];
70
+ let currentRoute = route;
71
+ const isStatic = typeof key === 'string';
72
+ while (currentRoute) {
73
+ const initial = currentRoute.snapshot.paramMap;
74
+ paramMapSignals.push(toSignal(currentRoute.paramMap, {
75
+ initialValue: initial,
76
+ }));
77
+ // For static keys, stop once we find the param, will find first in computed for loop already so basically noop for for loop
78
+ if (isStatic && initial.has(key))
79
+ break;
80
+ currentRoute = currentRoute.parent;
81
+ }
82
+ return computed(() => {
83
+ const paramKey = keySignal();
84
+ for (const map of paramMapSignals) {
85
+ const v = map().get(paramKey);
86
+ if (v)
87
+ return v;
88
+ }
89
+ return null;
90
+ });
91
+ }
92
+
56
93
  const CONFIG_TOKEN = new InjectionToken('mmstack-intl-config');
57
94
  function provideIntlConfig(config) {
58
- return {
59
- useValue: config,
60
- provide: CONFIG_TOKEN,
61
- };
95
+ const providers = [
96
+ {
97
+ useFactory: (localeId) => {
98
+ const next = {
99
+ ...config,
100
+ };
101
+ const defaultLocale = config.defaultLocale ?? config.supportedLocales?.at(0) ?? localeId;
102
+ if (next.supportedLocales &&
103
+ !next.supportedLocales.includes(defaultLocale)) {
104
+ next.supportedLocales = [...next.supportedLocales, defaultLocale];
105
+ }
106
+ return next;
107
+ },
108
+ deps: [LOCALE_ID],
109
+ provide: CONFIG_TOKEN,
110
+ },
111
+ ];
112
+ const defaultLocale = config.defaultLocale ?? config.supportedLocales?.at(0);
113
+ if (!defaultLocale)
114
+ return providers;
115
+ providers.push({
116
+ provide: LOCALE_ID,
117
+ useValue: defaultLocale,
118
+ });
119
+ return providers;
62
120
  }
63
121
  function injectIntlConfig() {
64
122
  return inject(CONFIG_TOKEN, { optional: true }) ?? undefined;
65
123
  }
66
124
  function injectDefaultLocale() {
67
- return injectIntlConfig()?.defaultLocale ?? 'en-US';
125
+ return injectIntlConfig()?.defaultLocale ?? inject(LOCALE_ID) ?? 'en-US';
126
+ }
127
+ function injectSupportedLocales() {
128
+ return injectIntlConfig()?.supportedLocales ?? [injectDefaultLocale()];
68
129
  }
69
130
  class TranslationStore {
70
131
  cache = createIntlCache();
71
132
  config = injectIntlConfig();
72
133
  loadQueue = signal([], ...(ngDevMode ? [{ debugName: "loadQueue" }] : []));
73
- locale = signal(inject(LOCALE_ID), ...(ngDevMode ? [{ debugName: "locale" }] : []));
134
+ locale = signal(injectDefaultLocale(), ...(ngDevMode ? [{ debugName: "locale" }] : []));
74
135
  defaultLocale = injectDefaultLocale();
75
136
  translations = signal({
76
137
  [this.defaultLocale]: {},
77
138
  }, ...(ngDevMode ? [{ debugName: "translations" }] : []));
139
+ attemptedFallbackLoad = false;
78
140
  onDemandLoaders = new Map();
79
141
  nonMessageConfig = computed(() => ({
80
142
  ...this.config,
@@ -125,6 +187,21 @@ class TranslationStore {
125
187
  messages: this.messages(),
126
188
  }, this.cache), ...(ngDevMode ? [{ debugName: "intl" }] : []));
127
189
  constructor() {
190
+ const paramName = this.config?.localeParamName;
191
+ if (paramName) {
192
+ const param = pathParam(paramName);
193
+ effect(() => {
194
+ const loc = param();
195
+ if (!loc ||
196
+ loc === untracked(this.locale) ||
197
+ untracked(this.loadQueue).includes(loc))
198
+ return;
199
+ if (this.hasLocaleLoaders(loc))
200
+ this.locale.set(loc);
201
+ else
202
+ this.loadQueue.update((q) => [...q, loc]);
203
+ });
204
+ }
128
205
  effect(() => {
129
206
  if (
130
207
  // should never be in error state, but best to check in case something throws
@@ -134,19 +211,34 @@ class TranslationStore {
134
211
  const dynamicLocales = this.dynamicLocaleLoader.value();
135
212
  if (!dynamicLocales)
136
213
  return;
214
+ // Register loaded translations
137
215
  for (const locale of dynamicLocales.locales) {
138
216
  this.register(locale.namespace, {
139
217
  [dynamicLocales.locale]: locale.flat,
140
218
  });
141
219
  }
142
- this.loadQueue.update((q) => q.filter((l) => l !== dynamicLocales.locale));
143
- this.locale.set(dynamicLocales.locale);
220
+ const hasTranslations = dynamicLocales.locales.length > 0 ||
221
+ this.translations()[dynamicLocales.locale];
222
+ if (hasTranslations) {
223
+ this.loadQueue.update((q) => q.filter((l) => l !== dynamicLocales.locale));
224
+ this.locale.set(dynamicLocales.locale);
225
+ }
144
226
  });
145
227
  }
146
228
  formatMessage(key, values) {
147
- const message = this.translations()[this.locale()]?.[key] ?? '';
148
- if (!message)
229
+ const message = this.translations()[this.locale()]?.[key] ??
230
+ this.translations()[this.defaultLocale]?.[key] ??
231
+ '';
232
+ if (!message) {
233
+ if (this.attemptedFallbackLoad)
234
+ return '';
235
+ this.attemptedFallbackLoad = true;
236
+ untracked(() => {
237
+ if (!this.loadQueue().includes(this.defaultLocale))
238
+ this.loadQueue.update((q) => [...q, this.defaultLocale]);
239
+ });
149
240
  return '';
241
+ }
150
242
  return this.intl().formatMessage({ id: key, defaultMessage: message }, values);
151
243
  }
152
244
  register(namespace, flat) {
@@ -204,12 +296,22 @@ function injectIntl() {
204
296
  */
205
297
  function injectDynamicLocale() {
206
298
  const store = inject(TranslationStore);
299
+ const supportedLocales = injectIntlConfig()?.supportedLocales;
207
300
  const source = computed(() => store.locale());
301
+ const inSupportedLocales = supportedLocales === undefined
302
+ ? () => true
303
+ : (locale) => supportedLocales.includes(locale);
208
304
  const set = (value) => {
209
305
  if (value === untracked(source) ||
210
- !store.hasLocaleLoaders(value) ||
211
306
  untracked(store.loadQueue).includes(value))
212
307
  return;
308
+ if (!inSupportedLocales(value)) {
309
+ if (isDevMode())
310
+ console.warn(`[Translate] Locale "${value}" is not in supportedLocales, switch prevented. Available options are:`, supportedLocales);
311
+ return;
312
+ }
313
+ if (isDevMode() && !store.hasLocaleLoaders(value))
314
+ console.warn(`[Translate] No loaders registered for locale "${value}". Switching to this locale will have no effect.`);
213
315
  store.loadQueue.update((q) => [...q, value]);
214
316
  };
215
317
  source.set = set;
@@ -274,10 +376,29 @@ function registerNamespace(defaultTranslation, other) {
274
376
  return addSignalFn(createT(store), store);
275
377
  };
276
378
  let defaultTranslationLoaded = false;
277
- const resolver = async () => {
379
+ const resolver = async (snapshot) => {
278
380
  const store = inject(TranslationStore);
279
- const locale = untracked(store.locale);
381
+ let locale = null;
382
+ const paramName = injectIntlConfig()?.localeParamName;
383
+ const routerConfig = inject(Router)['options'];
384
+ const alwaysInheritParams = typeof routerConfig === 'object' &&
385
+ !!routerConfig &&
386
+ routerConfig.paramsInheritanceStrategy === 'always';
387
+ if (paramName) {
388
+ locale = snapshot.paramMap.get(paramName);
389
+ if (!locale && !alwaysInheritParams) {
390
+ let currentRoute = snapshot;
391
+ while (currentRoute && !locale) {
392
+ locale = currentRoute.paramMap.get('locale');
393
+ currentRoute = currentRoute.parent;
394
+ }
395
+ }
396
+ }
397
+ if (!locale) {
398
+ locale = untracked(store.locale);
399
+ }
280
400
  const defaultLocale = injectDefaultLocale();
401
+ const shouldPreloadDefault = injectIntlConfig()?.preloadDefaultLocale ?? false;
281
402
  const tPromise = other[locale];
282
403
  const promise = tPromise ?? defaultTranslation;
283
404
  if (!promise && isDevMode()) {
@@ -286,28 +407,43 @@ function registerNamespace(defaultTranslation, other) {
286
407
  if (promise === defaultTranslation && defaultTranslationLoaded)
287
408
  return;
288
409
  try {
289
- const translation = await promise();
290
- if (promise !== defaultTranslation &&
291
- translation.locale !== locale &&
292
- isDevMode()) {
293
- return console.warn(`Expected locale to be ${locale} but got ${translation.locale}`);
294
- }
295
- store.registerOnDemandLoaders(translation.namespace, {
410
+ const promises = [promise()];
411
+ if (shouldPreloadDefault &&
412
+ !defaultTranslationLoaded &&
413
+ promise !== defaultTranslation)
414
+ promises.push(defaultTranslation());
415
+ const translations = await Promise.allSettled(promises);
416
+ const fullfilled = translations.map((t) => t.status === 'fulfilled' ? t.value : null);
417
+ if (fullfilled.at(0) === null && fullfilled.at(1) === null)
418
+ throw new Error('Failed to load translations');
419
+ const [t, defaultT] = fullfilled;
420
+ const ns = t?.namespace ?? defaultT?.namespace;
421
+ if (!ns)
422
+ throw new Error('No namespace found in translation');
423
+ if (isDevMode() && t && t.locale !== locale && t.locale !== defaultLocale)
424
+ console.warn(`Expected locale to be ${locale} but got ${t.locale}`);
425
+ store.registerOnDemandLoaders(ns, {
296
426
  ...other,
297
427
  [defaultLocale]: defaultTranslation,
298
428
  });
299
- store.register(translation.namespace, {
300
- [locale]: translation.flat,
301
- });
302
- if (promise === defaultTranslation) {
429
+ const toRegister = {};
430
+ if (t)
431
+ toRegister[locale] = t.flat;
432
+ if (defaultT)
433
+ toRegister[defaultLocale] = defaultT.flat;
434
+ store.register(ns, toRegister);
435
+ if (promise === defaultTranslation || defaultT)
303
436
  defaultTranslationLoaded = true;
304
- }
305
437
  }
306
438
  catch {
307
439
  if (isDevMode()) {
308
440
  console.warn(`Failed to load translation for locale: ${locale}`);
309
441
  }
310
442
  }
443
+ finally {
444
+ if (locale !== untracked(store.locale))
445
+ store.locale.set(locale);
446
+ }
311
447
  };
312
448
  return {
313
449
  injectNamespaceT: injectT,
@@ -315,6 +451,35 @@ function registerNamespace(defaultTranslation, other) {
315
451
  };
316
452
  }
317
453
 
454
+ /**
455
+ * Guard that validates the locale parameter against supported locales.
456
+ * Redirects to default locale if the locale is invalid.
457
+ *
458
+ * @param prefixSegments Optional array of path segments preceding the locale segment.
459
+ * if (you wanted to match /app/:locale/... you would pass ['app'] here) & the function would match the second parameter + redirect accordingly
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * {
464
+ * path: ':locale',
465
+ * canMatch: [canMatchLocale()],
466
+ * children: [...]
467
+ * }
468
+ * ```
469
+ */
470
+ function canMatchLocale(prefixSegments = []) {
471
+ return (_route, segments) => {
472
+ const supportedLocales = injectSupportedLocales();
473
+ const locale = segments.at(prefixSegments.length)?.path;
474
+ if (!locale || !supportedLocales.includes(locale))
475
+ return inject(Router).createUrlTree([
476
+ ...prefixSegments,
477
+ injectDefaultLocale(),
478
+ ]);
479
+ return true;
480
+ };
481
+ }
482
+
318
483
  class Translate {
319
484
  t = createT(inject(TranslationStore));
320
485
  translate = input.required(...(ngDevMode ? [{ debugName: "translate" }] : []));
@@ -374,5 +539,5 @@ class Translator {
374
539
  * Generated bundle index. Do not edit.
375
540
  */
376
541
 
377
- export { Translate, Translator, compileTranslation, createNamespace, injectDynamicLocale, injectIntl, provideIntlConfig, registerNamespace };
542
+ export { Translate, Translator, canMatchLocale, compileTranslation, createNamespace, injectDynamicLocale, injectIntl, injectSupportedLocales, provideIntlConfig, registerNamespace };
378
543
  //# sourceMappingURL=mmstack-translate.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mmstack-translate.mjs","sources":["../../../../packages/translate/src/lib/delim.ts","../../../../packages/translate/src/lib/compile.ts","../../../../packages/translate/src/lib/create-namespace.ts","../../../../packages/translate/src/lib/translation-store.ts","../../../../packages/translate/src/lib/register-namespace.ts","../../../../packages/translate/src/lib/translate.ts","../../../../packages/translate/src/lib/translator.ts","../../../../packages/translate/src/mmstack-translate.ts"],"sourcesContent":["const KEY_DELIM = '::MMT_DELIM::';\n\nexport function prependDelim(prefix: string, key: string): string {\n return `${prefix}${KEY_DELIM}${key}`;\n}\n\nexport function replaceWithDelim(str: string, repl = '.'): string {\n return str.replaceAll(repl, KEY_DELIM);\n}\n","import { prependDelim } from './delim';\nimport type {\n inferTranslationParamMap,\n inferTranslationShape,\n} from './parameterize.type';\nimport type { UnknownStringKeyObject } from './string-key-object.type';\n\nconst INTERNAL_SYMBOL = Symbol.for('mmstack-translate-internal');\n\ntype InternalSymbol = typeof INTERNAL_SYMBOL;\n\nexport type CompiledTranslation<\n T extends UnknownStringKeyObject,\n TNS extends string,\n TLocale extends string = string,\n> = {\n flat: Record<string, string>;\n locale?: TLocale;\n namespace: TNS;\n [INTERNAL_SYMBOL]: {\n shape: inferTranslationShape<T>;\n map: inferTranslationParamMap<TNS, T>;\n };\n};\n\nexport type mergeTranslationMaps<\n TMain extends CompiledTranslation<UnknownStringKeyObject, string>,\n TOther extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = Omit<TMain, InternalSymbol> & {\n [INTERNAL_SYMBOL]: {\n shape: inferCompiledTranslationShape<TMain>;\n map: inferCompiledTranslationMap<TOther> &\n inferCompiledTranslationMap<TMain>;\n };\n};\n\nexport type inferCompiledTranslationNamespace<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = T['namespace'];\n\nexport type inferCompiledTranslationShape<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = T[InternalSymbol]['shape'];\n\nexport type inferCompiledTranslationMap<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = T[InternalSymbol]['map'];\n\nfunction isTranslationObject(t: unknown): t is UnknownStringKeyObject {\n return typeof t === 'object' && t !== null;\n}\n\nfunction flattenTranslation<T extends UnknownStringKeyObject>(obj: T) {\n return Object.entries(obj).reduce(\n (acc, [key, value]) => {\n if (typeof value === 'string') {\n acc[key] = value;\n } else if (isTranslationObject(value)) {\n Object.entries(flattenTranslation(value)).forEach(\n ([nestedKey, nestedValue]) => {\n acc[prependDelim(key, nestedKey)] = nestedValue;\n },\n );\n }\n\n return acc;\n },\n {} as Record<string, string>,\n );\n}\n\nexport function compileTranslation<\n T extends UnknownStringKeyObject,\n TNS extends string,\n TLocale extends string = string,\n>(\n translation: T,\n ns: TNS,\n locale?: TLocale,\n): CompiledTranslation<T, TNS, TLocale> {\n type $Shape = inferTranslationShape<T>;\n type $Map = inferTranslationParamMap<TNS, T>;\n\n return {\n locale,\n flat: flattenTranslation(translation),\n namespace: ns,\n [INTERNAL_SYMBOL]: {\n shape: {} as $Shape,\n map: {} as $Map,\n },\n };\n}\n","import {\n CompiledTranslation,\n compileTranslation,\n inferCompiledTranslationShape,\n mergeTranslationMaps,\n} from './compile';\nimport { UnknownStringKeyObject } from './string-key-object.type';\n\ntype TranslationNamespace<\n TNS extends string,\n T extends CompiledTranslation<UnknownStringKeyObject, TNS>,\n TShape extends UnknownStringKeyObject,\n> = {\n translation: T;\n createTranslation: <TLocale extends string>(\n locale: TLocale,\n translation: TShape,\n ) => CompiledTranslation<TShape, TNS, TLocale>;\n createMergedNamespace: <\n TOtherNS extends string,\n const TOther extends UnknownStringKeyObject,\n TOtherCompiled extends CompiledTranslation<TOther, TOtherNS>,\n >(\n ns: TOtherNS,\n translation: TOther,\n ) => TranslationNamespace<\n TOtherNS,\n mergeTranslationMaps<TOtherCompiled, T>,\n inferCompiledTranslationShape<TOtherCompiled>\n >;\n};\n\nexport function createNamespace<\n const T extends UnknownStringKeyObject,\n TNS extends string,\n>(ns: TNS, translation: T) {\n const compiled = compileTranslation<T, TNS>(translation, ns);\n\n type TCompiled = typeof compiled;\n type TShape = inferCompiledTranslationShape<typeof compiled>;\n\n const namespace: TranslationNamespace<TNS, TCompiled, TShape> = {\n translation: compileTranslation(translation, ns),\n createTranslation: <TLocale extends string>(\n locale: TLocale,\n translation: TShape,\n ) => {\n return compileTranslation(translation, ns, locale);\n },\n createMergedNamespace: <\n TOther extends UnknownStringKeyObject,\n TOtherNS extends string,\n TOtherCompiled extends CompiledTranslation<\n TOther,\n TOtherNS\n > = CompiledTranslation<TOther, TOtherNS>,\n >(\n otherNs: TOtherNS,\n otherTranslation: TOther,\n ) => {\n return createNamespace(otherNs, otherTranslation) as TranslationNamespace<\n TOtherNS,\n mergeTranslationMaps<TOtherCompiled, TCompiled>,\n inferCompiledTranslationShape<TOtherCompiled>\n > as unknown as any;\n },\n };\n\n return namespace;\n}\n","import {\n computed,\n effect,\n inject,\n Injectable,\n InjectionToken,\n isDevMode,\n LOCALE_ID,\n Provider,\n resource,\n Signal,\n signal,\n untracked,\n WritableSignal,\n} from '@angular/core';\nimport { createIntl, createIntlCache, type IntlConfig } from '@formatjs/intl';\nimport { CompiledTranslation } from './compile';\nimport { prependDelim } from './delim';\nimport { UnknownStringKeyObject } from './string-key-object.type';\n\nconst CONFIG_TOKEN = new InjectionToken<\n Omit<IntlConfig, 'locale' | 'messages'>\n>('mmstack-intl-config');\n\nexport function provideIntlConfig(\n config: Omit<IntlConfig, 'locale' | 'messages'>,\n): Provider {\n return {\n useValue: config,\n provide: CONFIG_TOKEN,\n };\n}\n\nexport function injectIntlConfig() {\n return inject(CONFIG_TOKEN, { optional: true }) ?? undefined;\n}\n\nexport function injectDefaultLocale() {\n return injectIntlConfig()?.defaultLocale ?? 'en-US';\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class TranslationStore {\n private readonly cache = createIntlCache();\n private readonly config = injectIntlConfig();\n readonly loadQueue = signal<string[]>([]);\n readonly locale = signal(inject(LOCALE_ID));\n private readonly defaultLocale = injectDefaultLocale();\n private readonly translations = signal<\n Record<string, Record<string, string>>\n >({\n [this.defaultLocale]: {},\n });\n\n private readonly onDemandLoaders = new Map<\n string,\n Record<\n string,\n () => Promise<CompiledTranslation<UnknownStringKeyObject, string>>\n >\n >();\n\n private readonly nonMessageConfig = computed(() => ({\n ...this.config,\n locale: this.locale(),\n }));\n\n private readonly messages = computed(\n () =>\n this.translations()[this.locale()] ??\n this.translations()[this.defaultLocale] ??\n {},\n );\n\n readonly dynamicLocaleLoader = resource({\n params: computed(() => this.loadQueue().at(0) ?? null),\n loader: async ({ params: newLocale, abortSignal }) => {\n if (!newLocale) return;\n\n const currentTranslations = untracked(this.translations);\n\n const loadPromises: Promise<{\n namespace: string;\n flat: Record<string, string>;\n } | null>[] = [];\n\n for (const [namespace, loaders] of this.onDemandLoaders.entries()) {\n const loader = loaders[newLocale];\n if (loader) {\n const hasNamespaceForLocale =\n currentTranslations[newLocale] &&\n Object.keys(currentTranslations[newLocale]).some((key) =>\n key.startsWith(`${prependDelim(namespace, '').slice(0, -1)}`),\n );\n\n if (!hasNamespaceForLocale) {\n loadPromises.push(\n loader()\n .then((translation) => {\n if (abortSignal.aborted) return null;\n return {\n namespace: translation.namespace,\n flat: translation.flat,\n };\n })\n .catch((err) => {\n if (isDevMode()) {\n console.error(\n '[Translate] Failed to load',\n namespace,\n newLocale,\n err,\n );\n }\n\n return null;\n }),\n );\n }\n }\n }\n\n return Promise.all(loadPromises)\n .then((res) => res.filter((r) => r !== null))\n .then((res) => ({\n locales: res,\n locale: newLocale,\n }));\n },\n });\n\n readonly intl = computed(() =>\n createIntl(\n {\n ...this.nonMessageConfig(),\n messages: this.messages(),\n },\n this.cache,\n ),\n );\n\n constructor() {\n effect(() => {\n if (\n // should never be in error state, but best to check in case something throws\n this.dynamicLocaleLoader.error() ||\n this.dynamicLocaleLoader.isLoading()\n )\n return;\n const dynamicLocales = this.dynamicLocaleLoader.value();\n\n if (!dynamicLocales) return;\n for (const locale of dynamicLocales.locales) {\n this.register(locale.namespace, {\n [dynamicLocales.locale]: locale.flat,\n });\n }\n this.loadQueue.update((q) =>\n q.filter((l) => l !== dynamicLocales.locale),\n );\n this.locale.set(dynamicLocales.locale);\n });\n }\n\n formatMessage(key: string, values?: Record<string, string | number>) {\n const message = this.translations()[this.locale()]?.[key] ?? '';\n\n if (!message) return '';\n\n return this.intl().formatMessage(\n { id: key, defaultMessage: message },\n values,\n );\n }\n\n register(\n namespace: string,\n flat: Partial<Record<string, Record<string, string>>>,\n ) {\n this.translations.update((cur) => {\n return Object.entries(flat).reduce(\n (acc, [locale, translation]) => {\n const localeTranslation = acc[locale] ?? {};\n\n const withNS = Object.entries(translation ?? {}).reduce(\n (acc, [key, value]) => {\n acc[prependDelim(namespace, key)] = value;\n return acc;\n },\n {} as Record<string, string>,\n );\n\n acc[locale] = {\n ...localeTranslation,\n ...withNS,\n };\n\n return acc;\n },\n { ...cur },\n );\n });\n }\n\n registerOnDemandLoaders(\n namespace: string,\n loaders: Record<string, () => Promise<any>>,\n ) {\n this.onDemandLoaders.set(namespace, loaders);\n }\n\n hasLocaleLoaders(locale: string): boolean {\n return Array.from(this.onDemandLoaders.values()).some(\n (loaders) => loaders[locale],\n );\n }\n}\n\nexport function injectIntl() {\n return inject(TranslationStore).intl;\n}\n\n/**\n * Inject a dynamic locale signal that supports runtime language switching.\n *\n * @returns A writable signal with the current locale and loading state.\n * Only allows switching to locales that have registered loaders.\n *\n * @example\n * ```typescript\n * const locale = injectDynamicLocale();\n *\n * // Switch language (triggers automatic translation loading)\n * locale.set('sl-SI');\n *\n * // Check loading state\n * if (locale.isLoading()) {\n * // Show spinner\n * }\n * ```\n */\nexport function injectDynamicLocale(): WritableSignal<string> & {\n isLoading: Signal<boolean>;\n} {\n const store = inject(TranslationStore);\n\n const source = computed(() => store.locale()) as WritableSignal<string> & {\n isLoading: Signal<boolean>;\n };\n\n const set = (value: string) => {\n if (\n value === untracked(source) ||\n !store.hasLocaleLoaders(value) ||\n untracked(store.loadQueue).includes(value)\n )\n return;\n store.loadQueue.update((q) => [...q, value]);\n };\n source.set = set;\n source.update = (updater: (value: string) => string) => {\n const next = updater(untracked(source));\n source.set(next);\n };\n source.asReadonly = () => source;\n\n source.isLoading = store.dynamicLocaleLoader.isLoading;\n\n return source;\n}\n","import {\n computed,\n inject,\n isDevMode,\n isSignal,\n Signal,\n untracked,\n} from '@angular/core';\nimport {\n CompiledTranslation,\n inferCompiledTranslationMap,\n inferCompiledTranslationNamespace,\n} from './compile';\nimport { replaceWithDelim } from './delim';\nimport { UnknownStringKeyObject } from './string-key-object.type';\nimport { injectDefaultLocale, TranslationStore } from './translation-store';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyStringRecord = Record<string, any>;\n\nfunction createEqualsRecord<T extends AnyStringRecord>(keys: (keyof T)[] = []) {\n let keyMatcher: (a: T, b: T) => boolean;\n\n if (keys.length === 0) {\n keyMatcher = () => true;\n } else if (keys.length === 1) {\n const key = keys[0];\n keyMatcher = (a, b) => a[key] === b[key];\n } else {\n keyMatcher = (a, b) => {\n return keys.every((k) => a[k] === b[k]);\n };\n }\n\n return (a?: T, b?: T): boolean => {\n if (!a && !b) return true;\n if (!a || !b) return false;\n return keyMatcher(a, b);\n };\n}\n\ntype TFunction<TMap extends AnyStringRecord> = <\n TKey extends keyof TMap & string,\n>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [TMap[TKey]]\n) => string;\n\ntype SignalTFunction<TMap extends AnyStringRecord> = <\n TKey extends keyof TMap & string,\n>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [() => TMap[TKey]]\n) => Signal<string>;\n\ntype TFunctionWithSignalConstructor<\n TMap extends AnyStringRecord,\n TFN extends TFunction<TMap>,\n> = TFN & {\n asSignal: SignalTFunction<TMap>;\n};\n\nfunction addSignalFn<TMap extends AnyStringRecord, TFn extends TFunction<TMap>>(\n fn: TFn,\n store: TranslationStore,\n): TFunctionWithSignalConstructor<TMap, TFn> {\n const withSig = fn as TFunctionWithSignalConstructor<TMap, TFn>;\n\n const asSignal = <TKey extends keyof TMap & string>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [() => TMap[TKey]]\n ): Signal<string> => {\n const variables = args[0] as () => AnyStringRecord | undefined;\n const stringKey = key as string;\n\n const flatPath = replaceWithDelim(stringKey);\n\n const varsFn = variables ?? (() => undefined);\n const varsSignal = isSignal(varsFn)\n ? varsFn\n : computed(varsFn, {\n equal: createEqualsRecord(Object.keys(varsFn() ?? {})),\n });\n\n return computed(() => store.formatMessage(flatPath, varsSignal()));\n };\n\n withSig.asSignal = asSignal;\n\n return withSig;\n}\n\nexport function createT<TMap extends AnyStringRecord>(\n store: TranslationStore,\n): TFunction<TMap> {\n return <TKey extends keyof TMap & string>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [TMap[TKey]]\n ): string => {\n const variables = args[0] as AnyStringRecord | undefined;\n const stringKey = key as string;\n\n return store.formatMessage(replaceWithDelim(stringKey), variables);\n };\n}\n\nexport function registerNamespace<\n TDefault extends CompiledTranslation<UnknownStringKeyObject, string>,\n>(\n defaultTranslation: () => Promise<TDefault>,\n other: Record<\n string,\n () => Promise<\n CompiledTranslation<\n UnknownStringKeyObject,\n inferCompiledTranslationNamespace<TDefault>,\n string\n >\n >\n >,\n) {\n type $Map = inferCompiledTranslationMap<TDefault>;\n type $BaseTFN = TFunction<$Map>;\n type $TFN = TFunctionWithSignalConstructor<$Map, $BaseTFN>;\n\n const injectT = (): $TFN => {\n const store = inject(TranslationStore);\n\n return addSignalFn(createT(store), store);\n };\n\n let defaultTranslationLoaded = false;\n const resolver = async () => {\n const store = inject(TranslationStore);\n const locale = untracked(store.locale);\n const defaultLocale = injectDefaultLocale();\n const tPromise = other[locale] as (typeof other)[string] | undefined;\n\n const promise = tPromise ?? defaultTranslation;\n if (!promise && isDevMode()) {\n return console.warn(`No translation found for locale: ${locale}`);\n }\n\n if (promise === defaultTranslation && defaultTranslationLoaded) return;\n\n try {\n const translation = await promise();\n\n if (\n promise !== defaultTranslation &&\n translation.locale !== locale &&\n isDevMode()\n ) {\n return console.warn(\n `Expected locale to be ${locale} but got ${translation.locale}`,\n );\n }\n\n store.registerOnDemandLoaders(translation.namespace, {\n ...other,\n [defaultLocale]: defaultTranslation,\n });\n\n store.register(translation.namespace, {\n [locale]: translation.flat,\n });\n if (promise === defaultTranslation) {\n defaultTranslationLoaded = true;\n }\n } catch {\n if (isDevMode()) {\n console.warn(`Failed to load translation for locale: ${locale}`);\n }\n }\n };\n\n return {\n injectNamespaceT: injectT,\n resolveNamespaceTranslation: resolver,\n };\n}\n","import {\n afterRenderEffect,\n computed,\n Directive,\n ElementRef,\n inject,\n input,\n Renderer2,\n} from '@angular/core';\nimport { CompiledTranslation, inferCompiledTranslationMap } from './compile';\nimport { createT } from './register-namespace';\nimport { UnknownStringKeyObject } from './string-key-object.type';\nimport { TranslationStore } from './translation-store';\n\n@Directive()\nexport abstract class Translate<\n TInput extends string,\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>,\n TKey extends TInput & keyof TMap & string = TInput & keyof TMap & string,\n> {\n private readonly t = createT(inject(TranslationStore));\n\n readonly translate =\n input.required<\n TMap[TKey] extends void\n ? TKey | [key: TKey]\n : [key: TKey, vars: TMap[TKey]]\n >();\n\n constructor() {\n const key = computed(() => {\n const vars = this.translate();\n return (Array.isArray(vars) ? vars[0] : vars) as TKey;\n });\n\n const args = computed(\n () => {\n const vars = this.translate();\n return (Array.isArray(vars) ? vars[1] : undefined) as TMap[TKey];\n },\n {\n equal: (a, b) => {\n if (a === undefined && b === undefined) return true;\n if (a === undefined || b === undefined) return false;\n\n const aObj = a as Record<string, string>;\n const keys = Object.keys(aObj);\n const bObj = b as Record<string, string>;\n\n if (!keys.length) return !Object.keys(bObj).length;\n\n return keys.every((key) => aObj[key] === bObj[key]);\n },\n },\n );\n\n const translation = computed(() => this.t(key(), args()));\n\n const renderer = inject(Renderer2);\n const el = inject<ElementRef<HTMLElement>>(ElementRef);\n\n afterRenderEffect({\n write: () => {\n renderer.setProperty(el.nativeElement, 'textContent', translation());\n },\n });\n }\n}\n","import { ChangeDetectorRef, effect, inject } from '@angular/core';\nimport { CompiledTranslation, inferCompiledTranslationMap } from './compile';\nimport { createT } from './register-namespace';\nimport { UnknownStringKeyObject } from './string-key-object.type';\nimport { TranslationStore } from './translation-store';\n\nexport abstract class Translator<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>,\n> {\n private readonly store = inject(TranslationStore);\n private readonly t = createT<TMap>(this.store);\n\n constructor() {\n const cdr = inject(ChangeDetectorRef);\n\n effect(() => {\n this.store.locale();\n cdr.markForCheck();\n });\n }\n\n transform<K extends keyof TMap & string>(\n key: K,\n ...args: TMap[K] extends void\n ? [locale?: string]\n : [TMap[K], locale?: string]\n ): string {\n const actualArgs = args.filter(\n (a) => typeof a === 'object',\n ) as TMap[K] extends void ? [] : [TMap[K]];\n\n return this.t(key, ...actualArgs);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mmstack-translate.mjs","sources":["../../../../packages/translate/src/lib/delim.ts","../../../../packages/translate/src/lib/compile.ts","../../../../packages/translate/src/lib/create-namespace.ts","../../../../packages/translate/src/lib/path-param.ts","../../../../packages/translate/src/lib/translation-store.ts","../../../../packages/translate/src/lib/register-namespace.ts","../../../../packages/translate/src/lib/route-helpers.ts","../../../../packages/translate/src/lib/translate.ts","../../../../packages/translate/src/lib/translator.ts","../../../../packages/translate/src/mmstack-translate.ts"],"sourcesContent":["const KEY_DELIM = '::MMT_DELIM::';\n\nexport function prependDelim(prefix: string, key: string): string {\n return `${prefix}${KEY_DELIM}${key}`;\n}\n\nexport function replaceWithDelim(str: string, repl = '.'): string {\n return str.replaceAll(repl, KEY_DELIM);\n}\n","import { prependDelim } from './delim';\nimport type {\n inferTranslationParamMap,\n inferTranslationShape,\n} from './parameterize.type';\nimport type { UnknownStringKeyObject } from './string-key-object.type';\n\nconst INTERNAL_SYMBOL = Symbol.for('mmstack-translate-internal');\n\ntype InternalSymbol = typeof INTERNAL_SYMBOL;\n\nexport type CompiledTranslation<\n T extends UnknownStringKeyObject,\n TNS extends string,\n TLocale extends string = string,\n> = {\n flat: Record<string, string>;\n locale?: TLocale;\n namespace: TNS;\n [INTERNAL_SYMBOL]: {\n shape: inferTranslationShape<T>;\n map: inferTranslationParamMap<TNS, T>;\n };\n};\n\nexport type mergeTranslationMaps<\n TMain extends CompiledTranslation<UnknownStringKeyObject, string>,\n TOther extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = Omit<TMain, InternalSymbol> & {\n [INTERNAL_SYMBOL]: {\n shape: inferCompiledTranslationShape<TMain>;\n map: inferCompiledTranslationMap<TOther> &\n inferCompiledTranslationMap<TMain>;\n };\n};\n\nexport type inferCompiledTranslationNamespace<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = T['namespace'];\n\nexport type inferCompiledTranslationShape<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = T[InternalSymbol]['shape'];\n\nexport type inferCompiledTranslationMap<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n> = T[InternalSymbol]['map'];\n\nfunction isTranslationObject(t: unknown): t is UnknownStringKeyObject {\n return typeof t === 'object' && t !== null;\n}\n\nfunction flattenTranslation<T extends UnknownStringKeyObject>(obj: T) {\n return Object.entries(obj).reduce(\n (acc, [key, value]) => {\n if (typeof value === 'string') {\n acc[key] = value;\n } else if (isTranslationObject(value)) {\n Object.entries(flattenTranslation(value)).forEach(\n ([nestedKey, nestedValue]) => {\n acc[prependDelim(key, nestedKey)] = nestedValue;\n },\n );\n }\n\n return acc;\n },\n {} as Record<string, string>,\n );\n}\n\nexport function compileTranslation<\n T extends UnknownStringKeyObject,\n TNS extends string,\n TLocale extends string = string,\n>(\n translation: T,\n ns: TNS,\n locale?: TLocale,\n): CompiledTranslation<T, TNS, TLocale> {\n type $Shape = inferTranslationShape<T>;\n type $Map = inferTranslationParamMap<TNS, T>;\n\n return {\n locale,\n flat: flattenTranslation(translation),\n namespace: ns,\n [INTERNAL_SYMBOL]: {\n shape: {} as $Shape,\n map: {} as $Map,\n },\n };\n}\n","import {\n CompiledTranslation,\n compileTranslation,\n inferCompiledTranslationShape,\n mergeTranslationMaps,\n} from './compile';\nimport { UnknownStringKeyObject } from './string-key-object.type';\n\ntype TranslationNamespace<\n TNS extends string,\n T extends CompiledTranslation<UnknownStringKeyObject, TNS>,\n TShape extends UnknownStringKeyObject,\n> = {\n translation: T;\n createTranslation: <TLocale extends string>(\n locale: TLocale,\n translation: TShape,\n ) => CompiledTranslation<TShape, TNS, TLocale>;\n createMergedNamespace: <\n TOtherNS extends string,\n const TOther extends UnknownStringKeyObject,\n TOtherCompiled extends CompiledTranslation<TOther, TOtherNS>,\n >(\n ns: TOtherNS,\n translation: TOther,\n ) => TranslationNamespace<\n TOtherNS,\n mergeTranslationMaps<TOtherCompiled, T>,\n inferCompiledTranslationShape<TOtherCompiled>\n >;\n};\n\nexport function createNamespace<\n const T extends UnknownStringKeyObject,\n TNS extends string,\n>(ns: TNS, translation: T) {\n const compiled = compileTranslation<T, TNS>(translation, ns);\n\n type TCompiled = typeof compiled;\n type TShape = inferCompiledTranslationShape<typeof compiled>;\n\n const namespace: TranslationNamespace<TNS, TCompiled, TShape> = {\n translation: compileTranslation(translation, ns),\n createTranslation: <TLocale extends string>(\n locale: TLocale,\n translation: TShape,\n ) => {\n return compileTranslation(translation, ns, locale);\n },\n createMergedNamespace: <\n TOther extends UnknownStringKeyObject,\n TOtherNS extends string,\n TOtherCompiled extends CompiledTranslation<\n TOther,\n TOtherNS\n > = CompiledTranslation<TOther, TOtherNS>,\n >(\n otherNs: TOtherNS,\n otherTranslation: TOther,\n ) => {\n return createNamespace(otherNs, otherTranslation) as TranslationNamespace<\n TOtherNS,\n mergeTranslationMaps<TOtherCompiled, TCompiled>,\n inferCompiledTranslationShape<TOtherCompiled>\n > as unknown as any;\n },\n };\n\n return namespace;\n}\n","import { computed, inject, type Signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute, type ParamMap, Router } from '@angular/router';\n\nexport function pathParam(\n key: string | (() => string),\n route = inject(ActivatedRoute),\n): Signal<string | null> {\n const keySignal =\n typeof key === 'string' ? computed(() => key) : computed(key);\n\n const routerOptions = inject(Router)['options'];\n\n if (\n routerOptions &&\n typeof routerOptions === 'object' &&\n routerOptions.paramsInheritanceStrategy === 'always'\n ) {\n const params = toSignal(route.paramMap, {\n initialValue: route.snapshot.paramMap,\n });\n\n return computed(() => params().get(keySignal()));\n }\n\n const paramMapSignals: Signal<ParamMap>[] = [];\n let currentRoute: ActivatedRoute | null = route;\n\n const isStatic = typeof key === 'string';\n\n while (currentRoute) {\n const initial = currentRoute.snapshot.paramMap;\n paramMapSignals.push(\n toSignal(currentRoute.paramMap, {\n initialValue: initial,\n }),\n );\n\n // For static keys, stop once we find the param, will find first in computed for loop already so basically noop for for loop\n if (isStatic && initial.has(key as string)) break;\n\n currentRoute = currentRoute.parent;\n }\n\n return computed(() => {\n const paramKey = keySignal();\n\n for (const map of paramMapSignals) {\n const v = map().get(paramKey);\n if (v) return v;\n }\n\n return null;\n });\n}\n","import {\n computed,\n effect,\n inject,\n Injectable,\n InjectionToken,\n isDevMode,\n LOCALE_ID,\n type Provider,\n resource,\n type Signal,\n signal,\n untracked,\n type WritableSignal,\n} from '@angular/core';\nimport { createIntl, createIntlCache, type IntlConfig } from '@formatjs/intl';\nimport { type CompiledTranslation } from './compile';\nimport { prependDelim } from './delim';\nimport { pathParam } from './path-param';\nimport { type UnknownStringKeyObject } from './string-key-object.type';\n\nconst CONFIG_TOKEN = new InjectionToken<\n Omit<IntlConfig, 'locale' | 'messages'> & {\n supportedLocales?: string[];\n preloadDefaultLocale?: boolean;\n localeParamName?: string;\n }\n>('mmstack-intl-config');\n\nexport function provideIntlConfig(\n config: Omit<IntlConfig, 'locale' | 'messages'> & {\n /** Checks next locale is in provided array before switching locales */\n supportedLocales?: string[];\n /** Preloads the default locale ensuring sync fallback, not necessary for most cases as it will lazily load automatically when needed */\n preloadDefaultLocale?: boolean;\n /** Auto-resolution when using a locale parameter via angular router */\n localeParamName?: string;\n },\n): Provider[] {\n const providers: Provider[] = [\n {\n useFactory: (localeId: string) => {\n const next = {\n ...config,\n };\n\n const defaultLocale =\n config.defaultLocale ?? config.supportedLocales?.at(0) ?? localeId;\n\n if (\n next.supportedLocales &&\n !next.supportedLocales.includes(defaultLocale)\n ) {\n next.supportedLocales = [...next.supportedLocales, defaultLocale];\n }\n\n return next;\n },\n deps: [LOCALE_ID],\n provide: CONFIG_TOKEN,\n },\n ];\n\n const defaultLocale = config.defaultLocale ?? config.supportedLocales?.at(0);\n\n if (!defaultLocale) return providers;\n\n providers.push({\n provide: LOCALE_ID,\n useValue: defaultLocale,\n });\n\n return providers;\n}\n\nexport function injectIntlConfig() {\n return inject(CONFIG_TOKEN, { optional: true }) ?? undefined;\n}\n\nexport function injectDefaultLocale() {\n return injectIntlConfig()?.defaultLocale ?? inject(LOCALE_ID) ?? 'en-US';\n}\n\nexport function injectSupportedLocales() {\n return injectIntlConfig()?.supportedLocales ?? [injectDefaultLocale()];\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class TranslationStore {\n private readonly cache = createIntlCache();\n private readonly config = injectIntlConfig();\n readonly loadQueue = signal<string[]>([]);\n readonly locale = signal(injectDefaultLocale());\n private readonly defaultLocale = injectDefaultLocale();\n private readonly translations = signal<\n Record<string, Record<string, string>>\n >({\n [this.defaultLocale]: {},\n });\n private attemptedFallbackLoad = false;\n\n private readonly onDemandLoaders = new Map<\n string,\n Record<\n string,\n () => Promise<CompiledTranslation<UnknownStringKeyObject, string>>\n >\n >();\n\n private readonly nonMessageConfig = computed(() => ({\n ...this.config,\n locale: this.locale(),\n }));\n\n private readonly messages = computed(\n () =>\n this.translations()[this.locale()] ??\n this.translations()[this.defaultLocale] ??\n {},\n );\n\n readonly dynamicLocaleLoader = resource({\n params: computed(() => this.loadQueue().at(0) ?? null),\n loader: async ({ params: newLocale, abortSignal }) => {\n if (!newLocale) return;\n\n const currentTranslations = untracked(this.translations);\n\n const loadPromises: Promise<{\n namespace: string;\n flat: Record<string, string>;\n } | null>[] = [];\n\n for (const [namespace, loaders] of this.onDemandLoaders.entries()) {\n const loader = loaders[newLocale];\n if (loader) {\n const hasNamespaceForLocale =\n currentTranslations[newLocale] &&\n Object.keys(currentTranslations[newLocale]).some((key) =>\n key.startsWith(`${prependDelim(namespace, '').slice(0, -1)}`),\n );\n\n if (!hasNamespaceForLocale) {\n loadPromises.push(\n loader()\n .then((translation) => {\n if (abortSignal.aborted) return null;\n return {\n namespace: translation.namespace,\n flat: translation.flat,\n };\n })\n .catch((err) => {\n if (isDevMode()) {\n console.error(\n '[Translate] Failed to load',\n namespace,\n newLocale,\n err,\n );\n }\n\n return null;\n }),\n );\n }\n }\n }\n\n return Promise.all(loadPromises)\n .then((res) => res.filter((r) => r !== null))\n .then((res) => ({\n locales: res,\n locale: newLocale,\n }));\n },\n });\n\n readonly intl = computed(() =>\n createIntl(\n {\n ...this.nonMessageConfig(),\n messages: this.messages(),\n },\n this.cache,\n ),\n );\n\n constructor() {\n const paramName = this.config?.localeParamName;\n if (paramName) {\n const param = pathParam(paramName);\n\n effect(() => {\n const loc = param();\n if (\n !loc ||\n loc === untracked(this.locale) ||\n untracked(this.loadQueue).includes(loc)\n )\n return;\n if (this.hasLocaleLoaders(loc)) this.locale.set(loc);\n else this.loadQueue.update((q) => [...q, loc]);\n });\n }\n\n effect(() => {\n if (\n // should never be in error state, but best to check in case something throws\n this.dynamicLocaleLoader.error() ||\n this.dynamicLocaleLoader.isLoading()\n )\n return;\n const dynamicLocales = this.dynamicLocaleLoader.value();\n\n if (!dynamicLocales) return;\n\n // Register loaded translations\n for (const locale of dynamicLocales.locales) {\n this.register(locale.namespace, {\n [dynamicLocales.locale]: locale.flat,\n });\n }\n\n const hasTranslations =\n dynamicLocales.locales.length > 0 ||\n this.translations()[dynamicLocales.locale];\n\n if (hasTranslations) {\n this.loadQueue.update((q) =>\n q.filter((l) => l !== dynamicLocales.locale),\n );\n this.locale.set(dynamicLocales.locale);\n }\n });\n }\n\n formatMessage(key: string, values?: Record<string, string | number>) {\n const message =\n this.translations()[this.locale()]?.[key] ??\n this.translations()[this.defaultLocale]?.[key] ??\n '';\n\n if (!message) {\n if (this.attemptedFallbackLoad) return '';\n\n this.attemptedFallbackLoad = true;\n untracked(() => {\n if (!this.loadQueue().includes(this.defaultLocale))\n this.loadQueue.update((q) => [...q, this.defaultLocale]);\n });\n return '';\n }\n\n return this.intl().formatMessage(\n { id: key, defaultMessage: message },\n values,\n );\n }\n\n register(\n namespace: string,\n flat: Partial<Record<string, Record<string, string>>>,\n ) {\n this.translations.update((cur) => {\n return Object.entries(flat).reduce(\n (acc, [locale, translation]) => {\n const localeTranslation = acc[locale] ?? {};\n\n const withNS = Object.entries(translation ?? {}).reduce(\n (acc, [key, value]) => {\n acc[prependDelim(namespace, key)] = value;\n return acc;\n },\n {} as Record<string, string>,\n );\n\n acc[locale] = {\n ...localeTranslation,\n ...withNS,\n };\n\n return acc;\n },\n { ...cur },\n );\n });\n }\n\n registerOnDemandLoaders(\n namespace: string,\n loaders: Record<string, () => Promise<any>>,\n ) {\n this.onDemandLoaders.set(namespace, loaders);\n }\n\n hasLocaleLoaders(locale: string): boolean {\n return Array.from(this.onDemandLoaders.values()).some(\n (loaders) => loaders[locale],\n );\n }\n}\n\nexport function injectIntl() {\n return inject(TranslationStore).intl;\n}\n\n/**\n * Inject a dynamic locale signal that supports runtime language switching.\n *\n * @returns A writable signal with the current locale and loading state.\n * Only allows switching to locales that have registered loaders.\n *\n * @example\n * ```typescript\n * const locale = injectDynamicLocale();\n *\n * // Switch language (triggers automatic translation loading)\n * locale.set('sl-SI');\n *\n * // Check loading state\n * if (locale.isLoading()) {\n * // Show spinner\n * }\n * ```\n */\nexport function injectDynamicLocale(): WritableSignal<string> & {\n isLoading: Signal<boolean>;\n} {\n const store = inject(TranslationStore);\n const supportedLocales = injectIntlConfig()?.supportedLocales;\n\n const source = computed(() => store.locale()) as WritableSignal<string> & {\n isLoading: Signal<boolean>;\n };\n\n const inSupportedLocales =\n supportedLocales === undefined\n ? () => true\n : (locale: string) => supportedLocales.includes(locale);\n\n const set = (value: string) => {\n if (\n value === untracked(source) ||\n untracked(store.loadQueue).includes(value)\n )\n return;\n\n if (!inSupportedLocales(value)) {\n if (isDevMode())\n console.warn(\n `[Translate] Locale \"${value}\" is not in supportedLocales, switch prevented. Available options are:`,\n supportedLocales,\n );\n\n return;\n }\n\n if (isDevMode() && !store.hasLocaleLoaders(value))\n console.warn(\n `[Translate] No loaders registered for locale \"${value}\". Switching to this locale will have no effect.`,\n );\n\n store.loadQueue.update((q) => [...q, value]);\n };\n\n source.set = set;\n source.update = (updater: (value: string) => string) => {\n const next = updater(untracked(source));\n source.set(next);\n };\n source.asReadonly = () => source;\n\n source.isLoading = store.dynamicLocaleLoader.isLoading;\n\n return source;\n}\n","import {\n computed,\n inject,\n isDevMode,\n isSignal,\n type Signal,\n untracked,\n} from '@angular/core';\nimport {\n type ActivatedRouteSnapshot,\n ResolveFn,\n Router,\n} from '@angular/router';\nimport {\n type CompiledTranslation,\n type inferCompiledTranslationMap,\n type inferCompiledTranslationNamespace,\n} from './compile';\nimport { replaceWithDelim } from './delim';\nimport { type UnknownStringKeyObject } from './string-key-object.type';\nimport {\n injectDefaultLocale,\n injectIntlConfig,\n TranslationStore,\n} from './translation-store';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyStringRecord = Record<string, any>;\n\nfunction createEqualsRecord<T extends AnyStringRecord>(keys: (keyof T)[] = []) {\n let keyMatcher: (a: T, b: T) => boolean;\n\n if (keys.length === 0) {\n keyMatcher = () => true;\n } else if (keys.length === 1) {\n const key = keys[0];\n keyMatcher = (a, b) => a[key] === b[key];\n } else {\n keyMatcher = (a, b) => {\n return keys.every((k) => a[k] === b[k]);\n };\n }\n\n return (a?: T, b?: T): boolean => {\n if (!a && !b) return true;\n if (!a || !b) return false;\n return keyMatcher(a, b);\n };\n}\n\ntype TFunction<TMap extends AnyStringRecord> = <\n TKey extends keyof TMap & string,\n>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [TMap[TKey]]\n) => string;\n\ntype SignalTFunction<TMap extends AnyStringRecord> = <\n TKey extends keyof TMap & string,\n>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [() => TMap[TKey]]\n) => Signal<string>;\n\ntype TFunctionWithSignalConstructor<\n TMap extends AnyStringRecord,\n TFN extends TFunction<TMap>,\n> = TFN & {\n asSignal: SignalTFunction<TMap>;\n};\n\nfunction addSignalFn<TMap extends AnyStringRecord, TFn extends TFunction<TMap>>(\n fn: TFn,\n store: TranslationStore,\n): TFunctionWithSignalConstructor<TMap, TFn> {\n const withSig = fn as TFunctionWithSignalConstructor<TMap, TFn>;\n\n const asSignal = <TKey extends keyof TMap & string>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [() => TMap[TKey]]\n ): Signal<string> => {\n const variables = args[0] as () => AnyStringRecord | undefined;\n const stringKey = key as string;\n\n const flatPath = replaceWithDelim(stringKey);\n\n const varsFn = variables ?? (() => undefined);\n const varsSignal = isSignal(varsFn)\n ? varsFn\n : computed(varsFn, {\n equal: createEqualsRecord(Object.keys(varsFn() ?? {})),\n });\n\n return computed(() => store.formatMessage(flatPath, varsSignal()));\n };\n\n withSig.asSignal = asSignal;\n\n return withSig;\n}\n\nexport function createT<TMap extends AnyStringRecord>(\n store: TranslationStore,\n): TFunction<TMap> {\n return <TKey extends keyof TMap & string>(\n key: TKey,\n ...args: TMap[TKey] extends void ? [] : [TMap[TKey]]\n ): string => {\n const variables = args[0] as AnyStringRecord | undefined;\n const stringKey = key as string;\n\n return store.formatMessage(replaceWithDelim(stringKey), variables);\n };\n}\n\nexport function registerNamespace<\n TDefault extends CompiledTranslation<UnknownStringKeyObject, string>,\n>(\n defaultTranslation: () => Promise<TDefault>,\n other: Record<\n string,\n () => Promise<\n CompiledTranslation<\n UnknownStringKeyObject,\n inferCompiledTranslationNamespace<TDefault>,\n string\n >\n >\n >,\n) {\n type $Map = inferCompiledTranslationMap<TDefault>;\n type $BaseTFN = TFunction<$Map>;\n type $TFN = TFunctionWithSignalConstructor<$Map, $BaseTFN>;\n\n const injectT = (): $TFN => {\n const store = inject(TranslationStore);\n\n return addSignalFn(createT(store), store);\n };\n\n let defaultTranslationLoaded = false;\n const resolver: ResolveFn<void> = async (snapshot) => {\n const store = inject(TranslationStore);\n\n let locale: string | null = null;\n\n const paramName = injectIntlConfig()?.localeParamName;\n\n const routerConfig = inject(Router)['options'];\n const alwaysInheritParams =\n typeof routerConfig === 'object' &&\n !!routerConfig &&\n routerConfig.paramsInheritanceStrategy === 'always';\n\n if (paramName) {\n locale = snapshot.paramMap.get(paramName);\n\n if (!locale && !alwaysInheritParams) {\n let currentRoute: ActivatedRouteSnapshot | null = snapshot;\n while (currentRoute && !locale) {\n locale = currentRoute.paramMap.get('locale');\n currentRoute = currentRoute.parent;\n }\n }\n }\n\n if (!locale) {\n locale = untracked(store.locale);\n }\n\n const defaultLocale = injectDefaultLocale();\n const shouldPreloadDefault =\n injectIntlConfig()?.preloadDefaultLocale ?? false;\n\n const tPromise = other[locale] as (typeof other)[string] | undefined;\n\n const promise = tPromise ?? defaultTranslation;\n if (!promise && isDevMode()) {\n return console.warn(`No translation found for locale: ${locale}`);\n }\n\n if (promise === defaultTranslation && defaultTranslationLoaded) return;\n\n try {\n const promises = [promise()];\n\n if (\n shouldPreloadDefault &&\n !defaultTranslationLoaded &&\n promise !== defaultTranslation\n )\n promises.push(defaultTranslation());\n\n const translations = await Promise.allSettled(promises);\n\n const fullfilled = translations.map((t) =>\n t.status === 'fulfilled' ? t.value : null,\n );\n\n if (fullfilled.at(0) === null && fullfilled.at(1) === null)\n throw new Error('Failed to load translations');\n\n const [t, defaultT] = fullfilled;\n\n const ns = t?.namespace ?? defaultT?.namespace;\n if (!ns) throw new Error('No namespace found in translation');\n\n if (isDevMode() && t && t.locale !== locale && t.locale !== defaultLocale)\n console.warn(`Expected locale to be ${locale} but got ${t.locale}`);\n\n store.registerOnDemandLoaders(ns, {\n ...other,\n [defaultLocale]: defaultTranslation,\n });\n\n const toRegister: Record<string, Record<string, string>> = {};\n if (t) toRegister[locale] = t.flat;\n if (defaultT) toRegister[defaultLocale] = defaultT.flat;\n\n store.register(ns, toRegister);\n\n if (promise === defaultTranslation || defaultT)\n defaultTranslationLoaded = true;\n } catch {\n if (isDevMode()) {\n console.warn(`Failed to load translation for locale: ${locale}`);\n }\n } finally {\n if (locale !== untracked(store.locale)) store.locale.set(locale);\n }\n };\n\n return {\n injectNamespaceT: injectT,\n resolveNamespaceTranslation: resolver,\n };\n}\n","import { inject } from '@angular/core';\nimport {\n Router,\n type CanMatchFn,\n type Route,\n type UrlSegment,\n} from '@angular/router';\nimport {\n injectDefaultLocale,\n injectSupportedLocales,\n} from './translation-store';\n\n/**\n * Guard that validates the locale parameter against supported locales.\n * Redirects to default locale if the locale is invalid.\n *\n * @param prefixSegments Optional array of path segments preceding the locale segment.\n * if (you wanted to match /app/:locale/... you would pass ['app'] here) & the function would match the second parameter + redirect accordingly\n *\n * @example\n * ```typescript\n * {\n * path: ':locale',\n * canMatch: [canMatchLocale()],\n * children: [...]\n * }\n * ```\n */\nexport function canMatchLocale(prefixSegments: string[] = []): CanMatchFn {\n return (_route: Route, segments: UrlSegment[]) => {\n const supportedLocales = injectSupportedLocales();\n\n const locale = segments.at(prefixSegments.length)?.path;\n\n if (!locale || !supportedLocales.includes(locale))\n return inject(Router).createUrlTree([\n ...prefixSegments,\n injectDefaultLocale(),\n ]);\n\n return true;\n };\n}\n","import {\n afterRenderEffect,\n computed,\n Directive,\n ElementRef,\n inject,\n input,\n Renderer2,\n} from '@angular/core';\nimport {\n type CompiledTranslation,\n type inferCompiledTranslationMap,\n} from './compile';\nimport { createT } from './register-namespace';\nimport { type UnknownStringKeyObject } from './string-key-object.type';\nimport { TranslationStore } from './translation-store';\n\n@Directive()\nexport abstract class Translate<\n TInput extends string,\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>,\n TKey extends TInput & keyof TMap & string = TInput & keyof TMap & string,\n> {\n private readonly t = createT(inject(TranslationStore));\n\n readonly translate =\n input.required<\n TMap[TKey] extends void\n ? TKey | [key: TKey]\n : [key: TKey, vars: TMap[TKey]]\n >();\n\n constructor() {\n const key = computed(() => {\n const vars = this.translate();\n return (Array.isArray(vars) ? vars[0] : vars) as TKey;\n });\n\n const args = computed(\n () => {\n const vars = this.translate();\n return (Array.isArray(vars) ? vars[1] : undefined) as TMap[TKey];\n },\n {\n equal: (a, b) => {\n if (a === undefined && b === undefined) return true;\n if (a === undefined || b === undefined) return false;\n\n const aObj = a as Record<string, string>;\n const keys = Object.keys(aObj);\n const bObj = b as Record<string, string>;\n\n if (!keys.length) return !Object.keys(bObj).length;\n\n return keys.every((key) => aObj[key] === bObj[key]);\n },\n },\n );\n\n const translation = computed(() => this.t(key(), args()));\n\n const renderer = inject(Renderer2);\n const el = inject<ElementRef<HTMLElement>>(ElementRef);\n\n afterRenderEffect({\n write: () => {\n renderer.setProperty(el.nativeElement, 'textContent', translation());\n },\n });\n }\n}\n","import { ChangeDetectorRef, effect, inject } from '@angular/core';\nimport { CompiledTranslation, inferCompiledTranslationMap } from './compile';\nimport { createT } from './register-namespace';\nimport { UnknownStringKeyObject } from './string-key-object.type';\nimport { TranslationStore } from './translation-store';\n\nexport abstract class Translator<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>,\n> {\n private readonly store = inject(TranslationStore);\n private readonly t = createT<TMap>(this.store);\n\n constructor() {\n const cdr = inject(ChangeDetectorRef);\n\n effect(() => {\n this.store.locale();\n cdr.markForCheck();\n });\n }\n\n transform<K extends keyof TMap & string>(\n key: K,\n ...args: TMap[K] extends void\n ? [locale?: string]\n : [TMap[K], locale?: string]\n ): string {\n const actualArgs = args.filter(\n (a) => typeof a === 'object',\n ) as TMap[K] extends void ? [] : [TMap[K]];\n\n return this.t(key, ...actualArgs);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/translate",
3
- "version": "21.0.2",
3
+ "version": "21.1.0",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "localize",
@@ -1,5 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { WritableSignal, Signal, Provider } from '@angular/core';
3
+ import { ResolveFn, CanMatchFn } from '@angular/router';
3
4
  import * as _formatjs_intl from '@formatjs/intl';
4
5
  import { IntlConfig } from '@formatjs/intl';
5
6
 
@@ -64,7 +65,15 @@ type TranslationNamespace<TNS extends string, T extends CompiledTranslation<Unkn
64
65
  };
65
66
  declare function createNamespace<const T extends UnknownStringKeyObject, TNS extends string>(ns: TNS, translation: T): TranslationNamespace<TNS, CompiledTranslation<T, TNS, string>, inferTranslationShape<T>>;
66
67
 
67
- declare function provideIntlConfig(config: Omit<IntlConfig, 'locale' | 'messages'>): Provider;
68
+ declare function provideIntlConfig(config: Omit<IntlConfig, 'locale' | 'messages'> & {
69
+ /** Checks next locale is in provided array before switching locales */
70
+ supportedLocales?: string[];
71
+ /** Preloads the default locale ensuring sync fallback, not necessary for most cases as it will lazily load automatically when needed */
72
+ preloadDefaultLocale?: boolean;
73
+ /** Auto-resolution when using a locale parameter via angular router */
74
+ localeParamName?: string;
75
+ }): Provider[];
76
+ declare function injectSupportedLocales(): string[];
68
77
  declare function injectIntl(): Signal<_formatjs_intl.IntlShape<string>>;
69
78
  /**
70
79
  * Inject a dynamic locale signal that supports runtime language switching.
@@ -97,9 +106,27 @@ type TFunctionWithSignalConstructor<TMap extends AnyStringRecord, TFN extends TF
97
106
  };
98
107
  declare function registerNamespace<TDefault extends CompiledTranslation<UnknownStringKeyObject, string>>(defaultTranslation: () => Promise<TDefault>, other: Record<string, () => Promise<CompiledTranslation<UnknownStringKeyObject, inferCompiledTranslationNamespace<TDefault>, string>>>): {
99
108
  injectNamespaceT: () => TFunctionWithSignalConstructor<inferCompiledTranslationMap<TDefault>, TFunction<inferCompiledTranslationMap<TDefault>>>;
100
- resolveNamespaceTranslation: () => Promise<void>;
109
+ resolveNamespaceTranslation: ResolveFn<void>;
101
110
  };
102
111
 
112
+ /**
113
+ * Guard that validates the locale parameter against supported locales.
114
+ * Redirects to default locale if the locale is invalid.
115
+ *
116
+ * @param prefixSegments Optional array of path segments preceding the locale segment.
117
+ * if (you wanted to match /app/:locale/... you would pass ['app'] here) & the function would match the second parameter + redirect accordingly
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * {
122
+ * path: ':locale',
123
+ * canMatch: [canMatchLocale()],
124
+ * children: [...]
125
+ * }
126
+ * ```
127
+ */
128
+ declare function canMatchLocale(prefixSegments?: string[]): CanMatchFn;
129
+
103
130
  declare abstract class Translate<TInput extends string, T extends CompiledTranslation<UnknownStringKeyObject, string>, TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>, TKey extends TInput & keyof TMap & string = TInput & keyof TMap & string> {
104
131
  private readonly t;
105
132
  readonly translate: i0.InputSignal<TMap[TKey] extends void ? TKey | [key: TKey] : [key: TKey, vars: TMap[TKey]]>;
@@ -115,5 +142,5 @@ declare abstract class Translator<T extends CompiledTranslation<UnknownStringKey
115
142
  transform<K extends keyof TMap & string>(key: K, ...args: TMap[K] extends void ? [locale?: string] : [TMap[K], locale?: string]): string;
116
143
  }
117
144
 
118
- export { Translate, Translator, compileTranslation, createNamespace, injectDynamicLocale, injectIntl, provideIntlConfig, registerNamespace };
145
+ export { Translate, Translator, canMatchLocale, compileTranslation, createNamespace, injectDynamicLocale, injectIntl, injectSupportedLocales, provideIntlConfig, registerNamespace };
119
146
  export type { CompiledTranslation, inferCompiledTranslationMap, inferCompiledTranslationNamespace, inferCompiledTranslationShape, mergeTranslationMaps };