@mmstack/translate 0.0.1 → 19.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
@@ -1,3 +1,284 @@
1
1
  # @mmstack/translate
2
2
 
3
- Still in progress, published for testing purposes
3
+ **Type-Safe & modular localization for Modern Angular.**
4
+
5
+ [![npm version](https://badge.fury.io/js/%40mmstack%2Ftranslate.svg)](https://badge.fury.io/js/%40mmstack%2Ftranslate)
6
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/mihajm/mmstack/blob/master/LICENSE)
7
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](CONTRIBUTING.md)
8
+
9
+ `@mmstack/translate` is an opinionated internationalization (i18n) library for Angular applications built with three core priorities:
10
+
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.
13
+ 3. **Scalable Modularity:** Organize translations into **namespaces** (typically aligned with feature libraries) and load them on demand.
14
+
15
+ It uses the robust **FormatJS** Intl runtime (`@formatjs/intl`) for ICU message formatting, and integrates with Angular's dependency injection and routing.
16
+
17
+ ## Features
18
+
19
+ - ✅ **End-to-End Type Safety:** Compile-time checks for:
20
+ - Translation key existence (within a namespace).
21
+ - Correct parameter names and types.
22
+ - Required vs. optional parameters based on ICU message.
23
+ - Structural consistency check when defining non-default locales.
24
+ - 🚀 **Single Build Artifact:** Runtime translation loading.
25
+ - 📦 **Namespacing:** Organize translations by feature/library (e.g., 'quotes', 'userProfile', 'common').
26
+ - ⏳ **Lazy Loading:** Load namespaced translations on demand using Route Resolvers.
27
+ - ✨ **Reactive API:** Includes `t.asSignal()` for creating computed translation signals based on signal parameters.
28
+ - 🌍 **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.)
29
+ - 🔗 **Shared Namespace Support:** Define common translations (e.g., 'Save', 'Cancel') in one namespace and make them type-safely accessible from others.
30
+ - 🛠️ **Template Helpers:** Includes abstract `BaseTranslatePipe` and `BaseTranslateDirective` for easy, type-safe templating.
31
+
32
+ ### Comparison
33
+
34
+ While Angular offers excellent i18n solutions like `@angular/localize` and `transloco`, `@mmstack/translate` aims to fill a specific niche.
35
+
36
+ | Feature | `@mmstack/translate` | `@angular/localize` | `transloco` | `ngx-translate` |
37
+ | :----------------------- | :---------------------------------: | :----------------------: | :---------------------------: | :------------------------: |
38
+ | **Build Process** | ✅ Single Build | ❌ Multi-Build (Typical) | ✅ Single Build | ✅ Single Build |
39
+ | **Translation Timing** | Runtime | Compile Time | Runtime | Runtime |
40
+ | **Type Safety (Keys)** | ✅ Strong (Inferred from structure) | 🟡 via extraction | 🟡 Tooling/TS Files | 🟡 OK Manual/Tooling |
41
+ | **Type Safety (Params)** | ✅ Strong (Inferred from ICU) | ❌ None | 🟡 Manual | 🟡 Manual |
42
+ | **Locale Switching** | 🔄 Page Refresh Required | 🔄 Page Refresh Required | ✅ Dynamic (Runtime) | ✅ Dynamic (Runtime) |
43
+ | **Lazy Loading** | ✅ Built-in (Namespaces/Resolvers) | N/A (Compile Time) | ✅ Built-in (Scopes) | ✅ Yes (Custom Loaders) |
44
+ | **Namespacing/Scopes** | ✅ Built-in | ❌ None | ✅ Built-in (Scopes) | 🟡 Manual (File Structure) |
45
+ | **ICU Support** | ✅ Subset (via FormatJS Runtime) | ✅ Yes (Compile Time) | ✅ Yes (Runtime Intl/Plugins) | 🟡 Via Extensions |
46
+ | **Signal Integration** | ✅ Good (`t.asSignal()`) | N/A | ✅ Good (`translateSignal()`) | ❌ Minimal/None |
47
+ | **Maturity / Community** | ✨ New | Core Angular | ✅ Mature / Active | ✅ Mature |
48
+
49
+ ## Installation & Configuration
50
+
51
+ Install the library & its peer dependency, `@formatjs/intl`.
52
+
53
+ ```bash
54
+ npm install @mmstack/translate @formatjs/intl
55
+ ```
56
+
57
+ 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.
58
+
59
+ ```typescript
60
+ import { provideIntlConfig } from '@mmstack/translate';
61
+
62
+ const appConfig: Providers = [
63
+ provideIntlConfig({
64
+ defaultLocale: 'fr-FR', // defaults to 'en-US' if nothing is provided
65
+ }),
66
+ ];
67
+ ```
68
+
69
+ ```typescript
70
+ import { Component, LOCALE_ID } from '@angular/core';
71
+ import { ActivatedRoute } from '@angular/router';
72
+
73
+ const KNOWN_LOCALES = ['en-US', 'sl-SI'];
74
+
75
+ @Component({
76
+ selector: 'app-locale-shell',
77
+ providers: [
78
+ {
79
+ provide: LOCALE_ID,
80
+ useFactory: (route: ActivatedRoute) => {
81
+ const locale = route.snapshot.paramMap.get('locale') || 'en-US';
82
+ if (KNOWN_LOCALES.includes(locale)) return locale;
83
+ return 'en-US';
84
+ },
85
+ deps: [ActivatedRoute],
86
+ },
87
+ ],
88
+ template: `<ng-content />`,
89
+ })
90
+ export class LocaleShellComponent {}
91
+ ```
92
+
93
+ _Note:_ `@mmstack/translate` relies on Angular's `LOCALE_ID` provider. You must 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. The library assumes this `LOCALE_ID` is static for the duration of the application session and requires a page refresh to change.
94
+
95
+ ## Usage
96
+
97
+ The core workflow involves defining namespaces, registering them (often via lazy loading), and then using the injected translation function (t), pipe, or directive.
98
+
99
+ ### 1. Define Namespace & Translations
100
+
101
+ Define your default locale translations (e.g., 'en-US') as a `const` TypeScript object. Use createNamespace to process it and generate helpers.
102
+
103
+ ```typescript
104
+ // Example: packages/quote/src/lib/quote.namespace.ts
105
+ import { createNamespace } from '@mmstack/translate';
106
+
107
+ // Create the namespace definition object
108
+ const ns = createNamespace('quote', {
109
+ pageTitle: 'Famous Quotes',
110
+ greeting: 'Hello {name}!',
111
+ detail: {
112
+ authorLabel: 'Author',
113
+ },
114
+ errors: {
115
+ minLength: 'Quote must be at least {min} characters long.',
116
+ },
117
+ stats: '{count, plural, one {# quote} other {# quotes}} available',
118
+ } as const); // Note the as const, without it variable inference won't work correctly
119
+
120
+ export default ns.translation;
121
+
122
+ export type QuoteLocale = (typeof ns)['translation'];
123
+
124
+ export const createQuoteTranslation = ns.createTranslation;
125
+
126
+ // Note the translations should be in separate files, if you are using import() to lazy load them.
127
+ // packages/quote/src/lib/quote-sl.translation.ts
128
+
129
+ import { createQuoteTranslation } from './quote.namespace';
130
+
131
+ // shape is typesafe (errors if you have missing or additional keys)
132
+ export default createQuoteTranslation({
133
+ pageTitle: 'Znani Citati',
134
+ greeting: 'Zdravo {name}!',
135
+ detail: {
136
+ authorLabel: 'Avtor',
137
+ },
138
+ errors: {
139
+ minLength: 'Citat mora imeti vsaj {min} znakov.', // If original has variables, the translation must contain a subset of used variables (min 1)
140
+ },
141
+ 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
142
+ });
143
+ ```
144
+
145
+ ### 2. Register the namespace & load
146
+
147
+ Use registerNamespace to prepare your namespace definition and obtain the injectNamespaceT function and the resolveNamespaceTranslation resolver function. Use the resolver in your Angular routes.
148
+
149
+ ```typescript
150
+ import q from './quote.namespace';
151
+
152
+ // Register the namespace
153
+ // Example: packages/quote/src/lib/quote.t.ts
154
+ const r = registerNamespace(
155
+ q, // Default locale's compiled translation (functions as fallback if no locale of type provided)
156
+ {
157
+ // Map other locales to promise factories (dynamic imports)
158
+ 'sl-SI': () => import('./quote-sl.translation.ts').then((m) => m.default),
159
+ },
160
+ );
161
+
162
+ export const injectQuoteT = r.injectNamespaceT;
163
+ export const resolveQuoteTranslations = r.resolveNamespaceTranslation;
164
+
165
+ // in the main quote route add the provided resolver
166
+
167
+ import { type Routes } from '@angular/router';
168
+ import { resolveQuoteTranslations } from './quote.t';
169
+
170
+ export const MODULE_ROUTES: Routes = [
171
+ // ...
172
+ {
173
+ // ...
174
+ resolve: {
175
+ resolveQuoteTranslations,
176
+ },
177
+ },
178
+ ];
179
+ ```
180
+
181
+ #### 2b. [OPTIONAL] Configure the translation pipe and/or directive
182
+
183
+ ```typescript
184
+ import { Pipe, Directive } from '@angular/core';
185
+ import { BaseTranslatePipe, BaseTranslateDirective } from '@mmstack/translate';
186
+ import { type QuoteLocale } from './quote.namespace';
187
+
188
+ @Pipe({
189
+ name: 'translate',
190
+ })
191
+ export class QuoteTranslatePipe extends BaseTranslatePipe<QuoteLocale> {}
192
+
193
+ @Directive({
194
+ selector: '[translate]', // input in BaseTranslateDirective is named 'translate'
195
+ })
196
+ // TInput is necessary to correctly infer the variables to the key
197
+ export class QuoteTranslateDirective<TInput extends string> extends BaseTranslateDirective<TInput, QuoteLocale> {}
198
+ ```
199
+
200
+ ### 3. Have fun :)
201
+
202
+ ```typescript
203
+ @Component({
204
+ selector: 'app-quote',
205
+ imports: [QuoteTranslatePipe, QuoteTranslateDirective],
206
+ template: `
207
+ <!-- Pipe validates key & variables match -->
208
+ <h1>{{ 'quote.pageTitle' | translate }}</h1>
209
+ <!-- Non pluralized params must be string -->
210
+ <span>{{ 'quote.errors.minLength' | translate: { min: '5' } }}</span>
211
+
212
+ <!-- Directive replaces innerHTML of el -->
213
+ <h1 translate="quote.pageTitle"></h1>
214
+ <span [translate]="['quote.errors.minLength', { min: '5' }]"></span>
215
+ `,
216
+ })
217
+ export class QuoteComponent {
218
+ protected readonly count = signal(0);
219
+ private readonly t = injectQuoteT();
220
+
221
+ private readonly author = this.t('quote.detail.authorLabel'); // static translation
222
+
223
+ private readonly stats = this.t.asSignal('quote.stats', () => ({
224
+ count: this.count(), // must be object with count parameter & type number
225
+ }));
226
+ }
227
+ ```
228
+
229
+ ### 4. [OPTIONAL] - Creating a shared/common namespace
230
+
231
+ _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.
232
+
233
+ #### 4a. Define a shared namespace
234
+
235
+ Same as quote example, but this time also export the `createMergedNamespace` function. This will be your new factory for other namespaces.
236
+
237
+ ```typescript
238
+ // Example: packages/common-locales/src/lib/common.namespace.ts
239
+ import { createNamespace } from '@mmstack/translate';
240
+
241
+ const ns = createNamespace('common', {
242
+ yes: 'Yes',
243
+ no: 'No',
244
+ } as const);
245
+
246
+ // ... rest
247
+
248
+ export const createAppNamespace = ns.createMergedNamespace;
249
+ ```
250
+
251
+ ### 4b. Use the common namespace factory instead of the library one
252
+
253
+ ```typescript
254
+ // Example: packages/quote/src/lib/quote.namespace.ts
255
+ import { createAppNamespace } from '@org/common/locale'; // replace with your library import path
256
+
257
+ // Create the namespace definition object
258
+ const ns = createAppNamespace('quote', {
259
+ pageTitle: 'Famous Quotes',
260
+ } as const);
261
+
262
+ // registration & other stuff remains the same
263
+ ```
264
+
265
+ ### 4c. Have even more fun!
266
+
267
+ ```typescript
268
+ @Component({
269
+ //...
270
+ })
271
+ export class QuoteComponent {
272
+ private readonly t = injectQuoteT();
273
+
274
+ // t function is now 'aware' of the common namespace & its translations
275
+ private readonly yes = this.t('common.yes');
276
+
277
+ // quote translations also work the same
278
+ private readonly author = this.t('quote.detail.authorLabel');
279
+ }
280
+ ```
281
+
282
+ ## Contributing
283
+
284
+ Contributions, issues, and feature requests are welcome!
@@ -1,10 +1,257 @@
1
- function test(locale) {
2
- return locale;
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, LOCALE_ID, signal, computed, Injectable, isSignal, isDevMode, input, Renderer2, ElementRef, effect, Directive } from '@angular/core';
3
+ import { createIntlCache, createIntl } from '@formatjs/intl';
4
+
5
+ const KEY_DELIM = '::MMT_DELIM::';
6
+ function prependDelim(prefix, key) {
7
+ return `${prefix}${KEY_DELIM}${key}`;
8
+ }
9
+ function replaceWithDelim(str, repl = '.') {
10
+ return str.replaceAll(KEY_DELIM, repl);
11
+ }
12
+
13
+ const INTERNAL_SYMBOL = Symbol.for('mmstack-translate-internal');
14
+ function isTranslationObject(t) {
15
+ return typeof t === 'object' && t !== null;
16
+ }
17
+ function flattenTranslation(obj) {
18
+ return Object.entries(obj).reduce((acc, [key, value]) => {
19
+ if (typeof value === 'string') {
20
+ acc[key] = value;
21
+ }
22
+ else if (isTranslationObject(value)) {
23
+ Object.entries(flattenTranslation(value)).forEach(([nestedKey, nestedValue]) => {
24
+ acc[prependDelim(key, nestedKey)] = nestedValue;
25
+ });
26
+ }
27
+ return acc;
28
+ }, {});
29
+ }
30
+ function compileTranslation(translation, ns, locale) {
31
+ return {
32
+ locale,
33
+ flat: flattenTranslation(translation),
34
+ namespace: ns,
35
+ [INTERNAL_SYMBOL]: {
36
+ shape: {},
37
+ map: {},
38
+ },
39
+ };
40
+ }
41
+
42
+ function createNamespace(ns, translation) {
43
+ const compiled = compileTranslation(translation, ns);
44
+ const namespace = {
45
+ translation: compileTranslation(translation, ns),
46
+ createTranslation: (locale, translation) => {
47
+ return compileTranslation(translation, ns, locale);
48
+ },
49
+ createMergedNamespace: (otherNs, otherTranslation) => {
50
+ return createNamespace(otherNs, otherTranslation);
51
+ },
52
+ };
53
+ return namespace;
54
+ }
55
+
56
+ const CONFIG_TOKEN = new InjectionToken('mmstack-intl-config');
57
+ function provideIntlConfig(config) {
58
+ return {
59
+ useValue: config,
60
+ provide: CONFIG_TOKEN,
61
+ };
62
+ }
63
+ function injectIntlConfig() {
64
+ return inject(CONFIG_TOKEN, { optional: true }) ?? undefined;
65
+ }
66
+ function injectDefaultLocale() {
67
+ return injectIntlConfig()?.defaultLocale ?? 'en-US';
68
+ }
69
+ class TranslationStore {
70
+ cache = createIntlCache();
71
+ config = injectIntlConfig();
72
+ locale = inject(LOCALE_ID);
73
+ defaultLocale = injectDefaultLocale();
74
+ translations = signal({
75
+ [this.defaultLocale]: {},
76
+ });
77
+ nonMessageConfig = computed(() => ({
78
+ ...this.config,
79
+ locale: this.locale,
80
+ }));
81
+ messages = computed(() => this.translations()[this.locale] ??
82
+ this.translations()[this.defaultLocale] ??
83
+ {});
84
+ intl = computed(() => createIntl({
85
+ ...this.nonMessageConfig(),
86
+ messages: this.messages(),
87
+ }, this.cache));
88
+ formatMessage(key, values) {
89
+ const message = this.translations()[this.locale]?.[key] ?? '';
90
+ if (!message)
91
+ return '';
92
+ return this.intl().formatMessage({ id: key, defaultMessage: message }, values);
93
+ }
94
+ register(namespace, flat) {
95
+ this.translations.update((cur) => {
96
+ return Object.entries(flat).reduce((acc, [locale, translation]) => {
97
+ const localeTranslation = acc[locale] ?? {};
98
+ const withNS = Object.entries(translation ?? {}).reduce((acc, [key, value]) => {
99
+ acc[prependDelim(namespace, key)] = value;
100
+ return acc;
101
+ }, {});
102
+ acc[locale] = {
103
+ ...localeTranslation,
104
+ ...withNS,
105
+ };
106
+ return acc;
107
+ }, { ...cur });
108
+ });
109
+ }
110
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: TranslationStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
111
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: TranslationStore, providedIn: 'root' });
112
+ }
113
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: TranslationStore, decorators: [{
114
+ type: Injectable,
115
+ args: [{
116
+ providedIn: 'root',
117
+ }]
118
+ }] });
119
+ function injectIntl() {
120
+ return inject(TranslationStore).intl;
121
+ }
122
+
123
+ function createEqualsRecord(keys = []) {
124
+ let keyMatcher;
125
+ if (keys.length === 0) {
126
+ keyMatcher = () => true;
127
+ }
128
+ else if (keys.length === 1) {
129
+ const key = keys[0];
130
+ keyMatcher = (a, b) => a[key] === b[key];
131
+ }
132
+ else {
133
+ keyMatcher = (a, b) => {
134
+ return keys.every((k) => a[k] === b[k]);
135
+ };
136
+ }
137
+ return (a, b) => {
138
+ if (!a && !b)
139
+ return true;
140
+ if (!a || !b)
141
+ return false;
142
+ return keyMatcher(a, b);
143
+ };
144
+ }
145
+ function addSignalFn(fn, store) {
146
+ const withSig = fn;
147
+ const asSignal = (key, ...args) => {
148
+ const variables = args[0];
149
+ const stringKey = key;
150
+ const flatPath = replaceWithDelim(stringKey);
151
+ const varsFn = variables === undefined ? () => undefined : variables;
152
+ const varsSignal = isSignal(varsFn)
153
+ ? varsFn
154
+ : computed(varsFn, {
155
+ equal: createEqualsRecord(Object.keys(varsFn())),
156
+ });
157
+ return computed(() => store.formatMessage(flatPath, varsSignal()));
158
+ };
159
+ withSig.asSignal = asSignal;
160
+ return withSig;
161
+ }
162
+ function createT(store) {
163
+ return (key, ...args) => {
164
+ const variables = args[0];
165
+ const stringKey = key;
166
+ return store.formatMessage(replaceWithDelim(stringKey), variables);
167
+ };
168
+ }
169
+ function registerNamespace({ namespace: ns, flat: defaultTranslation }, other) {
170
+ const injectT = () => {
171
+ const store = inject(TranslationStore);
172
+ return addSignalFn(createT(store), store);
173
+ };
174
+ const resolver = async () => {
175
+ const locale = inject(LOCALE_ID);
176
+ const store = inject(TranslationStore);
177
+ const defaultLocale = injectDefaultLocale();
178
+ store.register(ns, {
179
+ [defaultLocale]: defaultTranslation,
180
+ });
181
+ if (locale === defaultLocale)
182
+ return;
183
+ const promise = other[locale];
184
+ if (!promise && isDevMode()) {
185
+ return console.warn(`No translation found for locale: ${locale}`);
186
+ }
187
+ try {
188
+ const translation = await promise();
189
+ if (translation.locale !== locale && isDevMode()) {
190
+ return console.warn(`Expected locale to be ${locale} but got ${translation.locale}`);
191
+ }
192
+ store.register(ns, {
193
+ [locale]: translation.flat,
194
+ });
195
+ }
196
+ catch {
197
+ if (isDevMode()) {
198
+ console.warn(`Failed to load translation for locale: ${locale}`);
199
+ }
200
+ }
201
+ };
202
+ return {
203
+ injectNamespaceT: injectT,
204
+ resolveNamespaceTranslation: resolver,
205
+ };
206
+ }
207
+
208
+ class BaseTranslateDirective {
209
+ t = createT(inject(TranslationStore));
210
+ translate = input.required();
211
+ constructor() {
212
+ const key = computed(() => {
213
+ const vars = this.translate();
214
+ return (Array.isArray(vars) ? vars[0] : vars);
215
+ });
216
+ const args = computed(() => {
217
+ const vars = this.translate();
218
+ return (Array.isArray(vars) ? vars[1] : undefined);
219
+ }, {
220
+ equal: (a, b) => {
221
+ if (a === undefined && b === undefined)
222
+ return true;
223
+ if (a === undefined || b === undefined)
224
+ return false;
225
+ const aObj = a;
226
+ const keys = Object.keys(aObj);
227
+ const bObj = b;
228
+ if (!keys.length)
229
+ return !Object.keys(bObj).length;
230
+ return keys.every((key) => aObj[key] === bObj[key]);
231
+ },
232
+ });
233
+ const translation = computed(() => this.t(key(), args()));
234
+ const renderer = inject(Renderer2);
235
+ const el = inject(ElementRef);
236
+ effect(() => renderer.setProperty(el.nativeElement, 'textContent', translation()));
237
+ }
238
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: BaseTranslateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
239
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.3", type: BaseTranslateDirective, isStandalone: true, inputs: { translate: { classPropertyName: "translate", publicName: "translate", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
240
+ }
241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: BaseTranslateDirective, decorators: [{
242
+ type: Directive
243
+ }], ctorParameters: () => [] });
244
+
245
+ class BaseTranslatePipe {
246
+ t = createT(inject(TranslationStore));
247
+ transform(key, ...args) {
248
+ return this.t(key, ...args);
249
+ }
3
250
  }
4
251
 
5
252
  /**
6
253
  * Generated bundle index. Do not edit.
7
254
  */
8
255
 
9
- export { test };
256
+ export { BaseTranslateDirective, BaseTranslatePipe, createNamespace, injectIntl, provideIntlConfig, registerNamespace };
10
257
  //# sourceMappingURL=mmstack-translate.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mmstack-translate.mjs","sources":["../../../../packages/translate/src/lib/test.ts","../../../../packages/translate/src/mmstack-translate.ts"],"sourcesContent":["import { Locale } from './supported-locales.type';\n\nexport function test(locale: Locale) {\n return locale;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"AAEM,SAAU,IAAI,CAAC,MAAc,EAAA;AACjC,IAAA,OAAO,MAAM;AACf;;ACJA;;AAEG;;;;"}
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.directive.ts","../../../../packages/translate/src/lib/translate.pipe.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(KEY_DELIM, repl);\n}\n","import { prependDelim } from './delim';\nimport {\n inferTranslationParamMap,\n inferTranslationShape,\n} from './parameterize.type';\nimport { 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 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 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 inject,\n Injectable,\n InjectionToken,\n LOCALE_ID,\n Provider,\n signal,\n} from '@angular/core';\nimport { createIntl, createIntlCache, IntlConfig } from '@formatjs/intl';\nimport { prependDelim } from './delim';\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 private readonly locale = 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 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 intl = computed(() =>\n createIntl(\n {\n ...this.nonMessageConfig(),\n messages: this.messages(),\n },\n this.cache,\n ),\n );\n\n formatMessage(key: string, values?: Record<string, string | number>) {\n const message = this.translations()[this.locale]?.[key] ?? '';\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\nexport function injectIntl() {\n return inject(TranslationStore).intl;\n}\n","import {\n computed,\n inject,\n isDevMode,\n isSignal,\n LOCALE_ID,\n Signal,\n} from '@angular/core';\nimport {\n CompiledTranslation,\n inferCompiledTranslationMap,\n inferCompiledTranslationShape,\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 ? () => undefined : variables;\n const varsSignal = isSignal(varsFn)\n ? varsFn\n : computed(varsFn, {\n equal: createEqualsRecord(Object.keys(varsFn() as object)),\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 TOther extends CompiledTranslation<\n inferCompiledTranslationShape<TDefault>,\n string\n >,\n>(\n { namespace: ns, flat: defaultTranslation }: TDefault,\n other: Record<string, () => Promise<TOther>>,\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 const resolver = async () => {\n const locale = inject(LOCALE_ID);\n const store = inject(TranslationStore);\n const defaultLocale = injectDefaultLocale();\n\n store.register(ns, {\n [defaultLocale]: defaultTranslation,\n });\n\n if (locale === defaultLocale) return;\n\n const promise = other[locale] as () => Promise<TOther>;\n\n if (!promise && isDevMode()) {\n return console.warn(`No translation found for locale: ${locale}`);\n }\n\n try {\n const translation = await promise();\n\n if (translation.locale !== locale && isDevMode()) {\n return console.warn(\n `Expected locale to be ${locale} but got ${translation.locale}`,\n );\n }\n\n store.register(ns, {\n [locale]: translation.flat,\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 computed,\n Directive,\n effect,\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 BaseTranslateDirective<\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 effect(() =>\n renderer.setProperty(el.nativeElement, 'textContent', translation()),\n );\n }\n}\n","import { 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 BaseTranslatePipe<\n T extends CompiledTranslation<UnknownStringKeyObject, string>,\n TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>,\n> {\n private readonly t = createT<TMap>(inject(TranslationStore));\n\n transform<K extends keyof TMap & string>(\n key: K,\n ...args: TMap[K] extends void ? [] : [TMap[K]]\n ): string {\n return this.t(key, ...args);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/index.d.ts CHANGED
@@ -1,2 +1,5 @@
1
- export { SupportedLocales } from './lib/supported-locales.type';
2
- export * from './lib/test';
1
+ export { createNamespace } from './lib/create-namespace';
2
+ export { registerNamespace } from './lib/register-namespace';
3
+ export { BaseTranslateDirective } from './lib/translate.directive';
4
+ export { BaseTranslatePipe } from './lib/translate.pipe';
5
+ export { injectIntl, provideIntlConfig } from './lib/translation.store';
@@ -0,0 +1,24 @@
1
+ import { inferTranslationParamMap, inferTranslationShape } from './parameterize.type';
2
+ import { UnknownStringKeyObject } from './string-key-object.type';
3
+ declare const INTERNAL_SYMBOL: unique symbol;
4
+ type InternalSymbol = typeof INTERNAL_SYMBOL;
5
+ export type CompiledTranslation<T extends UnknownStringKeyObject, TNS extends string, TLocale extends string = string> = {
6
+ flat: Record<string, string>;
7
+ locale?: TLocale;
8
+ namespace: TNS;
9
+ [INTERNAL_SYMBOL]: {
10
+ shape: inferTranslationShape<T>;
11
+ map: inferTranslationParamMap<TNS, T>;
12
+ };
13
+ };
14
+ export type mergeTranslationMaps<TMain extends CompiledTranslation<UnknownStringKeyObject, string>, TOther extends CompiledTranslation<UnknownStringKeyObject, string>> = Omit<TMain, InternalSymbol> & {
15
+ [INTERNAL_SYMBOL]: {
16
+ shape: inferCompiledTranslationShape<TMain>;
17
+ map: inferCompiledTranslationMap<TOther> & inferCompiledTranslationMap<TMain>;
18
+ };
19
+ };
20
+ export type inferCompiledTranslationNamespace<T extends CompiledTranslation<UnknownStringKeyObject, string>> = T['namespace'];
21
+ export type inferCompiledTranslationShape<T extends CompiledTranslation<UnknownStringKeyObject, string>> = T[InternalSymbol]['shape'];
22
+ export type inferCompiledTranslationMap<T extends CompiledTranslation<UnknownStringKeyObject, string>> = T[InternalSymbol]['map'];
23
+ export declare function compileTranslation<T extends UnknownStringKeyObject, TNS extends string, TLocale extends string = string>(translation: T, ns: TNS, locale?: TLocale): CompiledTranslation<T, TNS, TLocale>;
24
+ export {};
@@ -0,0 +1,9 @@
1
+ import { CompiledTranslation, inferCompiledTranslationShape, mergeTranslationMaps } from './compile';
2
+ import { UnknownStringKeyObject } from './string-key-object.type';
3
+ type TranslationNamespace<TNS extends string, T extends CompiledTranslation<UnknownStringKeyObject, TNS>, TShape extends UnknownStringKeyObject> = {
4
+ translation: T;
5
+ createTranslation: <TLocale extends string>(locale: TLocale, translation: TShape) => CompiledTranslation<TShape, TNS, TLocale>;
6
+ createMergedNamespace: <TOtherNS extends string, TOther extends UnknownStringKeyObject, TOtherCompiled extends CompiledTranslation<TOther, TOtherNS>>(ns: TOtherNS, translation: TOther) => TranslationNamespace<TOtherNS, mergeTranslationMaps<TOtherCompiled, T>, inferCompiledTranslationShape<TOtherCompiled>>;
7
+ };
8
+ export declare function createNamespace<T extends UnknownStringKeyObject, TNS extends string>(ns: TNS, translation: T): TranslationNamespace<TNS, CompiledTranslation<T, TNS, string>, import("./parameterize.type").inferTranslationShape<T>>;
9
+ export {};
package/lib/delim.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function prependDelim(prefix: string, key: string): string;
2
+ export declare function replaceWithDelim(str: string, repl?: string): string;
@@ -0,0 +1,30 @@
1
+ import { UnknownStringKeyObject } from './string-key-object.type';
2
+ type Simplify<T> = T extends infer U ? {
3
+ [K in keyof U]: Simplify<U[K]>;
4
+ } : never;
5
+ type Autocomplete<T extends string> = T | Omit<string, T>;
6
+ type extractSelectOptions<TOpt extends string> = TOpt extends `${infer Option}{${infer _}} ${infer Rest}` ? Option | extractSelectOptions<Rest> : TOpt extends `${infer Option}{${infer _}}` ? Option : never;
7
+ type extractSelectParam<TName extends string, TOpt extends string> = [
8
+ TName,
9
+ Autocomplete<Exclude<extractSelectOptions<TOpt>, 'other'>>
10
+ ];
11
+ type extractComplexParam<T extends string> = T extends `{${infer VarName}, plural, ${infer _}}}${infer REST}` | `{${infer VarName}, selectordinal, ${infer _}}}${infer REST}` ? [VarName, number] | extractParams<REST> : T extends `{${infer VarName}, select, ${infer SelectOptions}}}${infer REST}` ? extractSelectParam<VarName, `${SelectOptions}}`> | extractParams<REST> : never;
12
+ export type extractParams<T extends string> = T extends `${infer _Start}{${infer Var}}${infer End}` ? Var extends `${infer _}, ${infer __}` ? extractComplexParam<`{${Var}}${End}`> : [Var, string] | extractParams<End> : never;
13
+ type mergeParams<TExtracted extends [string, any]> = {
14
+ [K in TExtracted[0]]: TExtracted[1];
15
+ };
16
+ type flattenParams<TKey extends string, TVal> = TVal extends UnknownStringKeyObject ? inferParamTupples<TVal, `${TKey}.`> : TVal extends string ? extractParams<TVal> extends never ? [TKey] : [TKey, mergeParams<extractParams<TVal>>] : never;
17
+ type inferParamTupples<T extends UnknownStringKeyObject, TPrefix extends string = ''> = Simplify<{
18
+ [K in keyof T]: K extends string ? flattenParams<`${TPrefix}${K}`, T[K]> : never;
19
+ }[keyof T]>;
20
+ export type inferTranslationParamMap<TNS extends string, T extends UnknownStringKeyObject> = Simplify<{
21
+ [Tuple in inferParamTupples<T> as Tuple[0] extends string ? `${TNS}.${Tuple[0]}` : never]: Tuple extends [string, infer Vars] ? Vars : void;
22
+ }>;
23
+ type StringContaining<Placeholder extends string> = `${string}${Placeholder}${string}`;
24
+ type TypeEnsuringAllPlaceholders<PlaceholdersUnion extends string> = StringContaining<PlaceholdersUnion>;
25
+ export type extractParamString<T extends string> = T extends `${infer _Start}{${infer Var}}${infer End}` ? Var extends `${infer VarName},${string}` ? `{${VarName}}` | extractParamString<End> : `{${Var}}` | extractParamString<End> : never;
26
+ type inferParamsFromValue<V extends string> = extractParamString<V> extends never ? string : TypeEnsuringAllPlaceholders<extractParamString<V>>;
27
+ export type inferTranslationShape<T extends UnknownStringKeyObject> = {
28
+ [K in keyof T]: T[K] extends UnknownStringKeyObject ? inferTranslationShape<T[K]> : T[K] extends string ? inferParamsFromValue<T[K]> : never;
29
+ };
30
+ export {};
@@ -0,0 +1,16 @@
1
+ import { Signal } from '@angular/core';
2
+ import { CompiledTranslation, inferCompiledTranslationMap, inferCompiledTranslationShape } from './compile';
3
+ import { UnknownStringKeyObject } from './string-key-object.type';
4
+ import { TranslationStore } from './translation.store';
5
+ type AnyStringRecord = Record<string, any>;
6
+ type TFunction<TMap extends AnyStringRecord> = <TKey extends keyof TMap & string>(key: TKey, ...args: TMap[TKey] extends void ? [] : [TMap[TKey]]) => string;
7
+ type SignalTFunction<TMap extends AnyStringRecord> = <TKey extends keyof TMap & string>(key: TKey, ...args: TMap[TKey] extends void ? [] : [() => TMap[TKey]]) => Signal<string>;
8
+ type TFunctionWithSignalConstructor<TMap extends AnyStringRecord, TFN extends TFunction<TMap>> = TFN & {
9
+ asSignal: SignalTFunction<TMap>;
10
+ };
11
+ export declare function createT<TMap extends AnyStringRecord>(store: TranslationStore): TFunction<TMap>;
12
+ export declare function registerNamespace<TDefault extends CompiledTranslation<UnknownStringKeyObject, string>, TOther extends CompiledTranslation<inferCompiledTranslationShape<TDefault>, string>>({ namespace: ns, flat: defaultTranslation }: TDefault, other: Record<string, () => Promise<TOther>>): {
13
+ injectNamespaceT: () => TFunctionWithSignalConstructor<inferCompiledTranslationMap<TDefault>, TFunction<inferCompiledTranslationMap<TDefault>>>;
14
+ resolveNamespaceTranslation: () => Promise<void>;
15
+ };
16
+ export {};
@@ -0,0 +1 @@
1
+ export type UnknownStringKeyObject = Record<string, unknown>;
@@ -0,0 +1,10 @@
1
+ import { CompiledTranslation, inferCompiledTranslationMap } from './compile';
2
+ import { UnknownStringKeyObject } from './string-key-object.type';
3
+ import * as i0 from "@angular/core";
4
+ export declare abstract class BaseTranslateDirective<TInput extends string, T extends CompiledTranslation<UnknownStringKeyObject, string>, TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>, TKey extends TInput & keyof TMap & string = TInput & keyof TMap & string> {
5
+ private readonly t;
6
+ readonly translate: import("@angular/core").InputSignal<TMap[TKey] extends void ? TKey | [key: TKey] : [key: TKey, vars: TMap[TKey]]>;
7
+ constructor();
8
+ static ɵfac: i0.ɵɵFactoryDeclaration<BaseTranslateDirective<any, any, any, any>, never>;
9
+ static ɵdir: i0.ɵɵDirectiveDeclaration<BaseTranslateDirective<any, any, any, any>, never, never, { "translate": { "alias": "translate"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
10
+ }
@@ -0,0 +1,6 @@
1
+ import { CompiledTranslation, inferCompiledTranslationMap } from './compile';
2
+ import { UnknownStringKeyObject } from './string-key-object.type';
3
+ export declare abstract class BaseTranslatePipe<T extends CompiledTranslation<UnknownStringKeyObject, string>, TMap extends inferCompiledTranslationMap<T> = inferCompiledTranslationMap<T>> {
4
+ private readonly t;
5
+ transform<K extends keyof TMap & string>(key: K, ...args: TMap[K] extends void ? [] : [TMap[K]]): string;
6
+ }
@@ -0,0 +1,21 @@
1
+ import { Provider } from '@angular/core';
2
+ import { IntlConfig } from '@formatjs/intl';
3
+ import * as i0 from "@angular/core";
4
+ export declare function provideIntlConfig(config: Omit<IntlConfig, 'locale' | 'messages'>): Provider;
5
+ export declare function injectIntlConfig(): Omit<IntlConfig, "locale" | "messages"> | undefined;
6
+ export declare function injectDefaultLocale(): string;
7
+ export declare class TranslationStore {
8
+ private readonly cache;
9
+ private readonly config;
10
+ private readonly locale;
11
+ private readonly defaultLocale;
12
+ private readonly translations;
13
+ private readonly nonMessageConfig;
14
+ private readonly messages;
15
+ readonly intl: import("@angular/core").Signal<import("@formatjs/intl").IntlShape<string>>;
16
+ formatMessage(key: string, values?: Record<string, string | number>): string;
17
+ register(namespace: string, flat: Partial<Record<string, Record<string, string>>>): void;
18
+ static ɵfac: i0.ɵɵFactoryDeclaration<TranslationStore, never>;
19
+ static ɵprov: i0.ɵɵInjectableDeclaration<TranslationStore>;
20
+ }
21
+ export declare function injectIntl(): import("@angular/core").Signal<import("@formatjs/intl").IntlShape<string>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/translate",
3
- "version": "0.0.1",
3
+ "version": "19.1.0",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "localize",
@@ -13,6 +13,10 @@
13
13
  "type": "git",
14
14
  "url": "https://github.com/mihajm/mmstack"
15
15
  },
16
+ "peerDependencies": {
17
+ "@angular/core": "~19.2.3",
18
+ "@formatjs/intl": "~3.1.6"
19
+ },
16
20
  "homepage": "https://github.com/mihajm/mmstack/blob/master/packages/translate",
17
21
  "sideEffects": false,
18
22
  "module": "fesm2022/mmstack-translate.mjs",
@@ -1,22 +0,0 @@
1
- /**
2
- * Represents the known locales for the application.
3
- * Library consumers should augment this interface in their own projects
4
- * to add their specific locales using declaration merging.
5
- * Value types don't really matter, only the keys are used.
6
- *
7
- * @example
8
- * // In your's project (e.g., src/app/types/locale.d.ts)
9
- * declare module '@mmstack/translate' {
10
- * interface SupportedLocales {
11
- * 'en-US': string;
12
- * 'de-DE': string;
13
- * 'fr-CA': string;
14
- * }
15
- * }
16
- */
17
- export interface SupportedLocales {
18
- 'en-US': string;
19
- }
20
- type AssertStringKey<T> = T extends string ? T : never;
21
- export type Locale = AssertStringKey<keyof SupportedLocales>;
22
- export {};
package/lib/test.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { Locale } from './supported-locales.type';
2
- export declare function test(locale: Locale): "en-US";