@localheroai/cli 0.0.5 → 0.0.7
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 → README.md} +28 -10
- package/dist/api/auth.d.ts +2 -0
- package/dist/api/auth.js +28 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/client.d.ts +3 -0
- package/dist/api/client.js +80 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/imports.d.ts +5 -0
- package/dist/api/imports.js +43 -0
- package/dist/api/imports.js.map +1 -0
- package/dist/api/projects.d.ts +2 -0
- package/dist/api/projects.js +42 -0
- package/dist/api/projects.js.map +1 -0
- package/dist/api/translations.d.ts +15 -0
- package/dist/api/translations.js +71 -0
- package/dist/api/translations.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +79 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/_sync.js +22 -0
- package/dist/commands/_sync.js.map +1 -0
- package/dist/commands/_translate.js +3 -0
- package/dist/commands/_translate.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +439 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +16 -0
- package/dist/commands/login.js +58 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/pull.js +22 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.js +56 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/sync.d.ts +20 -0
- package/dist/commands/sync.js +22 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/translate.d.ts +14 -0
- package/dist/commands/translate.js +152 -0
- package/dist/commands/translate.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +75 -0
- package/dist/types/index.js +17 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/translate/index.js +2 -0
- package/dist/types/translate/index.js.map +1 -0
- package/dist/utils/auth.d.ts +2 -0
- package/dist/utils/auth.js +29 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/common.js +9 -0
- package/dist/utils/common.js.map +1 -0
- package/dist/utils/config.d.ts +23 -0
- package/dist/utils/config.js +137 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/errors.js +37 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/files.d.ts +32 -0
- package/dist/utils/files.js +355 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/git.d.ts +21 -0
- package/dist/utils/git.js +87 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/github.d.ts +241 -0
- package/dist/utils/github.js +161 -0
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/import-service.d.ts +4 -0
- package/dist/utils/import-service.js +218 -0
- package/dist/utils/import-service.js.map +1 -0
- package/dist/utils/prompt-service.d.ts +44 -0
- package/dist/utils/prompt-service.js +104 -0
- package/dist/utils/prompt-service.js.map +1 -0
- package/dist/utils/sync-service.d.ts +58 -0
- package/dist/utils/sync-service.js +159 -0
- package/dist/utils/sync-service.js.map +1 -0
- package/dist/utils/translation-processor.js +197 -0
- package/dist/utils/translation-processor.js.map +1 -0
- package/dist/utils/translation-updater/common.d.ts +6 -0
- package/{src → dist}/utils/translation-updater/common.js +16 -10
- package/dist/utils/translation-updater/common.js.map +1 -0
- package/dist/utils/translation-updater/index.d.ts +5 -0
- package/{src → dist}/utils/translation-updater/index.js +21 -9
- package/dist/utils/translation-updater/index.js.map +1 -0
- package/dist/utils/translation-updater/json-handler.d.ts +5 -0
- package/dist/utils/translation-updater/json-handler.js +123 -0
- package/dist/utils/translation-updater/json-handler.js.map +1 -0
- package/dist/utils/translation-updater/yaml-handler.d.ts +5 -0
- package/dist/utils/translation-updater/yaml-handler.js +193 -0
- package/dist/utils/translation-updater/yaml-handler.js.map +1 -0
- package/dist/utils/translation-utils.d.ts +30 -0
- package/dist/utils/translation-utils.js +334 -0
- package/dist/utils/translation-utils.js.map +1 -0
- package/dist/utils/updater.js +38 -0
- package/dist/utils/updater.js.map +1 -0
- package/package.json +26 -26
- package/src/api/auth.js +0 -24
- package/src/api/client.js +0 -83
- package/src/api/imports.js +0 -22
- package/src/api/projects.js +0 -24
- package/src/api/translations.js +0 -58
- package/src/cli.js +0 -78
- package/src/commands/init.js +0 -485
- package/src/commands/login.js +0 -80
- package/src/commands/sync.js +0 -28
- package/src/commands/translate.js +0 -267
- package/src/utils/auth.js +0 -23
- package/src/utils/config.js +0 -125
- package/src/utils/files.js +0 -381
- package/src/utils/git.js +0 -72
- package/src/utils/github.js +0 -128
- package/src/utils/import-service.js +0 -128
- package/src/utils/prompt-service.js +0 -67
- package/src/utils/sync-service.js +0 -147
- package/src/utils/translation-updater/json-handler.js +0 -111
- package/src/utils/translation-updater/yaml-handler.js +0 -207
- package/src/utils/translation-utils.js +0 -278
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { flattenTranslations, parseFile } from './files.js';
|
|
3
|
+
/**
|
|
4
|
+
* Find keys in source that are missing from target
|
|
5
|
+
* @param sourceKeys The source translation keys
|
|
6
|
+
* @param targetKeys The target translation keys
|
|
7
|
+
* @returns Object containing missing and skipped keys
|
|
8
|
+
*/
|
|
9
|
+
export function findMissingTranslations(sourceKeys, targetKeys) {
|
|
10
|
+
const missingKeys = {};
|
|
11
|
+
const skippedKeys = {};
|
|
12
|
+
for (const [key, details] of Object.entries(sourceKeys)) {
|
|
13
|
+
if (typeof details === 'string') {
|
|
14
|
+
if (details.toLowerCase().includes('wip_') ||
|
|
15
|
+
details.toLowerCase().includes('_wip') ||
|
|
16
|
+
details.toLowerCase().includes('__skip_translation__')) {
|
|
17
|
+
skippedKeys[key] = {
|
|
18
|
+
value: details,
|
|
19
|
+
reason: 'wip'
|
|
20
|
+
};
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (!targetKeys[key]) {
|
|
24
|
+
missingKeys[key] = {
|
|
25
|
+
value: details,
|
|
26
|
+
sourceKey: key
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (typeof details === 'boolean') {
|
|
32
|
+
if (!targetKeys[key]) {
|
|
33
|
+
missingKeys[key] = {
|
|
34
|
+
value: details,
|
|
35
|
+
sourceKey: key
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (typeof details === 'object' && details !== null &&
|
|
41
|
+
typeof details.value === 'string' &&
|
|
42
|
+
(details.value.toLowerCase().includes('wip_') || details.value.toLowerCase().includes('_wip') ||
|
|
43
|
+
details.value.toLowerCase().includes('__skip_translation__'))) {
|
|
44
|
+
skippedKeys[key] = {
|
|
45
|
+
...details,
|
|
46
|
+
reason: 'wip'
|
|
47
|
+
};
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!targetKeys[key]) {
|
|
51
|
+
if (typeof details === 'object' && details !== null && 'value' in details) {
|
|
52
|
+
missingKeys[key] = {
|
|
53
|
+
...details,
|
|
54
|
+
sourceKey: key
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
missingKeys[key] = {
|
|
59
|
+
value: details,
|
|
60
|
+
sourceKey: key
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { missingKeys, skippedKeys };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Find all missing translations grouped by locale and source file
|
|
69
|
+
* @param sourceFiles Array of source translation files
|
|
70
|
+
* @param targetFilesByLocale Record of target files grouped by locale
|
|
71
|
+
* @param config Project configuration with sourceLocale and outputLocales
|
|
72
|
+
* @param verbose Whether to show verbose output
|
|
73
|
+
* @param logger Optional logger for console output
|
|
74
|
+
* @returns Record of missing translations by locale and source file
|
|
75
|
+
*/
|
|
76
|
+
export function findMissingTranslationsByLocale(sourceFiles, targetFilesByLocale, config, verbose, logger = console) {
|
|
77
|
+
const missingByLocale = {};
|
|
78
|
+
for (const sourceFile of sourceFiles) {
|
|
79
|
+
if (!sourceFile.content)
|
|
80
|
+
continue;
|
|
81
|
+
const sourceContentRaw = Buffer.from(sourceFile.content, 'base64').toString();
|
|
82
|
+
const sourceContent = parseFile(sourceContentRaw, sourceFile.format, sourceFile.path);
|
|
83
|
+
const sourceKeys = flattenTranslations(sourceContent[config.sourceLocale] || sourceContent);
|
|
84
|
+
for (const targetLocale of config.outputLocales) {
|
|
85
|
+
const targetFiles = targetFilesByLocale[targetLocale] || [];
|
|
86
|
+
const result = processLocaleTranslations(sourceKeys, targetLocale, targetFiles, sourceFile, config.sourceLocale);
|
|
87
|
+
if (Object.keys(result.missingKeys).length > 0) {
|
|
88
|
+
const sourceFilePath = sourceFile.path;
|
|
89
|
+
const localeSourceKey = `${targetLocale}:${sourceFilePath}`;
|
|
90
|
+
// Create or update the entry for this locale and source file
|
|
91
|
+
if (!missingByLocale[localeSourceKey]) {
|
|
92
|
+
missingByLocale[localeSourceKey] = {
|
|
93
|
+
locale: targetLocale,
|
|
94
|
+
path: sourceFilePath,
|
|
95
|
+
targetPath: result.targetPath,
|
|
96
|
+
keys: {},
|
|
97
|
+
keyCount: 0
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Now it's safe to update since we made sure it exists
|
|
101
|
+
const entry = missingByLocale[localeSourceKey];
|
|
102
|
+
entry.keys = {
|
|
103
|
+
...entry.keys,
|
|
104
|
+
...result.missingKeys
|
|
105
|
+
};
|
|
106
|
+
entry.keyCount = (entry.keyCount || 0) + Object.keys(result.missingKeys).length;
|
|
107
|
+
}
|
|
108
|
+
if (verbose && Object.keys(result.skippedKeys).length > 0) {
|
|
109
|
+
logger.log(`\nℹ Skipped ${Object.keys(result.skippedKeys).length} keys marked as WIP in ${sourceFile.path}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return missingByLocale;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Batch missing keys by source file
|
|
117
|
+
* @param sourceFiles Array of source translation files
|
|
118
|
+
* @param missingByLocale Record of missing keys by locale and source file
|
|
119
|
+
* @returns Result containing batches and errors
|
|
120
|
+
*/
|
|
121
|
+
export function batchKeysWithMissing(sourceFiles, missingByLocale) {
|
|
122
|
+
const MAX_BATCH_SIZE = 100;
|
|
123
|
+
const batches = [];
|
|
124
|
+
const errors = [];
|
|
125
|
+
const entriesBySourceFile = {};
|
|
126
|
+
// Group entries by source file
|
|
127
|
+
for (const [localeSourceKey, entry] of Object.entries(missingByLocale)) {
|
|
128
|
+
const { path: sourceFilePath } = entry;
|
|
129
|
+
if (!entriesBySourceFile[sourceFilePath]) {
|
|
130
|
+
entriesBySourceFile[sourceFilePath] = {
|
|
131
|
+
localeEntries: [],
|
|
132
|
+
locales: new Set(),
|
|
133
|
+
keys: {}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
entriesBySourceFile[sourceFilePath].localeEntries.push(localeSourceKey);
|
|
137
|
+
entriesBySourceFile[sourceFilePath].locales.add(entry.locale);
|
|
138
|
+
for (const [key, value] of Object.entries(entry.keys)) {
|
|
139
|
+
if (!entriesBySourceFile[sourceFilePath].keys[key]) {
|
|
140
|
+
entriesBySourceFile[sourceFilePath].keys[key] = {};
|
|
141
|
+
}
|
|
142
|
+
entriesBySourceFile[sourceFilePath].keys[key][entry.locale] = value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Process each source file
|
|
146
|
+
for (const [sourceFilePath, data] of Object.entries(entriesBySourceFile)) {
|
|
147
|
+
const sourceFile = sourceFiles.find(f => f.path === sourceFilePath);
|
|
148
|
+
if (!sourceFile) {
|
|
149
|
+
errors.push({
|
|
150
|
+
type: 'missing_source_file',
|
|
151
|
+
message: `No source file found for path: ${sourceFilePath}`,
|
|
152
|
+
path: sourceFilePath
|
|
153
|
+
});
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const allKeys = Object.entries(data.keys);
|
|
157
|
+
const chunkedKeys = [];
|
|
158
|
+
for (let i = 0; i < allKeys.length; i += MAX_BATCH_SIZE) {
|
|
159
|
+
chunkedKeys.push(allKeys.slice(i, i + MAX_BATCH_SIZE));
|
|
160
|
+
}
|
|
161
|
+
for (const keyChunk of chunkedKeys) {
|
|
162
|
+
const contentObj = { keys: {} };
|
|
163
|
+
for (const [key, translations] of keyChunk) {
|
|
164
|
+
const value = Object.values(translations)[0];
|
|
165
|
+
let extractedValue;
|
|
166
|
+
if (Array.isArray(value)) {
|
|
167
|
+
extractedValue = value;
|
|
168
|
+
}
|
|
169
|
+
else if (typeof value === 'boolean') {
|
|
170
|
+
extractedValue = value;
|
|
171
|
+
}
|
|
172
|
+
else if (typeof value === 'string') {
|
|
173
|
+
extractedValue = value;
|
|
174
|
+
}
|
|
175
|
+
else if (typeof value === 'object' && value !== null) {
|
|
176
|
+
if ('value' in value) {
|
|
177
|
+
extractedValue = value.value;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
extractedValue = JSON.stringify(value);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
extractedValue = String(value);
|
|
185
|
+
}
|
|
186
|
+
contentObj.keys[key] = {
|
|
187
|
+
value: extractedValue
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
batches.push({
|
|
191
|
+
sourceFilePath,
|
|
192
|
+
sourceFile: {
|
|
193
|
+
path: sourceFile.path,
|
|
194
|
+
format: sourceFile.format,
|
|
195
|
+
content: Buffer.from(JSON.stringify(contentObj)).toString('base64')
|
|
196
|
+
},
|
|
197
|
+
localeEntries: data.localeEntries,
|
|
198
|
+
locales: Array.from(data.locales)
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { batches, errors };
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Find a target file that corresponds to the source file
|
|
206
|
+
* @param targetFiles Array of target translation files
|
|
207
|
+
* @param targetLocale The target locale
|
|
208
|
+
* @param sourceFile The source file
|
|
209
|
+
* @param sourceLocale The source locale
|
|
210
|
+
* @returns The matching target file, or undefined if not found
|
|
211
|
+
*/
|
|
212
|
+
export function findTargetFile(targetFiles, targetLocale, sourceFile, sourceLocale) {
|
|
213
|
+
// First try exact directory matching (existing logic)
|
|
214
|
+
let found = targetFiles.find(f => f.locale === targetLocale &&
|
|
215
|
+
path.dirname(f.path) === path.dirname(sourceFile.path) &&
|
|
216
|
+
path.basename(f.path, path.extname(f.path)) === path.basename(sourceFile.path, path.extname(sourceFile.path)).replace(sourceLocale, targetLocale));
|
|
217
|
+
if (found)
|
|
218
|
+
return found;
|
|
219
|
+
// Then try filename-based matching regardless of directory (existing logic)
|
|
220
|
+
found = targetFiles.find(f => f.locale === targetLocale &&
|
|
221
|
+
path.basename(f.path, path.extname(f.path)) === path.basename(sourceFile.path, path.extname(sourceFile.path)).replace(sourceLocale, targetLocale));
|
|
222
|
+
if (found)
|
|
223
|
+
return found;
|
|
224
|
+
const sourceDirParts = path.dirname(sourceFile.path).split(path.sep);
|
|
225
|
+
const sourceFileBaseName = path.basename(sourceFile.path, path.extname(sourceFile.path));
|
|
226
|
+
// Check for corresponding file in subdirectories or parent directories
|
|
227
|
+
return targetFiles.find(f => {
|
|
228
|
+
if (f.locale !== targetLocale)
|
|
229
|
+
return false;
|
|
230
|
+
// Handle cases where files are in different subdirectories
|
|
231
|
+
const targetDirParts = path.dirname(f.path).split(path.sep);
|
|
232
|
+
const targetFileBaseName = path.basename(f.path, path.extname(f.path));
|
|
233
|
+
if (sourceFileBaseName === sourceLocale &&
|
|
234
|
+
targetFileBaseName === targetLocale &&
|
|
235
|
+
sourceDirParts.length === targetDirParts.length) {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
// Nested directory structure
|
|
239
|
+
if (sourceDirParts.includes(sourceLocale) && targetDirParts.includes(targetLocale)) {
|
|
240
|
+
const sourceBasePath = sourceDirParts.slice(0, sourceDirParts.indexOf(sourceLocale)).join(path.sep);
|
|
241
|
+
const targetBasePath = targetDirParts.slice(0, targetDirParts.indexOf(targetLocale)).join(path.sep);
|
|
242
|
+
return sourceBasePath === targetBasePath &&
|
|
243
|
+
sourceFileBaseName === targetFileBaseName;
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Generate a target file path based on source file and locales
|
|
250
|
+
* @param sourceFile The source translation file
|
|
251
|
+
* @param targetLocale The target locale
|
|
252
|
+
* @param sourceLocale The source locale
|
|
253
|
+
* @returns The generated target file path
|
|
254
|
+
*/
|
|
255
|
+
export function generateTargetPath(sourceFile, targetLocale, sourceLocale) {
|
|
256
|
+
const sourceExt = path.extname(sourceFile.path);
|
|
257
|
+
const sourceDir = path.dirname(sourceFile.path);
|
|
258
|
+
const sourceName = path.basename(sourceFile.path, sourceExt);
|
|
259
|
+
// Case 1: File is named exactly as the source locale (e.g., "en.yml")
|
|
260
|
+
if (sourceName === sourceLocale) {
|
|
261
|
+
return path.join(sourceDir, `${targetLocale}${sourceExt}`);
|
|
262
|
+
}
|
|
263
|
+
// Case 2: File ends with .locale (e.g., "translations.en.yml")
|
|
264
|
+
if (sourceName.endsWith(`.${sourceLocale}`)) {
|
|
265
|
+
const baseName = sourceName.slice(0, -(sourceLocale.length + 1));
|
|
266
|
+
return path.join(sourceDir, `${baseName}.${targetLocale}${sourceExt}`);
|
|
267
|
+
}
|
|
268
|
+
// Case 3: File uses hyphen-locale format (e.g., "translations-en.yml")
|
|
269
|
+
if (sourceName.includes(`-${sourceLocale}`)) {
|
|
270
|
+
const baseName = sourceName.slice(0, -(sourceLocale.length + 1));
|
|
271
|
+
return path.join(sourceDir, `${baseName}-${targetLocale}${sourceExt}`);
|
|
272
|
+
}
|
|
273
|
+
// Case 4: Source locale is a directory name
|
|
274
|
+
const sourceParentDir = path.basename(sourceDir);
|
|
275
|
+
if (sourceParentDir === sourceLocale) {
|
|
276
|
+
const grandParentDir = path.dirname(sourceDir);
|
|
277
|
+
return path.join(grandParentDir, targetLocale, path.basename(sourceFile.path));
|
|
278
|
+
}
|
|
279
|
+
// Default case: If none of the above patterns match,
|
|
280
|
+
// construct the target path by replacing the locale in the filename only
|
|
281
|
+
const dirPath = path.dirname(sourceFile.path);
|
|
282
|
+
const fileName = path.basename(sourceFile.path);
|
|
283
|
+
const localeRegex = new RegExp(`\\b${sourceLocale}\\b`, 'g');
|
|
284
|
+
const newFileName = fileName.replace(localeRegex, targetLocale);
|
|
285
|
+
return path.join(dirPath, newFileName);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Process target content to extract translation keys
|
|
289
|
+
* @param targetContent The target content object
|
|
290
|
+
* @param targetLocale The target locale
|
|
291
|
+
* @returns Flattened translations
|
|
292
|
+
*/
|
|
293
|
+
export function processTargetContent(targetContent, targetLocale) {
|
|
294
|
+
if (targetContent[targetLocale]) {
|
|
295
|
+
return flattenTranslations(targetContent[targetLocale]);
|
|
296
|
+
}
|
|
297
|
+
return flattenTranslations(targetContent);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Process translations for a specific locale
|
|
301
|
+
* @param sourceKeys The source translation keys
|
|
302
|
+
* @param targetLocale The target locale
|
|
303
|
+
* @param targetFiles Array of target translation files
|
|
304
|
+
* @param sourceFile The source file
|
|
305
|
+
* @param sourceLocale The source locale
|
|
306
|
+
* @returns Result with target path and missing/skipped keys
|
|
307
|
+
*/
|
|
308
|
+
export function processLocaleTranslations(sourceKeys, targetLocale, targetFiles, sourceFile, sourceLocale) {
|
|
309
|
+
try {
|
|
310
|
+
const targetFile = findTargetFile(targetFiles, targetLocale, sourceFile, sourceLocale);
|
|
311
|
+
let targetKeys = {};
|
|
312
|
+
let targetPath = '';
|
|
313
|
+
if (targetFile) {
|
|
314
|
+
const targetContentRaw = Buffer.from(targetFile.content || '', 'base64').toString();
|
|
315
|
+
const targetContent = parseFile(targetContentRaw, targetFile.format, targetFile.path);
|
|
316
|
+
targetKeys = processTargetContent(targetContent, targetLocale);
|
|
317
|
+
targetPath = targetFile.path;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
targetPath = generateTargetPath(sourceFile, targetLocale, sourceLocale);
|
|
321
|
+
}
|
|
322
|
+
const { missingKeys, skippedKeys } = findMissingTranslations(sourceKeys, targetKeys);
|
|
323
|
+
return {
|
|
324
|
+
targetPath,
|
|
325
|
+
missingKeys,
|
|
326
|
+
skippedKeys,
|
|
327
|
+
targetFile
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
throw new Error(`Failed to process translations for ${targetLocale}: ${error.message}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=translation-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translation-utils.js","sourceRoot":"","sources":["../../src/utils/translation-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { readTranslationFile } from './files.js';
|
|
2
|
+
import { updateTranslationFile } from './translation-updater/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Apply translation updates to files
|
|
5
|
+
* @param files Array of translation files to update
|
|
6
|
+
* @returns Result of the update operation
|
|
7
|
+
*/
|
|
8
|
+
export async function applyTranslationUpdates(files) {
|
|
9
|
+
const errors = [];
|
|
10
|
+
try {
|
|
11
|
+
for (const file of files) {
|
|
12
|
+
try {
|
|
13
|
+
const currentFile = await readTranslationFile(file.path);
|
|
14
|
+
await updateTranslationFile(file.path, file.translations || {}, file.locale, currentFile.path);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
errors.push({
|
|
18
|
+
path: file.path,
|
|
19
|
+
message: error instanceof Error ? error.message : String(error)
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
success: errors.length === 0,
|
|
25
|
+
errors: errors.length > 0 ? errors : undefined
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return {
|
|
30
|
+
success: false,
|
|
31
|
+
errors: [{
|
|
32
|
+
path: 'unknown',
|
|
33
|
+
message: error instanceof Error ? error.message : String(error)
|
|
34
|
+
}]
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=updater.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updater.js","sourceRoot":"","sources":["../../src/utils/updater.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAOvE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAAwB;IACpE,MAAM,MAAM,GAA6C,EAAE,CAAC;IAE5D,IAAI,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACzD,MAAM,qBAAqB,CACzB,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,YAAY,IAAI,EAAE,EACvB,IAAI,CAAC,MAAM,EACX,WAAW,CAAC,IAAI,CACjB,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAChE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;YAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SAC/C,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,CAAC;oBACP,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAChE,CAAC;SACH,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localheroai/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "CLI tool for managing translations with LocalHero.ai",
|
|
5
5
|
"homepage": "https://localhero.ai",
|
|
6
6
|
"repository": {
|
|
@@ -8,25 +8,31 @@
|
|
|
8
8
|
"url": "https://github.com/localheroai/cli"
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|
|
11
|
-
"main": "
|
|
11
|
+
"main": "dist/index.js",
|
|
12
12
|
"bin": {
|
|
13
|
-
"localheroai": "./
|
|
13
|
+
"localheroai": "./dist/cli.js"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"
|
|
16
|
+
"build": "tsc && chmod +x dist/cli.js",
|
|
17
|
+
"start": "node dist/cli.js",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
17
20
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
18
|
-
"lint": "eslint
|
|
19
|
-
"postinstall": "chmod +x
|
|
21
|
+
"lint": "eslint 'src/**/*.{js,ts}'",
|
|
22
|
+
"postinstall": "chmod +x dist/cli.js",
|
|
23
|
+
"build:run": "npm run build && npm start"
|
|
20
24
|
},
|
|
21
25
|
"keywords": [
|
|
22
26
|
"i18n",
|
|
23
27
|
"translation",
|
|
24
28
|
"cli",
|
|
25
|
-
"localization"
|
|
29
|
+
"localization",
|
|
30
|
+
"localhero.ai"
|
|
26
31
|
],
|
|
27
32
|
"author": "LocalHero.ai",
|
|
28
33
|
"license": "MIT",
|
|
29
34
|
"dependencies": {
|
|
35
|
+
"@oclif/core": "^3.25.0",
|
|
30
36
|
"chalk": "^5.3.0",
|
|
31
37
|
"commander": "^12.0.0",
|
|
32
38
|
"glob": "^10.3.10",
|
|
@@ -35,33 +41,27 @@
|
|
|
35
41
|
},
|
|
36
42
|
"devDependencies": {
|
|
37
43
|
"@babel/preset-env": "^7.24.0",
|
|
44
|
+
"@eslint/js": "^9.24.0",
|
|
38
45
|
"@inquirer/testing": "^2.1.36",
|
|
39
46
|
"@jest/globals": "^29.7.0",
|
|
40
|
-
"
|
|
47
|
+
"@types/inquirer": "^9.0.7",
|
|
48
|
+
"@types/jest": "^29.5.14",
|
|
49
|
+
"@types/node": "^22.14.0",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^8.30.1",
|
|
51
|
+
"@typescript-eslint/parser": "^8.30.1",
|
|
52
|
+
"@typescript-eslint/typescript-estree": "^8.30.1",
|
|
53
|
+
"eslint": "^8.57.0",
|
|
41
54
|
"globals": "^15.14.0",
|
|
42
|
-
"jest": "^29.7.0"
|
|
55
|
+
"jest": "^29.7.0",
|
|
56
|
+
"ts-jest": "^29.1.2",
|
|
57
|
+
"typescript": "^5.8.3"
|
|
43
58
|
},
|
|
44
59
|
"engines": {
|
|
45
60
|
"node": ">=22.0.0"
|
|
46
61
|
},
|
|
47
62
|
"files": [
|
|
48
|
-
"
|
|
63
|
+
"dist",
|
|
49
64
|
"README",
|
|
50
65
|
"LICENSE"
|
|
51
|
-
]
|
|
52
|
-
"jest": {
|
|
53
|
-
"testEnvironment": "node",
|
|
54
|
-
"transform": {},
|
|
55
|
-
"moduleNameMapper": {
|
|
56
|
-
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
57
|
-
},
|
|
58
|
-
"testMatch": [
|
|
59
|
-
"**/tests/**/*.test.js"
|
|
60
|
-
],
|
|
61
|
-
"testEnvironmentOptions": {
|
|
62
|
-
"extensionsToTreatAsEsm": [
|
|
63
|
-
".js"
|
|
64
|
-
]
|
|
65
|
-
}
|
|
66
|
-
}
|
|
66
|
+
]
|
|
67
67
|
}
|
package/src/api/auth.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { apiRequest } from './client.js';
|
|
2
|
-
|
|
3
|
-
export async function verifyApiKey(apiKey) {
|
|
4
|
-
try {
|
|
5
|
-
return await apiRequest('/api/v1/auth/verify', {
|
|
6
|
-
apiKey
|
|
7
|
-
});
|
|
8
|
-
} catch (error) {
|
|
9
|
-
if (error.code === 'invalid_api_key') {
|
|
10
|
-
return {
|
|
11
|
-
error: {
|
|
12
|
-
code: 'invalid_api_key',
|
|
13
|
-
message: error.message
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
return {
|
|
18
|
-
error: {
|
|
19
|
-
code: 'verification_failed',
|
|
20
|
-
message: error.message || 'Failed to verify API key'
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
}
|
package/src/api/client.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
const DEFAULT_API_HOST = 'https://api.localhero.ai';
|
|
2
|
-
|
|
3
|
-
export function getApiHost() {
|
|
4
|
-
return process.env.LOCALHERO_API_HOST || DEFAULT_API_HOST;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function getNetworkErrorMessage(error) {
|
|
8
|
-
if (error.code === 'ECONNREFUSED') {
|
|
9
|
-
return `Unable to connect to ${getApiHost()}. Please check your internet connection and try again.`;
|
|
10
|
-
}
|
|
11
|
-
if (error.cause?.code === 'ENOTFOUND') {
|
|
12
|
-
return `Could not resolve host ${getApiHost()}. Please check your internet connection and try again.`;
|
|
13
|
-
}
|
|
14
|
-
if (error.cause?.code === 'ETIMEDOUT') {
|
|
15
|
-
return `Connection to ${getApiHost()} timed out. Please try again later.`;
|
|
16
|
-
}
|
|
17
|
-
return `Network error while connecting to ${getApiHost()}. Please check your internet connection and try again.`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function apiRequest(endpoint, options = {}) {
|
|
21
|
-
const apiHost = getApiHost();
|
|
22
|
-
const url = `${apiHost}${endpoint}`;
|
|
23
|
-
const apiKey = process.env.LOCALHERO_API_KEY || options.apiKey;
|
|
24
|
-
|
|
25
|
-
const headers = {
|
|
26
|
-
'Content-Type': 'application/json',
|
|
27
|
-
...options.headers
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
if (apiKey) {
|
|
31
|
-
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const fetchOptions = {
|
|
35
|
-
method: options.method || 'GET',
|
|
36
|
-
headers,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
if (options.body) {
|
|
40
|
-
fetchOptions.body = options.body;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let response;
|
|
44
|
-
try {
|
|
45
|
-
response = await fetch(url, fetchOptions);
|
|
46
|
-
} catch (error) {
|
|
47
|
-
const message = getNetworkErrorMessage(error);
|
|
48
|
-
error.message = message;
|
|
49
|
-
error.cliErrorMessage = message;
|
|
50
|
-
throw error;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let data;
|
|
54
|
-
try {
|
|
55
|
-
data = await response.json();
|
|
56
|
-
} catch (error) {
|
|
57
|
-
const message = 'Failed to parse API response';
|
|
58
|
-
error.message = message;
|
|
59
|
-
error.cliErrorMessage = message;
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!response.ok) {
|
|
64
|
-
if (response.status === 401 && data?.error?.code === 'invalid_api_key') {
|
|
65
|
-
const message = 'Your API key is invalid or has been revoked. Please run `npx @localheroai/cli login` to update your API key.';
|
|
66
|
-
const error = new Error(message);
|
|
67
|
-
error.cliErrorMessage = message;
|
|
68
|
-
error.code = 'invalid_api_key';
|
|
69
|
-
error.data = data;
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
const message = Array.isArray(data?.errors)
|
|
73
|
-
? data.errors.map(err => typeof err === 'string' ? err : err.message).join(', ')
|
|
74
|
-
: data?.error?.message || 'API request failed';
|
|
75
|
-
const error = new Error(message);
|
|
76
|
-
error.cliErrorMessage = message;
|
|
77
|
-
error.code = data?.error?.code || 'API_ERROR';
|
|
78
|
-
error.data = data;
|
|
79
|
-
throw error;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return data;
|
|
83
|
-
}
|
package/src/api/imports.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { getApiKey } from '../utils/auth.js';
|
|
2
|
-
import { apiRequest } from './client.js';
|
|
3
|
-
|
|
4
|
-
export async function createImport({ projectId, translations }) {
|
|
5
|
-
const apiKey = await getApiKey();
|
|
6
|
-
const response = await apiRequest(`/api/v1/projects/${projectId}/imports`, {
|
|
7
|
-
method: 'POST',
|
|
8
|
-
body: JSON.stringify({
|
|
9
|
-
translations
|
|
10
|
-
}),
|
|
11
|
-
apiKey
|
|
12
|
-
});
|
|
13
|
-
return response;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function checkImportStatus(projectId, importId) {
|
|
17
|
-
const apiKey = await getApiKey();
|
|
18
|
-
const response = await apiRequest(`/api/v1/projects/${projectId}/imports/${importId}`, {
|
|
19
|
-
apiKey
|
|
20
|
-
});
|
|
21
|
-
return response.import;
|
|
22
|
-
}
|
package/src/api/projects.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { getApiKey } from '../utils/auth.js';
|
|
2
|
-
import { apiRequest } from './client.js';
|
|
3
|
-
|
|
4
|
-
export async function listProjects() {
|
|
5
|
-
const apiKey = await getApiKey();
|
|
6
|
-
const response = await apiRequest('/api/v1/projects', { apiKey });
|
|
7
|
-
return response.projects;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function createProject(data) {
|
|
11
|
-
const apiKey = await getApiKey();
|
|
12
|
-
const response = await apiRequest('/api/v1/projects', {
|
|
13
|
-
method: 'POST',
|
|
14
|
-
body: JSON.stringify({
|
|
15
|
-
project: {
|
|
16
|
-
name: data.name,
|
|
17
|
-
source_language: data.sourceLocale,
|
|
18
|
-
target_languages: data.targetLocales
|
|
19
|
-
}
|
|
20
|
-
}),
|
|
21
|
-
apiKey
|
|
22
|
-
});
|
|
23
|
-
return response.project;
|
|
24
|
-
}
|