i18n-keyless-core 2.3.1 → 2.4.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/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/package.json +1 -1
- package/dist/service.d.ts +14 -1
- package/dist/service.js +49 -8
- package/dist/types.d.ts +25 -0
- package/dist/types.js +6 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AVAILABLE_LANGS } from "./types.ts";
|
|
1
|
+
export { AVAILABLE_LANGS, DEFAULT_NAMESPACE } from "./types.ts";
|
|
2
2
|
export type { Lang, PrimaryLang, Translations, TranslationsUsage, HandleTranslateFunction, GetAllTranslationsFunction, SendTranslationsUsageFunction, GetAllTranslationsForAllLanguagesFunction, LanguagesConfig, LastRefresh, UniqueId, I18nKeylessRequestBody, I18nKeylessResponse, I18nKeylessTranslationsUsageRequestBody, I18nKeylessAllTranslationsResponse, FetchTranslationParams, TranslationOptions } from "./types.ts";
|
|
3
|
-
export { getTranslationCore, getAllTranslationsFromLanguage, sendTranslationsUsageToI18nKeyless, queue } from "./service.ts";
|
|
3
|
+
export { getTranslationCore, getAllTranslationsFromLanguage, sendTranslationsUsageToI18nKeyless, getNamespacesToFetchAfterTranslationFinished, resolveNamespace, queue } from "./service.ts";
|
|
4
4
|
export { api } from "./api.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { AVAILABLE_LANGS } from "./types.js";
|
|
2
|
-
export { getTranslationCore, getAllTranslationsFromLanguage, sendTranslationsUsageToI18nKeyless, queue } from "./service.js";
|
|
1
|
+
export { AVAILABLE_LANGS, DEFAULT_NAMESPACE } from "./types.js";
|
|
2
|
+
export { getTranslationCore, getAllTranslationsFromLanguage, sendTranslationsUsageToI18nKeyless, getNamespacesToFetchAfterTranslationFinished, resolveNamespace, queue } from "./service.js";
|
|
3
3
|
export { api } from "./api.js";
|
package/dist/package.json
CHANGED
package/dist/service.d.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import type { Lang, TranslationOptions, I18nKeylessResponse, FetchTranslationParams, TranslationsUsage } from "./types.ts";
|
|
2
2
|
import MyPQueue from "./my-pqueue.ts";
|
|
3
3
|
export declare const queue: MyPQueue;
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the effective namespace for a translation call: an explicit per-call
|
|
6
|
+
* `namespace` wins, then the config-level `defaultNamespace`, then `DEFAULT_NAMESPACE`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveNamespace(options: TranslationOptions | undefined, config: FetchTranslationParams["config"]): string;
|
|
9
|
+
/**
|
|
10
|
+
* Returns the namespaces queued since the last call (with their `unpersisted` flag) and
|
|
11
|
+
* clears the map.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getNamespacesToFetchAfterTranslationFinished(): Array<{
|
|
14
|
+
namespace: string;
|
|
15
|
+
unpersisted: boolean;
|
|
16
|
+
}>;
|
|
4
17
|
/**
|
|
5
18
|
* Gets a translation for the specified key from the store
|
|
6
19
|
* @param key - The translation key (text in primary language)
|
|
@@ -24,7 +37,7 @@ export declare function translateKey(key: string, store: FetchTranslationParams,
|
|
|
24
37
|
* @param store - The translation store
|
|
25
38
|
* @returns Promise resolving to the translation response or void if failed
|
|
26
39
|
*/
|
|
27
|
-
export declare function getAllTranslationsFromLanguage(targetLanguage: Lang, store: FetchTranslationParams): Promise<I18nKeylessResponse | void>;
|
|
40
|
+
export declare function getAllTranslationsFromLanguage(targetLanguage: Lang, store: FetchTranslationParams, namespace?: string): Promise<I18nKeylessResponse | void>;
|
|
28
41
|
/**
|
|
29
42
|
* Send the translations usage to i18n-keyless API
|
|
30
43
|
*
|
package/dist/service.js
CHANGED
|
@@ -1,7 +1,35 @@
|
|
|
1
|
+
import { DEFAULT_NAMESPACE } from "./types.js";
|
|
1
2
|
import MyPQueue from "./my-pqueue.js";
|
|
2
3
|
import packageJson from "./package.json" with { type: "json" };
|
|
3
4
|
import { api } from "./api.js";
|
|
4
5
|
export const queue = new MyPQueue({ concurrency: 30 });
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the effective namespace for a translation call: an explicit per-call
|
|
8
|
+
* `namespace` wins, then the config-level `defaultNamespace`, then `DEFAULT_NAMESPACE`.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveNamespace(options, config) {
|
|
11
|
+
return options?.namespace || config.defaultNamespace || DEFAULT_NAMESPACE;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Scratchpad of namespaces that had at least one missing key queued for translation since
|
|
15
|
+
* the last bulk fetch (mapped to whether that namespace is `unpersisted`). The queue's
|
|
16
|
+
* "empty" handler (in the react store / node service) reads this to know which namespaces
|
|
17
|
+
* to bulk-fetch — so we only re-download the namespaces that were actually rendered, never
|
|
18
|
+
* the whole project — and whether to persist the result.
|
|
19
|
+
*/
|
|
20
|
+
const namespacesToFetchAfterTranslationFinished = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* Returns the namespaces queued since the last call (with their `unpersisted` flag) and
|
|
23
|
+
* clears the map.
|
|
24
|
+
*/
|
|
25
|
+
export function getNamespacesToFetchAfterTranslationFinished() {
|
|
26
|
+
const namespaces = Array.from(namespacesToFetchAfterTranslationFinished, ([namespace, unpersisted]) => ({
|
|
27
|
+
namespace,
|
|
28
|
+
unpersisted,
|
|
29
|
+
}));
|
|
30
|
+
namespacesToFetchAfterTranslationFinished.clear();
|
|
31
|
+
return namespaces;
|
|
32
|
+
}
|
|
5
33
|
/**
|
|
6
34
|
* Gets a translation for the specified key from the store
|
|
7
35
|
* @param key - The translation key (text in primary language)
|
|
@@ -61,6 +89,7 @@ export function translateKey(key, store, options) {
|
|
|
61
89
|
}
|
|
62
90
|
const context = options?.context;
|
|
63
91
|
const debug = options?.debug;
|
|
92
|
+
const namespace = resolveNamespace(options, config);
|
|
64
93
|
// if (key.length > 280) {
|
|
65
94
|
// console.error("i18n-keyless: Key length exceeds 280 characters limit:", key);
|
|
66
95
|
// return;
|
|
@@ -69,7 +98,7 @@ export function translateKey(key, store, options) {
|
|
|
69
98
|
return;
|
|
70
99
|
}
|
|
71
100
|
if (debug) {
|
|
72
|
-
console.log("translateKey", key, context, debug);
|
|
101
|
+
console.log("translateKey", key, context, namespace, debug);
|
|
73
102
|
}
|
|
74
103
|
const forceTemporaryLang = options?.forceTemporary?.[currentLanguage];
|
|
75
104
|
const translation = context ? translations[`${key}__${context}`] : translations[key];
|
|
@@ -79,13 +108,19 @@ export function translateKey(key, store, options) {
|
|
|
79
108
|
}
|
|
80
109
|
return;
|
|
81
110
|
}
|
|
111
|
+
// Remember this namespace (and whether it's unpersisted) so the queue's "empty" handler
|
|
112
|
+
// bulk-fetches it (and only it) and persists the result accordingly.
|
|
113
|
+
namespacesToFetchAfterTranslationFinished.set(namespace, !!options?.unpersistedNamespace);
|
|
114
|
+
// Dedup/guard per namespace so the same source text can be queued independently under
|
|
115
|
+
// different namespaces.
|
|
116
|
+
const queueId = `${namespace}:${key}`;
|
|
82
117
|
queue.add(async () => {
|
|
83
118
|
try {
|
|
84
|
-
if (translating[
|
|
119
|
+
if (translating[queueId]) {
|
|
85
120
|
return;
|
|
86
121
|
}
|
|
87
122
|
else {
|
|
88
|
-
translating[
|
|
123
|
+
translating[queueId] = true;
|
|
89
124
|
}
|
|
90
125
|
if (config.handleTranslate) {
|
|
91
126
|
await config.handleTranslate?.(key);
|
|
@@ -94,6 +129,9 @@ export function translateKey(key, store, options) {
|
|
|
94
129
|
const body = {
|
|
95
130
|
key,
|
|
96
131
|
context,
|
|
132
|
+
// Omit the default namespace so the wire format is unchanged for projects that
|
|
133
|
+
// don't use namespaces (the backend treats "no namespace" as the default).
|
|
134
|
+
namespace: namespace === DEFAULT_NAMESPACE ? undefined : namespace,
|
|
97
135
|
forceTemporary: options?.forceTemporary,
|
|
98
136
|
languages: config.languages.supported,
|
|
99
137
|
primaryLanguage: config.languages.primary,
|
|
@@ -122,14 +160,14 @@ export function translateKey(key, store, options) {
|
|
|
122
160
|
console.warn("i18n-keyless: ", response.message);
|
|
123
161
|
}
|
|
124
162
|
}
|
|
125
|
-
translating[
|
|
163
|
+
translating[queueId] = false;
|
|
126
164
|
return;
|
|
127
165
|
}
|
|
128
166
|
catch (error) {
|
|
129
167
|
console.error("i18n-keyless: Error translating key:", error);
|
|
130
|
-
translating[
|
|
168
|
+
translating[queueId] = false;
|
|
131
169
|
}
|
|
132
|
-
}, { priority: 1, id:
|
|
170
|
+
}, { priority: 1, id: queueId });
|
|
133
171
|
}
|
|
134
172
|
/**
|
|
135
173
|
* Fetches all translations for a target language
|
|
@@ -137,7 +175,7 @@ export function translateKey(key, store, options) {
|
|
|
137
175
|
* @param store - The translation store
|
|
138
176
|
* @returns Promise resolving to the translation response or void if failed
|
|
139
177
|
*/
|
|
140
|
-
export async function getAllTranslationsFromLanguage(targetLanguage, store) {
|
|
178
|
+
export async function getAllTranslationsFromLanguage(targetLanguage, store, namespace) {
|
|
141
179
|
const config = store.config;
|
|
142
180
|
const lastRefresh = store.lastRefresh;
|
|
143
181
|
const uniqueId = store.uniqueId;
|
|
@@ -148,11 +186,14 @@ export async function getAllTranslationsFromLanguage(targetLanguage, store) {
|
|
|
148
186
|
// if (config.languages.primary === targetLanguage) {
|
|
149
187
|
// return;
|
|
150
188
|
// }
|
|
189
|
+
// Omit the default namespace from the query so existing (non-namespaced) installs keep
|
|
190
|
+
// hitting the exact same URL.
|
|
191
|
+
const namespaceQuery = namespace && namespace !== DEFAULT_NAMESPACE ? `&namespace=${encodeURIComponent(namespace)}` : "";
|
|
151
192
|
try {
|
|
152
193
|
const response = config.getAllTranslations
|
|
153
194
|
? await config.getAllTranslations()
|
|
154
195
|
: await api
|
|
155
|
-
.fetchTranslationsForOneLanguage(`${config.API_URL || "https://api.i18n-keyless.com"}/translate/${targetLanguage}?last_refresh=${lastRefresh}`, {
|
|
196
|
+
.fetchTranslationsForOneLanguage(`${config.API_URL || "https://api.i18n-keyless.com"}/translate/${targetLanguage}?last_refresh=${lastRefresh}${namespaceQuery}`, {
|
|
156
197
|
method: "GET",
|
|
157
198
|
headers: {
|
|
158
199
|
"Content-Type": "application/json",
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export type PrimaryLang = "fr" | "en";
|
|
2
2
|
export declare const AVAILABLE_LANGS: readonly ["fr", "en", "nl", "it", "de", "es", "pl", "pt", "ro", "hu", "sv", "tr", "ja", "cn", "cz", "ru", "ko", "ar"];
|
|
3
3
|
export type Lang = (typeof AVAILABLE_LANGS)[number];
|
|
4
|
+
/**
|
|
5
|
+
* The namespace used when none is provided (per call or via `defaultNamespace` in config).
|
|
6
|
+
* The default namespace reuses the legacy storage keys (`i18n-keyless-translations` and
|
|
7
|
+
* `i18n-keyless-last-refresh`) so existing installs keep working without any migration.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_NAMESPACE = "default";
|
|
4
10
|
/**
|
|
5
11
|
* The translations for a key
|
|
6
12
|
* { "un text": "a text" }
|
|
@@ -63,6 +69,23 @@ export type TranslationOptions = {
|
|
|
63
69
|
* You'll find it useful when it occurs to you, don't worry :)
|
|
64
70
|
*/
|
|
65
71
|
context?: string;
|
|
72
|
+
/**
|
|
73
|
+
* The namespace this translation belongs to.
|
|
74
|
+
* Translations are fetched and persisted per namespace, so splitting a large project
|
|
75
|
+
* into several namespaces keeps each storage item small (avoids the localStorage quota
|
|
76
|
+
* error) and lets the app download only the namespaces it actually renders.
|
|
77
|
+
* Defaults to `defaultNamespace` from the config, or "default" if neither is set.
|
|
78
|
+
*/
|
|
79
|
+
namespace?: string;
|
|
80
|
+
/**
|
|
81
|
+
* When true, this namespace's translations live in memory only: they are never written
|
|
82
|
+
* to storage, never added to the persisted namespaces index, and never reloaded at boot
|
|
83
|
+
* or refetched on language change from storage. Use it for high-cardinality, transient
|
|
84
|
+
* namespaces (e.g. one namespace per discussion) so they add zero storage weight and zero
|
|
85
|
+
* boot / language-switch cost. Defaults to false (persisted).
|
|
86
|
+
* Only affects the client (i18n-keyless-react); the node lib is in-memory regardless.
|
|
87
|
+
*/
|
|
88
|
+
unpersistedNamespace?: boolean;
|
|
66
89
|
/**
|
|
67
90
|
* Could be helpful if something weird happens with this particular key.
|
|
68
91
|
*/
|
|
@@ -84,6 +107,7 @@ export type TranslationOptions = {
|
|
|
84
107
|
export interface I18nKeylessRequestBody {
|
|
85
108
|
key: string;
|
|
86
109
|
context?: string;
|
|
110
|
+
namespace?: string;
|
|
87
111
|
forceTemporary?: TranslationOptions["forceTemporary"];
|
|
88
112
|
languages: LanguagesConfig["supported"];
|
|
89
113
|
primaryLanguage: LanguagesConfig["primary"];
|
|
@@ -120,6 +144,7 @@ export type FetchTranslationParams = {
|
|
|
120
144
|
API_KEY: string;
|
|
121
145
|
API_URL?: string;
|
|
122
146
|
languages: LanguagesConfig;
|
|
147
|
+
defaultNamespace?: string;
|
|
123
148
|
addMissingTranslations?: boolean;
|
|
124
149
|
debug?: boolean;
|
|
125
150
|
handleTranslate?: HandleTranslateFunction;
|
package/dist/types.js
CHANGED
|
@@ -18,3 +18,9 @@ export const AVAILABLE_LANGS = [
|
|
|
18
18
|
"ko",
|
|
19
19
|
"ar"
|
|
20
20
|
];
|
|
21
|
+
/**
|
|
22
|
+
* The namespace used when none is provided (per call or via `defaultNamespace` in config).
|
|
23
|
+
* The default namespace reuses the legacy storage keys (`i18n-keyless-translations` and
|
|
24
|
+
* `i18n-keyless-last-refresh`) so existing installs keep working without any migration.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_NAMESPACE = "default";
|