@c9up/rosetta 0.1.3 → 0.1.5
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/dist/Rosetta.d.ts +100 -0
- package/dist/Rosetta.d.ts.map +1 -0
- package/dist/Rosetta.js +381 -0
- package/dist/Rosetta.js.map +1 -0
- package/dist/RosettaProvider.d.ts +55 -0
- package/dist/RosettaProvider.d.ts.map +1 -0
- package/dist/RosettaProvider.js +48 -0
- package/dist/RosettaProvider.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/FileSystemLoader.d.ts +10 -0
- package/dist/loaders/FileSystemLoader.d.ts.map +1 -0
- package/dist/loaders/FileSystemLoader.js +175 -0
- package/dist/loaders/FileSystemLoader.js.map +1 -0
- package/dist/native.d.ts +18 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +89 -0
- package/dist/native.js.map +1 -0
- package/dist/services/main.d.ts +21 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +34 -0
- package/dist/services/main.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
- package/src/Rosetta.ts +9 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export type TranslationParams = Record<string, string | number | boolean | Date | null | undefined>;
|
|
2
|
+
export type MessageValue = string | number | boolean | null;
|
|
3
|
+
export type MessageTree = {
|
|
4
|
+
[key: string]: MessageValue | MessageTree;
|
|
5
|
+
};
|
|
6
|
+
export type MessageCatalog = Record<string, string>;
|
|
7
|
+
export interface TranslateOptions {
|
|
8
|
+
locale?: string;
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface LocaleResolverInput {
|
|
12
|
+
header?: string | null;
|
|
13
|
+
accepted?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface RosettaLoader {
|
|
16
|
+
load(locale: string): Promise<MessageTree | MessageCatalog | null | undefined>;
|
|
17
|
+
}
|
|
18
|
+
export interface RosettaOptions {
|
|
19
|
+
defaultLocale?: string;
|
|
20
|
+
supportedLocales?: string[];
|
|
21
|
+
fallbackLocale?: string;
|
|
22
|
+
fallbackLocales?: Record<string, string>;
|
|
23
|
+
messages?: Record<string, MessageTree | MessageCatalog>;
|
|
24
|
+
loaders?: RosettaLoader[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Numeric separators for a resolved locale. Returned by
|
|
28
|
+
* `Rosetta.getNumberFormatData()` so consumers (e.g. `@c9up/atom`'s
|
|
29
|
+
* `Decimal.parseLocale`) can honor Rosetta's `fallbackLocales`
|
|
30
|
+
* chain when extracting group / decimal separators — without
|
|
31
|
+
* importing `@c9up/rosetta` directly (structural typing).
|
|
32
|
+
*/
|
|
33
|
+
export interface NumberFormatData {
|
|
34
|
+
decimal: string;
|
|
35
|
+
group: string;
|
|
36
|
+
minus: string;
|
|
37
|
+
plusSign: string;
|
|
38
|
+
}
|
|
39
|
+
export declare class RosettaLocale {
|
|
40
|
+
#private;
|
|
41
|
+
constructor(manager: Rosetta, locale: string);
|
|
42
|
+
getLocale(): string;
|
|
43
|
+
has(key: string): boolean;
|
|
44
|
+
t(key: string, params?: TranslationParams, options?: Omit<TranslateOptions, "locale">): string;
|
|
45
|
+
formatNumber(value: number, options?: Intl.NumberFormatOptions): string;
|
|
46
|
+
formatCurrency(value: number, currency: string, options?: Intl.NumberFormatOptions): string;
|
|
47
|
+
formatDate(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
|
|
48
|
+
formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;
|
|
49
|
+
getNumberFormatData(): NumberFormatData;
|
|
50
|
+
formatNumberString(value: string, options?: Intl.NumberFormatOptions): string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Rosetta i18n manager.
|
|
54
|
+
*
|
|
55
|
+
* Framework-agnostic with request-scoped locale instances.
|
|
56
|
+
*/
|
|
57
|
+
export declare class Rosetta {
|
|
58
|
+
#private;
|
|
59
|
+
constructor(options?: RosettaOptions);
|
|
60
|
+
boot(): Promise<void>;
|
|
61
|
+
locale(locale: string): RosettaLocale;
|
|
62
|
+
loadMessages(locale: string, messages: MessageTree | MessageCatalog): this;
|
|
63
|
+
loadLocale(locale: string): Promise<this>;
|
|
64
|
+
setLocale(locale: string): this;
|
|
65
|
+
getLocale(): string;
|
|
66
|
+
setDefaultLocale(locale: string): this;
|
|
67
|
+
getDefaultLocale(): string;
|
|
68
|
+
setSupportedLocales(locales: string[]): this;
|
|
69
|
+
getSupportedLocales(): string[] | undefined;
|
|
70
|
+
setFallbackLocale(locale: string): this;
|
|
71
|
+
getFallbackLocale(): string;
|
|
72
|
+
setFallbackLocales(locales: Record<string, string>): this;
|
|
73
|
+
getFallbackLocales(): Record<string, string>;
|
|
74
|
+
resolveLocale(input: string | LocaleResolverInput): string;
|
|
75
|
+
has(key: string, locale?: string): boolean;
|
|
76
|
+
t(key: string, params?: TranslationParams, options?: TranslateOptions): string;
|
|
77
|
+
formatNumber(value: number, options?: Intl.NumberFormatOptions, locale?: string): string;
|
|
78
|
+
formatCurrency(value: number, currency: string, options?: Intl.NumberFormatOptions, locale?: string): string;
|
|
79
|
+
formatDate(value: Date | number | string, options?: Intl.DateTimeFormatOptions, locale?: string): string;
|
|
80
|
+
formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions, locale?: string): string;
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the numeric separators for `locale`, honoring the
|
|
83
|
+
* configured `fallbackLocales` chain. Returns the first locale
|
|
84
|
+
* in the chain that `Intl.NumberFormat` actually supports.
|
|
85
|
+
*
|
|
86
|
+
* Cached per resolved locale so the `formatToParts` work runs
|
|
87
|
+
* once per chain destination.
|
|
88
|
+
*/
|
|
89
|
+
getNumberFormatData(locale?: string): NumberFormatData;
|
|
90
|
+
/**
|
|
91
|
+
* Format a string-valued number through `Intl.NumberFormat`'s
|
|
92
|
+
* string-accepting overload — preserves precision on 18+-digit
|
|
93
|
+
* values that would otherwise truncate via `Number()`.
|
|
94
|
+
*
|
|
95
|
+
* Locale resolution uses the same fallback chain as
|
|
96
|
+
* `getNumberFormatData`.
|
|
97
|
+
*/
|
|
98
|
+
formatNumberString(value: string, options?: Intl.NumberFormatOptions, locale?: string): string;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=Rosetta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rosetta.d.ts","sourceRoot":"","sources":["../src/Rosetta.ts"],"names":[],"mappings":"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"}
|
package/dist/Rosetta.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { createNativeEngine } from "./native.js";
|
|
2
|
+
const DEFAULT_LOCALE = "en";
|
|
3
|
+
export class RosettaLocale {
|
|
4
|
+
#manager;
|
|
5
|
+
#locale;
|
|
6
|
+
constructor(manager, locale) {
|
|
7
|
+
this.#manager = manager;
|
|
8
|
+
this.#locale = locale;
|
|
9
|
+
}
|
|
10
|
+
getLocale() {
|
|
11
|
+
return this.#locale;
|
|
12
|
+
}
|
|
13
|
+
has(key) {
|
|
14
|
+
return this.#manager.has(key, this.#locale);
|
|
15
|
+
}
|
|
16
|
+
t(key, params, options) {
|
|
17
|
+
return this.#manager.t(key, params, { ...options, locale: this.#locale });
|
|
18
|
+
}
|
|
19
|
+
formatNumber(value, options) {
|
|
20
|
+
return this.#manager.formatNumber(value, options, this.#locale);
|
|
21
|
+
}
|
|
22
|
+
formatCurrency(value, currency, options) {
|
|
23
|
+
return this.#manager.formatCurrency(value, currency, options, this.#locale);
|
|
24
|
+
}
|
|
25
|
+
formatDate(value, options) {
|
|
26
|
+
return this.#manager.formatDate(value, options, this.#locale);
|
|
27
|
+
}
|
|
28
|
+
formatRelativeTime(value, unit, options) {
|
|
29
|
+
return this.#manager.formatRelativeTime(value, unit, options, this.#locale);
|
|
30
|
+
}
|
|
31
|
+
getNumberFormatData() {
|
|
32
|
+
return this.#manager.getNumberFormatData(this.#locale);
|
|
33
|
+
}
|
|
34
|
+
formatNumberString(value, options) {
|
|
35
|
+
return this.#manager.formatNumberString(value, options, this.#locale);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Rosetta i18n manager.
|
|
40
|
+
*
|
|
41
|
+
* Framework-agnostic with request-scoped locale instances.
|
|
42
|
+
*/
|
|
43
|
+
export class Rosetta {
|
|
44
|
+
#messages = new Map();
|
|
45
|
+
#catalogsCache;
|
|
46
|
+
#catalogsCacheDirty = true;
|
|
47
|
+
/** Stateful Rust engine (Story 37.9) — holds parsed catalogs in Rust memory. */
|
|
48
|
+
#nativeEngine = createNativeEngine();
|
|
49
|
+
#locale;
|
|
50
|
+
#defaultLocale;
|
|
51
|
+
#fallbackLocale;
|
|
52
|
+
#fallbackLocales;
|
|
53
|
+
#supportedLocales;
|
|
54
|
+
#loaders;
|
|
55
|
+
#numberFormatDataCache = new Map();
|
|
56
|
+
constructor(options = {}) {
|
|
57
|
+
this.#defaultLocale = normalizeLocale(options.defaultLocale ?? DEFAULT_LOCALE);
|
|
58
|
+
this.#locale = this.#defaultLocale;
|
|
59
|
+
this.#fallbackLocale = normalizeLocale(options.fallbackLocale ?? this.#defaultLocale);
|
|
60
|
+
this.#fallbackLocales = normalizeFallbackMap(options.fallbackLocales ?? {});
|
|
61
|
+
this.#supportedLocales = options.supportedLocales
|
|
62
|
+
? new Set(options.supportedLocales.map(normalizeLocale))
|
|
63
|
+
: undefined;
|
|
64
|
+
this.#loaders = options.loaders ?? [];
|
|
65
|
+
if (options.messages) {
|
|
66
|
+
for (const [locale, catalog] of Object.entries(options.messages)) {
|
|
67
|
+
this.loadMessages(locale, catalog);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async boot() {
|
|
72
|
+
if (!this.#supportedLocales || this.#loaders.length === 0) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const locale of this.#supportedLocales) {
|
|
76
|
+
await this.#loadFromLoaders(locale);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
locale(locale) {
|
|
80
|
+
return new RosettaLocale(this, normalizeLocale(locale));
|
|
81
|
+
}
|
|
82
|
+
loadMessages(locale, messages) {
|
|
83
|
+
const normalizedLocale = normalizeLocale(locale);
|
|
84
|
+
const existing = this.#messages.get(normalizedLocale) ?? {};
|
|
85
|
+
const flattened = flattenMessages(messages);
|
|
86
|
+
this.#messages.set(normalizedLocale, { ...existing, ...flattened });
|
|
87
|
+
this.#catalogsCacheDirty = true;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
async loadLocale(locale) {
|
|
91
|
+
await this.#loadFromLoaders(normalizeLocale(locale));
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
setLocale(locale) {
|
|
95
|
+
this.#locale = normalizeLocale(locale);
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
getLocale() {
|
|
99
|
+
return this.#locale;
|
|
100
|
+
}
|
|
101
|
+
setDefaultLocale(locale) {
|
|
102
|
+
this.#defaultLocale = normalizeLocale(locale);
|
|
103
|
+
// Invalidate the number-format-data cache: even though the
|
|
104
|
+
// cache is keyed by the *resolved* locale (so most entries
|
|
105
|
+
// stay correct across config changes), an entry produced by
|
|
106
|
+
// the previous "ultimate fallback" path may now mismatch.
|
|
107
|
+
this.#numberFormatDataCache.clear();
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
getDefaultLocale() {
|
|
111
|
+
return this.#defaultLocale;
|
|
112
|
+
}
|
|
113
|
+
setSupportedLocales(locales) {
|
|
114
|
+
this.#supportedLocales = new Set(locales.map(normalizeLocale));
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
getSupportedLocales() {
|
|
118
|
+
return this.#supportedLocales
|
|
119
|
+
? Array.from(this.#supportedLocales)
|
|
120
|
+
: undefined;
|
|
121
|
+
}
|
|
122
|
+
setFallbackLocale(locale) {
|
|
123
|
+
this.#fallbackLocale = normalizeLocale(locale);
|
|
124
|
+
this.#numberFormatDataCache.clear();
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
getFallbackLocale() {
|
|
128
|
+
return this.#fallbackLocale;
|
|
129
|
+
}
|
|
130
|
+
setFallbackLocales(locales) {
|
|
131
|
+
this.#fallbackLocales = normalizeFallbackMap(locales);
|
|
132
|
+
this.#numberFormatDataCache.clear();
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
getFallbackLocales() {
|
|
136
|
+
return { ...this.#fallbackLocales };
|
|
137
|
+
}
|
|
138
|
+
resolveLocale(input) {
|
|
139
|
+
const requested = typeof input === "string"
|
|
140
|
+
? parseAcceptLanguage(input)
|
|
141
|
+
: (input.accepted ?? parseAcceptLanguage(input.header ?? ""));
|
|
142
|
+
for (const candidate of requested) {
|
|
143
|
+
const normalized = normalizeLocale(candidate);
|
|
144
|
+
const supported = this.#pickFirstSupported(this.#localeChainFor(normalized));
|
|
145
|
+
if (supported) {
|
|
146
|
+
return supported;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const fallback = this.#pickFirstSupported(this.#localeChainFor(this.#defaultLocale));
|
|
150
|
+
return fallback ?? this.#defaultLocale;
|
|
151
|
+
}
|
|
152
|
+
has(key, locale = this.#locale) {
|
|
153
|
+
const normalizedLocale = normalizeLocale(locale);
|
|
154
|
+
const chain = this.#localeChainFor(normalizedLocale);
|
|
155
|
+
if (!this.#nativeEngine) {
|
|
156
|
+
throw new Error("[ROSETTA_NAPI_REQUIRED] The Rust ICU engine is required. Build it with `cd packages/rosetta && pnpm build:napi`.");
|
|
157
|
+
}
|
|
158
|
+
this.#syncNativeEngine();
|
|
159
|
+
return this.#nativeEngine.has(key, JSON.stringify(chain));
|
|
160
|
+
}
|
|
161
|
+
t(key, params, options) {
|
|
162
|
+
const requestedLocale = normalizeLocale(options?.locale ?? this.#locale);
|
|
163
|
+
const chain = this.#localeChainFor(requestedLocale);
|
|
164
|
+
if (!this.#nativeEngine) {
|
|
165
|
+
throw new Error("[ROSETTA_NAPI_REQUIRED] The Rust ICU engine is required. Build it with `cd packages/rosetta && pnpm build:napi`.");
|
|
166
|
+
}
|
|
167
|
+
this.#syncNativeEngine();
|
|
168
|
+
// `bigint` makes a plain `JSON.stringify` THROW, crashing `t()` — serialise
|
|
169
|
+
// it as its decimal string so it survives the boundary and renders/pluralises
|
|
170
|
+
// correctly (RO7). `Date` already serialises to an ISO string via `toJSON`,
|
|
171
|
+
// which the engine's date/time formatter now slices.
|
|
172
|
+
const paramsJson = params
|
|
173
|
+
? JSON.stringify(params, (_k, v) => typeof v === "bigint" ? v.toString() : v)
|
|
174
|
+
: undefined;
|
|
175
|
+
return this.#nativeEngine.translate(key, paramsJson, JSON.stringify(chain), options?.defaultValue);
|
|
176
|
+
}
|
|
177
|
+
formatNumber(value, options, locale = this.#locale) {
|
|
178
|
+
return new Intl.NumberFormat(normalizeLocale(locale), options).format(value);
|
|
179
|
+
}
|
|
180
|
+
formatCurrency(value, currency, options, locale = this.#locale) {
|
|
181
|
+
// Spread `options` FIRST so explicit `style: "currency"` and
|
|
182
|
+
// `currency` always win — a caller's stray `{ style: "decimal" }`
|
|
183
|
+
// must not silently disable currency formatting.
|
|
184
|
+
return this.formatNumber(value, { ...options, style: "currency", currency }, locale);
|
|
185
|
+
}
|
|
186
|
+
formatDate(value, options, locale = this.#locale) {
|
|
187
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
188
|
+
return new Intl.DateTimeFormat(normalizeLocale(locale), options).format(date);
|
|
189
|
+
}
|
|
190
|
+
formatRelativeTime(value, unit, options, locale = this.#locale) {
|
|
191
|
+
return new Intl.RelativeTimeFormat(normalizeLocale(locale), options).format(value, unit);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Resolve the numeric separators for `locale`, honoring the
|
|
195
|
+
* configured `fallbackLocales` chain. Returns the first locale
|
|
196
|
+
* in the chain that `Intl.NumberFormat` actually supports.
|
|
197
|
+
*
|
|
198
|
+
* Cached per resolved locale so the `formatToParts` work runs
|
|
199
|
+
* once per chain destination.
|
|
200
|
+
*/
|
|
201
|
+
getNumberFormatData(locale = this.#locale) {
|
|
202
|
+
const resolved = this.#resolveNumberLocale(locale);
|
|
203
|
+
const cached = this.#numberFormatDataCache.get(resolved);
|
|
204
|
+
if (cached)
|
|
205
|
+
return cached;
|
|
206
|
+
const formatter = new Intl.NumberFormat(resolved);
|
|
207
|
+
const negativeParts = formatter.formatToParts(-12345.6);
|
|
208
|
+
const positiveParts = new Intl.NumberFormat(resolved, {
|
|
209
|
+
signDisplay: "always",
|
|
210
|
+
}).formatToParts(1);
|
|
211
|
+
const data = {
|
|
212
|
+
decimal: negativeParts.find((p) => p.type === "decimal")?.value ?? ".",
|
|
213
|
+
group: negativeParts.find((p) => p.type === "group")?.value ?? ",",
|
|
214
|
+
minus: negativeParts.find((p) => p.type === "minusSign")?.value ?? "-",
|
|
215
|
+
plusSign: positiveParts.find((p) => p.type === "plusSign")?.value ?? "+",
|
|
216
|
+
};
|
|
217
|
+
this.#numberFormatDataCache.set(resolved, data);
|
|
218
|
+
return data;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Format a string-valued number through `Intl.NumberFormat`'s
|
|
222
|
+
* string-accepting overload — preserves precision on 18+-digit
|
|
223
|
+
* values that would otherwise truncate via `Number()`.
|
|
224
|
+
*
|
|
225
|
+
* Locale resolution uses the same fallback chain as
|
|
226
|
+
* `getNumberFormatData`.
|
|
227
|
+
*/
|
|
228
|
+
formatNumberString(value, options, locale = this.#locale) {
|
|
229
|
+
const resolved = this.#resolveNumberLocale(locale);
|
|
230
|
+
const formatter = new Intl.NumberFormat(resolved, options);
|
|
231
|
+
return formatter.format(value);
|
|
232
|
+
}
|
|
233
|
+
#resolveNumberLocale(locale) {
|
|
234
|
+
const chain = this.#localeChainFor(normalizeLocale(locale));
|
|
235
|
+
for (const candidate of chain) {
|
|
236
|
+
const supported = Intl.NumberFormat.supportedLocalesOf([candidate], {
|
|
237
|
+
localeMatcher: "lookup",
|
|
238
|
+
});
|
|
239
|
+
if (supported.length > 0)
|
|
240
|
+
return supported[0];
|
|
241
|
+
}
|
|
242
|
+
// Last-resort: the configured #defaultLocale itself may not be
|
|
243
|
+
// Intl-supported (e.g. an invented private-use tag). Validate
|
|
244
|
+
// it once so the cache key is always a real Intl locale.
|
|
245
|
+
const defaultSupported = Intl.NumberFormat.supportedLocalesOf([this.#defaultLocale], { localeMatcher: "lookup" });
|
|
246
|
+
if (defaultSupported.length > 0)
|
|
247
|
+
return defaultSupported[0];
|
|
248
|
+
// Truly unresolvable — fall back to "en", which every Intl
|
|
249
|
+
// implementation must support per ECMA-402.
|
|
250
|
+
return "en";
|
|
251
|
+
}
|
|
252
|
+
async #loadFromLoaders(locale) {
|
|
253
|
+
for (const loader of this.#loaders) {
|
|
254
|
+
const messages = await loader.load(locale);
|
|
255
|
+
if (messages) {
|
|
256
|
+
this.loadMessages(locale, messages);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
#pickFirstSupported(candidates) {
|
|
261
|
+
if (!this.#supportedLocales) {
|
|
262
|
+
return candidates[0];
|
|
263
|
+
}
|
|
264
|
+
return candidates.find((candidate) => this.#supportedLocales?.has(candidate));
|
|
265
|
+
}
|
|
266
|
+
#localeChainFor(locale) {
|
|
267
|
+
const chain = [];
|
|
268
|
+
const visited = new Set();
|
|
269
|
+
const push = (value) => {
|
|
270
|
+
const normalized = normalizeLocale(value);
|
|
271
|
+
if (!normalized || visited.has(normalized))
|
|
272
|
+
return;
|
|
273
|
+
visited.add(normalized);
|
|
274
|
+
chain.push(normalized);
|
|
275
|
+
};
|
|
276
|
+
push(locale);
|
|
277
|
+
const localeParts = locale.split("-");
|
|
278
|
+
if (localeParts.length > 1) {
|
|
279
|
+
push(localeParts[0]);
|
|
280
|
+
}
|
|
281
|
+
const explicitFallback = this.#fallbackLocales[locale];
|
|
282
|
+
if (explicitFallback) {
|
|
283
|
+
push(explicitFallback);
|
|
284
|
+
const explicitParts = explicitFallback.split("-");
|
|
285
|
+
if (explicitParts.length > 1) {
|
|
286
|
+
push(explicitParts[0]);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
push(this.#fallbackLocale);
|
|
290
|
+
const fallbackParts = this.#fallbackLocale.split("-");
|
|
291
|
+
if (fallbackParts.length > 1) {
|
|
292
|
+
push(fallbackParts[0]);
|
|
293
|
+
}
|
|
294
|
+
push(this.#defaultLocale);
|
|
295
|
+
const defaultParts = this.#defaultLocale.split("-");
|
|
296
|
+
if (defaultParts.length > 1) {
|
|
297
|
+
push(defaultParts[0]);
|
|
298
|
+
}
|
|
299
|
+
return chain;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Sync the Rust-resident catalog with the TS-side `#messages` map. Only
|
|
303
|
+
* performs JSON.stringify when the dirty flag is set (a locale was loaded or
|
|
304
|
+
* edited). On subsequent `t()` / `has()` calls with no changes, this is a
|
|
305
|
+
* no-op — the Rust engine already holds the parsed catalog in memory.
|
|
306
|
+
*
|
|
307
|
+
* Story 37.9: this replaces the old `#catalogsJson()` which serialized the
|
|
308
|
+
* entire catalog on EVERY `t()` call.
|
|
309
|
+
*/
|
|
310
|
+
#syncNativeEngine() {
|
|
311
|
+
if (!this.#nativeEngine)
|
|
312
|
+
return;
|
|
313
|
+
if (!this.#catalogsCacheDirty && this.#catalogsCache)
|
|
314
|
+
return;
|
|
315
|
+
const cache = {};
|
|
316
|
+
for (const [locale, catalog] of this.#messages.entries()) {
|
|
317
|
+
cache[locale] = catalog;
|
|
318
|
+
}
|
|
319
|
+
// loadCatalogs may throw (malformed JSON, lock poisoned). The dirty flag
|
|
320
|
+
// is only cleared AFTER a successful load so a retry on the next t()
|
|
321
|
+
// call re-attempts instead of silently using a stale/empty catalog.
|
|
322
|
+
this.#nativeEngine.loadCatalogs(JSON.stringify(cache));
|
|
323
|
+
this.#catalogsCache = cache;
|
|
324
|
+
this.#catalogsCacheDirty = false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function normalizeLocale(locale) {
|
|
328
|
+
return locale.trim().replace(/_/g, "-").toLowerCase();
|
|
329
|
+
}
|
|
330
|
+
function normalizeFallbackMap(map) {
|
|
331
|
+
const normalized = {};
|
|
332
|
+
for (const [from, to] of Object.entries(map)) {
|
|
333
|
+
normalized[normalizeLocale(from)] = normalizeLocale(to);
|
|
334
|
+
}
|
|
335
|
+
return normalized;
|
|
336
|
+
}
|
|
337
|
+
function parseAcceptLanguage(header) {
|
|
338
|
+
return header
|
|
339
|
+
.split(",")
|
|
340
|
+
.map((item) => item.trim())
|
|
341
|
+
.filter(Boolean)
|
|
342
|
+
.map((item) => {
|
|
343
|
+
const [localePart, ...rest] = item.split(";");
|
|
344
|
+
let quality = 1;
|
|
345
|
+
for (const part of rest) {
|
|
346
|
+
const trimmed = part.trim();
|
|
347
|
+
if (trimmed.startsWith("q=")) {
|
|
348
|
+
const n = Number(trimmed.slice(2));
|
|
349
|
+
if (Number.isFinite(n))
|
|
350
|
+
quality = n;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return { locale: normalizeLocale(localePart), quality };
|
|
354
|
+
})
|
|
355
|
+
.filter((entry) => entry.locale.length > 0)
|
|
356
|
+
.sort((a, b) => b.quality - a.quality)
|
|
357
|
+
.map((entry) => entry.locale);
|
|
358
|
+
}
|
|
359
|
+
function flattenMessages(messages) {
|
|
360
|
+
const out = {};
|
|
361
|
+
walkFlatten("", messages, out);
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
function walkFlatten(prefix, value, out) {
|
|
365
|
+
if (typeof value === "string") {
|
|
366
|
+
out[prefix] = value;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
370
|
+
out[prefix] = String(value);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (value === null || value === undefined) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
for (const [key, child] of Object.entries(value)) {
|
|
377
|
+
const next = prefix ? `${prefix}.${key}` : key;
|
|
378
|
+
walkFlatten(next, child, out);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=Rosetta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rosetta.js","sourceRoot":"","sources":["../src/Rosetta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type RosettaOptions } from "./Rosetta.js";
|
|
2
|
+
/**
|
|
3
|
+
* Duck-typed host context — rosetta stays publishable without
|
|
4
|
+
* importing `@c9up/ream`. Any framework that exposes a Container + a
|
|
5
|
+
* config store satisfies the contract.
|
|
6
|
+
*/
|
|
7
|
+
interface RosettaContainer {
|
|
8
|
+
singleton(token: unknown, factory: () => unknown): void;
|
|
9
|
+
resolve<T = unknown>(token: unknown): T;
|
|
10
|
+
}
|
|
11
|
+
interface RosettaConfigStore {
|
|
12
|
+
get<T = unknown>(key: string): T | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface RosettaAppContext {
|
|
15
|
+
container: RosettaContainer;
|
|
16
|
+
config: RosettaConfigStore;
|
|
17
|
+
}
|
|
18
|
+
export interface RosettaProviderConfig extends RosettaOptions {
|
|
19
|
+
/**
|
|
20
|
+
* If set, a `FileSystemLoader` reading from this directory is
|
|
21
|
+
* appended to `options.loaders`. Most apps only need this and the
|
|
22
|
+
* `supportedLocales` list — the provider's `boot()` calls
|
|
23
|
+
* `rosetta.boot()` so catalogs are warm before the first request.
|
|
24
|
+
*/
|
|
25
|
+
rootDir?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* RosettaProvider — registers a shared `Rosetta` instance under
|
|
29
|
+
* `Rosetta` + `"i18n"` tokens, then awaits its boot so the configured
|
|
30
|
+
* locale catalogs are loaded by the time the first request lands.
|
|
31
|
+
*
|
|
32
|
+
* // reamrc.ts
|
|
33
|
+
* providers: [() => import('@c9up/rosetta/provider')]
|
|
34
|
+
*
|
|
35
|
+
* // config/i18n.ts
|
|
36
|
+
* import { resolve } from 'node:path'
|
|
37
|
+
* export default {
|
|
38
|
+
* defaultLocale: 'en',
|
|
39
|
+
* supportedLocales: ['en', 'fr'],
|
|
40
|
+
* rootDir: resolve('./resources/lang'),
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* // anywhere
|
|
44
|
+
* import i18n from '@c9up/rosetta/services/main'
|
|
45
|
+
* const t = i18n.locale('fr').t('greeting', { name: 'Alice' })
|
|
46
|
+
*/
|
|
47
|
+
export default class RosettaProvider {
|
|
48
|
+
protected app: RosettaAppContext;
|
|
49
|
+
constructor(app: RosettaAppContext);
|
|
50
|
+
register(): void;
|
|
51
|
+
boot(): Promise<void>;
|
|
52
|
+
shutdown(): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=RosettaProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RosettaProvider.d.ts","sourceRoot":"","sources":["../src/RosettaProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAG5D;;;;GAIG;AACH,UAAU,gBAAgB;IACzB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC;CACxC;AACD,UAAU,kBAAkB;IAC3B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AACD,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,MAAM,EAAE,kBAAkB,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAsB,SAAQ,cAAc;IAC5D;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IACvB,SAAS,CAAC,GAAG,EAAE,iBAAiB;gBAAtB,GAAG,EAAE,iBAAiB;IAE5C,QAAQ,IAAI,IAAI;IAeV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { FileSystemLoader } from "./loaders/FileSystemLoader.js";
|
|
2
|
+
import { Rosetta } from "./Rosetta.js";
|
|
3
|
+
import { setI18n } from "./services/main.js";
|
|
4
|
+
/**
|
|
5
|
+
* RosettaProvider — registers a shared `Rosetta` instance under
|
|
6
|
+
* `Rosetta` + `"i18n"` tokens, then awaits its boot so the configured
|
|
7
|
+
* locale catalogs are loaded by the time the first request lands.
|
|
8
|
+
*
|
|
9
|
+
* // reamrc.ts
|
|
10
|
+
* providers: [() => import('@c9up/rosetta/provider')]
|
|
11
|
+
*
|
|
12
|
+
* // config/i18n.ts
|
|
13
|
+
* import { resolve } from 'node:path'
|
|
14
|
+
* export default {
|
|
15
|
+
* defaultLocale: 'en',
|
|
16
|
+
* supportedLocales: ['en', 'fr'],
|
|
17
|
+
* rootDir: resolve('./resources/lang'),
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* // anywhere
|
|
21
|
+
* import i18n from '@c9up/rosetta/services/main'
|
|
22
|
+
* const t = i18n.locale('fr').t('greeting', { name: 'Alice' })
|
|
23
|
+
*/
|
|
24
|
+
export default class RosettaProvider {
|
|
25
|
+
app;
|
|
26
|
+
constructor(app) {
|
|
27
|
+
this.app = app;
|
|
28
|
+
}
|
|
29
|
+
register() {
|
|
30
|
+
this.app.container.singleton(Rosetta, () => {
|
|
31
|
+
const config = this.app.config.get("i18n");
|
|
32
|
+
const options = { ...(config ?? {}) };
|
|
33
|
+
if (config?.rootDir) {
|
|
34
|
+
const fsLoader = new FileSystemLoader({ rootDir: config.rootDir });
|
|
35
|
+
options.loaders = [...(options.loaders ?? []), fsLoader];
|
|
36
|
+
}
|
|
37
|
+
return new Rosetta(options);
|
|
38
|
+
});
|
|
39
|
+
this.app.container.singleton("i18n", () => this.app.container.resolve(Rosetta));
|
|
40
|
+
}
|
|
41
|
+
async boot() {
|
|
42
|
+
const rosetta = this.app.container.resolve(Rosetta);
|
|
43
|
+
await rosetta.boot();
|
|
44
|
+
setI18n(rosetta);
|
|
45
|
+
}
|
|
46
|
+
async shutdown() { }
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=RosettaProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RosettaProvider.js","sourceRoot":"","sources":["../src/RosettaProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,OAAO,EAAuB,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AA6B7C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IACb;IAAtB,YAAsB,GAAsB;QAAtB,QAAG,GAAH,GAAG,CAAmB;IAAG,CAAC;IAEhD,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAwB,MAAM,CAAC,CAAC;YAClE,MAAM,OAAO,GAAmB,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YACtD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACrB,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;gBACnE,OAAO,CAAC,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;YAC1D,CAAC;YACD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CACzC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAU,OAAO,CAAC,CAC5C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACT,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAU,OAAO,CAAC,CAAC;QAC7D,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,QAAQ,KAAmB,CAAC;CAClC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @c9up/rosetta — framework-agnostic internationalization module.
|
|
3
|
+
*/
|
|
4
|
+
export type { FileSystemLoaderOptions } from "./loaders/FileSystemLoader.js";
|
|
5
|
+
export { FileSystemLoader } from "./loaders/FileSystemLoader.js";
|
|
6
|
+
export { isNativeAvailable } from "./native.js";
|
|
7
|
+
export type { LocaleResolverInput, MessageCatalog, MessageTree, NumberFormatData, RosettaLoader, RosettaLocale, RosettaOptions, TranslateOptions, TranslationParams, } from "./Rosetta.js";
|
|
8
|
+
export { Rosetta } from "./Rosetta.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,YAAY,EACX,mBAAmB,EACnB,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @c9up/rosetta — framework-agnostic internationalization module.
|
|
3
|
+
*/
|
|
4
|
+
export { FileSystemLoader } from "./loaders/FileSystemLoader.js";
|
|
5
|
+
export { isNativeAvailable } from "./native.js";
|
|
6
|
+
export { Rosetta } from "./Rosetta.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAYhD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MessageCatalog, MessageTree, RosettaLoader } from "../Rosetta.js";
|
|
2
|
+
export interface FileSystemLoaderOptions {
|
|
3
|
+
rootDir: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class FileSystemLoader implements RosettaLoader {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(options: FileSystemLoaderOptions);
|
|
8
|
+
load(locale: string): Promise<MessageTree | MessageCatalog | null>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=FileSystemLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSystemLoader.d.ts","sourceRoot":"","sources":["../../src/loaders/FileSystemLoader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAKhF,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,gBAAiB,YAAW,aAAa;;gBAGzC,OAAO,EAAE,uBAAuB;IAItC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,cAAc,GAAG,IAAI,CAAC;CA6BxE"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as fsp from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
/** IANA BCP 47 locale ID pattern — allows `en`, `en-US`, `zh-Hant-TW`, etc. */
|
|
4
|
+
const SAFE_LOCALE_PATTERN = /^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{2,8})*$/;
|
|
5
|
+
export class FileSystemLoader {
|
|
6
|
+
#rootDir;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.#rootDir = options.rootDir;
|
|
9
|
+
}
|
|
10
|
+
async load(locale) {
|
|
11
|
+
// Validate locale to prevent path traversal. A locale like
|
|
12
|
+
// `../../etc/passwd` would escape the rootDir without this check.
|
|
13
|
+
if (!SAFE_LOCALE_PATTERN.test(locale))
|
|
14
|
+
return null;
|
|
15
|
+
for (const ext of ["json", "yaml", "yml"]) {
|
|
16
|
+
const fullPath = path.join(this.#rootDir, `${locale}.${ext}`);
|
|
17
|
+
// Defense-in-depth: verify the resolved path is still inside rootDir
|
|
18
|
+
// even after the regex check (belt + suspenders).
|
|
19
|
+
if (!path.resolve(fullPath).startsWith(path.resolve(this.#rootDir)))
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
const raw = await fsp.readFile(fullPath, "utf8");
|
|
23
|
+
return ext === "json"
|
|
24
|
+
? JSON.parse(raw)
|
|
25
|
+
: parseYaml(raw);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
// ENOENT = file doesn't exist → try next extension. Any other error
|
|
29
|
+
// (permission denied, invalid JSON, disk failure) is a real problem
|
|
30
|
+
// that must surface — silently swallowing it would turn a config bug
|
|
31
|
+
// into a "missing translation" ghost that's impossible to debug in prod.
|
|
32
|
+
const code = err?.code;
|
|
33
|
+
if (code === "ENOENT")
|
|
34
|
+
continue;
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Minimal YAML parser for locale files. Supports:
|
|
43
|
+
* - Nested keys via indentation
|
|
44
|
+
* - Quoted and unquoted scalar values
|
|
45
|
+
* - Colons in values (URLs like `https://example.com`)
|
|
46
|
+
* - Multiline block scalars: `|` (literal) and `>` (folded)
|
|
47
|
+
* - Comments (`#`)
|
|
48
|
+
*
|
|
49
|
+
* Does NOT support: anchors/aliases (`&`/`*`), flow sequences (`[a, b]`),
|
|
50
|
+
* flow mappings (`{a: 1}`), complex keys, merge keys (`<<`), or tags (`!!`).
|
|
51
|
+
* For production apps with complex locale files, replace with `js-yaml`.
|
|
52
|
+
*/
|
|
53
|
+
function parseYaml(input) {
|
|
54
|
+
const root = {};
|
|
55
|
+
const stack = [
|
|
56
|
+
{ indent: -1, node: root },
|
|
57
|
+
];
|
|
58
|
+
const lines = input.split(/\r?\n/);
|
|
59
|
+
let i = 0;
|
|
60
|
+
while (i < lines.length) {
|
|
61
|
+
const rawLine = lines[i];
|
|
62
|
+
i++;
|
|
63
|
+
if (!rawLine.trim() || rawLine.trim().startsWith("#"))
|
|
64
|
+
continue;
|
|
65
|
+
// Match `key:` with an optional value. The key stops at the first unquoted
|
|
66
|
+
// colon that is NOT inside a URL scheme (`://`). We use a simpler heuristic:
|
|
67
|
+
// split on the first `:` followed by a space or end-of-line. This correctly
|
|
68
|
+
// handles `url: https://example.com` because the `: ` after `url` is the
|
|
69
|
+
// first split point, and `://` inside the value has no preceding space.
|
|
70
|
+
const match = rawLine.match(/^(\s*)([^:#\s][^:]*?)\s*:\s*(.*)$/);
|
|
71
|
+
if (!match)
|
|
72
|
+
continue;
|
|
73
|
+
const indent = match[1].length;
|
|
74
|
+
const key = match[2].trim();
|
|
75
|
+
let valueRaw = match[3].trim();
|
|
76
|
+
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
|
77
|
+
stack.pop();
|
|
78
|
+
}
|
|
79
|
+
const parent = stack[stack.length - 1].node;
|
|
80
|
+
// Block scalar: `|` (literal) or `>` (folded).
|
|
81
|
+
if (valueRaw === "|" || valueRaw === ">") {
|
|
82
|
+
const block = parseBlockScalar(lines, i, indent, valueRaw === ">");
|
|
83
|
+
parent[key] = block.value;
|
|
84
|
+
i = block.nextIndex;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!valueRaw) {
|
|
88
|
+
const child = {};
|
|
89
|
+
parent[key] = child;
|
|
90
|
+
stack.push({ indent, node: child });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Strip inline comments: `value # comment` → `value`. But only if the `#`
|
|
94
|
+
// is preceded by whitespace (to avoid stripping inside URLs like `#fragment`).
|
|
95
|
+
const commentIdx = valueRaw.search(/\s+#/);
|
|
96
|
+
if (commentIdx > 0)
|
|
97
|
+
valueRaw = valueRaw.slice(0, commentIdx).trim();
|
|
98
|
+
parent[key] = parseScalar(valueRaw);
|
|
99
|
+
}
|
|
100
|
+
return root;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Consume a `|` (literal) or `>` (folded) block scalar starting at `startIndex`.
|
|
104
|
+
* Returns the joined value and the index of the first line past the block.
|
|
105
|
+
*/
|
|
106
|
+
function parseBlockScalar(lines, startIndex, indent, fold) {
|
|
107
|
+
const blockLines = [];
|
|
108
|
+
const blockIndent = indent + 2;
|
|
109
|
+
let i = startIndex;
|
|
110
|
+
while (i < lines.length) {
|
|
111
|
+
const bl = lines[i];
|
|
112
|
+
// A line with less indent (or empty after content) ends the block.
|
|
113
|
+
if (bl.trim() && bl.search(/\S/) < blockIndent)
|
|
114
|
+
break;
|
|
115
|
+
blockLines.push(bl.slice(blockIndent) || "");
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
// Trim trailing empty lines.
|
|
119
|
+
while (blockLines.length > 0 && blockLines[blockLines.length - 1] === "") {
|
|
120
|
+
blockLines.pop();
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
value: fold ? foldParagraphs(blockLines) : blockLines.join("\n"),
|
|
124
|
+
nextIndex: i,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Fold a `>` block scalar: consecutive non-blank lines join with a space, blank
|
|
129
|
+
* lines become hard newlines (paragraph breaks per the YAML spec).
|
|
130
|
+
*/
|
|
131
|
+
function foldParagraphs(blockLines) {
|
|
132
|
+
const paragraphs = [];
|
|
133
|
+
let current = [];
|
|
134
|
+
for (const bl of blockLines) {
|
|
135
|
+
if (bl === "") {
|
|
136
|
+
if (current.length > 0)
|
|
137
|
+
paragraphs.push(current.join(" "));
|
|
138
|
+
current = [];
|
|
139
|
+
paragraphs.push("");
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
current.push(bl);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (current.length > 0)
|
|
146
|
+
paragraphs.push(current.join(" "));
|
|
147
|
+
return paragraphs.join("\n");
|
|
148
|
+
}
|
|
149
|
+
function parseScalar(value) {
|
|
150
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
151
|
+
// Double-quoted: process YAML escape sequences (\n, \t, \\, \', \")
|
|
152
|
+
return value
|
|
153
|
+
.slice(1, -1)
|
|
154
|
+
.replace(/\\n/g, "\n")
|
|
155
|
+
.replace(/\\t/g, "\t")
|
|
156
|
+
.replace(/\\\\/g, "\\")
|
|
157
|
+
.replace(/\\'/g, "'")
|
|
158
|
+
.replace(/\\"/g, '"');
|
|
159
|
+
}
|
|
160
|
+
if (value.startsWith("'") && value.endsWith("'")) {
|
|
161
|
+
// Single-quoted: only '' escape (doubled apostrophe → single)
|
|
162
|
+
return value.slice(1, -1).replace(/''/g, "'");
|
|
163
|
+
}
|
|
164
|
+
if (value === "true")
|
|
165
|
+
return true;
|
|
166
|
+
if (value === "false")
|
|
167
|
+
return false;
|
|
168
|
+
if (value === "null")
|
|
169
|
+
return null;
|
|
170
|
+
const n = Number(value);
|
|
171
|
+
if (Number.isFinite(n) && value !== "")
|
|
172
|
+
return n;
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=FileSystemLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSystemLoader.js","sourceRoot":"","sources":["../../src/loaders/FileSystemLoader.ts"],"names":[],"mappings":"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"}
|
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal engine loader — auto-detects Node (NAPI) vs Browser (WASM).
|
|
3
|
+
*
|
|
4
|
+
* Node gets the stateful `RosettaEngine` class (resident catalog in Rust memory).
|
|
5
|
+
* Browser gets the stateless WASM functions (re-parse catalog per call — acceptable
|
|
6
|
+
* because browser apps typically have smaller catalogs than server-rendered i18n).
|
|
7
|
+
*/
|
|
8
|
+
export interface NativeRosettaEngine {
|
|
9
|
+
loadCatalogs(catalogsJson: string): void;
|
|
10
|
+
translate(key: string, paramsJson: string | undefined, chainJson: string, defaultValue: string | undefined): string;
|
|
11
|
+
has(key: string, chainJson: string): boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function isNativeAvailable(): boolean;
|
|
14
|
+
/** Create a stateful engine (Node NAPI only). Returns null in browser (WASM is stateless). */
|
|
15
|
+
export declare function createNativeEngine(): NativeRosettaEngine | null;
|
|
16
|
+
export declare function nativeTranslate(catalogsJson: string, key: string, paramsJson: string | undefined, chainJson: string, defaultValue: string | undefined): string;
|
|
17
|
+
export declare function nativeHas(catalogsJson: string, key: string, chainJson: string): boolean;
|
|
18
|
+
//# sourceMappingURL=native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,mBAAmB;IACnC,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,SAAS,CACR,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,CAAC;IACV,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAuED,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,8FAA8F;AAC9F,wBAAgB,kBAAkB,IAAI,mBAAmB,GAAG,IAAI,CAwB/D;AAGD,wBAAgB,eAAe,CAC9B,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,CAqBR;AAED,wBAAgB,SAAS,CACxB,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GACf,OAAO,CAQT"}
|
package/dist/native.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal engine loader — auto-detects Node (NAPI) vs Browser (WASM).
|
|
3
|
+
*
|
|
4
|
+
* Node gets the stateful `RosettaEngine` class (resident catalog in Rust memory).
|
|
5
|
+
* Browser gets the stateless WASM functions (re-parse catalog per call — acceptable
|
|
6
|
+
* because browser apps typically have smaller catalogs than server-rendered i18n).
|
|
7
|
+
*/
|
|
8
|
+
let native;
|
|
9
|
+
let wasmModule;
|
|
10
|
+
let loadError;
|
|
11
|
+
const isNode = typeof globalThis.process !== "undefined" &&
|
|
12
|
+
typeof globalThis.process.versions?.node === "string";
|
|
13
|
+
if (isNode) {
|
|
14
|
+
try {
|
|
15
|
+
const { createRequire } = await import("node:module");
|
|
16
|
+
const { dirname, join } = await import("node:path");
|
|
17
|
+
const { fileURLToPath } = await import("node:url");
|
|
18
|
+
const { arch, platform } = await import("node:process");
|
|
19
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
20
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const platformMap = {
|
|
22
|
+
"linux-x64": "linux-x64-gnu",
|
|
23
|
+
"linux-arm64": "linux-arm64-gnu",
|
|
24
|
+
"darwin-x64": "darwin-x64",
|
|
25
|
+
"darwin-arm64": "darwin-arm64",
|
|
26
|
+
"win32-x64": "win32-x64-msvc",
|
|
27
|
+
};
|
|
28
|
+
const suffix = platformMap[`${platform}-${arch}`];
|
|
29
|
+
if (suffix) {
|
|
30
|
+
native = nodeRequire(join(currentDir, `../index.${suffix}.node`));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
loadError = e;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
try {
|
|
39
|
+
// The WASM glue generated by wasm-pack exports `translate` and `has` as
|
|
40
|
+
// top-level functions with the exact same signatures as `WasmStatelessApi`.
|
|
41
|
+
// We declare the import shape explicitly so no cast is needed.
|
|
42
|
+
const wasm = await import("../wasm/rosetta_engine_wasm.js");
|
|
43
|
+
await wasm.default();
|
|
44
|
+
wasmModule = wasm;
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
loadError = e;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function isNativeAvailable() {
|
|
51
|
+
return native !== undefined || wasmModule !== undefined;
|
|
52
|
+
}
|
|
53
|
+
/** Create a stateful engine (Node NAPI only). Returns null in browser (WASM is stateless). */
|
|
54
|
+
export function createNativeEngine() {
|
|
55
|
+
if (!native) {
|
|
56
|
+
if (!wasmModule)
|
|
57
|
+
return null;
|
|
58
|
+
const wasm = wasmModule;
|
|
59
|
+
let catalogsJson = "{}";
|
|
60
|
+
return {
|
|
61
|
+
loadCatalogs(json) {
|
|
62
|
+
catalogsJson = json;
|
|
63
|
+
},
|
|
64
|
+
translate(key, paramsJson, chainJson, defaultValue) {
|
|
65
|
+
return wasm.translate(catalogsJson, key, paramsJson ?? undefined, chainJson, defaultValue ?? undefined);
|
|
66
|
+
},
|
|
67
|
+
has(key, chainJson) {
|
|
68
|
+
return wasm.has(catalogsJson, key, chainJson);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return new native.RosettaEngine();
|
|
73
|
+
}
|
|
74
|
+
// Legacy stateless exports (used by tests)
|
|
75
|
+
export function nativeTranslate(catalogsJson, key, paramsJson, chainJson, defaultValue) {
|
|
76
|
+
if (native)
|
|
77
|
+
return native.translate(catalogsJson, key, paramsJson, chainJson, defaultValue);
|
|
78
|
+
if (wasmModule)
|
|
79
|
+
return wasmModule.translate(catalogsJson, key, paramsJson, chainJson, defaultValue);
|
|
80
|
+
throw new Error(`[ROSETTA_ENGINE_NOT_FOUND] ${loadError ?? "binary not found"}`, { cause: loadError });
|
|
81
|
+
}
|
|
82
|
+
export function nativeHas(catalogsJson, key, chainJson) {
|
|
83
|
+
if (native)
|
|
84
|
+
return native.has(catalogsJson, key, chainJson);
|
|
85
|
+
if (wasmModule)
|
|
86
|
+
return wasmModule.has(catalogsJson, key, chainJson);
|
|
87
|
+
throw new Error(`[ROSETTA_ENGINE_NOT_FOUND] ${loadError ?? "binary not found"}`, { cause: loadError });
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default `Rosetta` singleton — mirror of Adonis's
|
|
3
|
+
* `import i18n from '@adonisjs/i18n/services/main'` shape.
|
|
4
|
+
*
|
|
5
|
+
* import i18n from '@c9up/rosetta/services/main'
|
|
6
|
+
*
|
|
7
|
+
* const locale = i18n.locale(i18n.resolveLocale({ header: req.headers['accept-language'] }))
|
|
8
|
+
* locale.t('greeting', { name: user.name })
|
|
9
|
+
*
|
|
10
|
+
* Populated by `RosettaProvider.boot()` or by the app directly through
|
|
11
|
+
* `setI18n(myRosetta)` when the i18n config has to be built outside
|
|
12
|
+
* the provider flow.
|
|
13
|
+
*/
|
|
14
|
+
import type { Rosetta } from "../Rosetta.js";
|
|
15
|
+
/** @internal Bind the singleton (called by RosettaProvider or the app). */
|
|
16
|
+
export declare function setI18n(value: Rosetta): void;
|
|
17
|
+
/** @internal Read the singleton (or `undefined` pre-boot). */
|
|
18
|
+
export declare function getI18n(): Rosetta | undefined;
|
|
19
|
+
declare const i18n: Rosetta;
|
|
20
|
+
export default i18n;
|
|
21
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7C,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE5C;AAED,8DAA8D;AAC9D,wBAAgB,OAAO,IAAI,OAAO,GAAG,SAAS,CAE7C;AAED,QAAA,MAAM,IAAI,EAAE,OAWV,CAAC;AAEH,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default `Rosetta` singleton — mirror of Adonis's
|
|
3
|
+
* `import i18n from '@adonisjs/i18n/services/main'` shape.
|
|
4
|
+
*
|
|
5
|
+
* import i18n from '@c9up/rosetta/services/main'
|
|
6
|
+
*
|
|
7
|
+
* const locale = i18n.locale(i18n.resolveLocale({ header: req.headers['accept-language'] }))
|
|
8
|
+
* locale.t('greeting', { name: user.name })
|
|
9
|
+
*
|
|
10
|
+
* Populated by `RosettaProvider.boot()` or by the app directly through
|
|
11
|
+
* `setI18n(myRosetta)` when the i18n config has to be built outside
|
|
12
|
+
* the provider flow.
|
|
13
|
+
*/
|
|
14
|
+
let instance;
|
|
15
|
+
/** @internal Bind the singleton (called by RosettaProvider or the app). */
|
|
16
|
+
export function setI18n(value) {
|
|
17
|
+
instance = value;
|
|
18
|
+
}
|
|
19
|
+
/** @internal Read the singleton (or `undefined` pre-boot). */
|
|
20
|
+
export function getI18n() {
|
|
21
|
+
return instance;
|
|
22
|
+
}
|
|
23
|
+
const i18n = new Proxy({}, {
|
|
24
|
+
get(_target, prop) {
|
|
25
|
+
if (!instance) {
|
|
26
|
+
throw new Error("[rosetta] Rosetta singleton accessed before RosettaProvider.boot() ran " +
|
|
27
|
+
"or `setI18n(myRosetta)` was called. Wire one of them first.");
|
|
28
|
+
}
|
|
29
|
+
const value = Reflect.get(instance, prop, instance);
|
|
30
|
+
return typeof value === "function" ? value.bind(instance) : value;
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
export default i18n;
|
|
34
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/services/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,IAAI,QAA6B,CAAC;AAElC,2EAA2E;AAC3E,MAAM,UAAU,OAAO,CAAC,KAAc;IACrC,QAAQ,GAAG,KAAK,CAAC;AAClB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,OAAO;IACtB,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED,MAAM,IAAI,GAAY,IAAI,KAAK,CAAC,EAAa,EAAE;IAC9C,GAAG,CAAC,OAAO,EAAE,IAAI;QAChB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACd,yEAAyE;gBACxE,6DAA6D,CAC9D,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACpD,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnE,CAAC;CACD,CAAC,CAAC;AAEH,eAAe,IAAI,CAAC"}
|
package/index.darwin-arm64.node
CHANGED
|
Binary file
|
package/index.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/src/Rosetta.ts
CHANGED
|
@@ -287,7 +287,15 @@ export class Rosetta {
|
|
|
287
287
|
);
|
|
288
288
|
}
|
|
289
289
|
this.#syncNativeEngine();
|
|
290
|
-
|
|
290
|
+
// `bigint` makes a plain `JSON.stringify` THROW, crashing `t()` — serialise
|
|
291
|
+
// it as its decimal string so it survives the boundary and renders/pluralises
|
|
292
|
+
// correctly (RO7). `Date` already serialises to an ISO string via `toJSON`,
|
|
293
|
+
// which the engine's date/time formatter now slices.
|
|
294
|
+
const paramsJson = params
|
|
295
|
+
? JSON.stringify(params, (_k, v) =>
|
|
296
|
+
typeof v === "bigint" ? v.toString() : v,
|
|
297
|
+
)
|
|
298
|
+
: undefined;
|
|
291
299
|
return this.#nativeEngine.translate(
|
|
292
300
|
key,
|
|
293
301
|
paramsJson,
|