@scoutello/i18n-magic 0.64.0 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -7
- package/dist/cli.js +20 -15
- package/dist/cli.js.map +1 -1
- package/dist/commands/check-missing.d.ts +11 -1
- package/dist/commands/check-missing.d.ts.map +1 -1
- package/dist/commands/check-missing.js +56 -96
- package/dist/commands/check-missing.js.map +1 -1
- package/dist/commands/replace.d.ts +6 -0
- package/dist/commands/replace.d.ts.map +1 -1
- package/dist/commands/replace.js +48 -29
- package/dist/commands/replace.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +114 -80
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/sync-locales.d.ts.map +1 -1
- package/dist/commands/sync-locales.js +90 -50
- package/dist/commands/sync-locales.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/languges.d.ts.map +1 -1
- package/dist/lib/languges.js +20 -0
- package/dist/lib/languges.js.map +1 -1
- package/dist/lib/locale-storage.d.ts +7 -0
- package/dist/lib/locale-storage.d.ts.map +1 -0
- package/dist/lib/locale-storage.js +49 -0
- package/dist/lib/locale-storage.js.map +1 -0
- package/dist/lib/translation-response.d.ts +8 -0
- package/dist/lib/translation-response.d.ts.map +1 -0
- package/dist/lib/translation-response.js +43 -0
- package/dist/lib/translation-response.js.map +1 -0
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/update-translation-key.d.ts +16 -0
- package/dist/lib/update-translation-key.d.ts.map +1 -0
- package/dist/lib/update-translation-key.js +67 -0
- package/dist/lib/update-translation-key.js.map +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +69 -51
- package/dist/lib/utils.js.map +1 -1
- package/dist/mcp-server.js +28 -79
- package/dist/mcp-server.js.map +1 -1
- package/package.json +5 -9
- package/src/cli.ts +26 -18
- package/src/commands/check-missing.ts +83 -137
- package/src/commands/replace.ts +81 -43
- package/src/commands/scan.ts +130 -108
- package/src/commands/sync-locales.ts +125 -86
- package/src/index.ts +6 -1
- package/src/lib/languges.ts +20 -0
- package/src/lib/locale-storage.ts +76 -0
- package/src/lib/translation-response.ts +59 -0
- package/src/lib/types.ts +1 -1
- package/src/lib/update-translation-key.ts +119 -0
- package/src/lib/utils.ts +92 -76
- package/src/mcp-server.ts +31 -112
package/dist/lib/languges.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"languges.js","sourceRoot":"","sources":["../../src/lib/languges.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"languges.js","sourceRoot":"","sources":["../../src/lib/languges.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Configuration } from "./types.js";
|
|
2
|
+
export declare class LocaleConfigurationError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare const resolveConfiguredLocale: (config: Pick<Configuration, "defaultLocale" | "locales">, requested?: string) => string;
|
|
6
|
+
export declare const resolveLocaleTemplatePath: (template: string, locale: string, namespace: string) => string;
|
|
7
|
+
//# sourceMappingURL=locale-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locale-storage.d.ts","sourceRoot":"","sources":["../../src/lib/locale-storage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,uBAAuB,GAClC,QAAQ,IAAI,CAAC,aAAa,EAAE,eAAe,GAAG,SAAS,CAAC,EACxD,YAAY,MAAM,WAWnB,CAAA;AA2BD,eAAO,MAAM,yBAAyB,GACpC,UAAU,MAAM,EAChB,QAAQ,MAAM,EACd,WAAW,MAAM,WAsBlB,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export class LocaleConfigurationError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "LocaleConfigurationError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export const resolveConfiguredLocale = (config, requested) => {
|
|
9
|
+
const locale = requested ?? config.defaultLocale;
|
|
10
|
+
if (!config.locales.includes(locale)) {
|
|
11
|
+
throw new LocaleConfigurationError(`Unsupported locale "${locale}". Expected one of: ${config.locales.join(", ")}.`);
|
|
12
|
+
}
|
|
13
|
+
return locale;
|
|
14
|
+
};
|
|
15
|
+
const getConfiguredBasePath = (template) => {
|
|
16
|
+
const placeholderIndexes = [
|
|
17
|
+
template.indexOf("{{lng}}"),
|
|
18
|
+
template.indexOf("{{ns}}"),
|
|
19
|
+
].filter((index) => index >= 0);
|
|
20
|
+
if (placeholderIndexes.length === 0) {
|
|
21
|
+
return path.dirname(path.resolve(template));
|
|
22
|
+
}
|
|
23
|
+
const firstPlaceholder = Math.min(...placeholderIndexes);
|
|
24
|
+
const staticPrefix = template.slice(0, firstPlaceholder);
|
|
25
|
+
if (staticPrefix.length === 0 ||
|
|
26
|
+
staticPrefix.endsWith(path.sep) ||
|
|
27
|
+
staticPrefix.endsWith("/") ||
|
|
28
|
+
staticPrefix.endsWith("\\")) {
|
|
29
|
+
return path.resolve(staticPrefix || ".");
|
|
30
|
+
}
|
|
31
|
+
return path.dirname(path.resolve(staticPrefix));
|
|
32
|
+
};
|
|
33
|
+
export const resolveLocaleTemplatePath = (template, locale, namespace) => {
|
|
34
|
+
const configuredBase = getConfiguredBasePath(template);
|
|
35
|
+
const expandedPath = template
|
|
36
|
+
.split("{{lng}}")
|
|
37
|
+
.join(locale)
|
|
38
|
+
.split("{{ns}}")
|
|
39
|
+
.join(namespace);
|
|
40
|
+
const resolvedPath = path.resolve(expandedPath);
|
|
41
|
+
const relativePath = path.relative(configuredBase, resolvedPath);
|
|
42
|
+
if (relativePath === ".." ||
|
|
43
|
+
relativePath.startsWith(`..${path.sep}`) ||
|
|
44
|
+
path.isAbsolute(relativePath)) {
|
|
45
|
+
throw new LocaleConfigurationError("Expanded locale path escapes the configured storage base.");
|
|
46
|
+
}
|
|
47
|
+
return resolvedPath;
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=locale-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locale-storage.js","sourceRoot":"","sources":["../../src/lib/locale-storage.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAG5B,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAA;IACxC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,MAAwD,EACxD,SAAkB,EAClB,EAAE;IACF,MAAM,MAAM,GAAG,SAAS,IAAI,MAAM,CAAC,aAAa,CAAA;IAEhD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,wBAAwB,CAChC,uBAAuB,MAAM,uBAAuB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACjF,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,MAAM,qBAAqB,GAAG,CAAC,QAAgB,EAAE,EAAE;IACjD,MAAM,kBAAkB,GAAG;QACzB,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;QAC3B,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;KAC3B,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAA;IAE/B,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC7C,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,CAAA;IACxD,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAA;IAExD,IACE,YAAY,CAAC,MAAM,KAAK,CAAC;QACzB,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QAC/B,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC1B,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,EAC3B,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,CAAA;IAC1C,CAAC;IAED,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAA;AACjD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,QAAgB,EAChB,MAAc,EACd,SAAiB,EACjB,EAAE;IACF,MAAM,cAAc,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAA;IACtD,MAAM,YAAY,GAAG,QAAQ;SAC1B,KAAK,CAAC,SAAS,CAAC;SAChB,IAAI,CAAC,MAAM,CAAC;SACZ,KAAK,CAAC,QAAQ,CAAC;SACf,IAAI,CAAC,SAAS,CAAC,CAAA;IAClB,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC,CAAA;IAEhE,IACE,YAAY,KAAK,IAAI;QACrB,YAAY,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAC7B,CAAC;QACD,MAAM,IAAI,wBAAwB,CAChC,2DAA2D,CAC5D,CAAA;IACH,CAAC;IAED,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class TranslationResponseError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class TranslationProviderUnavailableError extends Error {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare const parseTranslationResponse: (content: string | null, requestedKeys: string[]) => Record<string, string>;
|
|
8
|
+
//# sourceMappingURL=translation-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translation-response.d.ts","sourceRoot":"","sources":["../../src/lib/translation-response.ts"],"names":[],"mappings":"AAAA,qBAAa,wBAAyB,SAAQ,KAAK;gBACrC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mCAAoC,SAAQ,KAAK;;CAO7D;AAED,eAAO,MAAM,wBAAwB,GACnC,SAAS,MAAM,GAAG,IAAI,EACtB,eAAe,MAAM,EAAE,KACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAuCvB,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class TranslationResponseError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "TranslationResponseError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class TranslationProviderUnavailableError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("A translation provider is required for this operation, but no provider credentials are configured.");
|
|
10
|
+
this.name = "TranslationProviderUnavailableError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export const parseTranslationResponse = (content, requestedKeys) => {
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = content === null ? null : JSON.parse(content);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new TranslationResponseError("Translation provider returned malformed JSON.");
|
|
20
|
+
}
|
|
21
|
+
if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
22
|
+
throw new TranslationResponseError("Translation provider returned a value that is not a JSON object.");
|
|
23
|
+
}
|
|
24
|
+
const response = parsed;
|
|
25
|
+
const requested = new Set(requestedKeys);
|
|
26
|
+
const responseKeys = Object.keys(response);
|
|
27
|
+
const missingKeys = requestedKeys.filter((key) => !Object.hasOwn(response, key));
|
|
28
|
+
const extraKeys = responseKeys.filter((key) => !requested.has(key));
|
|
29
|
+
const invalidValueKeys = responseKeys.filter((key) => typeof response[key] !== "string");
|
|
30
|
+
if (missingKeys.length > 0 || extraKeys.length > 0 || invalidValueKeys.length > 0) {
|
|
31
|
+
const problems = [];
|
|
32
|
+
if (missingKeys.length > 0)
|
|
33
|
+
problems.push(`missing keys: ${missingKeys.join(", ")}`);
|
|
34
|
+
if (extraKeys.length > 0)
|
|
35
|
+
problems.push(`extra keys: ${extraKeys.join(", ")}`);
|
|
36
|
+
if (invalidValueKeys.length > 0) {
|
|
37
|
+
problems.push(`non-string values: ${invalidValueKeys.join(", ")}`);
|
|
38
|
+
}
|
|
39
|
+
throw new TranslationResponseError(`Translation provider returned an invalid key/value shape (${problems.join("; ")}).`);
|
|
40
|
+
}
|
|
41
|
+
return response;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=translation-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translation-response.js","sourceRoot":"","sources":["../../src/lib/translation-response.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAA;IACxC,CAAC;CACF;AAED,MAAM,OAAO,mCAAoC,SAAQ,KAAK;IAC5D;QACE,KAAK,CACH,oGAAoG,CACrG,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,qCAAqC,CAAA;IACnD,CAAC;CACF;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,OAAsB,EACtB,aAAuB,EACC,EAAE;IAC1B,IAAI,MAAe,CAAA;IAEnB,IAAI,CAAC;QACH,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,wBAAwB,CAChC,+CAA+C,CAChD,CAAA;IACH,CAAC;IAED,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC3E,MAAM,IAAI,wBAAwB,CAChC,kEAAkE,CACnE,CAAA;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAiC,CAAA;IAClD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAA;IACxC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC1C,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAA;IAChF,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IACnE,MAAM,gBAAgB,GAAG,YAAY,CAAC,MAAM,CAC1C,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,QAAQ,CAC3C,CAAA;IAED,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClF,MAAM,QAAQ,GAAa,EAAE,CAAA;QAC7B,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACpF,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,eAAe,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC9E,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,QAAQ,CAAC,IAAI,CAAC,sBAAsB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACpE,CAAC;QACD,MAAM,IAAI,wBAAwB,CAChC,6DAA6D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CACrF,CAAA;IACH,CAAC;IAED,OAAO,QAAkC,CAAA;AAC3C,CAAC,CAAA"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface Configuration {
|
|
|
24
24
|
export interface CommandType {
|
|
25
25
|
name: string;
|
|
26
26
|
description: string;
|
|
27
|
-
action: (config: Configuration, ...args: any[]) => Promise<
|
|
27
|
+
action: (config: Configuration, ...args: any[]) => Promise<unknown>;
|
|
28
28
|
}
|
|
29
29
|
export {};
|
|
30
30
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAChC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAE3D,KAAK,KAAK,GACN,SAAS,GACT,0BAA0B,GAC1B,kBAAkB,GAClB,uBAAuB,CAAA;AAE3B,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EACJ,MAAM,GACN,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IAC5E,QAAQ,EACJ,MAAM,GACN,CAAC,CACC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACzB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,YAAY,EAAE,CAAC,MAAM,GAAG,iBAAiB,CAAC,EAAE,CAAA;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4BAA4B,CAAC,EAAE,OAAO,CAAA;IACtC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAChC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAE3D,KAAK,KAAK,GACN,SAAS,GACT,0BAA0B,GAC1B,kBAAkB,GAClB,uBAAuB,CAAA;AAE3B,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EACJ,MAAM,GACN,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IAC5E,QAAQ,EACJ,MAAM,GACN,CAAC,CACC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACzB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,YAAY,EAAE,CAAC,MAAM,GAAG,iBAAiB,CAAC,EAAE,CAAA;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4BAA4B,CAAC,EAAE,OAAO,CAAA;IACtC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACpE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Configuration } from "./types.js";
|
|
2
|
+
export interface UpdateTranslationKeyResult {
|
|
3
|
+
key: string;
|
|
4
|
+
value: string;
|
|
5
|
+
sourceLocale: string;
|
|
6
|
+
targetNamespaces: string[];
|
|
7
|
+
updatedLocales: string[];
|
|
8
|
+
pendingLocales: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare const updateTranslationKeyOperation: ({ config, key, value, language, }: {
|
|
11
|
+
config: Configuration;
|
|
12
|
+
key: string;
|
|
13
|
+
value: string;
|
|
14
|
+
language?: string;
|
|
15
|
+
}) => Promise<UpdateTranslationKeyResult>;
|
|
16
|
+
//# sourceMappingURL=update-translation-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-translation-key.d.ts","sourceRoot":"","sources":["../../src/lib/update-translation-key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAQ/C,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED,eAAO,MAAM,6BAA6B,GAAU,mCAKjD;IACD,MAAM,EAAE,aAAa,CAAA;IACrB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,KAAG,OAAO,CAAC,0BAA0B,CA2FrC,CAAA"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { resolveConfiguredLocale } from "./locale-storage.js";
|
|
2
|
+
import { loadLocalesFile, translateKey, writeLocalesFile, } from "./utils.js";
|
|
3
|
+
export const updateTranslationKeyOperation = async ({ config, key, value, language, }) => {
|
|
4
|
+
const sourceLocale = resolveConfiguredLocale(config, language);
|
|
5
|
+
const sourceFiles = await Promise.all(config.namespaces.map(async (namespace) => ({
|
|
6
|
+
namespace,
|
|
7
|
+
translations: await loadLocalesFile(config.loadPath, sourceLocale, namespace, { silent: true }),
|
|
8
|
+
})));
|
|
9
|
+
const targetNamespaces = sourceFiles
|
|
10
|
+
.filter(({ translations }) => Object.hasOwn(translations, key))
|
|
11
|
+
.map(({ namespace }) => namespace);
|
|
12
|
+
if (targetNamespaces.length === 0) {
|
|
13
|
+
throw new Error(`Key "${key}" does not exist in the ${sourceLocale} translation files. Use add_translation_key to create it.`);
|
|
14
|
+
}
|
|
15
|
+
const pendingLocales = config.openai
|
|
16
|
+
? []
|
|
17
|
+
: config.locales.filter((locale) => locale !== sourceLocale);
|
|
18
|
+
const updatedLocales = config.openai ? [...config.locales] : [sourceLocale];
|
|
19
|
+
const translatedValues = {
|
|
20
|
+
[sourceLocale]: value,
|
|
21
|
+
};
|
|
22
|
+
if (config.openai) {
|
|
23
|
+
await Promise.all(config.locales
|
|
24
|
+
.filter((locale) => locale !== sourceLocale)
|
|
25
|
+
.map(async (locale) => {
|
|
26
|
+
const translation = await translateKey({
|
|
27
|
+
context: config.context || "",
|
|
28
|
+
inputLanguage: sourceLocale,
|
|
29
|
+
outputLanguage: locale,
|
|
30
|
+
object: { [key]: value },
|
|
31
|
+
openai: config.openai,
|
|
32
|
+
model: config.model,
|
|
33
|
+
});
|
|
34
|
+
translatedValues[locale] = translation[key];
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
const snapshots = new Map();
|
|
38
|
+
await Promise.all(updatedLocales.flatMap((locale) => targetNamespaces.map(async (namespace) => {
|
|
39
|
+
const sourceSnapshot = locale === sourceLocale
|
|
40
|
+
? sourceFiles.find((entry) => entry.namespace === namespace)
|
|
41
|
+
?.translations
|
|
42
|
+
: undefined;
|
|
43
|
+
const existingKeys = sourceSnapshot ??
|
|
44
|
+
(await loadLocalesFile(config.loadPath, locale, namespace, {
|
|
45
|
+
silent: true,
|
|
46
|
+
}));
|
|
47
|
+
snapshots.set(JSON.stringify([locale, namespace]), { ...existingKeys, [key]: translatedValues[locale] });
|
|
48
|
+
})));
|
|
49
|
+
for (const locale of updatedLocales) {
|
|
50
|
+
for (const namespace of targetNamespaces) {
|
|
51
|
+
const snapshot = snapshots.get(JSON.stringify([locale, namespace]));
|
|
52
|
+
if (!snapshot) {
|
|
53
|
+
throw new Error(`Internal error: no staged snapshot for ${locale}/${namespace}.`);
|
|
54
|
+
}
|
|
55
|
+
await writeLocalesFile(config.savePath, locale, namespace, snapshot);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
key,
|
|
60
|
+
value,
|
|
61
|
+
sourceLocale,
|
|
62
|
+
targetNamespaces,
|
|
63
|
+
updatedLocales,
|
|
64
|
+
pendingLocales,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=update-translation-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-translation-key.js","sourceRoot":"","sources":["../../src/lib/update-translation-key.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ export declare const addTranslationKeys: ({ keys, config, }: {
|
|
|
85
85
|
results: {
|
|
86
86
|
key: string;
|
|
87
87
|
value: string;
|
|
88
|
+
providedLanguage: string;
|
|
88
89
|
namespace: string;
|
|
89
90
|
locale: string;
|
|
90
91
|
}[];
|
|
@@ -103,6 +104,7 @@ export declare const addTranslationKey: ({ key, value, language, config, }: {
|
|
|
103
104
|
}) => Promise<{
|
|
104
105
|
key: string;
|
|
105
106
|
value: string;
|
|
107
|
+
providedLanguage: string;
|
|
106
108
|
namespace: string;
|
|
107
109
|
locale: string;
|
|
108
110
|
}>;
|
package/dist/lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/utils.js
CHANGED
|
@@ -6,11 +6,12 @@ import { Parser } from "i18next-scanner";
|
|
|
6
6
|
import { minimatch } from "minimatch";
|
|
7
7
|
import prompts from "prompts";
|
|
8
8
|
import { languages } from "./languges.js";
|
|
9
|
+
import { resolveConfiguredLocale, resolveLocaleTemplatePath, } from "./locale-storage.js";
|
|
10
|
+
import { parseTranslationResponse, TranslationProviderUnavailableError, TranslationResponseError, } from "./translation-response.js";
|
|
9
11
|
export const loadConfig = async ({ configPath = "i18n-magic.js", } = {}) => {
|
|
10
12
|
const filePath = path.join(process.cwd(), configPath);
|
|
11
13
|
if (!fs.existsSync(filePath)) {
|
|
12
|
-
|
|
13
|
-
process.exit(1);
|
|
14
|
+
throw new Error(`Config file does not exist: ${filePath}`);
|
|
14
15
|
}
|
|
15
16
|
try {
|
|
16
17
|
// Use dynamic import for ESM compatibility
|
|
@@ -23,18 +24,10 @@ export const loadConfig = async ({ configPath = "i18n-magic.js", } = {}) => {
|
|
|
23
24
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
24
25
|
const isModuleNotFound = errorMessage.includes("MODULE_NOT_FOUND") ||
|
|
25
26
|
errorMessage.includes("Cannot find module");
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
console.error(" 2. Dependencies are installed but have missing peer dependencies");
|
|
31
|
-
console.error(" 3. You're using a custom storage solution (e.g., AWS S3) without proper dependencies");
|
|
32
|
-
console.error("\n To fix:");
|
|
33
|
-
console.error(" - Check if your config file imports any external packages");
|
|
34
|
-
console.error(" - Ensure all dependencies are properly installed: pnpm install");
|
|
35
|
-
console.error(" - If using AWS SDK, ensure all @smithy/* peer dependencies are installed");
|
|
36
|
-
}
|
|
37
|
-
process.exit(1);
|
|
27
|
+
const hint = isModuleNotFound
|
|
28
|
+
? " Check the config imports and installed dependencies."
|
|
29
|
+
: "";
|
|
30
|
+
throw new Error(`Error while loading config: ${errorMessage}.${hint}`);
|
|
38
31
|
}
|
|
39
32
|
};
|
|
40
33
|
export function removeDuplicatesFromArray(arr) {
|
|
@@ -63,6 +56,10 @@ const extractKeysWithRegex = (content) => {
|
|
|
63
56
|
export const translateKey = async ({ inputLanguage, context, object, openai, outputLanguage, model, onProgress, }) => {
|
|
64
57
|
// Split object into chunks of 100 keys
|
|
65
58
|
const entries = Object.entries(object);
|
|
59
|
+
if (entries.length === 0)
|
|
60
|
+
return {};
|
|
61
|
+
if (!openai)
|
|
62
|
+
throw new TranslationProviderUnavailableError();
|
|
66
63
|
const chunks = [];
|
|
67
64
|
for (let i = 0; i < entries.length; i += 100) {
|
|
68
65
|
chunks.push(entries.slice(i, i + 100));
|
|
@@ -77,29 +74,47 @@ export const translateKey = async ({ inputLanguage, context, object, openai, out
|
|
|
77
74
|
const totalKeys = entries.length;
|
|
78
75
|
for (const chunk of chunks) {
|
|
79
76
|
const chunkObject = Object.fromEntries(chunk);
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
const requestedKeys = chunk.map(([key]) => key);
|
|
78
|
+
let translatedChunk;
|
|
79
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
80
|
+
const correctivePrompt = attempt === 1
|
|
81
|
+
? ` The previous response was invalid. Return exactly these keys with string values and no additional keys: ${JSON.stringify(requestedKeys)}.`
|
|
82
|
+
: "";
|
|
83
|
+
const completion = await openai.chat.completions.create({
|
|
84
|
+
model,
|
|
85
|
+
messages: [
|
|
86
|
+
{
|
|
87
|
+
content: `You are a bot that translates the values of a locales JSON. ${context
|
|
88
|
+
? `The user provided some additional context or guidelines about what to fill in the blanks: "${context}". `
|
|
89
|
+
: ""}The user provides you a JSON with a field named "inputLanguage", which defines the language the values of the JSON are defined in. It also has a field named "outputLanguage", which defines the language you should translate the values to. The last field is named "data", which includes the object with the values to translate. The keys of the values should never be changed. You output only a JSON, which has the same keys as the input, but with translated values. I give you an example input: {"inputLanguage": "English", outputLanguage: "German", "keys": {"hello": "Hello", "world": "World"}}. The output should be {"hello": "Hallo", "world": "Welt"}.${correctivePrompt}`,
|
|
90
|
+
role: "system",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
content: JSON.stringify({
|
|
94
|
+
inputLanguage: input,
|
|
95
|
+
outputLanguage: output,
|
|
96
|
+
data: chunkObject,
|
|
97
|
+
}),
|
|
98
|
+
role: "user",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
response_format: {
|
|
102
|
+
type: "json_object",
|
|
88
103
|
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
});
|
|
105
|
+
try {
|
|
106
|
+
translatedChunk = parseTranslationResponse(completion.choices[0]?.message.content ?? null, requestedKeys);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (!(error instanceof TranslationResponseError) || attempt === 1) {
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!translatedChunk) {
|
|
116
|
+
throw new TranslationResponseError("Translation provider did not return a valid response.");
|
|
117
|
+
}
|
|
103
118
|
// Merge translated chunk with result
|
|
104
119
|
result = { ...result, ...translatedChunk };
|
|
105
120
|
// Report progress
|
|
@@ -115,9 +130,7 @@ export const translateKey = async ({ inputLanguage, context, object, openai, out
|
|
|
115
130
|
export const loadLocalesFile = async (loadPath, locale, namespace, options) => {
|
|
116
131
|
const silent = options?.silent ?? false;
|
|
117
132
|
if (typeof loadPath === "string") {
|
|
118
|
-
const resolvedPath = loadPath
|
|
119
|
-
.replace("{{lng}}", locale)
|
|
120
|
-
.replace("{{ns}}", namespace);
|
|
133
|
+
const resolvedPath = resolveLocaleTemplatePath(loadPath, locale, namespace);
|
|
121
134
|
// Check if file exists, return empty object if it doesn't
|
|
122
135
|
if (!fs.existsSync(resolvedPath)) {
|
|
123
136
|
if (!silent) {
|
|
@@ -138,9 +151,7 @@ export const loadLocalesFile = async (loadPath, locale, namespace, options) => {
|
|
|
138
151
|
};
|
|
139
152
|
export const writeLocalesFile = async (savePath, locale, namespace, data) => {
|
|
140
153
|
if (typeof savePath === "string") {
|
|
141
|
-
const resolvedSavePath = savePath
|
|
142
|
-
.replace("{{lng}}", locale)
|
|
143
|
-
.replace("{{ns}}", namespace);
|
|
154
|
+
const resolvedSavePath = resolveLocaleTemplatePath(savePath, locale, namespace);
|
|
144
155
|
// Ensure directory exists
|
|
145
156
|
const dir = path.dirname(resolvedSavePath);
|
|
146
157
|
if (!fs.existsSync(dir)) {
|
|
@@ -592,6 +603,11 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
592
603
|
},
|
|
593
604
|
};
|
|
594
605
|
}
|
|
606
|
+
const validatedKeys = keys.map(({ key, value, language }) => ({
|
|
607
|
+
key,
|
|
608
|
+
value,
|
|
609
|
+
language: resolveConfiguredLocale(config, language),
|
|
610
|
+
}));
|
|
595
611
|
const log = console.log;
|
|
596
612
|
const namespaceResolutionDebug = process.env.DEBUG_NAMESPACE_RESOLUTION === "1" ||
|
|
597
613
|
process.env.DEBUG_NAMESPACE_RESOLUTION === "true";
|
|
@@ -632,7 +648,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
632
648
|
defaultLocaleKeysByNamespace.set(namespace, {});
|
|
633
649
|
}
|
|
634
650
|
}));
|
|
635
|
-
const preparedKeys =
|
|
651
|
+
const preparedKeys = validatedKeys.map(({ key, value, language }) => {
|
|
636
652
|
const splitKey = key.split(":");
|
|
637
653
|
const hasNamespacedKey = splitKey.length > 1 && namespaces.includes(splitKey[0]);
|
|
638
654
|
const normalizedKey = hasNamespacedKey ? splitKey.slice(1).join(":") : key;
|
|
@@ -709,6 +725,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
709
725
|
const fileIOStartTime = performance.now();
|
|
710
726
|
const localeFiles = new Map();
|
|
711
727
|
const originalKeyCounts = new Map();
|
|
728
|
+
const changedFiles = new Set();
|
|
712
729
|
const loadErrors = [];
|
|
713
730
|
// Load files for all locales × all affected namespaces in parallel (read-only)
|
|
714
731
|
const localeLoadPromises = [];
|
|
@@ -746,6 +763,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
746
763
|
existingKeys[key] = value;
|
|
747
764
|
}
|
|
748
765
|
localeFiles.set(fileKey, existingKeys);
|
|
766
|
+
changedFiles.add(fileKey);
|
|
749
767
|
}
|
|
750
768
|
// Step 7: Batch translate if OpenAI is configured
|
|
751
769
|
const translationStartTime = performance.now();
|
|
@@ -776,12 +794,8 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
776
794
|
object: keysToTranslate,
|
|
777
795
|
openai,
|
|
778
796
|
model,
|
|
779
|
-
})
|
|
780
|
-
.then((translated) => {
|
|
797
|
+
}).then((translated) => {
|
|
781
798
|
translationCache.set(`${inputLanguage}:${targetLocale}`, translated);
|
|
782
|
-
})
|
|
783
|
-
.catch((error) => {
|
|
784
|
-
log(`⚠️ Failed to translate ${keyValues.length} key(s) from ${inputLanguage} to ${targetLocale}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
785
799
|
}));
|
|
786
800
|
}
|
|
787
801
|
}
|
|
@@ -794,7 +808,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
794
808
|
if (!translated)
|
|
795
809
|
continue;
|
|
796
810
|
for (const { key, namespaces } of keyValues) {
|
|
797
|
-
if (translated
|
|
811
|
+
if (Object.hasOwn(translated, key)) {
|
|
798
812
|
for (const namespace of namespaces) {
|
|
799
813
|
const fileKey = `${targetLocale}:${namespace}`;
|
|
800
814
|
// File MUST already be loaded from step 5 - if not, something went wrong
|
|
@@ -804,6 +818,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
804
818
|
}
|
|
805
819
|
// Merge translated key with existing keys (don't overwrite the whole file)
|
|
806
820
|
localeFiles.get(fileKey)[key] = translated[key];
|
|
821
|
+
changedFiles.add(fileKey);
|
|
807
822
|
}
|
|
808
823
|
}
|
|
809
824
|
}
|
|
@@ -828,6 +843,8 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
828
843
|
}
|
|
829
844
|
// All validations passed, now write files sequentially to avoid race conditions
|
|
830
845
|
for (const [fileKey, keys] of localeFiles) {
|
|
846
|
+
if (!changedFiles.has(fileKey))
|
|
847
|
+
continue;
|
|
831
848
|
const [locale, namespace] = fileKey.split(":");
|
|
832
849
|
await writeLocalesFile(savePath, locale, namespace, keys);
|
|
833
850
|
}
|
|
@@ -843,7 +860,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
843
860
|
config.locales.forEach((locale) => {
|
|
844
861
|
if (locale !== language) {
|
|
845
862
|
const translated = translationCache.get(`${language}:${locale}`);
|
|
846
|
-
if (translated
|
|
863
|
+
if (translated && Object.hasOwn(translated, key)) {
|
|
847
864
|
savedLocales.add(locale);
|
|
848
865
|
}
|
|
849
866
|
}
|
|
@@ -852,6 +869,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
852
869
|
return {
|
|
853
870
|
key,
|
|
854
871
|
value,
|
|
872
|
+
providedLanguage: language,
|
|
855
873
|
namespace: resolvedNamespaces.join(", "),
|
|
856
874
|
locale: Array.from(savedLocales).sort().join(", "),
|
|
857
875
|
};
|
|
@@ -866,7 +884,7 @@ export const addTranslationKeys = async ({ keys, config, }) => {
|
|
|
866
884
|
},
|
|
867
885
|
};
|
|
868
886
|
};
|
|
869
|
-
export const addTranslationKey = async ({ key, value, language
|
|
887
|
+
export const addTranslationKey = async ({ key, value, language, config, }) => {
|
|
870
888
|
const startTime = performance.now();
|
|
871
889
|
const result = await addTranslationKeys({
|
|
872
890
|
keys: [{ key, value, language }],
|