@fluojs/i18n 2.0.0 → 3.0.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.ko.md +69 -3
- package/README.md +69 -3
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +7 -25
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +19 -11
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +10 -30
- package/dist/icu.d.ts.map +1 -1
- package/dist/icu.js +2 -10
- package/dist/loaders/remote.d.ts.map +1 -1
- package/dist/loaders/remote.js +10 -4
- package/dist/message-provenance.d.ts +7 -0
- package/dist/message-provenance.d.ts.map +1 -0
- package/dist/message-provenance.js +13 -0
- package/dist/resolver-chain.d.ts +12 -0
- package/dist/resolver-chain.d.ts.map +1 -0
- package/dist/resolver-chain.js +22 -0
- package/dist/service.d.ts +1 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +15 -6
- package/dist/typegen.d.ts.map +1 -1
- package/dist/typegen.js +4 -0
- package/package.json +8 -8
package/README.ko.md
CHANGED
|
@@ -9,6 +9,7 @@ fluo 애플리케이션을 위한 프레임워크 비종속 국제화 코어 표
|
|
|
9
9
|
- [설치](#설치)
|
|
10
10
|
- [사용 시점](#사용-시점)
|
|
11
11
|
- [빠른 시작](#빠른-시작)
|
|
12
|
+
- [Catalog Aggregation 및 Fallback Migration](#catalog-aggregation-및-fallback-migration)
|
|
12
13
|
- [코어 번역](#코어-번역)
|
|
13
14
|
- [포맷팅](#포맷팅)
|
|
14
15
|
- [ICU MessageFormat](#icu-messageformat)
|
|
@@ -70,6 +71,71 @@ class AppModule {}
|
|
|
70
71
|
|
|
71
72
|
`I18nModule.forRoot(...)`는 기본적으로 `I18nService`를 global provider로 export하므로 root package를 한 번 import한 뒤 sibling module에서도 shared service를 inject할 수 있습니다. Service를 i18n module을 import한 module 안에만 보이게 하려면 `global: false`를 전달하세요.
|
|
72
73
|
|
|
74
|
+
`I18nModule.forRoot(...)`는 module graph가 정의될 때 최종 option을 capture하는 동기 registration입니다. 비동기 catalog 또는 configuration loading은 `I18nModule.forRoot(...)` 전에 application-owned bootstrap boundary에서 완료하고, resolve된 catalog와 option을 해당 registration call에 전달하세요. 이 방식은 framework-agnostic root contract를 유지하며 NestJS dynamic-module runtime bridge나 `forRootAsync(...)` compatibility surface를 제공하지 않습니다.
|
|
75
|
+
|
|
76
|
+
## Catalog Aggregation 및 Fallback Migration
|
|
77
|
+
|
|
78
|
+
NestJS i18n에서 migration할 때는 동기 `I18nModule.forRoot(...)` 호출 전에 필요한 모든 locale과 namespace를 load하세요. NestJS loader configuration을 module registration으로 가져오지 않습니다.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { Module } from '@fluojs/core';
|
|
82
|
+
import { I18nModule } from '@fluojs/i18n';
|
|
83
|
+
import { createFileSystemI18nLoader } from '@fluojs/i18n/loaders/fs';
|
|
84
|
+
|
|
85
|
+
const locales = ['en', 'ko'] as const;
|
|
86
|
+
const namespaces = ['common', 'validation'] as const;
|
|
87
|
+
const catalogLoader = createFileSystemI18nLoader({
|
|
88
|
+
rootDir: new URL('./locales', import.meta.url).pathname,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const catalogEntries = await Promise.all(
|
|
92
|
+
locales.map(async (locale) => {
|
|
93
|
+
const namespaceEntries = await Promise.all(
|
|
94
|
+
namespaces.map(async (namespace) => [
|
|
95
|
+
namespace,
|
|
96
|
+
await catalogLoader.load(locale, namespace),
|
|
97
|
+
] as const),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return [locale, Object.fromEntries(namespaceEntries)] as const;
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
const catalogs = Object.fromEntries(catalogEntries);
|
|
104
|
+
|
|
105
|
+
@Module({
|
|
106
|
+
imports: [
|
|
107
|
+
I18nModule.forRoot({
|
|
108
|
+
defaultLocale: 'en',
|
|
109
|
+
supportedLocales: locales,
|
|
110
|
+
fallbackLocales: { ko: ['en'] },
|
|
111
|
+
catalogs,
|
|
112
|
+
}),
|
|
113
|
+
],
|
|
114
|
+
})
|
|
115
|
+
class AppModule {}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Aggregate는 namespace boundary를 보존합니다. `locales/ko/common.json`은 namespace tree를 shallow merge하지 말고 `i18n.translate('title', { locale: 'ko', namespace: 'common' })`로 조회하세요. Catalog file이 없으면 aggregation은 `I18N_MISSING_CATALOG`으로 reject됩니다. `fallbackLocales`는 catalog가 존재한 뒤 메시지를 조회할 때만 적용됩니다.
|
|
119
|
+
|
|
120
|
+
NestJS i18n fallback intent는 명시적으로 변환합니다.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// NestJS i18n
|
|
124
|
+
I18nModule.forRoot({
|
|
125
|
+
fallbackLanguage: 'en',
|
|
126
|
+
fallbacks: { ko: 'en' },
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// fluo
|
|
130
|
+
I18nModule.forRoot({
|
|
131
|
+
defaultLocale: 'en',
|
|
132
|
+
fallbackLocales: { ko: ['en'] },
|
|
133
|
+
catalogs,
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
이 방식은 lookup order를 보존합니다. 명시적 locale, 구성된 fallback chain, `defaultLocale`, 호출별 `defaultValue`, `missingMessage` 순서입니다. Registration 전에 비동기 aggregation을 완료해도 이 순서는 바뀌지 않으며 `forRootAsync(...)`도 추가되지 않습니다.
|
|
138
|
+
|
|
73
139
|
## 코어 번역
|
|
74
140
|
|
|
75
141
|
`I18nService`는 결정론적인 번역 조회를 제공합니다.
|
|
@@ -344,11 +410,11 @@ const loader = createRemoteI18nLoader({
|
|
|
344
410
|
const common = await loader.load('en', 'common');
|
|
345
411
|
```
|
|
346
412
|
|
|
347
|
-
Provider는 validated `locale`, `namespace`, 그리고 loader timeout과 optional per-call cancellation을 결합한 `AbortSignal`을 받습니다. Provider는 raw object message tree 또는 JSON string을 반환할 수 있습니다. `undefined`와 `null`은 missing catalog로 취급되어 `I18N_MISSING_CATALOG`를 throw합니다. Malformed JSON과 invalid message tree shape는 `I18N_INVALID_CATALOG`, provider failure는 `I18N_LOADER_FAILED`, timeout은 `I18N_LOADER_TIMEOUT`, caller cancellation은 `I18N_LOADER_ABORTED`로 보고됩니다. 반환된 catalog는 항상 detached immutable `I18nMessageTree` snapshot입니다.
|
|
413
|
+
Provider는 validated `locale`, `namespace`, 그리고 loader timeout과 optional per-call cancellation을 결합한 `AbortSignal`을 받습니다. `timeoutMs`는 runtime timer가 정확하게 표현할 수 있는 최대 지연값인 `2_147_483_647` 이하의 양의 정수여야 합니다. Provider는 raw object message tree 또는 JSON string을 반환할 수 있습니다. `undefined`와 `null`은 missing catalog로 취급되어 `I18N_MISSING_CATALOG`를 throw합니다. Malformed JSON과 invalid message tree shape는 `I18N_INVALID_CATALOG`, provider가 throw한 `I18nError` instance는 변경 없이 다시 throw되며 그 외 provider failure는 `I18N_LOADER_FAILED`, timeout은 `I18N_LOADER_TIMEOUT`, caller cancellation은 `I18N_LOADER_ABORTED`로 보고됩니다. 반환된 catalog는 항상 detached immutable `I18nMessageTree` snapshot입니다.
|
|
348
414
|
|
|
349
415
|
Remote loader는 기본적으로 cache하지 않습니다. 모든 `load(locale, namespace)` 호출은 provider를 호출하고 그 provider result를 snapshot합니다. Memory, HTTP, CDN, database 또는 stale-while-revalidate caching이 필요한 애플리케이션은 cache invalidation이 application boundary에서 명시적으로 유지되도록 provider 내부 또는 provider wrapper에서 구현해야 합니다.
|
|
350
416
|
|
|
351
|
-
First-party in-memory policy가 필요한 애플리케이션은 loader를 명시적으로 wrap할 수 있습니다. Cache entry는 caller가 custom key를 제공하지 않는 한 `(locale, namespace, version)`으로 keying
|
|
417
|
+
First-party in-memory policy가 필요한 애플리케이션은 loader를 명시적으로 wrap할 수 있습니다. Cache entry는 caller가 custom key를 제공하지 않는 한 `(locale, namespace, version)`으로 keying되고 successful load 이후에만 TTL을 시작하며, `invalidate(...)` / `clear()`가 invalidation을 application-owned 상태로 유지합니다.
|
|
352
418
|
|
|
353
419
|
```ts
|
|
354
420
|
import { createCachedRemoteI18nLoader, createRemoteI18nLoader } from '@fluojs/i18n/loaders/remote';
|
|
@@ -413,7 +479,7 @@ typedI18n.translateInNamespace('admin/common', 'dashboard.title', { locale: 'en'
|
|
|
413
479
|
|
|
414
480
|
이 helper declaration은 type-only이며 application-owned입니다. Runtime wrapper를 추가하지 않고, framework bridge를 import하지 않으며, 넓은 runtime `I18nService.translate(key: string, options)` signature도 바꾸지 않습니다.
|
|
415
481
|
|
|
416
|
-
두 helper 모두 locale 간 key를 deduplicate하고 stable diff를 위해 output을 sort하며, invalid catalog shape는 `I18N_INVALID_CATALOG`, unsafe locale 또는 namespace path는 `I18N_INVALID_LOADER_OPTIONS`로 거부합니다.
|
|
482
|
+
두 helper 모두 locale 간 key를 deduplicate하고 stable diff를 위해 output을 sort하며, invalid catalog shape는 `I18N_INVALID_CATALOG`, unsafe locale 또는 namespace path는 `I18N_INVALID_LOADER_OPTIONS`로 거부합니다. Custom output name은 유효하고 서로 다른 TypeScript identifier여야 하며, invalid 또는 충돌하는 name은 `I18N_INVALID_OPTIONS`로 거부합니다.
|
|
417
483
|
|
|
418
484
|
## 공개 API
|
|
419
485
|
|
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Framework-agnostic internationalization core surface for fluo applications.
|
|
|
9
9
|
- [Installation](#installation)
|
|
10
10
|
- [When to Use](#when-to-use)
|
|
11
11
|
- [Quick Start](#quick-start)
|
|
12
|
+
- [Catalog Aggregation and Fallback Migration](#catalog-aggregation-and-fallback-migration)
|
|
12
13
|
- [Core Translation](#core-translation)
|
|
13
14
|
- [Formatting](#formatting)
|
|
14
15
|
- [ICU MessageFormat](#icu-messageformat)
|
|
@@ -70,6 +71,71 @@ class AppModule {}
|
|
|
70
71
|
|
|
71
72
|
`I18nModule.forRoot(...)` exports `I18nService` as a global provider by default so sibling modules can inject the shared service after the root package is imported once. Pass `global: false` when the service should stay visible only to the module that imports the i18n module.
|
|
72
73
|
|
|
74
|
+
`I18nModule.forRoot(...)` is synchronous and captures final options when the module graph is defined. Finish asynchronous catalog or configuration loading at the application-owned bootstrap boundary before `I18nModule.forRoot(...)`, then pass the resolved catalogs and options into that registration call. This preserves the framework-agnostic root contract; it does not provide a NestJS dynamic-module runtime bridge or a `forRootAsync(...)` compatibility surface.
|
|
75
|
+
|
|
76
|
+
## Catalog Aggregation and Fallback Migration
|
|
77
|
+
|
|
78
|
+
When migrating from NestJS i18n, load every required locale and namespace before the synchronous `I18nModule.forRoot(...)` call. Do not carry a NestJS loader configuration into module registration:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { Module } from '@fluojs/core';
|
|
82
|
+
import { I18nModule } from '@fluojs/i18n';
|
|
83
|
+
import { createFileSystemI18nLoader } from '@fluojs/i18n/loaders/fs';
|
|
84
|
+
|
|
85
|
+
const locales = ['en', 'ko'] as const;
|
|
86
|
+
const namespaces = ['common', 'validation'] as const;
|
|
87
|
+
const catalogLoader = createFileSystemI18nLoader({
|
|
88
|
+
rootDir: new URL('./locales', import.meta.url).pathname,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const catalogEntries = await Promise.all(
|
|
92
|
+
locales.map(async (locale) => {
|
|
93
|
+
const namespaceEntries = await Promise.all(
|
|
94
|
+
namespaces.map(async (namespace) => [
|
|
95
|
+
namespace,
|
|
96
|
+
await catalogLoader.load(locale, namespace),
|
|
97
|
+
] as const),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return [locale, Object.fromEntries(namespaceEntries)] as const;
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
const catalogs = Object.fromEntries(catalogEntries);
|
|
104
|
+
|
|
105
|
+
@Module({
|
|
106
|
+
imports: [
|
|
107
|
+
I18nModule.forRoot({
|
|
108
|
+
defaultLocale: 'en',
|
|
109
|
+
supportedLocales: locales,
|
|
110
|
+
fallbackLocales: { ko: ['en'] },
|
|
111
|
+
catalogs,
|
|
112
|
+
}),
|
|
113
|
+
],
|
|
114
|
+
})
|
|
115
|
+
class AppModule {}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The aggregate preserves namespace boundaries: use `i18n.translate('title', { locale: 'ko', namespace: 'common' })` for `locales/ko/common.json`, rather than shallow-merging namespace trees. A missing catalog file rejects aggregation with `I18N_MISSING_CATALOG`; `fallbackLocales` applies only to message lookup after catalogs exist.
|
|
119
|
+
|
|
120
|
+
Convert NestJS i18n fallback intent explicitly:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// NestJS i18n
|
|
124
|
+
I18nModule.forRoot({
|
|
125
|
+
fallbackLanguage: 'en',
|
|
126
|
+
fallbacks: { ko: 'en' },
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// fluo
|
|
130
|
+
I18nModule.forRoot({
|
|
131
|
+
defaultLocale: 'en',
|
|
132
|
+
fallbackLocales: { ko: ['en'] },
|
|
133
|
+
catalogs,
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This preserves the lookup order: explicit locale, configured fallback chain, `defaultLocale`, per-call `defaultValue`, then `missingMessage`. Completing asynchronous aggregation before registration does not change that order or add `forRootAsync(...)`.
|
|
138
|
+
|
|
73
139
|
## Core Translation
|
|
74
140
|
|
|
75
141
|
The `I18nService` provides deterministic translation lookup.
|
|
@@ -344,11 +410,11 @@ const loader = createRemoteI18nLoader({
|
|
|
344
410
|
const common = await loader.load('en', 'common');
|
|
345
411
|
```
|
|
346
412
|
|
|
347
|
-
The provider receives the validated `locale`, `namespace`, and an `AbortSignal` that combines the loader timeout with optional per-call cancellation. Providers may return a raw object message tree or a JSON string. `undefined` and `null` are treated as missing catalogs and throw `I18N_MISSING_CATALOG`; malformed JSON and invalid message tree shapes throw `I18N_INVALID_CATALOG`; provider failures are wrapped as `I18N_LOADER_FAILED`; timeouts throw `I18N_LOADER_TIMEOUT`; caller cancellation throws `I18N_LOADER_ABORTED`. Returned catalogs are always detached immutable `I18nMessageTree` snapshots.
|
|
413
|
+
The provider receives the validated `locale`, `namespace`, and an `AbortSignal` that combines the loader timeout with optional per-call cancellation. `timeoutMs` must be a positive integer no greater than `2_147_483_647`, the largest delay that the runtime timer can represent truthfully. Providers may return a raw object message tree or a JSON string. `undefined` and `null` are treated as missing catalogs and throw `I18N_MISSING_CATALOG`; malformed JSON and invalid message tree shapes throw `I18N_INVALID_CATALOG`; provider-thrown `I18nError` instances are rethrown unchanged, while other provider failures are wrapped as `I18N_LOADER_FAILED`; timeouts throw `I18N_LOADER_TIMEOUT`; caller cancellation throws `I18N_LOADER_ABORTED`. Returned catalogs are always detached immutable `I18nMessageTree` snapshots.
|
|
348
414
|
|
|
349
415
|
The remote loader never caches by default: every `load(locale, namespace)` call invokes the provider and snapshots that provider result. Applications that need memory, HTTP, CDN, database, or stale-while-revalidate caching should implement it inside the provider or in a wrapper around the provider so cache invalidation remains explicit at the application boundary.
|
|
350
416
|
|
|
351
|
-
Applications that want a first-party in-memory policy can wrap the loader explicitly. Cache entries are keyed by `(locale, namespace, version)` unless the caller provides a custom key, and `invalidate(...)` / `clear()
|
|
417
|
+
Applications that want a first-party in-memory policy can wrap the loader explicitly. Cache entries are keyed by `(locale, namespace, version)` unless the caller provides a custom key, begin their TTL only after a successful load, and keep invalidation application-owned through `invalidate(...)` / `clear()`:
|
|
352
418
|
|
|
353
419
|
```ts
|
|
354
420
|
import { createCachedRemoteI18nLoader, createRemoteI18nLoader } from '@fluojs/i18n/loaders/remote';
|
|
@@ -413,7 +479,7 @@ typedI18n.translateInNamespace('admin/common', 'dashboard.title', { locale: 'en'
|
|
|
413
479
|
|
|
414
480
|
These helper declarations are type-only and application-owned. They do not add runtime wrappers, do not import framework bridges, and do not change the broad runtime `I18nService.translate(key: string, options)` signature.
|
|
415
481
|
|
|
416
|
-
Both helpers deduplicate keys across locales, sort output for stable diffs, reject invalid catalog shapes with `I18N_INVALID_CATALOG`, and reject unsafe locale or namespace paths with `I18N_INVALID_LOADER_OPTIONS`.
|
|
482
|
+
Both helpers deduplicate keys across locales, sort output for stable diffs, reject invalid catalog shapes with `I18N_INVALID_CATALOG`, and reject unsafe locale or namespace paths with `I18N_INVALID_LOADER_OPTIONS`. Custom output names must be valid, distinct TypeScript identifiers; invalid or colliding names reject with `I18N_INVALID_OPTIONS`.
|
|
417
483
|
|
|
418
484
|
## Public API
|
|
419
485
|
|
package/dist/adapters.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iCAAiC,
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
|
package/dist/adapters.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { isSupportedLocale, isValidLocale,
|
|
1
|
+
import { isSupportedLocale, isValidLocale, parseLocalePreferences, resolveSupportedLocale, selectLocaleFromAcceptLanguagePolicy } from './locale-resolution.js';
|
|
2
|
+
import { resolveLocaleResolverChain } from './resolver-chain.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Locale metadata resolved for a non-HTTP transport context.
|
|
@@ -102,30 +103,11 @@ export function getAdapterLocale(store, context) {
|
|
|
102
103
|
* @throws {TypeError} When the configured default locale is invalid or unsupported.
|
|
103
104
|
*/
|
|
104
105
|
export function resolveLocale(context, options) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
for (const resolver of options.resolvers ?? []) {
|
|
112
|
-
const result = normalizeLocaleResolverResult(resolver({
|
|
113
|
-
context,
|
|
114
|
-
defaultLocale: options.defaultLocale,
|
|
115
|
-
supportedLocales: options.supportedLocales
|
|
116
|
-
}));
|
|
117
|
-
if (result === undefined || !isValidLocale(result.locale) || !isSupportedLocale(result.locale, options.supportedLocales)) {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
return Object.freeze({
|
|
121
|
-
locale: result.locale,
|
|
122
|
-
source: result.source
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
return Object.freeze({
|
|
126
|
-
locale: options.defaultLocale,
|
|
127
|
-
source: 'default'
|
|
128
|
-
});
|
|
106
|
+
return resolveLocaleResolverChain({
|
|
107
|
+
context,
|
|
108
|
+
defaultLocale: options.defaultLocale,
|
|
109
|
+
supportedLocales: options.supportedLocales
|
|
110
|
+
}, options);
|
|
129
111
|
}
|
|
130
112
|
|
|
131
113
|
/**
|
package/dist/catalog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAO9E;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAO9E;AAmCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAEjF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqBxG"}
|
package/dist/catalog.js
CHANGED
|
@@ -12,19 +12,15 @@ export function isPlainObject(value) {
|
|
|
12
12
|
const prototype = Object.getPrototypeOf(value);
|
|
13
13
|
return prototype === Object.prototype || prototype === null;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Creates a detached immutable message tree from untrusted catalog-like input.
|
|
18
|
-
*
|
|
19
|
-
* @param value Catalog-like value to validate and snapshot.
|
|
20
|
-
* @param path Diagnostic path used in validation errors.
|
|
21
|
-
* @returns A frozen message tree detached from caller-owned data.
|
|
22
|
-
*/
|
|
23
|
-
export function snapshotMessageTree(value, path) {
|
|
15
|
+
function snapshotMessageTreeAtPath(value, path, ancestors) {
|
|
24
16
|
if (!isPlainObject(value)) {
|
|
25
17
|
throw new I18nError(`${path} must be a plain object message tree.`, 'I18N_INVALID_CATALOG');
|
|
26
18
|
}
|
|
27
|
-
|
|
19
|
+
if (ancestors.has(value)) {
|
|
20
|
+
throw new I18nError(`${path} contains a cyclic message tree.`, 'I18N_INVALID_CATALOG');
|
|
21
|
+
}
|
|
22
|
+
ancestors.add(value);
|
|
23
|
+
const snapshot = Object.create(null);
|
|
28
24
|
for (const [key, entry] of Object.entries(value)) {
|
|
29
25
|
if (key.trim() === '') {
|
|
30
26
|
throw new I18nError(`${path} contains an empty message key segment.`, 'I18N_INVALID_CATALOG');
|
|
@@ -34,14 +30,26 @@ export function snapshotMessageTree(value, path) {
|
|
|
34
30
|
continue;
|
|
35
31
|
}
|
|
36
32
|
if (isPlainObject(entry)) {
|
|
37
|
-
snapshot[key] =
|
|
33
|
+
snapshot[key] = snapshotMessageTreeAtPath(entry, `${path}.${key}`, ancestors);
|
|
38
34
|
continue;
|
|
39
35
|
}
|
|
40
36
|
throw new I18nError(`${path}.${key} must be a string or nested message tree.`, 'I18N_INVALID_CATALOG');
|
|
41
37
|
}
|
|
38
|
+
ancestors.delete(value);
|
|
42
39
|
return Object.freeze(snapshot);
|
|
43
40
|
}
|
|
44
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Creates a detached immutable message tree from untrusted catalog-like input.
|
|
44
|
+
*
|
|
45
|
+
* @param value Catalog-like value to validate and snapshot.
|
|
46
|
+
* @param path Diagnostic path used in validation errors.
|
|
47
|
+
* @returns A frozen message tree detached from caller-owned data.
|
|
48
|
+
*/
|
|
49
|
+
export function snapshotMessageTree(value, path) {
|
|
50
|
+
return snapshotMessageTreeAtPath(value, path, new Set());
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
/**
|
|
46
54
|
* Resolves a direct or dot-path message from an immutable i18n message tree.
|
|
47
55
|
*
|
package/dist/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,iCAAiC,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,iCAAiC,EAIvC,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,gGAAgG;IAChG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClD,uEAAuE;IACvE,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,gGAAgG;IAChG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClD,2EAA2E;IAC3E,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;IACnC,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAED;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,yCAA0C,SAAQ,mCAAmC,EAAE,iCAAiC;CAAG;AAE5I;;GAEG;AACH,eAAO,MAAM,uBAAuB,sDAA+D,CAAC;AAepG;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,UAAU,EAClB,QAAQ,GAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAM,GAC/C,IAAI,CAEN;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,GAAG,SAAS,CAEpF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,SAAS,wBAAwB,EAAE,CAEvH;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,GAAE,mCAAwC,GAChD,kBAAkB,CAsBpB;AAED;;;;;GAKG;AACH,wBAAgB,wCAAwC,CACtD,OAAO,GAAE,yCAA8C,GACtD,kBAAkB,CAkBpB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAQ/G"}
|
package/dist/http.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createContextKey, getContextValue, setContextValue } from '@fluojs/http';
|
|
2
|
-
import {
|
|
2
|
+
import { parseLocalePreferences, resolveSupportedLocale, selectLocaleFromAcceptLanguagePolicy } from './locale-resolution.js';
|
|
3
|
+
import { resolveLocaleResolverChain } from './resolver-chain.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Locale metadata stored on a fluo HTTP request context.
|
|
@@ -147,34 +148,13 @@ export function createAcceptLanguageLocalePolicyResolver(options = {}) {
|
|
|
147
148
|
* @throws {TypeError} When the configured default locale is invalid or unsupported.
|
|
148
149
|
*/
|
|
149
150
|
export function resolveHttpLocale(context, options) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const result = normalizeLocaleResolverResult(resolver({
|
|
158
|
-
context,
|
|
159
|
-
defaultLocale: options.defaultLocale,
|
|
160
|
-
supportedLocales: options.supportedLocales
|
|
161
|
-
}));
|
|
162
|
-
if (result === undefined || !isValidLocale(result.locale) || !isSupportedLocale(result.locale, options.supportedLocales)) {
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
setHttpLocale(context, result.locale, {
|
|
166
|
-
source: result.source
|
|
167
|
-
});
|
|
168
|
-
return getHttpLocale(context) ?? {
|
|
169
|
-
locale: result.locale,
|
|
170
|
-
source: result.source
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
setHttpLocale(context, options.defaultLocale, {
|
|
174
|
-
source: 'default'
|
|
151
|
+
const resolved = resolveLocaleResolverChain({
|
|
152
|
+
context,
|
|
153
|
+
defaultLocale: options.defaultLocale,
|
|
154
|
+
supportedLocales: options.supportedLocales
|
|
155
|
+
}, options);
|
|
156
|
+
setHttpLocale(context, resolved.locale, {
|
|
157
|
+
source: resolved.source
|
|
175
158
|
});
|
|
176
|
-
return getHttpLocale(context) ??
|
|
177
|
-
locale: options.defaultLocale,
|
|
178
|
-
source: 'default'
|
|
179
|
-
};
|
|
159
|
+
return getHttpLocale(context) ?? resolved;
|
|
180
160
|
}
|
package/dist/icu.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icu.d.ts","sourceRoot":"","sources":["../src/icu.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"icu.d.ts","sourceRoot":"","sources":["../src/icu.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAA4C,MAAM,cAAc,CAAC;AACrF,OAAO,KAAK,EAAuC,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE/G;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;AAExF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC;IACnF,kGAAkG;IAClG,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,mEAAmE;IACnE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC;AA4CD;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IAEtC;;;;OAIG;gBACS,OAAO,GAAE,iBAAiB,GAAG,WAAgB;IAIzD;;;;OAIG;IACH,cAAc,IAAI,WAAW;IAI7B;;;;;;;OAOG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,GAAG,MAAM;CAsBjE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,iBAAiB,GAAG,WAAgB,GAAG,cAAc,CAE3F"}
|
package/dist/icu.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { FormatError, IntlMessageFormat } from 'intl-messageformat';
|
|
2
|
-
import { resolveCatalogMessage } from './catalog.js';
|
|
3
2
|
import { I18nError } from './errors.js';
|
|
4
|
-
import { I18nService, createI18n } from './service.js';
|
|
3
|
+
import { I18nService, createI18n, resolveI18nMessageProvenance } from './service.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Primitive values accepted by the ICU MessageFormat subpath.
|
|
@@ -39,14 +38,7 @@ function toMessageFormatValues(values) {
|
|
|
39
38
|
return Object.fromEntries(Object.entries(values));
|
|
40
39
|
}
|
|
41
40
|
function resolveMessageLocale(service, key, options) {
|
|
42
|
-
|
|
43
|
-
const snapshot = service.snapshotOptions();
|
|
44
|
-
for (const locale of service.resolveLocales(options.locale)) {
|
|
45
|
-
if (resolveCatalogMessage(snapshot.catalogs?.[locale], resolvedKey) !== undefined) {
|
|
46
|
-
return locale;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return options.locale;
|
|
41
|
+
return resolveI18nMessageProvenance(service, key, options.locale, options.namespace)?.locale ?? options.locale;
|
|
50
42
|
}
|
|
51
43
|
function normalizeMessageFormatError(error, key) {
|
|
52
44
|
if (error instanceof FormatError || error instanceof SyntaxError || error instanceof Error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/loaders/remote.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAQrE,YAAY,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/loaders/remote.ts"],"names":[],"mappings":"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"}
|
package/dist/loaders/remote.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { I18nError } from '../errors.js';
|
|
2
2
|
import { isPlainObject, snapshotLoaderMessageTree, validateLoaderLocale, validateLoaderNamespace } from './shared.js';
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
4
|
+
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Request metadata passed to a remote catalog provider.
|
|
@@ -30,8 +31,8 @@ function validateTimeout(timeoutMs) {
|
|
|
30
31
|
if (timeoutMs === undefined) {
|
|
31
32
|
return DEFAULT_TIMEOUT_MS;
|
|
32
33
|
}
|
|
33
|
-
if (typeof timeoutMs !== 'number' || !Number.isInteger(timeoutMs) || timeoutMs <= 0) {
|
|
34
|
-
throw new I18nError(
|
|
34
|
+
if (typeof timeoutMs !== 'number' || !Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS) {
|
|
35
|
+
throw new I18nError(`Remote i18n loader timeoutMs must be a positive integer no greater than ${MAX_TIMEOUT_MS} when provided.`, 'I18N_INVALID_LOADER_OPTIONS');
|
|
35
36
|
}
|
|
36
37
|
return timeoutMs;
|
|
37
38
|
}
|
|
@@ -137,8 +138,10 @@ export class RemoteI18nLoader {
|
|
|
137
138
|
const controller = new AbortController();
|
|
138
139
|
const unlinkCallerAbort = linkCallerAbort(options.signal, controller);
|
|
139
140
|
const timeout = setTimeout(() => controller.abort(createTimeoutError()), this.timeoutMs);
|
|
141
|
+
let rejectOnAbort;
|
|
140
142
|
const abortRace = new Promise((_resolve, reject) => {
|
|
141
|
-
|
|
143
|
+
rejectOnAbort = () => reject(controller.signal.reason instanceof I18nError ? controller.signal.reason : createAbortError());
|
|
144
|
+
controller.signal.addEventListener('abort', rejectOnAbort, {
|
|
142
145
|
once: true
|
|
143
146
|
});
|
|
144
147
|
});
|
|
@@ -159,6 +162,9 @@ export class RemoteI18nLoader {
|
|
|
159
162
|
} finally {
|
|
160
163
|
clearTimeout(timeout);
|
|
161
164
|
unlinkCallerAbort();
|
|
165
|
+
if (rejectOnAbort !== undefined) {
|
|
166
|
+
controller.signal.removeEventListener('abort', rejectOnAbort);
|
|
167
|
+
}
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
}
|
|
@@ -218,7 +224,7 @@ export class CachedRemoteI18nLoader {
|
|
|
218
224
|
const catalog = await this.loader.load(locale, namespace, options);
|
|
219
225
|
this.cache.set(cacheKey, {
|
|
220
226
|
catalog,
|
|
221
|
-
expiresAt:
|
|
227
|
+
expiresAt: this.now() + this.ttlMs
|
|
222
228
|
});
|
|
223
229
|
return catalog;
|
|
224
230
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { I18nLocale, I18nMessageCatalogs } from './types.js';
|
|
2
|
+
export interface I18nMessageProvenance {
|
|
3
|
+
readonly locale: I18nLocale;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveMessageProvenance(catalogs: I18nMessageCatalogs | undefined, locales: readonly I18nLocale[], key: string): I18nMessageProvenance | undefined;
|
|
7
|
+
//# sourceMappingURL=message-provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-provenance.d.ts","sourceRoot":"","sources":["../src/message-provenance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAElE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,mBAAmB,GAAG,SAAS,EACzC,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,GAAG,EAAE,MAAM,GACV,qBAAqB,GAAG,SAAS,CAUnC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { resolveCatalogMessage } from './catalog.js';
|
|
2
|
+
export function resolveMessageProvenance(catalogs, locales, key) {
|
|
3
|
+
for (const locale of locales) {
|
|
4
|
+
const message = resolveCatalogMessage(catalogs?.[locale], key);
|
|
5
|
+
if (message !== undefined) {
|
|
6
|
+
return Object.freeze({
|
|
7
|
+
locale,
|
|
8
|
+
message
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { I18nLocale } from './types.js';
|
|
2
|
+
export interface LocaleResolverChainResult {
|
|
3
|
+
readonly locale: I18nLocale;
|
|
4
|
+
readonly source?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface LocaleResolverChainOptions<TInput> {
|
|
7
|
+
readonly defaultLocale: I18nLocale;
|
|
8
|
+
readonly supportedLocales?: readonly I18nLocale[];
|
|
9
|
+
readonly resolvers?: readonly ((input: TInput) => unknown)[];
|
|
10
|
+
}
|
|
11
|
+
export declare function resolveLocaleResolverChain<TInput>(input: TInput, options: LocaleResolverChainOptions<TInput>): LocaleResolverChainResult;
|
|
12
|
+
//# sourceMappingURL=resolver-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver-chain.d.ts","sourceRoot":"","sources":["../src/resolver-chain.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B,CAAC,MAAM;IAChD,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,EAAE,CAAC;CAC9D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAC/C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,0BAA0B,CAAC,MAAM,CAAC,GAC1C,yBAAyB,CAkB3B"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isSupportedLocale, isValidLocale, normalizeLocaleResolverResult } from './locale-resolution.js';
|
|
2
|
+
export function resolveLocaleResolverChain(input, options) {
|
|
3
|
+
if (!isValidLocale(options.defaultLocale)) {
|
|
4
|
+
throw new TypeError('defaultLocale must be a syntactically valid locale string.');
|
|
5
|
+
}
|
|
6
|
+
if (!isSupportedLocale(options.defaultLocale, options.supportedLocales)) {
|
|
7
|
+
throw new TypeError('defaultLocale must be listed in supportedLocales when supportedLocales is provided.');
|
|
8
|
+
}
|
|
9
|
+
for (const resolver of options.resolvers ?? []) {
|
|
10
|
+
const result = normalizeLocaleResolverResult(resolver(input));
|
|
11
|
+
if (result !== undefined && isValidLocale(result.locale) && isSupportedLocale(result.locale, options.supportedLocales)) {
|
|
12
|
+
return Object.freeze({
|
|
13
|
+
locale: result.locale,
|
|
14
|
+
source: result.source
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
locale: options.defaultLocale,
|
|
20
|
+
source: 'default'
|
|
21
|
+
});
|
|
22
|
+
}
|
package/dist/service.d.ts
CHANGED
|
@@ -94,6 +94,7 @@ export declare class I18nService {
|
|
|
94
94
|
*/
|
|
95
95
|
translate(key: string, options: I18nTranslateOptions): string;
|
|
96
96
|
}
|
|
97
|
+
export declare function resolveI18nMessageProvenance(service: I18nService, key: string, locale: I18nLocale, namespace: string | undefined): import("./message-provenance.js").I18nMessageProvenance | undefined;
|
|
97
98
|
/**
|
|
98
99
|
* Creates a standalone i18n service without registering a fluo module.
|
|
99
100
|
*
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EAEzB,qBAAqB,EACrB,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EACvB,6BAA6B,EAC7B,oBAAoB,EACrB,MAAM,YAAY,CAAC;AA6GpB;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C;;;;OAIG;gBACS,OAAO,GAAE,iBAAsB;IAK3C;;;;OAIG;IACH,eAAe,IAAI,iBAAiB;IAIpC;;;;;;OAMG;IACH,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE;IA2BzD,OAAO,CAAC,mBAAmB;IAkB3B;;;;;;;OAOG;IACH,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAAE,OAAO,EAAE,yBAAyB,GAAG,MAAM;IAWhF;;;;;;;OAOG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,GAAG,MAAM;IAWrE;;;;;;;OAOG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,GAAG,MAAM;IAiBzE;;;;;;;OAOG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,GAAG,MAAM;IAWtE;;;;;;;OAOG;IACH,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG,MAAM;IAY7E;;;;;;;;OAQG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,OAAO,EAAE,6BAA6B,GAAG,MAAM;IAWpH;;;;;;;OAOG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,MAAM;CAkC9D;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,GAAG,SAAS,uEAU9B;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,WAAW,CAEvE"}
|
package/dist/service.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { isPlainObject
|
|
1
|
+
import { isPlainObject } from './catalog.js';
|
|
2
|
+
import { resolveMessageProvenance } from './message-provenance.js';
|
|
2
3
|
import { snapshotI18nModuleOptions } from './options.js';
|
|
3
4
|
import { I18nError } from './errors.js';
|
|
5
|
+
const serviceOptions = new WeakMap();
|
|
4
6
|
function normalizeTranslationKey(key, namespace) {
|
|
5
7
|
if (typeof key !== 'string') {
|
|
6
8
|
throw new I18nError('Translation key must be a string.', 'I18N_INVALID_OPTIONS');
|
|
@@ -103,6 +105,7 @@ export class I18nService {
|
|
|
103
105
|
*/
|
|
104
106
|
constructor(options = {}) {
|
|
105
107
|
this.options = snapshotI18nModuleOptions(options);
|
|
108
|
+
serviceOptions.set(this, this.options);
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
/**
|
|
@@ -289,11 +292,9 @@ export class I18nService {
|
|
|
289
292
|
assertDefaultValue(options.defaultValue);
|
|
290
293
|
const resolvedKey = normalizeTranslationKey(key, options.namespace);
|
|
291
294
|
const locales = this.resolveLocales(options.locale);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return interpolate(message, options.values);
|
|
296
|
-
}
|
|
295
|
+
const message = resolveMessageProvenance(this.options.catalogs, locales, resolvedKey);
|
|
296
|
+
if (message !== undefined) {
|
|
297
|
+
return interpolate(message.message, options.values);
|
|
297
298
|
}
|
|
298
299
|
if (options.defaultValue !== undefined) {
|
|
299
300
|
return interpolate(options.defaultValue, options.values);
|
|
@@ -310,6 +311,14 @@ export class I18nService {
|
|
|
310
311
|
throw new I18nError(`Missing i18n message: ${resolvedKey}`, 'I18N_MISSING_MESSAGE');
|
|
311
312
|
}
|
|
312
313
|
}
|
|
314
|
+
export function resolveI18nMessageProvenance(service, key, locale, namespace) {
|
|
315
|
+
const options = serviceOptions.get(service);
|
|
316
|
+
if (options === undefined) {
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
const resolvedKey = normalizeTranslationKey(key, namespace);
|
|
320
|
+
return resolveMessageProvenance(options.catalogs, service.resolveLocales(locale), resolvedKey);
|
|
321
|
+
}
|
|
313
322
|
|
|
314
323
|
/**
|
|
315
324
|
* Creates a standalone i18n service without registering a fluo module.
|
package/dist/typegen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typegen.d.ts","sourceRoot":"","sources":["../src/typegen.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkBlF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,mGAAmG;IACnG,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IACxC,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,8EAA8E;IAC9E,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAChD,uEAAuE;IACvE,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,wEAAwE;IACxE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,kCAAmC,SAAQ,yBAAyB;IACnF,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC1C;AA+JD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,uBAAuB,EAAE,EAC1C,OAAO,GAAE,yBAA8B,GACtC,MAAM,
|
|
1
|
+
{"version":3,"file":"typegen.d.ts","sourceRoot":"","sources":["../src/typegen.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkBlF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,mGAAmG;IACnG,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IACxC,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,8EAA8E;IAC9E,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAChD,uEAAuE;IACvE,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,wEAAwE;IACxE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,kCAAmC,SAAQ,yBAAyB;IACnF,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC1C;AA+JD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,uBAAuB,EAAE,EAC1C,OAAO,GAAE,yBAA8B,GACtC,MAAM,CA0FR;AAED;;;;;;GAMG;AACH,wBAAsB,qCAAqC,CAAC,OAAO,EAAE,kCAAkC,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBxH"}
|
package/dist/typegen.js
CHANGED
|
@@ -154,6 +154,10 @@ export function generateI18nCatalogTypes(inputs, options = {}) {
|
|
|
154
154
|
const typedTranslateTypeName = assertIdentifier(options.typedTranslateTypeName, DEFAULT_TYPED_TRANSLATE_TYPE_NAME, 'Catalog typegen typedTranslateTypeName');
|
|
155
155
|
const typedTranslateOptionsTypeName = assertIdentifier(options.typedTranslateOptionsTypeName, DEFAULT_TYPED_TRANSLATE_OPTIONS_TYPE_NAME, 'Catalog typegen typedTranslateOptionsTypeName');
|
|
156
156
|
const typedServiceTypeName = assertIdentifier(options.typedServiceTypeName, DEFAULT_TYPED_SERVICE_TYPE_NAME, 'Catalog typegen typedServiceTypeName');
|
|
157
|
+
const generatedIdentifiers = [keyTypeName, namespaceTypeName, keyByNamespaceTypeName, namespaceKeyTypeName, typedTranslateOptionsTypeName, typedTranslateTypeName, typedServiceTypeName];
|
|
158
|
+
if (new Set(generatedIdentifiers).size !== generatedIdentifiers.length) {
|
|
159
|
+
throw new I18nError('Catalog typegen output identifiers must be unique.', 'I18N_INVALID_OPTIONS');
|
|
160
|
+
}
|
|
157
161
|
const keys = new Set();
|
|
158
162
|
const namespaces = new Set();
|
|
159
163
|
const keysByNamespace = new Map();
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"localization",
|
|
9
9
|
"translations"
|
|
10
10
|
],
|
|
11
|
-
"version": "
|
|
11
|
+
"version": "3.0.0",
|
|
12
12
|
"private": false,
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"dist"
|
|
61
61
|
],
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@fluojs/core": "^
|
|
63
|
+
"@fluojs/core": "^2.0.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"intl-messageformat": "^11.2.4",
|
|
67
|
-
"@fluojs/http": "^
|
|
68
|
-
"@fluojs/validation": "^
|
|
67
|
+
"@fluojs/http": "^3.0.0",
|
|
68
|
+
"@fluojs/validation": "^2.0.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependenciesMeta": {
|
|
71
71
|
"@fluojs/http": {
|
|
@@ -80,10 +80,10 @@
|
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"intl-messageformat": "^11.2.4",
|
|
83
|
-
"vitest": "^
|
|
84
|
-
"@fluojs/testing": "^
|
|
85
|
-
"@fluojs/http": "^
|
|
86
|
-
"@fluojs/validation": "^
|
|
83
|
+
"vitest": "^4.1.11",
|
|
84
|
+
"@fluojs/testing": "^3.0.0",
|
|
85
|
+
"@fluojs/http": "^3.0.0",
|
|
86
|
+
"@fluojs/validation": "^2.0.0"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
89
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|