@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
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { flattenTranslations, parseFile } from './files.js';
|
|
3
|
-
|
|
4
|
-
export function findMissingTranslations(sourceKeys, targetKeys) {
|
|
5
|
-
const missingKeys = {};
|
|
6
|
-
const skippedKeys = {};
|
|
7
|
-
|
|
8
|
-
for (const [key, details] of Object.entries(sourceKeys)) {
|
|
9
|
-
if (typeof details === 'string') {
|
|
10
|
-
if (
|
|
11
|
-
details.toLowerCase().includes('wip_') ||
|
|
12
|
-
details.toLowerCase().includes('_wip') ||
|
|
13
|
-
details.toLowerCase().includes('__skip_translation__')
|
|
14
|
-
) {
|
|
15
|
-
skippedKeys[key] = {
|
|
16
|
-
value: details,
|
|
17
|
-
reason: 'wip'
|
|
18
|
-
};
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!targetKeys[key]) {
|
|
23
|
-
missingKeys[key] = {
|
|
24
|
-
value: details,
|
|
25
|
-
sourceKey: key
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
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
|
-
|
|
41
|
-
if (
|
|
42
|
-
typeof details === 'object' && details !== null &&
|
|
43
|
-
typeof details.value === 'string' &&
|
|
44
|
-
(details.value.toLowerCase().includes('wip_') || details.value.toLowerCase().includes('_wip') ||
|
|
45
|
-
details.value.toLowerCase().includes('__skip_translation__'))
|
|
46
|
-
) {
|
|
47
|
-
skippedKeys[key] = {
|
|
48
|
-
...details,
|
|
49
|
-
reason: 'wip'
|
|
50
|
-
};
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!targetKeys[key]) {
|
|
55
|
-
if (typeof details === 'object' && details !== null && 'value' in details) {
|
|
56
|
-
missingKeys[key] = {
|
|
57
|
-
...details,
|
|
58
|
-
sourceKey: key
|
|
59
|
-
};
|
|
60
|
-
} else {
|
|
61
|
-
missingKeys[key] = {
|
|
62
|
-
value: details,
|
|
63
|
-
sourceKey: key
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { missingKeys, skippedKeys };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
export function batchKeysWithMissing(sourceFiles, missingByLocale, batchSize = 100) {
|
|
74
|
-
const batches = [];
|
|
75
|
-
const errors = [];
|
|
76
|
-
const sourceFileEntries = new Map();
|
|
77
|
-
|
|
78
|
-
for (const [locale, localeData] of Object.entries(missingByLocale)) {
|
|
79
|
-
const sourceFile = sourceFiles.find(f => f.path === localeData.path);
|
|
80
|
-
if (!sourceFile) {
|
|
81
|
-
errors.push({
|
|
82
|
-
type: 'missing_source_file',
|
|
83
|
-
message: `No source file found for path: ${localeData.path}`,
|
|
84
|
-
locale,
|
|
85
|
-
path: localeData.path
|
|
86
|
-
});
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!sourceFileEntries.has(sourceFile.path)) {
|
|
91
|
-
sourceFileEntries.set(sourceFile.path, {
|
|
92
|
-
path: sourceFile.path,
|
|
93
|
-
format: sourceFile.format || 'json',
|
|
94
|
-
keys: {},
|
|
95
|
-
locales: new Set()
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const entry = sourceFileEntries.get(sourceFile.path);
|
|
100
|
-
|
|
101
|
-
const formattedKeys = {};
|
|
102
|
-
for (const [key, value] of Object.entries(localeData.keys)) {
|
|
103
|
-
let extractedValue;
|
|
104
|
-
|
|
105
|
-
if (Array.isArray(value)) {
|
|
106
|
-
extractedValue = value;
|
|
107
|
-
} else if (typeof value === 'boolean') {
|
|
108
|
-
extractedValue = value;
|
|
109
|
-
} else if (typeof value === 'string') {
|
|
110
|
-
extractedValue = value;
|
|
111
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
112
|
-
if ('value' in value) {
|
|
113
|
-
extractedValue = value.value;
|
|
114
|
-
} else if (Object.keys(value).some(k => !isNaN(parseInt(k, 10)))) {
|
|
115
|
-
extractedValue = Object.values(value).join('');
|
|
116
|
-
} else {
|
|
117
|
-
extractedValue = JSON.stringify(value);
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
extractedValue = String(value);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
formattedKeys[key] = extractedValue;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
entry.keys = { ...entry.keys, ...formattedKeys };
|
|
127
|
-
entry.locales.add(locale);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
for (const entry of sourceFileEntries.values()) {
|
|
131
|
-
const keyEntries = Object.entries(entry.keys);
|
|
132
|
-
|
|
133
|
-
for (let i = 0; i < keyEntries.length; i += batchSize) {
|
|
134
|
-
const batchKeys = Object.fromEntries(keyEntries.slice(i, i + batchSize));
|
|
135
|
-
|
|
136
|
-
const contentObj = { keys: {} };
|
|
137
|
-
for (const [key, value] of Object.entries(batchKeys)) {
|
|
138
|
-
contentObj.keys[key] = {
|
|
139
|
-
value
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
batches.push({
|
|
144
|
-
files: [{
|
|
145
|
-
path: entry.path,
|
|
146
|
-
format: entry.format,
|
|
147
|
-
content: Buffer.from(JSON.stringify(contentObj)).toString('base64')
|
|
148
|
-
}],
|
|
149
|
-
locales: Array.from(entry.locales)
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return { batches, errors };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function findTargetFile(targetFiles, targetLocale, sourceFile, sourceLocale) {
|
|
158
|
-
// First try exact directory matching (existing logic)
|
|
159
|
-
let found = targetFiles.find(f =>
|
|
160
|
-
f.locale === targetLocale &&
|
|
161
|
-
path.dirname(f.path) === path.dirname(sourceFile.path) &&
|
|
162
|
-
path.basename(f.path, path.extname(f.path)) === path.basename(sourceFile.path, path.extname(sourceFile.path)).replace(sourceLocale, targetLocale)
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
if (found) return found;
|
|
166
|
-
|
|
167
|
-
// Then try filename-based matching regardless of directory (existing logic)
|
|
168
|
-
found = targetFiles.find(f =>
|
|
169
|
-
f.locale === targetLocale &&
|
|
170
|
-
path.basename(f.path, path.extname(f.path)) === path.basename(sourceFile.path, path.extname(sourceFile.path)).replace(sourceLocale, targetLocale)
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
if (found) return found;
|
|
174
|
-
|
|
175
|
-
const sourceDirParts = path.dirname(sourceFile.path).split(path.sep);
|
|
176
|
-
const sourceFileBaseName = path.basename(sourceFile.path, path.extname(sourceFile.path));
|
|
177
|
-
|
|
178
|
-
// Check for corresponding file in subdirectories or parent directories
|
|
179
|
-
return targetFiles.find(f => {
|
|
180
|
-
if (f.locale !== targetLocale) return false;
|
|
181
|
-
|
|
182
|
-
// Handle cases where files are in different subdirectories
|
|
183
|
-
const targetDirParts = path.dirname(f.path).split(path.sep);
|
|
184
|
-
const targetFileBaseName = path.basename(f.path, path.extname(f.path));
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
sourceFileBaseName === sourceLocale &&
|
|
188
|
-
targetFileBaseName === targetLocale &&
|
|
189
|
-
sourceDirParts.length === targetDirParts.length
|
|
190
|
-
) {
|
|
191
|
-
return true;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Nested directory structure
|
|
195
|
-
if (sourceDirParts.includes(sourceLocale) && targetDirParts.includes(targetLocale)) {
|
|
196
|
-
const sourceBasePath = sourceDirParts.slice(0, sourceDirParts.indexOf(sourceLocale)).join(path.sep);
|
|
197
|
-
const targetBasePath = targetDirParts.slice(0, targetDirParts.indexOf(targetLocale)).join(path.sep);
|
|
198
|
-
|
|
199
|
-
return sourceBasePath === targetBasePath &&
|
|
200
|
-
sourceFileBaseName === targetFileBaseName;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return false;
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function generateTargetPath(sourceFile, targetLocale, sourceLocale) {
|
|
208
|
-
const sourceExt = path.extname(sourceFile.path);
|
|
209
|
-
const sourceDir = path.dirname(sourceFile.path);
|
|
210
|
-
const sourceName = path.basename(sourceFile.path, sourceExt);
|
|
211
|
-
|
|
212
|
-
// Case 1: File is named exactly as the source locale (e.g., "en.yml")
|
|
213
|
-
if (sourceName === sourceLocale) {
|
|
214
|
-
return path.join(sourceDir, `${targetLocale}${sourceExt}`);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Case 2: File ends with .locale (e.g., "translations.en.yml")
|
|
218
|
-
if (sourceName.endsWith(`.${sourceLocale}`)) {
|
|
219
|
-
const baseName = sourceName.slice(0, -(sourceLocale.length + 1));
|
|
220
|
-
return path.join(sourceDir, `${baseName}.${targetLocale}${sourceExt}`);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Case 3: File uses hyphen-locale format (e.g., "translations-en.yml")
|
|
224
|
-
if (sourceName.includes(`-${sourceLocale}`)) {
|
|
225
|
-
const baseName = sourceName.slice(0, -(sourceLocale.length + 1));
|
|
226
|
-
return path.join(sourceDir, `${baseName}-${targetLocale}${sourceExt}`);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Case 4: Source locale is a directory name
|
|
230
|
-
const sourceParentDir = path.basename(sourceDir);
|
|
231
|
-
if (sourceParentDir === sourceLocale) {
|
|
232
|
-
const grandParentDir = path.dirname(sourceDir);
|
|
233
|
-
return path.join(grandParentDir, targetLocale, path.basename(sourceFile.path));
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Default case: If none of the above patterns match,
|
|
237
|
-
// construct the target path by replacing the locale in the filename only
|
|
238
|
-
const dirPath = path.dirname(sourceFile.path);
|
|
239
|
-
const fileName = path.basename(sourceFile.path);
|
|
240
|
-
const localeRegex = new RegExp(`\\b${sourceLocale}\\b`, 'g');
|
|
241
|
-
const newFileName = fileName.replace(localeRegex, targetLocale);
|
|
242
|
-
return path.join(dirPath, newFileName);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export function processTargetContent(targetContent, targetLocale) {
|
|
246
|
-
if (targetContent[targetLocale]) {
|
|
247
|
-
return flattenTranslations(targetContent[targetLocale]);
|
|
248
|
-
}
|
|
249
|
-
return flattenTranslations(targetContent);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export function processLocaleTranslations(sourceKeys, targetLocale, targetFiles, sourceFile, sourceLocale) {
|
|
253
|
-
try {
|
|
254
|
-
const targetFile = findTargetFile(targetFiles, targetLocale, sourceFile, sourceLocale);
|
|
255
|
-
let targetKeys = {};
|
|
256
|
-
let targetPath = '';
|
|
257
|
-
|
|
258
|
-
if (targetFile) {
|
|
259
|
-
const targetContentRaw = Buffer.from(targetFile.content, 'base64').toString();
|
|
260
|
-
const targetContent = parseFile(targetContentRaw, targetFile.format);
|
|
261
|
-
targetKeys = processTargetContent(targetContent, targetLocale);
|
|
262
|
-
targetPath = targetFile.path;
|
|
263
|
-
} else {
|
|
264
|
-
targetPath = generateTargetPath(sourceFile, targetLocale, sourceLocale);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const { missingKeys, skippedKeys } = findMissingTranslations(sourceKeys, targetKeys);
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
targetPath,
|
|
271
|
-
missingKeys,
|
|
272
|
-
skippedKeys,
|
|
273
|
-
targetFile
|
|
274
|
-
};
|
|
275
|
-
} catch (error) {
|
|
276
|
-
throw new Error(`Failed to process translations for ${targetLocale}: ${error.message}`);
|
|
277
|
-
}
|
|
278
|
-
}
|