@scoutello/i18n-magic 0.30.0 ā 0.32.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-locales.d.ts","sourceRoot":"","sources":["../../src/commands/sync-locales.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"sync-locales.d.ts","sourceRoot":"","sources":["../../src/commands/sync-locales.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AASpD,eAAO,MAAM,WAAW,GAAU,QAAQ,aAAa,kBAmMtD,CAAA"}
|
|
@@ -1,65 +1,124 @@
|
|
|
1
|
-
import { TranslationError, loadLocalesFile, translateKey, writeLocalesFile, } from "../lib/utils.js";
|
|
1
|
+
import { TranslationError, loadLocalesFile, translateKey, writeLocalesFile, findExistingTranslation, } from "../lib/utils.js";
|
|
2
2
|
export const syncLocales = async (config) => {
|
|
3
|
-
const { loadPath, savePath, defaultLocale, namespaces, locales, context, openai, } = config;
|
|
3
|
+
const { loadPath, savePath, defaultLocale, namespaces, locales, context, openai, disableTranslation, } = config;
|
|
4
|
+
console.log(`š Syncing translations for locales: ${locales.join(", ")}`);
|
|
5
|
+
console.log(`š¦ Namespaces: ${namespaces.join(", ")}`);
|
|
6
|
+
console.log(`š Default locale: ${defaultLocale}`);
|
|
7
|
+
console.log(`š§ Translation disabled: ${disableTranslation}`);
|
|
4
8
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
// First, collect all missing keys across all namespaces for each locale
|
|
10
|
+
for (const locale of locales) {
|
|
11
|
+
if (locale === defaultLocale)
|
|
12
|
+
continue;
|
|
13
|
+
console.log(`\nš Processing locale: ${locale}`);
|
|
14
|
+
// Collect all missing keys for this locale across all namespaces
|
|
15
|
+
const allMissingKeys = {};
|
|
16
|
+
const namespaceKeys = {};
|
|
17
|
+
// Load existing keys for all namespaces
|
|
18
|
+
for (const namespace of namespaces) {
|
|
19
|
+
let defaultLocaleKeys;
|
|
16
20
|
let localeKeys;
|
|
21
|
+
try {
|
|
22
|
+
defaultLocaleKeys = await loadLocalesFile(loadPath, defaultLocale, namespace);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.warn(`Warning: Could not load default locale file for ${defaultLocale} (namespace: ${namespace}). Skipping.`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
17
28
|
try {
|
|
18
29
|
localeKeys = await loadLocalesFile(loadPath, locale, namespace);
|
|
19
30
|
}
|
|
20
31
|
catch (error) {
|
|
21
|
-
|
|
32
|
+
const filePath = typeof savePath === "string"
|
|
33
|
+
? savePath.replace("{{lng}}", locale).replace("{{ns}}", namespace)
|
|
34
|
+
: `${locale}/${namespace}.json`;
|
|
35
|
+
console.log(`š Creating new namespace file: ${filePath}`);
|
|
22
36
|
localeKeys = {};
|
|
23
37
|
}
|
|
24
|
-
|
|
38
|
+
namespaceKeys[namespace] = localeKeys;
|
|
25
39
|
// Check which keys from default locale are missing in current locale
|
|
26
40
|
for (const [key, value] of Object.entries(defaultLocaleKeys)) {
|
|
27
41
|
if (!localeKeys[key]) {
|
|
28
|
-
|
|
42
|
+
if (allMissingKeys[key]) {
|
|
43
|
+
// Key already exists, add this namespace to the list
|
|
44
|
+
allMissingKeys[key].namespaces.push(namespace);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// New missing key
|
|
48
|
+
allMissingKeys[key] = {
|
|
49
|
+
value,
|
|
50
|
+
namespaces: [namespace],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
29
53
|
}
|
|
30
54
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
}
|
|
56
|
+
const missingKeysList = Object.keys(allMissingKeys);
|
|
57
|
+
if (missingKeysList.length === 0) {
|
|
58
|
+
console.log(`ā
No missing keys found for ${locale}`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
console.log(`Found ${missingKeysList.length} unique missing keys in ${locale}`);
|
|
62
|
+
// Check for existing translations of these keys in other namespaces
|
|
63
|
+
const keysToTranslate = {};
|
|
64
|
+
const existingTranslations = {};
|
|
65
|
+
for (const key of missingKeysList) {
|
|
66
|
+
const existingValue = await findExistingTranslation(key, namespaces, locale, loadPath);
|
|
67
|
+
if (existingValue) {
|
|
68
|
+
console.log(`š Reusing existing translation for "${key}": "${existingValue}"`);
|
|
69
|
+
existingTranslations[key] = existingValue;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
keysToTranslate[key] = allMissingKeys[key].value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
let translatedValues = {};
|
|
76
|
+
// Translate only the keys that don't have existing translations
|
|
77
|
+
if (Object.keys(keysToTranslate).length > 0 && !disableTranslation) {
|
|
78
|
+
console.log(`š¤ Translating ${Object.keys(keysToTranslate).length} new keys for ${locale}`);
|
|
79
|
+
try {
|
|
80
|
+
translatedValues = await translateKey({
|
|
81
|
+
inputLanguage: defaultLocale,
|
|
82
|
+
outputLanguage: locale,
|
|
83
|
+
context,
|
|
84
|
+
object: keysToTranslate,
|
|
85
|
+
openai,
|
|
86
|
+
model: config.model,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
throw new TranslationError(`Failed to translate keys for locale "${locale}"`, locale, undefined, error instanceof Error ? error : undefined);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Combine existing translations with new translations
|
|
94
|
+
const allTranslations = { ...existingTranslations, ...translatedValues };
|
|
95
|
+
// Distribute translations to all relevant namespaces
|
|
96
|
+
for (const namespace of namespaces) {
|
|
97
|
+
let hasChanges = false;
|
|
98
|
+
const updatedKeys = { ...namespaceKeys[namespace] };
|
|
99
|
+
for (const key of missingKeysList) {
|
|
100
|
+
if (allMissingKeys[key].namespaces.includes(namespace)) {
|
|
101
|
+
const translation = disableTranslation
|
|
102
|
+
? ""
|
|
103
|
+
: allTranslations[key] || "";
|
|
104
|
+
updatedKeys[key] = translation;
|
|
105
|
+
hasChanges = true;
|
|
47
106
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
...localeKeys,
|
|
51
|
-
...translatedValues,
|
|
52
|
-
};
|
|
107
|
+
}
|
|
108
|
+
if (hasChanges) {
|
|
53
109
|
try {
|
|
54
|
-
await writeLocalesFile(savePath, locale, namespace,
|
|
110
|
+
await writeLocalesFile(savePath, locale, namespace, updatedKeys);
|
|
111
|
+
const addedKeysCount = Object.keys(allMissingKeys).filter((key) => allMissingKeys[key].namespaces.includes(namespace)).length;
|
|
112
|
+
if (disableTranslation) {
|
|
113
|
+
console.log(`š Created empty namespace file for ${locale} (${namespace}): ${addedKeysCount} keys`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.log(`ā
Updated ${locale} (${namespace}): ${addedKeysCount} keys`);
|
|
117
|
+
}
|
|
55
118
|
}
|
|
56
119
|
catch (error) {
|
|
57
120
|
throw new TranslationError(`Failed to save translations for locale "${locale}" (namespace: ${namespace})`, locale, namespace, error instanceof Error ? error : undefined);
|
|
58
121
|
}
|
|
59
|
-
console.log(`Successfully translated and saved ${Object.keys(missingKeys).length} keys for ${locale} (namespace: ${namespace})`);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
console.log(`No missing keys found for ${locale} (namespace: ${namespace})`);
|
|
63
122
|
}
|
|
64
123
|
}
|
|
65
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-locales.js","sourceRoot":"","sources":["../../src/commands/sync-locales.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,
|
|
1
|
+
{"version":3,"file":"sync-locales.js","sourceRoot":"","sources":["../../src/commands/sync-locales.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scoutello/i18n-magic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Intelligent CLI toolkit that automates internationalization workflows with AI-powered translations for JavaScript/TypeScript projects",
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
loadLocalesFile,
|
|
5
5
|
translateKey,
|
|
6
6
|
writeLocalesFile,
|
|
7
|
+
findExistingTranslation,
|
|
7
8
|
} from "../lib/utils.js"
|
|
8
9
|
|
|
9
10
|
export const syncLocales = async (config: Configuration) => {
|
|
@@ -15,87 +16,170 @@ export const syncLocales = async (config: Configuration) => {
|
|
|
15
16
|
locales,
|
|
16
17
|
context,
|
|
17
18
|
openai,
|
|
19
|
+
disableTranslation,
|
|
18
20
|
} = config
|
|
19
21
|
|
|
22
|
+
console.log(`š Syncing translations for locales: ${locales.join(", ")}`)
|
|
23
|
+
console.log(`š¦ Namespaces: ${namespaces.join(", ")}`)
|
|
24
|
+
console.log(`š Default locale: ${defaultLocale}`)
|
|
25
|
+
console.log(`š§ Translation disabled: ${disableTranslation}`)
|
|
26
|
+
|
|
20
27
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
// First, collect all missing keys across all namespaces for each locale
|
|
29
|
+
for (const locale of locales) {
|
|
30
|
+
if (locale === defaultLocale) continue
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
defaultLocaleKeys = await loadLocalesFile(
|
|
26
|
-
loadPath,
|
|
27
|
-
defaultLocale,
|
|
28
|
-
namespace,
|
|
29
|
-
)
|
|
30
|
-
} catch (error) {
|
|
31
|
-
throw new TranslationError(
|
|
32
|
-
`Failed to load default locale file for namespace "${namespace}"`,
|
|
33
|
-
defaultLocale,
|
|
34
|
-
namespace,
|
|
35
|
-
error instanceof Error ? error : undefined,
|
|
36
|
-
)
|
|
37
|
-
}
|
|
32
|
+
console.log(`\nš Processing locale: ${locale}`)
|
|
38
33
|
|
|
39
|
-
for
|
|
40
|
-
|
|
34
|
+
// Collect all missing keys for this locale across all namespaces
|
|
35
|
+
const allMissingKeys: Record<
|
|
36
|
+
string,
|
|
37
|
+
{ value: string; namespaces: string[] }
|
|
38
|
+
> = {}
|
|
39
|
+
const namespaceKeys: Record<string, Record<string, string>> = {}
|
|
41
40
|
|
|
41
|
+
// Load existing keys for all namespaces
|
|
42
|
+
for (const namespace of namespaces) {
|
|
43
|
+
let defaultLocaleKeys: Record<string, string>
|
|
42
44
|
let localeKeys: Record<string, string>
|
|
45
|
+
|
|
43
46
|
try {
|
|
44
|
-
|
|
47
|
+
defaultLocaleKeys = await loadLocalesFile(
|
|
48
|
+
loadPath,
|
|
49
|
+
defaultLocale,
|
|
50
|
+
namespace,
|
|
51
|
+
)
|
|
45
52
|
} catch (error) {
|
|
46
53
|
console.warn(
|
|
47
|
-
`Warning: Could not load locale file for ${
|
|
54
|
+
`Warning: Could not load default locale file for ${defaultLocale} (namespace: ${namespace}). Skipping.`,
|
|
48
55
|
)
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
localeKeys = await loadLocalesFile(loadPath, locale, namespace)
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const filePath =
|
|
63
|
+
typeof savePath === "string"
|
|
64
|
+
? savePath.replace("{{lng}}", locale).replace("{{ns}}", namespace)
|
|
65
|
+
: `${locale}/${namespace}.json`
|
|
66
|
+
console.log(`š Creating new namespace file: ${filePath}`)
|
|
49
67
|
localeKeys = {}
|
|
50
68
|
}
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
namespaceKeys[namespace] = localeKeys
|
|
53
71
|
|
|
54
72
|
// Check which keys from default locale are missing in current locale
|
|
55
73
|
for (const [key, value] of Object.entries(defaultLocaleKeys)) {
|
|
56
74
|
if (!localeKeys[key]) {
|
|
57
|
-
|
|
75
|
+
if (allMissingKeys[key]) {
|
|
76
|
+
// Key already exists, add this namespace to the list
|
|
77
|
+
allMissingKeys[key].namespaces.push(namespace)
|
|
78
|
+
} else {
|
|
79
|
+
// New missing key
|
|
80
|
+
allMissingKeys[key] = {
|
|
81
|
+
value,
|
|
82
|
+
namespaces: [namespace],
|
|
83
|
+
}
|
|
84
|
+
}
|
|
58
85
|
}
|
|
59
86
|
}
|
|
87
|
+
}
|
|
60
88
|
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
const missingKeysList = Object.keys(allMissingKeys)
|
|
90
|
+
if (missingKeysList.length === 0) {
|
|
91
|
+
console.log(`ā
No missing keys found for ${locale}`)
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(
|
|
96
|
+
`Found ${missingKeysList.length} unique missing keys in ${locale}`,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
// Check for existing translations of these keys in other namespaces
|
|
100
|
+
const keysToTranslate: Record<string, string> = {}
|
|
101
|
+
const existingTranslations: Record<string, string> = {}
|
|
102
|
+
|
|
103
|
+
for (const key of missingKeysList) {
|
|
104
|
+
const existingValue = await findExistingTranslation(
|
|
105
|
+
key,
|
|
106
|
+
namespaces,
|
|
107
|
+
locale,
|
|
108
|
+
loadPath,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if (existingValue) {
|
|
63
112
|
console.log(
|
|
64
|
-
|
|
113
|
+
`š Reusing existing translation for "${key}": "${existingValue}"`,
|
|
65
114
|
)
|
|
115
|
+
existingTranslations[key] = existingValue
|
|
116
|
+
} else {
|
|
117
|
+
keysToTranslate[key] = allMissingKeys[key].value
|
|
118
|
+
}
|
|
119
|
+
}
|
|
66
120
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
121
|
+
let translatedValues: Record<string, string> = {}
|
|
122
|
+
|
|
123
|
+
// Translate only the keys that don't have existing translations
|
|
124
|
+
if (Object.keys(keysToTranslate).length > 0 && !disableTranslation) {
|
|
125
|
+
console.log(
|
|
126
|
+
`š¤ Translating ${Object.keys(keysToTranslate).length} new keys for ${locale}`,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
translatedValues = await translateKey({
|
|
131
|
+
inputLanguage: defaultLocale,
|
|
132
|
+
outputLanguage: locale,
|
|
133
|
+
context,
|
|
134
|
+
object: keysToTranslate,
|
|
135
|
+
openai,
|
|
136
|
+
model: config.model,
|
|
137
|
+
})
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw new TranslationError(
|
|
140
|
+
`Failed to translate keys for locale "${locale}"`,
|
|
141
|
+
locale,
|
|
142
|
+
undefined,
|
|
143
|
+
error instanceof Error ? error : undefined,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Combine existing translations with new translations
|
|
149
|
+
const allTranslations = { ...existingTranslations, ...translatedValues }
|
|
85
150
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
151
|
+
// Distribute translations to all relevant namespaces
|
|
152
|
+
for (const namespace of namespaces) {
|
|
153
|
+
let hasChanges = false
|
|
154
|
+
const updatedKeys = { ...namespaceKeys[namespace] }
|
|
155
|
+
|
|
156
|
+
for (const key of missingKeysList) {
|
|
157
|
+
if (allMissingKeys[key].namespaces.includes(namespace)) {
|
|
158
|
+
const translation = disableTranslation
|
|
159
|
+
? ""
|
|
160
|
+
: allTranslations[key] || ""
|
|
161
|
+
updatedKeys[key] = translation
|
|
162
|
+
hasChanges = true
|
|
90
163
|
}
|
|
164
|
+
}
|
|
91
165
|
|
|
166
|
+
if (hasChanges) {
|
|
92
167
|
try {
|
|
93
|
-
await writeLocalesFile(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
namespace,
|
|
97
|
-
|
|
98
|
-
|
|
168
|
+
await writeLocalesFile(savePath, locale, namespace, updatedKeys)
|
|
169
|
+
|
|
170
|
+
const addedKeysCount = Object.keys(allMissingKeys).filter((key) =>
|
|
171
|
+
allMissingKeys[key].namespaces.includes(namespace),
|
|
172
|
+
).length
|
|
173
|
+
|
|
174
|
+
if (disableTranslation) {
|
|
175
|
+
console.log(
|
|
176
|
+
`š Created empty namespace file for ${locale} (${namespace}): ${addedKeysCount} keys`,
|
|
177
|
+
)
|
|
178
|
+
} else {
|
|
179
|
+
console.log(
|
|
180
|
+
`ā
Updated ${locale} (${namespace}): ${addedKeysCount} keys`,
|
|
181
|
+
)
|
|
182
|
+
}
|
|
99
183
|
} catch (error) {
|
|
100
184
|
throw new TranslationError(
|
|
101
185
|
`Failed to save translations for locale "${locale}" (namespace: ${namespace})`,
|
|
@@ -104,14 +188,6 @@ export const syncLocales = async (config: Configuration) => {
|
|
|
104
188
|
error instanceof Error ? error : undefined,
|
|
105
189
|
)
|
|
106
190
|
}
|
|
107
|
-
|
|
108
|
-
console.log(
|
|
109
|
-
`Successfully translated and saved ${Object.keys(missingKeys).length} keys for ${locale} (namespace: ${namespace})`,
|
|
110
|
-
)
|
|
111
|
-
} else {
|
|
112
|
-
console.log(
|
|
113
|
-
`No missing keys found for ${locale} (namespace: ${namespace})`,
|
|
114
|
-
)
|
|
115
191
|
}
|
|
116
192
|
}
|
|
117
193
|
}
|