@better-translate/core 1.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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/chunk-PE2HFT6R.js +397 -0
- package/dist/core.d.ts +161 -0
- package/dist/core.js +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.js +53 -0
- package/dist/types.d.ts +232 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jorge Alvarenga
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# @better-translate/core
|
|
2
|
+
|
|
3
|
+
`@better-translate/core` is the framework-agnostic translation engine for Better Translate. Use it in plain TypeScript, servers, APIs, scripts, or as the base package for every adapter.
|
|
4
|
+
|
|
5
|
+
Full docs: [better-translate-placeholder.com/en/docs/adapters/core](https://better-translate-placeholder.com/en/docs/adapters/core)
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// src/validation.ts
|
|
2
|
+
function isTranslationConfigOptions(value) {
|
|
3
|
+
return "availableLocales" in value && "defaultLocale" in value && "messages" in value;
|
|
4
|
+
}
|
|
5
|
+
function isTranslationMessages(value) {
|
|
6
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
return Object.values(value).every((entry) => {
|
|
10
|
+
if (entry === void 0 || typeof entry === "string") {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
return isTranslationMessages(entry);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/create-translation-json-schema.ts
|
|
18
|
+
function createSchemaNode(messages) {
|
|
19
|
+
const properties = {};
|
|
20
|
+
for (const [key, value] of Object.entries(messages)) {
|
|
21
|
+
properties[key] = typeof value === "string" ? { type: "string" } : createSchemaNode(value);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
type: "object",
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
required: Object.keys(messages),
|
|
27
|
+
properties
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function createTranslationJsonSchema(sourceMessages) {
|
|
31
|
+
if (!isTranslationMessages(sourceMessages)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"createTranslationJsonSchema(...) requires a valid translation object."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
38
|
+
...createSchemaNode(sourceMessages)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/interpolate-message.ts
|
|
43
|
+
var PLACEHOLDER_PATTERN = /\{([^{}]+)\}/g;
|
|
44
|
+
function interpolateMessage(message, key, params) {
|
|
45
|
+
const missingParams = /* @__PURE__ */ new Set();
|
|
46
|
+
const interpolatedMessage = message.replace(
|
|
47
|
+
PLACEHOLDER_PATTERN,
|
|
48
|
+
(match, placeholderName) => {
|
|
49
|
+
if (params && Object.prototype.hasOwnProperty.call(params, placeholderName)) {
|
|
50
|
+
return String(params[placeholderName]);
|
|
51
|
+
}
|
|
52
|
+
missingParams.add(placeholderName);
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
for (const placeholderName of missingParams) {
|
|
57
|
+
console.warn(
|
|
58
|
+
`Missing translation param "{${placeholderName}}" for key "${key}".`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return interpolatedMessage;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/resolve-message-value.ts
|
|
65
|
+
function resolveMessageValue(messages, key) {
|
|
66
|
+
if (!messages) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
const parts = key.split(".");
|
|
70
|
+
let current = messages;
|
|
71
|
+
for (const part of parts) {
|
|
72
|
+
if (typeof current !== "object" || current === null) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
current = current[part];
|
|
76
|
+
}
|
|
77
|
+
return typeof current === "string" ? current : void 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/snapshot-languages.ts
|
|
81
|
+
function snapshotLanguages(languages) {
|
|
82
|
+
return Object.freeze(
|
|
83
|
+
languages.map(
|
|
84
|
+
(language) => Object.freeze({
|
|
85
|
+
...language
|
|
86
|
+
})
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/snapshot-messages.ts
|
|
92
|
+
function cloneTranslationNode(node) {
|
|
93
|
+
if (typeof node === "string" || node === void 0) {
|
|
94
|
+
return node;
|
|
95
|
+
}
|
|
96
|
+
const clonedEntries = Object.entries(node).map(([key, value]) => [
|
|
97
|
+
key,
|
|
98
|
+
cloneTranslationNode(value)
|
|
99
|
+
]);
|
|
100
|
+
return Object.freeze(Object.fromEntries(clonedEntries));
|
|
101
|
+
}
|
|
102
|
+
function snapshotMessages(messages) {
|
|
103
|
+
const snapshotEntries = Object.entries(messages).map(([locale, value]) => [
|
|
104
|
+
locale,
|
|
105
|
+
cloneTranslationNode(value)
|
|
106
|
+
]);
|
|
107
|
+
return Object.freeze(
|
|
108
|
+
Object.fromEntries(snapshotEntries)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/create-configured-translator.ts
|
|
113
|
+
function createConfiguredTranslator(config) {
|
|
114
|
+
const messageCache = {
|
|
115
|
+
...config.messages
|
|
116
|
+
};
|
|
117
|
+
const loaders = config.loaders;
|
|
118
|
+
const availableLanguages = config.languages;
|
|
119
|
+
const loadPromises = /* @__PURE__ */ new Map();
|
|
120
|
+
function resolveLocale(options) {
|
|
121
|
+
return options?.config?.locale ?? options?.locale ?? config.defaultLocale;
|
|
122
|
+
}
|
|
123
|
+
function resolveDirection(options) {
|
|
124
|
+
if (typeof options?.config?.rtl === "boolean") {
|
|
125
|
+
return options.config.rtl ? "rtl" : "ltr";
|
|
126
|
+
}
|
|
127
|
+
return config.directions[resolveLocale(options)] ?? "ltr";
|
|
128
|
+
}
|
|
129
|
+
const loadLocale2 = async (locale) => {
|
|
130
|
+
const cachedMessages = messageCache[locale];
|
|
131
|
+
if (cachedMessages) {
|
|
132
|
+
return cachedMessages;
|
|
133
|
+
}
|
|
134
|
+
const existingPromise = loadPromises.get(locale);
|
|
135
|
+
if (existingPromise) {
|
|
136
|
+
return existingPromise;
|
|
137
|
+
}
|
|
138
|
+
const loader = loaders[locale];
|
|
139
|
+
if (!loader) {
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
const pendingLoad = Promise.resolve(loader()).then((loadedMessages) => {
|
|
143
|
+
if (!isTranslationMessages(loadedMessages)) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Locale "${locale}" did not resolve to a valid translation object.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
messageCache[locale] = loadedMessages;
|
|
149
|
+
return loadedMessages;
|
|
150
|
+
});
|
|
151
|
+
loadPromises.set(locale, pendingLoad);
|
|
152
|
+
try {
|
|
153
|
+
return await pendingLoad;
|
|
154
|
+
} finally {
|
|
155
|
+
loadPromises.delete(locale);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const translate = ((...args) => {
|
|
159
|
+
const [key, options] = args;
|
|
160
|
+
const locale = resolveLocale(options);
|
|
161
|
+
const activeValue = resolveMessageValue(messageCache[locale], key);
|
|
162
|
+
if (typeof activeValue === "string") {
|
|
163
|
+
return interpolateMessage(activeValue, key, options?.params);
|
|
164
|
+
}
|
|
165
|
+
const fallbackValue = resolveMessageValue(
|
|
166
|
+
messageCache[config.fallbackLocale],
|
|
167
|
+
key
|
|
168
|
+
);
|
|
169
|
+
if (typeof fallbackValue === "string") {
|
|
170
|
+
return interpolateMessage(fallbackValue, key, options?.params);
|
|
171
|
+
}
|
|
172
|
+
return key;
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
defaultLocale: config.defaultLocale,
|
|
176
|
+
fallbackLocale: config.fallbackLocale,
|
|
177
|
+
supportedLocales: [
|
|
178
|
+
...config.supportedLocales
|
|
179
|
+
],
|
|
180
|
+
getDirection(options) {
|
|
181
|
+
return resolveDirection(options);
|
|
182
|
+
},
|
|
183
|
+
isRtl(options) {
|
|
184
|
+
return resolveDirection(options) === "rtl";
|
|
185
|
+
},
|
|
186
|
+
t: translate,
|
|
187
|
+
loadLocale: loadLocale2,
|
|
188
|
+
getSupportedLocales() {
|
|
189
|
+
return [...config.supportedLocales];
|
|
190
|
+
},
|
|
191
|
+
getAvailableLanguages() {
|
|
192
|
+
return snapshotLanguages(availableLanguages);
|
|
193
|
+
},
|
|
194
|
+
getMessages() {
|
|
195
|
+
return snapshotMessages(messageCache);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/global-store.ts
|
|
201
|
+
var GLOBAL_STORE_KEY = "__better_translate_core__";
|
|
202
|
+
function getGlobalStore() {
|
|
203
|
+
const globalScope = globalThis;
|
|
204
|
+
if (!globalScope[GLOBAL_STORE_KEY]) {
|
|
205
|
+
globalScope[GLOBAL_STORE_KEY] = {};
|
|
206
|
+
}
|
|
207
|
+
return globalScope[GLOBAL_STORE_KEY];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/normalize-config.ts
|
|
211
|
+
function createNormalizedDirections(locales, directions) {
|
|
212
|
+
return Object.fromEntries(
|
|
213
|
+
locales.map((locale) => [locale, directions?.[locale] ?? "ltr"])
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
function createDefaultLanguageMetadata(locale) {
|
|
217
|
+
return {
|
|
218
|
+
locale,
|
|
219
|
+
nativeLabel: locale,
|
|
220
|
+
shortLabel: locale.toUpperCase()
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function createNormalizedLanguages(locales, languages) {
|
|
224
|
+
const supportedLocaleSet = new Set(locales);
|
|
225
|
+
const seenLocales = /* @__PURE__ */ new Set();
|
|
226
|
+
const normalizedLanguages = [];
|
|
227
|
+
for (const language of languages ?? []) {
|
|
228
|
+
if (!supportedLocaleSet.has(language.locale)) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`The locale "${language.locale}" is present in languages but not in availableLocales.`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
if (seenLocales.has(language.locale)) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Duplicate locale "${language.locale}" found in languages config.`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
seenLocales.add(language.locale);
|
|
239
|
+
normalizedLanguages.push({
|
|
240
|
+
...language
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
for (const locale of locales) {
|
|
244
|
+
if (seenLocales.has(locale)) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
normalizedLanguages.push(createDefaultLanguageMetadata(locale));
|
|
248
|
+
}
|
|
249
|
+
return normalizedLanguages;
|
|
250
|
+
}
|
|
251
|
+
function normalizeConfig(input) {
|
|
252
|
+
if (!isTranslationConfigOptions(input)) {
|
|
253
|
+
const locales = Object.keys(input);
|
|
254
|
+
if (locales.length === 0) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
"configureTranslations(...) requires at least one locale."
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
const defaultLocale = locales[0];
|
|
260
|
+
return {
|
|
261
|
+
defaultLocale,
|
|
262
|
+
fallbackLocale: defaultLocale,
|
|
263
|
+
supportedLocales: locales,
|
|
264
|
+
directions: createNormalizedDirections(locales),
|
|
265
|
+
languages: createNormalizedLanguages(locales),
|
|
266
|
+
messages: input,
|
|
267
|
+
loaders: {}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const supportedLocales = [...input.availableLocales];
|
|
271
|
+
if (supportedLocales.length === 0) {
|
|
272
|
+
throw new Error("configureTranslations(...) requires at least one locale.");
|
|
273
|
+
}
|
|
274
|
+
if (!supportedLocales.includes(input.defaultLocale)) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`The default locale "${input.defaultLocale}" is not included in availableLocales.`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
if (input.fallbackLocale && !supportedLocales.includes(input.fallbackLocale)) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`The fallback locale "${input.fallbackLocale}" is not included in availableLocales.`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (!input.messages[input.defaultLocale]) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`Missing source messages for default locale "${input.defaultLocale}".`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
for (const locale of Object.keys(input.messages)) {
|
|
290
|
+
if (!supportedLocales.includes(locale)) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`The locale "${locale}" is present in messages but not in availableLocales.`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
for (const locale of Object.keys(input.loaders ?? {})) {
|
|
297
|
+
if (!supportedLocales.includes(locale)) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`The locale "${locale}" is present in loaders but not in availableLocales.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const locale of Object.keys(input.directions ?? {})) {
|
|
304
|
+
if (!supportedLocales.includes(locale)) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
`The locale "${locale}" is present in directions but not in availableLocales.`
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
defaultLocale: input.defaultLocale,
|
|
312
|
+
fallbackLocale: input.fallbackLocale ?? input.defaultLocale,
|
|
313
|
+
supportedLocales,
|
|
314
|
+
directions: createNormalizedDirections(supportedLocales, input.directions),
|
|
315
|
+
languages: createNormalizedLanguages(supportedLocales, input.languages),
|
|
316
|
+
messages: input.messages,
|
|
317
|
+
loaders: input.loaders ?? {}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/core.ts
|
|
322
|
+
async function configureTranslations(config) {
|
|
323
|
+
const normalizedConfig = normalizeConfig(config);
|
|
324
|
+
const translator = createConfiguredTranslator(normalizedConfig);
|
|
325
|
+
getGlobalStore().translator = translator;
|
|
326
|
+
return translator;
|
|
327
|
+
}
|
|
328
|
+
function createTranslationHelpers(input) {
|
|
329
|
+
if ("t" in input && "getMessages" in input && "getSupportedLocales" in input) {
|
|
330
|
+
const translator = input;
|
|
331
|
+
return {
|
|
332
|
+
translator,
|
|
333
|
+
t: translator.t,
|
|
334
|
+
loadLocale(locale) {
|
|
335
|
+
return translator.loadLocale(locale);
|
|
336
|
+
},
|
|
337
|
+
getSupportedLocales() {
|
|
338
|
+
return translator.getSupportedLocales();
|
|
339
|
+
},
|
|
340
|
+
getAvailableLanguages() {
|
|
341
|
+
return translator.getAvailableLanguages();
|
|
342
|
+
},
|
|
343
|
+
getDirection(options) {
|
|
344
|
+
return translator.getDirection(options);
|
|
345
|
+
},
|
|
346
|
+
isRtl(options) {
|
|
347
|
+
return translator.isRtl(options);
|
|
348
|
+
},
|
|
349
|
+
getMessages() {
|
|
350
|
+
return translator.getMessages();
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
return configureTranslations(input).then(
|
|
355
|
+
(translator) => createTranslationHelpers(translator)
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
function getTranslator() {
|
|
359
|
+
const translator = getGlobalStore().translator;
|
|
360
|
+
if (!translator) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
'Translations have not been configured. Call configureTranslations(...) before using "t(...)".'
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
return translator;
|
|
366
|
+
}
|
|
367
|
+
function t(key, options) {
|
|
368
|
+
return getTranslator().t(key, options);
|
|
369
|
+
}
|
|
370
|
+
async function loadLocale(locale) {
|
|
371
|
+
return getTranslator().loadLocale(locale);
|
|
372
|
+
}
|
|
373
|
+
function getSupportedLocales() {
|
|
374
|
+
return getTranslator().getSupportedLocales();
|
|
375
|
+
}
|
|
376
|
+
function getAvailableLanguages() {
|
|
377
|
+
return getTranslator().getAvailableLanguages();
|
|
378
|
+
}
|
|
379
|
+
function getMessages() {
|
|
380
|
+
return getTranslator().getMessages();
|
|
381
|
+
}
|
|
382
|
+
function resetTranslationsForTests() {
|
|
383
|
+
getGlobalStore().translator = void 0;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export {
|
|
387
|
+
createTranslationJsonSchema,
|
|
388
|
+
configureTranslations,
|
|
389
|
+
createTranslationHelpers,
|
|
390
|
+
getTranslator,
|
|
391
|
+
t,
|
|
392
|
+
loadLocale,
|
|
393
|
+
getSupportedLocales,
|
|
394
|
+
getAvailableLanguages,
|
|
395
|
+
getMessages,
|
|
396
|
+
resetTranslationsForTests
|
|
397
|
+
};
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnyConfiguredTranslator,
|
|
3
|
+
AnyTranslationHelpers,
|
|
4
|
+
CachedMessages,
|
|
5
|
+
ConfiguredTranslator,
|
|
6
|
+
DeepPartialMessages,
|
|
7
|
+
OptionsFormTranslator,
|
|
8
|
+
ShortFormTranslator,
|
|
9
|
+
StrictTranslationLocaleMap,
|
|
10
|
+
TranslateOptions,
|
|
11
|
+
TranslationConfigOptions,
|
|
12
|
+
TranslationHelpers,
|
|
13
|
+
TranslationLanguageMetadata,
|
|
14
|
+
TranslationJsonSchema,
|
|
15
|
+
TranslationLoader,
|
|
16
|
+
TranslationMessages,
|
|
17
|
+
} from "./types.js";
|
|
18
|
+
|
|
19
|
+
export type {
|
|
20
|
+
AnyTranslationHelpers,
|
|
21
|
+
CachedMessages,
|
|
22
|
+
ConfiguredTranslator,
|
|
23
|
+
DeepPartialMessages,
|
|
24
|
+
DeepStringify,
|
|
25
|
+
DotKeys,
|
|
26
|
+
TranslateCall,
|
|
27
|
+
TranslateFunction,
|
|
28
|
+
TranslationHelpers,
|
|
29
|
+
TranslationKey,
|
|
30
|
+
TranslationConfig,
|
|
31
|
+
TranslationParamValue,
|
|
32
|
+
TranslationParams,
|
|
33
|
+
TranslationParamsForKey,
|
|
34
|
+
TranslationPlaceholderNames,
|
|
35
|
+
TranslationDirection,
|
|
36
|
+
TranslationDirectionOptions,
|
|
37
|
+
TranslationLanguageMetadata,
|
|
38
|
+
TranslationValueAtKey,
|
|
39
|
+
TranslateOptions,
|
|
40
|
+
StrictTranslationLocaleMap,
|
|
41
|
+
TranslationJsonSchema,
|
|
42
|
+
TranslationJsonSchemaNode,
|
|
43
|
+
TranslationJsonObjectSchema,
|
|
44
|
+
TranslationJsonStringSchema,
|
|
45
|
+
TranslationKeysWithOptionalParams,
|
|
46
|
+
TranslationKeysWithRequiredParams,
|
|
47
|
+
TranslationLocaleMap,
|
|
48
|
+
TranslationConfigOptions,
|
|
49
|
+
TranslationLeaf,
|
|
50
|
+
TranslationLoader,
|
|
51
|
+
TranslationMessages,
|
|
52
|
+
} from "./types.js";
|
|
53
|
+
|
|
54
|
+
export declare function configureTranslations<
|
|
55
|
+
const TMessages extends Record<string, TranslationMessages>,
|
|
56
|
+
>(
|
|
57
|
+
messages: StrictTranslationLocaleMap<TMessages>,
|
|
58
|
+
): Promise<ShortFormTranslator<TMessages>>;
|
|
59
|
+
|
|
60
|
+
export declare function configureTranslations<
|
|
61
|
+
const TLocales extends readonly string[],
|
|
62
|
+
const TMessages extends Partial<
|
|
63
|
+
Record<TLocales[number], TranslationMessages>
|
|
64
|
+
>,
|
|
65
|
+
const TLoaders extends
|
|
66
|
+
| Partial<Record<TLocales[number], TranslationLoader<unknown>>>
|
|
67
|
+
| undefined = undefined,
|
|
68
|
+
const TDefaultLocale extends TLocales[number] = TLocales[number],
|
|
69
|
+
>(
|
|
70
|
+
config: TranslationConfigOptions<
|
|
71
|
+
TLocales,
|
|
72
|
+
TMessages,
|
|
73
|
+
TLoaders,
|
|
74
|
+
TDefaultLocale
|
|
75
|
+
>,
|
|
76
|
+
): Promise<OptionsFormTranslator<TLocales, TMessages, TDefaultLocale>>;
|
|
77
|
+
|
|
78
|
+
export declare function configureTranslations(
|
|
79
|
+
config:
|
|
80
|
+
| Record<string, TranslationMessages>
|
|
81
|
+
| TranslationConfigOptions<
|
|
82
|
+
readonly string[],
|
|
83
|
+
Partial<Record<string, TranslationMessages>>,
|
|
84
|
+
Partial<Record<string, TranslationLoader<unknown>>> | undefined,
|
|
85
|
+
string
|
|
86
|
+
>,
|
|
87
|
+
): Promise<AnyConfiguredTranslator>;
|
|
88
|
+
|
|
89
|
+
export declare function createTranslationHelpers<
|
|
90
|
+
TLocale extends string,
|
|
91
|
+
TSourceMessages extends TranslationMessages,
|
|
92
|
+
>(
|
|
93
|
+
translator: ConfiguredTranslator<TLocale, TSourceMessages>,
|
|
94
|
+
): TranslationHelpers<TLocale, TSourceMessages>;
|
|
95
|
+
|
|
96
|
+
export declare function createTranslationHelpers<
|
|
97
|
+
const TMessages extends Record<string, TranslationMessages>,
|
|
98
|
+
>(
|
|
99
|
+
messages: StrictTranslationLocaleMap<TMessages>,
|
|
100
|
+
): Promise<
|
|
101
|
+
TranslationHelpers<
|
|
102
|
+
Extract<keyof TMessages, string>,
|
|
103
|
+
ShortFormTranslator<TMessages> extends ConfiguredTranslator<
|
|
104
|
+
any,
|
|
105
|
+
infer TSourceMessages
|
|
106
|
+
>
|
|
107
|
+
? TSourceMessages
|
|
108
|
+
: TranslationMessages
|
|
109
|
+
>
|
|
110
|
+
>;
|
|
111
|
+
|
|
112
|
+
export declare function createTranslationHelpers<
|
|
113
|
+
const TLocales extends readonly string[],
|
|
114
|
+
const TMessages extends Partial<
|
|
115
|
+
Record<TLocales[number], TranslationMessages>
|
|
116
|
+
>,
|
|
117
|
+
const TLoaders extends
|
|
118
|
+
| Partial<Record<TLocales[number], TranslationLoader<unknown>>>
|
|
119
|
+
| undefined = undefined,
|
|
120
|
+
const TDefaultLocale extends TLocales[number] = TLocales[number],
|
|
121
|
+
>(
|
|
122
|
+
config: TranslationConfigOptions<
|
|
123
|
+
TLocales,
|
|
124
|
+
TMessages,
|
|
125
|
+
TLoaders,
|
|
126
|
+
TDefaultLocale
|
|
127
|
+
>,
|
|
128
|
+
): Promise<
|
|
129
|
+
TranslationHelpers<
|
|
130
|
+
TLocales[number],
|
|
131
|
+
Extract<TMessages[TDefaultLocale], TranslationMessages>
|
|
132
|
+
>
|
|
133
|
+
>;
|
|
134
|
+
|
|
135
|
+
export declare function createTranslationJsonSchema(
|
|
136
|
+
messages: TranslationMessages,
|
|
137
|
+
): TranslationJsonSchema;
|
|
138
|
+
|
|
139
|
+
export declare function getTranslator(): AnyConfiguredTranslator;
|
|
140
|
+
|
|
141
|
+
export declare function t(
|
|
142
|
+
key: string,
|
|
143
|
+
options?: TranslateOptions<string>,
|
|
144
|
+
): string;
|
|
145
|
+
|
|
146
|
+
export declare function loadLocale(
|
|
147
|
+
locale: string,
|
|
148
|
+
): Promise<
|
|
149
|
+
DeepPartialMessages<TranslationMessages> | TranslationMessages | undefined
|
|
150
|
+
>;
|
|
151
|
+
|
|
152
|
+
export declare function getSupportedLocales(): readonly string[];
|
|
153
|
+
|
|
154
|
+
export declare function getAvailableLanguages(): readonly TranslationLanguageMetadata<string>[];
|
|
155
|
+
|
|
156
|
+
export declare function getMessages(): CachedMessages<
|
|
157
|
+
string,
|
|
158
|
+
TranslationMessages
|
|
159
|
+
>;
|
|
160
|
+
|
|
161
|
+
export declare function resetTranslationsForTests(): void;
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configureTranslations,
|
|
3
|
+
createTranslationHelpers,
|
|
4
|
+
createTranslationJsonSchema,
|
|
5
|
+
getAvailableLanguages,
|
|
6
|
+
getMessages,
|
|
7
|
+
getSupportedLocales,
|
|
8
|
+
getTranslator,
|
|
9
|
+
loadLocale,
|
|
10
|
+
resetTranslationsForTests,
|
|
11
|
+
t
|
|
12
|
+
} from "./chunk-PE2HFT6R.js";
|
|
13
|
+
export {
|
|
14
|
+
configureTranslations,
|
|
15
|
+
createTranslationHelpers,
|
|
16
|
+
createTranslationJsonSchema,
|
|
17
|
+
getAvailableLanguages,
|
|
18
|
+
getMessages,
|
|
19
|
+
getSupportedLocales,
|
|
20
|
+
getTranslator,
|
|
21
|
+
loadLocale,
|
|
22
|
+
resetTranslationsForTests,
|
|
23
|
+
t
|
|
24
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./core.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configureTranslations,
|
|
3
|
+
createTranslationHelpers,
|
|
4
|
+
createTranslationJsonSchema,
|
|
5
|
+
getAvailableLanguages,
|
|
6
|
+
getMessages,
|
|
7
|
+
getSupportedLocales,
|
|
8
|
+
getTranslator,
|
|
9
|
+
loadLocale,
|
|
10
|
+
resetTranslationsForTests,
|
|
11
|
+
t
|
|
12
|
+
} from "./chunk-PE2HFT6R.js";
|
|
13
|
+
export {
|
|
14
|
+
configureTranslations,
|
|
15
|
+
createTranslationHelpers,
|
|
16
|
+
createTranslationJsonSchema,
|
|
17
|
+
getAvailableLanguages,
|
|
18
|
+
getMessages,
|
|
19
|
+
getSupportedLocales,
|
|
20
|
+
getTranslator,
|
|
21
|
+
loadLocale,
|
|
22
|
+
resetTranslationsForTests,
|
|
23
|
+
t
|
|
24
|
+
};
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ConfiguredTranslator, TranslationMessages } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export declare function setRequestLocale(locale: string | undefined): void;
|
|
4
|
+
|
|
5
|
+
export declare function getRequestLocale(): string | undefined;
|
|
6
|
+
|
|
7
|
+
export declare function resolveRequestLocale<
|
|
8
|
+
TLocale extends string,
|
|
9
|
+
TMessages extends TranslationMessages,
|
|
10
|
+
>(
|
|
11
|
+
translator: ConfiguredTranslator<TLocale, TMessages>,
|
|
12
|
+
options?: {
|
|
13
|
+
locale?: TLocale;
|
|
14
|
+
requestLocale?: string;
|
|
15
|
+
configLocale?: TLocale;
|
|
16
|
+
},
|
|
17
|
+
): TLocale;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/server.ts
|
|
2
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
var requestLocaleStorage = new AsyncLocalStorage();
|
|
5
|
+
var require2 = createRequire(import.meta.url);
|
|
6
|
+
function createRequestLocaleStore() {
|
|
7
|
+
return {
|
|
8
|
+
locale: void 0
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function createAsyncLocalRequestLocaleStore() {
|
|
12
|
+
return () => {
|
|
13
|
+
const existingStore = requestLocaleStorage.getStore();
|
|
14
|
+
if (existingStore) {
|
|
15
|
+
return existingStore;
|
|
16
|
+
}
|
|
17
|
+
const nextStore = createRequestLocaleStore();
|
|
18
|
+
requestLocaleStorage.enterWith(nextStore);
|
|
19
|
+
return nextStore;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
var getOrCreateAsyncLocalRequestLocaleStore = createAsyncLocalRequestLocaleStore();
|
|
23
|
+
function createReactRequestLocaleStore() {
|
|
24
|
+
try {
|
|
25
|
+
const react = require2("react");
|
|
26
|
+
if (typeof react.cache === "function") {
|
|
27
|
+
return react.cache(() => getOrCreateAsyncLocalRequestLocaleStore());
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
var getOrCreateRequestLocaleStore = createReactRequestLocaleStore() ?? getOrCreateAsyncLocalRequestLocaleStore;
|
|
34
|
+
function setRequestLocale(locale) {
|
|
35
|
+
getOrCreateRequestLocaleStore().locale = locale;
|
|
36
|
+
}
|
|
37
|
+
function getRequestLocale() {
|
|
38
|
+
return getOrCreateRequestLocaleStore().locale;
|
|
39
|
+
}
|
|
40
|
+
function resolveRequestLocale(translator, options) {
|
|
41
|
+
const locale = options?.locale ?? options?.requestLocale ?? options?.configLocale ?? translator.defaultLocale;
|
|
42
|
+
if (!translator.getSupportedLocales().includes(locale)) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`The locale "${locale}" is not included in the translator's supported locales.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return locale;
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
getRequestLocale,
|
|
51
|
+
resolveRequestLocale,
|
|
52
|
+
setRequestLocale
|
|
53
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
export type TranslationLeaf = string;
|
|
2
|
+
export type TranslationMessages = {
|
|
3
|
+
readonly [key: string]: TranslationLeaf | TranslationMessages;
|
|
4
|
+
};
|
|
5
|
+
export type TranslationLoader<TMessages = TranslationMessages> = () => TMessages | Promise<TMessages>;
|
|
6
|
+
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
7
|
+
type Simplify<T> = {
|
|
8
|
+
[K in keyof T]: T[K];
|
|
9
|
+
} & {};
|
|
10
|
+
type UnionToIntersection<T> = (T extends unknown ? (value: T) => void : never) extends (value: infer I) => void ? I : never;
|
|
11
|
+
type AllTrue<T> = false extends T ? false : true;
|
|
12
|
+
type MergeUnion<T> = T extends Primitive ? T : T extends ReadonlyArray<unknown> ? never : T extends Record<string, unknown> ? Simplify<{
|
|
13
|
+
[K in keyof UnionToIntersection<T>]: MergeUnion<T extends unknown ? (K extends keyof T ? T[K] : never) : never>;
|
|
14
|
+
}> : never;
|
|
15
|
+
type AsTranslationMessages<TMessages> = TMessages extends TranslationMessages ? TMessages : never;
|
|
16
|
+
export type DeepPartialMessages<TMessages> = {
|
|
17
|
+
[K in keyof TMessages]?: TMessages[K] extends string ? string : TMessages[K] extends TranslationMessages ? DeepPartialMessages<TMessages[K]> : never;
|
|
18
|
+
};
|
|
19
|
+
export type DeepStringify<TMessages> = {
|
|
20
|
+
[K in keyof TMessages]: TMessages[K] extends string ? string : TMessages[K] extends TranslationMessages ? DeepStringify<TMessages[K]> : never;
|
|
21
|
+
};
|
|
22
|
+
export type DotKeys<TMessages, TPrefix extends string = ""> = {
|
|
23
|
+
[K in keyof TMessages & string]: TMessages[K] extends string ? `${TPrefix}${K}` : TMessages[K] extends TranslationMessages ? DotKeys<TMessages[K], `${TPrefix}${K}.`> : never;
|
|
24
|
+
}[keyof TMessages & string];
|
|
25
|
+
type IsExactMessageShape<TReference, TCandidate> = [TReference] extends [string] ? [TCandidate] extends [string] ? true : false : [TReference] extends [TranslationMessages] ? [TCandidate] extends [TranslationMessages] ? Exclude<keyof TReference, keyof TCandidate> extends never ? Exclude<keyof TCandidate, keyof TReference> extends never ? AllTrue<{
|
|
26
|
+
[K in keyof TReference]: IsExactMessageShape<TReference[K], K extends keyof TCandidate ? TCandidate[K] : never>;
|
|
27
|
+
}[keyof TReference]> : false : false : false : false;
|
|
28
|
+
type ExactMessageShape<TReference, TCandidate extends TranslationMessages> = IsExactMessageShape<TReference, TCandidate> extends true ? TCandidate : never;
|
|
29
|
+
type EnforceLocaleMapShapeParity<TMessages extends Record<string, TranslationMessages>> = {
|
|
30
|
+
[TLocale in keyof TMessages]: AllTrue<{
|
|
31
|
+
[TOtherLocale in keyof TMessages]: IsExactMessageShape<TMessages[TOtherLocale], TMessages[TLocale]>;
|
|
32
|
+
}[keyof TMessages]> extends true ? TMessages[TLocale] : never;
|
|
33
|
+
};
|
|
34
|
+
type EnforceReferenceMessageShape<TMessages extends Partial<Record<string, TranslationMessages>>, TReference extends TranslationMessages> = {
|
|
35
|
+
[TLocale in keyof TMessages]: TMessages[TLocale] extends TranslationMessages ? ExactMessageShape<DeepStringify<TReference>, TMessages[TLocale]> : never;
|
|
36
|
+
};
|
|
37
|
+
type StrictOptionsMessages<TMessages extends Partial<Record<string, TranslationMessages>>, TDefaultLocale extends string> = TMessages[TDefaultLocale] extends TranslationMessages ? Simplify<TMessages & EnforceReferenceMessageShape<TMessages, Extract<TMessages[TDefaultLocale], TranslationMessages>>> : never;
|
|
38
|
+
export type TranslationKey<TMessages extends TranslationMessages> = string extends keyof TMessages ? string : DotKeys<TMessages>;
|
|
39
|
+
export type TranslationLocaleMap<TLocale extends string, TSourceMessages extends TranslationMessages> = Record<TLocale, DeepStringify<TSourceMessages>>;
|
|
40
|
+
export type TranslationParamValue = string | number | boolean;
|
|
41
|
+
export type TranslationParams = Record<string, TranslationParamValue>;
|
|
42
|
+
export type TranslationDirection = "ltr" | "rtl";
|
|
43
|
+
export interface TranslationLanguageMetadata<TLocale extends string> {
|
|
44
|
+
icon?: string;
|
|
45
|
+
locale: TLocale;
|
|
46
|
+
nativeLabel: string;
|
|
47
|
+
shortLabel: string;
|
|
48
|
+
}
|
|
49
|
+
export type TranslationValueAtKey<TMessages extends TranslationMessages, TKey extends string> = TKey extends `${infer THead}.${infer TRest}` ? THead extends keyof TMessages ? TMessages[THead] extends TranslationMessages ? TranslationValueAtKey<TMessages[THead], TRest> : never : never : TKey extends keyof TMessages ? TMessages[TKey] : never;
|
|
50
|
+
type ExtractPlaceholderNames<TMessage extends string> = string extends TMessage ? string : TMessage extends `${string}{${infer TParam}}${infer TRest}` ? TParam | ExtractPlaceholderNames<TRest> : never;
|
|
51
|
+
type TranslationParamsObject<TParamNames extends string> = Simplify<{
|
|
52
|
+
[TName in TParamNames]: TranslationParamValue;
|
|
53
|
+
}>;
|
|
54
|
+
export type TranslationPlaceholderNames<TMessages extends TranslationMessages, TKey extends string> = ExtractPlaceholderNames<Extract<TranslationValueAtKey<TMessages, TKey>, string>>;
|
|
55
|
+
export type TranslationParamsForKey<TMessages extends TranslationMessages, TKey extends string> = string extends TranslationPlaceholderNames<TMessages, TKey> ? TranslationParams : TranslationParamsObject<TranslationPlaceholderNames<TMessages, TKey>>;
|
|
56
|
+
export type TranslationKeysWithRequiredParams<TMessages extends TranslationMessages> = TranslationKey<TMessages> extends infer TKey ? TKey extends string ? [TranslationPlaceholderNames<TMessages, TKey>] extends [never] ? never : string extends TranslationPlaceholderNames<TMessages, TKey> ? never : TKey : never : never;
|
|
57
|
+
export type TranslationKeysWithOptionalParams<TMessages extends TranslationMessages> = Exclude<TranslationKey<TMessages>, TranslationKeysWithRequiredParams<TMessages>>;
|
|
58
|
+
export type TranslateOptionsForKey<TLocale extends string, TMessages extends TranslationMessages, TKey extends string> = [TranslationPlaceholderNames<TMessages, TKey>] extends [never] ? Simplify<TranslateOptions<TLocale> & {
|
|
59
|
+
params?: never;
|
|
60
|
+
}> : string extends TranslationPlaceholderNames<TMessages, TKey> ? Simplify<TranslateOptions<TLocale> & {
|
|
61
|
+
params?: TranslationParams;
|
|
62
|
+
}> : Simplify<Omit<TranslateOptions<TLocale>, "params"> & {
|
|
63
|
+
params: TranslationParamsForKey<TMessages, TKey>;
|
|
64
|
+
}>;
|
|
65
|
+
export type TranslateArgs<TLocale extends string, TMessages extends TranslationMessages, TKey extends string> = [TranslationPlaceholderNames<TMessages, TKey>] extends [never] ? [options?: TranslateOptionsForKey<TLocale, TMessages, TKey>] : string extends TranslationPlaceholderNames<TMessages, TKey> ? [options?: TranslateOptionsForKey<TLocale, TMessages, TKey>] : [options: TranslateOptionsForKey<TLocale, TMessages, TKey>];
|
|
66
|
+
export type TranslateCall<TLocale extends string, TMessages extends TranslationMessages, TKey extends TranslationKey<TMessages> = TranslationKey<TMessages>> = [key: TKey, ...args: TranslateArgs<TLocale, TMessages, TKey>];
|
|
67
|
+
export type TranslateFunction<TLocale extends string, TMessages extends TranslationMessages> = <TKey extends TranslationKey<TMessages>>(...args: TranslateCall<TLocale, TMessages, TKey>) => string;
|
|
68
|
+
export interface TranslationJsonStringSchema {
|
|
69
|
+
type: "string";
|
|
70
|
+
}
|
|
71
|
+
export interface TranslationJsonObjectSchema {
|
|
72
|
+
type: "object";
|
|
73
|
+
additionalProperties: false;
|
|
74
|
+
required: string[];
|
|
75
|
+
properties: Record<string, TranslationJsonSchemaNode>;
|
|
76
|
+
}
|
|
77
|
+
export interface TranslationJsonSchema extends TranslationJsonObjectSchema {
|
|
78
|
+
$schema: "https://json-schema.org/draft/2020-12/schema";
|
|
79
|
+
}
|
|
80
|
+
export type TranslationJsonSchemaNode = TranslationJsonObjectSchema | TranslationJsonStringSchema;
|
|
81
|
+
export interface TranslationConfig<TLocale extends string> {
|
|
82
|
+
/**
|
|
83
|
+
* Uses a specific locale for this call instead of the configured default locale.
|
|
84
|
+
*/
|
|
85
|
+
locale?: TLocale;
|
|
86
|
+
/**
|
|
87
|
+
* Overrides the resolved text direction for this call.
|
|
88
|
+
*/
|
|
89
|
+
rtl?: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Optional overrides for a single translation lookup.
|
|
93
|
+
*/
|
|
94
|
+
export interface TranslateOptions<TLocale extends string> {
|
|
95
|
+
/**
|
|
96
|
+
* Uses a specific locale for this call instead of the configured default locale.
|
|
97
|
+
*/
|
|
98
|
+
locale?: TLocale;
|
|
99
|
+
/**
|
|
100
|
+
* Replaces `{token}` placeholders found in the translation string.
|
|
101
|
+
*/
|
|
102
|
+
params?: TranslationParams;
|
|
103
|
+
/**
|
|
104
|
+
* Per-call configuration options for the translation method.
|
|
105
|
+
*/
|
|
106
|
+
config?: TranslationConfig<TLocale>;
|
|
107
|
+
}
|
|
108
|
+
export interface TranslationDirectionOptions<TLocale extends string> {
|
|
109
|
+
/**
|
|
110
|
+
* Uses a specific locale for this call instead of the configured default locale.
|
|
111
|
+
*/
|
|
112
|
+
locale?: TLocale;
|
|
113
|
+
/**
|
|
114
|
+
* Per-call configuration options used to resolve direction metadata.
|
|
115
|
+
*/
|
|
116
|
+
config?: TranslationConfig<TLocale>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Translator returned by `configureTranslations(...)`.
|
|
120
|
+
*
|
|
121
|
+
* It keeps the configured locale metadata, resolves keys with fallback behavior,
|
|
122
|
+
* and can lazily load locales registered through async loaders.
|
|
123
|
+
*/
|
|
124
|
+
export interface ConfiguredTranslator<TLocale extends string, TSourceMessages extends TranslationMessages> {
|
|
125
|
+
/**
|
|
126
|
+
* Source locale used for key inference and default translation lookups.
|
|
127
|
+
*/
|
|
128
|
+
readonly defaultLocale: TLocale;
|
|
129
|
+
/**
|
|
130
|
+
* Locale used when a key is missing in the requested locale.
|
|
131
|
+
*/
|
|
132
|
+
readonly fallbackLocale: TLocale;
|
|
133
|
+
/**
|
|
134
|
+
* All locales declared for this translator.
|
|
135
|
+
*/
|
|
136
|
+
readonly supportedLocales: readonly TLocale[];
|
|
137
|
+
/**
|
|
138
|
+
* Resolves a translation key using the provided locale override or the default locale.
|
|
139
|
+
*
|
|
140
|
+
* If a key is missing in the active locale, it falls back to the configured
|
|
141
|
+
* fallback locale. If the key is still missing, the key itself is returned.
|
|
142
|
+
*/
|
|
143
|
+
t: TranslateFunction<TLocale, TSourceMessages>;
|
|
144
|
+
/**
|
|
145
|
+
* Loads a locale through its registered async loader and caches the result.
|
|
146
|
+
*
|
|
147
|
+
* Returns the cached or loaded locale messages, or `undefined` when the locale
|
|
148
|
+
* does not have a loader or preloaded messages.
|
|
149
|
+
*/
|
|
150
|
+
loadLocale(locale: TLocale): Promise<DeepPartialMessages<TSourceMessages> | TSourceMessages | undefined>;
|
|
151
|
+
/**
|
|
152
|
+
* Returns the list of configured locales for this translator.
|
|
153
|
+
*/
|
|
154
|
+
getSupportedLocales(): readonly TLocale[];
|
|
155
|
+
/**
|
|
156
|
+
* Returns the configured or synthesized language metadata for each locale.
|
|
157
|
+
*/
|
|
158
|
+
getAvailableLanguages(): readonly TranslationLanguageMetadata<TLocale>[];
|
|
159
|
+
/**
|
|
160
|
+
* Returns the resolved direction metadata for the requested locale or override.
|
|
161
|
+
*/
|
|
162
|
+
getDirection(options?: TranslationDirectionOptions<TLocale>): TranslationDirection;
|
|
163
|
+
/**
|
|
164
|
+
* Returns whether the resolved direction metadata is right-to-left.
|
|
165
|
+
*/
|
|
166
|
+
isRtl(options?: TranslationDirectionOptions<TLocale>): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Returns the cached messages currently known by this translator.
|
|
169
|
+
*
|
|
170
|
+
* This includes messages passed during configuration and any locales loaded
|
|
171
|
+
* later through async loaders.
|
|
172
|
+
*/
|
|
173
|
+
getMessages(): CachedMessages<TLocale, TSourceMessages>;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Typed helper surface bound to one configured translator.
|
|
177
|
+
*
|
|
178
|
+
* This is useful for app-level setup modules that want to export typed
|
|
179
|
+
* `t(...)`, `getMessages()`, and related helpers without relying on ambient
|
|
180
|
+
* module augmentation.
|
|
181
|
+
*/
|
|
182
|
+
export interface TranslationHelpers<TLocale extends string, TSourceMessages extends TranslationMessages> {
|
|
183
|
+
readonly translator: ConfiguredTranslator<TLocale, TSourceMessages>;
|
|
184
|
+
readonly t: TranslateFunction<TLocale, TSourceMessages>;
|
|
185
|
+
loadLocale(locale: TLocale): Promise<DeepPartialMessages<TSourceMessages> | TSourceMessages | undefined>;
|
|
186
|
+
getSupportedLocales(): readonly TLocale[];
|
|
187
|
+
getAvailableLanguages(): readonly TranslationLanguageMetadata<TLocale>[];
|
|
188
|
+
getDirection(options?: TranslationDirectionOptions<TLocale>): TranslationDirection;
|
|
189
|
+
isRtl(options?: TranslationDirectionOptions<TLocale>): boolean;
|
|
190
|
+
getMessages(): CachedMessages<TLocale, TSourceMessages>;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Main configuration shape for the framework-agnostic translation runtime.
|
|
194
|
+
*
|
|
195
|
+
* `availableLocales` defines the locale contract first. `defaultLocale`,
|
|
196
|
+
* `fallbackLocale`, `messages`, and `loaders` are validated against that list.
|
|
197
|
+
*/
|
|
198
|
+
export type TranslationConfigOptions<TLocales extends readonly string[], TMessages extends Partial<Record<TLocales[number], TranslationMessages>>, TLoaders extends Partial<Record<TLocales[number], TranslationLoader<unknown>>> | undefined = undefined, TDefaultLocale extends TLocales[number] = TLocales[number]> = {
|
|
199
|
+
availableLocales: TLocales;
|
|
200
|
+
defaultLocale: TDefaultLocale;
|
|
201
|
+
fallbackLocale?: TLocales[number];
|
|
202
|
+
directions?: Partial<Record<TLocales[number], TranslationDirection>>;
|
|
203
|
+
languages?: readonly TranslationLanguageMetadata<TLocales[number]>[];
|
|
204
|
+
messages: StrictOptionsMessages<TMessages, TDefaultLocale>;
|
|
205
|
+
loaders?: TLoaders;
|
|
206
|
+
};
|
|
207
|
+
export type AnyConfiguredTranslator = ConfiguredTranslator<string, TranslationMessages>;
|
|
208
|
+
export type AnyTranslationHelpers = TranslationHelpers<string, TranslationMessages>;
|
|
209
|
+
export type InternalTranslationNode = string | undefined | {
|
|
210
|
+
readonly [key: string]: InternalTranslationNode;
|
|
211
|
+
};
|
|
212
|
+
export type InternalTranslationMessages = {
|
|
213
|
+
readonly [key: string]: InternalTranslationNode;
|
|
214
|
+
};
|
|
215
|
+
export type RuntimeConfigInput = Record<string, TranslationMessages> | TranslationConfigOptions<readonly string[], Partial<Record<string, TranslationMessages>>, Partial<Record<string, TranslationLoader<unknown>>> | undefined, string>;
|
|
216
|
+
export interface InternalNormalizedConfig {
|
|
217
|
+
defaultLocale: string;
|
|
218
|
+
fallbackLocale: string;
|
|
219
|
+
supportedLocales: readonly string[];
|
|
220
|
+
directions: Readonly<Record<string, TranslationDirection>>;
|
|
221
|
+
languages: readonly TranslationLanguageMetadata<string>[];
|
|
222
|
+
messages: Partial<Record<string, InternalTranslationMessages>>;
|
|
223
|
+
loaders: Partial<Record<string, TranslationLoader<unknown>>>;
|
|
224
|
+
}
|
|
225
|
+
export interface GlobalStore {
|
|
226
|
+
translator?: AnyConfiguredTranslator;
|
|
227
|
+
}
|
|
228
|
+
export type CachedMessages<TLocale extends string, TSourceMessages extends TranslationMessages> = Readonly<Partial<Record<TLocale, DeepPartialMessages<TSourceMessages> | TSourceMessages>>>;
|
|
229
|
+
export type ShortFormTranslator<TMessages extends Record<string, TranslationMessages>> = ConfiguredTranslator<Extract<keyof TMessages, string>, AsTranslationMessages<MergeUnion<TMessages[keyof TMessages & string]>>>;
|
|
230
|
+
export type StrictTranslationLocaleMap<TMessages extends Record<string, TranslationMessages>> = Simplify<TMessages & EnforceLocaleMapShapeParity<TMessages>>;
|
|
231
|
+
export type OptionsFormTranslator<TLocales extends readonly string[], TMessages extends Partial<Record<TLocales[number], TranslationMessages>>, TDefaultLocale extends TLocales[number]> = ConfiguredTranslator<TLocales[number], Extract<TMessages[TDefaultLocale], TranslationMessages>>;
|
|
232
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@better-translate/core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Framework-agnostic translation configuration runtime for TypeScript projects.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"typesVersions": {
|
|
12
|
+
"*": {
|
|
13
|
+
"core": ["./dist/core.d.ts"],
|
|
14
|
+
"server": ["./dist/server.d.ts"]
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./core": {
|
|
23
|
+
"types": "./dist/core.d.ts",
|
|
24
|
+
"import": "./dist/core.js"
|
|
25
|
+
},
|
|
26
|
+
"./server": {
|
|
27
|
+
"types": "./dist/server.d.ts",
|
|
28
|
+
"import": "./dist/server.js"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": ["dist", "LICENSE", "README.md"],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup src/index.ts src/core.ts src/server.ts --format esm --clean && bun x tsc --declaration --emitDeclarationOnly --module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist src/types.ts && cp src/core.d.ts src/index.d.ts src/server.d.ts dist/",
|
|
35
|
+
"check-types": "tsc --noEmit",
|
|
36
|
+
"test": "bun test tests/runtime",
|
|
37
|
+
"test:types": "tsc -p tests/types/tsconfig.json --noEmit"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/jralvarenga/better-translate.git",
|
|
45
|
+
"directory": "packages/core"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/jralvarenga/better-translate/tree/main/packages/core#readme",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/jralvarenga/better-translate/issues"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@repo/typescript-config": "*"
|
|
53
|
+
},
|
|
54
|
+
"keywords": ["i18n", "l10n", "translations", "typescript"]
|
|
55
|
+
}
|