i18ntk 4.5.4 → 4.6.1
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/CHANGELOG.md +45 -2
- package/README.md +124 -536
- package/main/i18ntk-init.js +56 -25
- package/main/i18ntk-scanner.js +148 -29
- package/main/i18ntk-translate.js +6 -3
- package/main/manage/commands/ScannerCommand.js +145 -28
- package/main/manage/services/FrameworkDetectionService.js +67 -9
- package/main/manage/services/InitService.js +56 -25
- package/package.json +27 -43
- package/utils/config-helper.js +2 -2
- package/utils/framework-detector.js +317 -6
- package/utils/report-model.js +17 -4
- package/utils/usage-source.js +2 -1
- package/utils/validation-risk.js +22 -13
package/main/i18ntk-init.js
CHANGED
|
@@ -29,29 +29,56 @@ const { createPrompt, isInteractive } = require('../utils/prompt-helper');
|
|
|
29
29
|
const { parseConfirmation } = require('../utils/localized-confirm');
|
|
30
30
|
const { normalizeReportFormat, writeReportFile } = require('../utils/report-writer');
|
|
31
31
|
|
|
32
|
-
// Language configurations with native names
|
|
33
|
-
const LANGUAGE_CONFIG = {
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
}
|
|
32
|
+
// Language configurations with native names — comprehensive ISO 639-1 coverage
|
|
33
|
+
const LANGUAGE_CONFIG = {
|
|
34
|
+
'af': { name: 'Afrikaans', nativeName: 'Afrikaans' }, 'sq': { name: 'Albanian', nativeName: 'Shqip' },
|
|
35
|
+
'ar': { name: 'Arabic', nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629' }, 'hy': { name: 'Armenian', nativeName: '\u0540\u0561\u0575\u0565\u0580\u0565\u0576' },
|
|
36
|
+
'az': { name: 'Azerbaijani', nativeName: 'Az\u0259rbaycan' }, 'eu': { name: 'Basque', nativeName: 'Euskara' },
|
|
37
|
+
'be': { name: 'Belarusian', nativeName: '\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f' },
|
|
38
|
+
'bn': { name: 'Bengali', nativeName: '\u09ac\u09be\u0982\u09b2\u09be' },
|
|
39
|
+
'bs': { name: 'Bosnian', nativeName: 'Bosanski' }, 'bg': { name: 'Bulgarian', nativeName: '\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438' },
|
|
40
|
+
'ca': { name: 'Catalan', nativeName: 'Catal\u00e0' }, 'zh': { name: 'Chinese', nativeName: '\u4e2d\u6587' },
|
|
41
|
+
'hr': { name: 'Croatian', nativeName: 'Hrvatski' }, 'cs': { name: 'Czech', nativeName: '\u010ce\u0161tina' },
|
|
42
|
+
'da': { name: 'Danish', nativeName: 'Dansk' }, 'nl': { name: 'Dutch', nativeName: 'Nederlands' },
|
|
43
|
+
'en': { name: 'English', nativeName: 'English' }, 'et': { name: 'Estonian', nativeName: 'Eesti' },
|
|
44
|
+
'fi': { name: 'Finnish', nativeName: 'Suomi' }, 'fr': { name: 'French', nativeName: 'Fran\u00e7ais' },
|
|
45
|
+
'ka': { name: 'Georgian', nativeName: '\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8' },
|
|
46
|
+
'de': { name: 'German', nativeName: 'Deutsch' }, 'el': { name: 'Greek', nativeName: '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac' },
|
|
47
|
+
'gu': { name: 'Gujarati', nativeName: '\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0' },
|
|
48
|
+
'he': { name: 'Hebrew', nativeName: '\u05e2\u05d1\u05e8\u05d9\u05ea' },
|
|
49
|
+
'hi': { name: 'Hindi', nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940' },
|
|
50
|
+
'hu': { name: 'Hungarian', nativeName: 'Magyar' }, 'is': { name: 'Icelandic', nativeName: '\u00cdslenska' },
|
|
51
|
+
'id': { name: 'Indonesian', nativeName: 'Bahasa Indonesia' },
|
|
52
|
+
'ga': { name: 'Irish', nativeName: 'Gaeilge' }, 'it': { name: 'Italian', nativeName: 'Italiano' },
|
|
53
|
+
'ja': { name: 'Japanese', nativeName: '\u65e5\u672c\u8a9e' },
|
|
54
|
+
'kk': { name: 'Kazakh', nativeName: '\u049a\u0430\u0437\u0430\u049b' },
|
|
55
|
+
'ko': { name: 'Korean', nativeName: '\ud55c\uad6d\uc5b4' },
|
|
56
|
+
'lv': { name: 'Latvian', nativeName: 'Latvie\u0161u' }, 'lt': { name: 'Lithuanian', nativeName: 'Lietuvi\u0173' },
|
|
57
|
+
'mk': { name: 'Macedonian', nativeName: '\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438' },
|
|
58
|
+
'ms': { name: 'Malay', nativeName: 'Bahasa Melayu' },
|
|
59
|
+
'ml': { name: 'Malayalam', nativeName: '\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02' },
|
|
60
|
+
'mr': { name: 'Marathi', nativeName: '\u092e\u0930\u093e\u0920\u0940' },
|
|
61
|
+
'mn': { name: 'Mongolian', nativeName: '\u041c\u043e\u043d\u0433\u043e\u043b' },
|
|
62
|
+
'no': { name: 'Norwegian', nativeName: 'Norsk' },
|
|
63
|
+
'fa': { name: 'Persian', nativeName: '\u0641\u0627\u0631\u0633\u06cc' },
|
|
64
|
+
'pl': { name: 'Polish', nativeName: 'Polski' }, 'pt': { name: 'Portuguese', nativeName: 'Portugu\u00eas' },
|
|
65
|
+
'pa': { name: 'Punjabi', nativeName: '\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40' },
|
|
66
|
+
'ro': { name: 'Romanian', nativeName: 'Rom\u00e2n\u0103' },
|
|
67
|
+
'ru': { name: 'Russian', nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439' },
|
|
68
|
+
'sr': { name: 'Serbian', nativeName: '\u0421\u0440\u043f\u0441\u043a\u0438' },
|
|
69
|
+
'sk': { name: 'Slovak', nativeName: 'Sloven\u010dina' },
|
|
70
|
+
'sl': { name: 'Slovenian', nativeName: 'Sloven\u0161\u010dina' },
|
|
71
|
+
'es': { name: 'Spanish', nativeName: 'Espa\u00f1ol' },
|
|
72
|
+
'sw': { name: 'Swahili', nativeName: 'Kiswahili' }, 'sv': { name: 'Swedish', nativeName: 'Svenska' },
|
|
73
|
+
'tl': { name: 'Filipino', nativeName: 'Filipino' },
|
|
74
|
+
'ta': { name: 'Tamil', nativeName: '\u0ba4\u0bae\u0bbf\u0bb4\u0bcd' },
|
|
75
|
+
'te': { name: 'Telugu', nativeName: '\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41' },
|
|
76
|
+
'th': { name: 'Thai', nativeName: '\u0e44\u0e17\u0e22' },
|
|
77
|
+
'tr': { name: 'Turkish', nativeName: 'T\u00fcrk\u00e7e' },
|
|
78
|
+
'uk': { name: 'Ukrainian', nativeName: '\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430' },
|
|
79
|
+
'ur': { name: 'Urdu', nativeName: '\u0627\u0631\u062f\u0648' },
|
|
80
|
+
'vi': { name: 'Vietnamese', nativeName: 'Ti\u1ebfng Vi\u1ec7t' },
|
|
81
|
+
};
|
|
55
82
|
|
|
56
83
|
class I18nInitializer {
|
|
57
84
|
constructor(config = {}) {
|
|
@@ -67,7 +94,9 @@ class I18nInitializer {
|
|
|
67
94
|
...config
|
|
68
95
|
};
|
|
69
96
|
this.format = getFormatAdapter(this.config.format);
|
|
70
|
-
this.config.supportedExtensions
|
|
97
|
+
if (!this.config.supportedExtensions || !this.config.supportedExtensions.length) {
|
|
98
|
+
this.config.supportedExtensions = [this.format.extension];
|
|
99
|
+
}
|
|
71
100
|
this.detectedFramework = detectFramework(process.cwd());
|
|
72
101
|
if (this.detectedFramework && !this.config.translationPatterns) {
|
|
73
102
|
this.config.translationPatterns = this.detectedFramework.patterns;
|
|
@@ -79,7 +108,9 @@ class I18nInitializer {
|
|
|
79
108
|
: path.join(this.sourceDir, this.config.sourceLanguage);
|
|
80
109
|
|
|
81
110
|
// Ensure defaultLanguages is properly initialized from config
|
|
82
|
-
this.config.defaultLanguages
|
|
111
|
+
if (!this.config.defaultLanguages || !Array.isArray(this.config.defaultLanguages) || this.config.defaultLanguages.length === 0) {
|
|
112
|
+
this.config.defaultLanguages = ['en', 'de', 'es', 'fr', 'ru'];
|
|
113
|
+
}
|
|
83
114
|
|
|
84
115
|
// No longer create readline interface here - use CLI helpers
|
|
85
116
|
this.rl = null;
|
package/main/i18ntk-scanner.js
CHANGED
|
@@ -22,6 +22,7 @@ const { getUnifiedConfig, displayHelp } = require('../utils/config-helper');
|
|
|
22
22
|
const { loadTranslations } = require('../utils/i18n-helper');
|
|
23
23
|
const SecurityUtils = require('../utils/security');
|
|
24
24
|
const SetupEnforcer = require('../utils/setup-enforcer');
|
|
25
|
+
const { detectProjectFramework, getFrameworkPatterns, getFrameworkSuggestions, SCANNER_EXTENSIONS, WRAPPER_SKIP_PATTERNS, getExcludeDirs } = require('../utils/framework-detector');
|
|
25
26
|
|
|
26
27
|
// Ensure setup is complete before running
|
|
27
28
|
(async () => {
|
|
@@ -225,11 +226,26 @@ class I18nTextScanner {
|
|
|
225
226
|
const packageJson = SecurityUtils.safeParseJSON(packageJsonContent);
|
|
226
227
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
227
228
|
|
|
229
|
+
if (deps.next || deps['next-intl'] || deps['next-i18next']) return 'next';
|
|
228
230
|
if (deps.react || deps['react-dom']) return 'react';
|
|
229
231
|
if (deps.vue || deps['vue-router']) return 'vue';
|
|
230
232
|
if (deps['@angular/core'] || deps.angular) return 'angular';
|
|
231
|
-
if (deps.next) return 'next';
|
|
232
233
|
if (deps.svelte) return 'svelte';
|
|
234
|
+
if (deps.astro || deps['astro-i18next'] || deps['@astrojs/i18n']) return 'astro';
|
|
235
|
+
if (deps['@remix-run/react'] || deps['remix-i18next']) return 'remix';
|
|
236
|
+
if (deps.gatsby || deps['gatsby-plugin-react-i18next']) return 'gatsby';
|
|
237
|
+
if (deps['@builder.io/qwik'] || deps['qwik-speak']) return 'qwik';
|
|
238
|
+
if (deps['solid-js'] || deps['@solid-primitives/i18n']) return 'solid';
|
|
239
|
+
if (deps['ember-source'] || deps['ember-intl']) return 'ember';
|
|
240
|
+
if (deps.nuxt || deps['@nuxtjs/i18n']) return 'nuxt';
|
|
241
|
+
|
|
242
|
+
// Check for Rust project
|
|
243
|
+
const cargoTomlPath = path.join(projectRoot, 'Cargo.toml');
|
|
244
|
+
if (SecurityUtils.safeExistsSync(cargoTomlPath, projectRoot)) return 'rust';
|
|
245
|
+
|
|
246
|
+
// Check for Go project
|
|
247
|
+
const goModPath = path.join(projectRoot, 'go.mod');
|
|
248
|
+
if (SecurityUtils.safeExistsSync(goModPath, projectRoot)) return 'go';
|
|
233
249
|
|
|
234
250
|
return 'vanilla';
|
|
235
251
|
} catch (error) {
|
|
@@ -255,57 +271,110 @@ class I18nTextScanner {
|
|
|
255
271
|
|
|
256
272
|
const frameworkSpecific = {
|
|
257
273
|
react: [
|
|
258
|
-
// React specific patterns - enhanced for i18next detection
|
|
259
274
|
/children:\s*["']([^"']{2,99})["']/g,
|
|
260
275
|
/dangerouslySetInnerHTML={{\s*__html:\s*["']([^"']{2,99})["']/g,
|
|
261
|
-
// JSX text content without translation
|
|
262
276
|
/>([^<{][^<>{]*[^}>])</g,
|
|
263
|
-
// Button text
|
|
264
277
|
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
265
|
-
|
|
266
|
-
/<
|
|
278
|
+
/<span[^>]*>([^<]{2,99})<\/span>/g,
|
|
279
|
+
/<(?:FormattedMessage|Trans)[\s\S]*?\b(?:id|defaultMessage|i18nKey)\s*=\s*(?:\{|)(['"`])([^'"`}]+)\1[^>]*>/g,
|
|
280
|
+
/<(?:FormattedMessage|Trans)[\s\S]*?\b(?:id|defaultMessage|i18nKey)\s*=\s*\{[']\s*([^'}]+\.[^'}]+)\s*[']/g
|
|
267
281
|
],
|
|
268
282
|
vue: [
|
|
269
|
-
// Vue specific patterns - enhanced for vue-i18n detection
|
|
270
283
|
/v-text=["']([^"']{2,99})["']/g,
|
|
271
284
|
/v-html=["']([^"']{2,99})["']/g,
|
|
272
|
-
// Vue template text
|
|
273
285
|
/>([^<{][^<>{]*[^}>])</g,
|
|
274
|
-
// Button text
|
|
275
286
|
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
276
|
-
|
|
277
|
-
|
|
287
|
+
/<span[^>]*>([^<]{2,99})<\/span>/g,
|
|
288
|
+
/\$t\(["']([^"']{2,99})["']\)/g,
|
|
289
|
+
/v-t=["']([^"']{2,99})["']/g
|
|
278
290
|
],
|
|
279
291
|
angular: [
|
|
280
|
-
// Angular specific patterns - enhanced for ngx-translate detection
|
|
281
292
|
/\[innerHTML\]=["']([^"']{2,99})["']/g,
|
|
282
293
|
/\[textContent\]=["']([^"']{2,99})["']/g,
|
|
283
|
-
// Angular template text
|
|
284
294
|
/>([^<{][^<>{]*[^}>])</g,
|
|
285
|
-
// Button text
|
|
286
295
|
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
287
|
-
|
|
288
|
-
|
|
296
|
+
/<span[^>]*>([^<]{2,99})<\/span>/g,
|
|
297
|
+
/i18n=["']([^"']{2,99})["']/g,
|
|
298
|
+
/\[attr\.title\]=["']([^"']{2,99})["']/g
|
|
299
|
+
],
|
|
300
|
+
next: [
|
|
301
|
+
/children:\s*["']([^"']{2,99})["']/g,
|
|
302
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
303
|
+
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
304
|
+
/<(?:FormattedMessage|Trans)[\s\S]*?\b(?:id|defaultMessage|i18nKey)\s*=\s*(?:\{|)(['"`])([^'"`}]+)\1[^>]*>/g
|
|
305
|
+
],
|
|
306
|
+
svelte: [
|
|
307
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
308
|
+
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
309
|
+
/\$_\(["']([^"']{2,99})["']\)/g,
|
|
310
|
+
/t\.set\(["']([^"']{2,99})["']/g,
|
|
311
|
+
/\{#if[^}]*\}([^<]{2,99})\{\/if\}/g
|
|
312
|
+
],
|
|
313
|
+
astro: [
|
|
314
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
315
|
+
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
316
|
+
/t\(["']([^"']{2,99})["']\)/g
|
|
317
|
+
],
|
|
318
|
+
remix: [
|
|
319
|
+
/children:\s*["']([^"']{2,99})["']/g,
|
|
320
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
321
|
+
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
322
|
+
/<(?:FormattedMessage|Trans)[\s\S]*?\b(?:id|defaultMessage|i18nKey)\s*=\s*(?:\{|)(['"`])([^'"`}]+)\1[^>]*>/g
|
|
323
|
+
],
|
|
324
|
+
qwik: [
|
|
325
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
326
|
+
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
327
|
+
/t\(["']([^"']{2,99})["']\)/g,
|
|
328
|
+
/useTranslate\(\)/g
|
|
329
|
+
],
|
|
330
|
+
solid: [
|
|
331
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
332
|
+
/<button[^>]*>([^<]{2,99})<\/button>/g,
|
|
333
|
+
/t\(["']([^"']{2,99})["']\)/g,
|
|
334
|
+
/useI18n\(\)\.t\(["']([^"']{2,99})["']\)/g
|
|
335
|
+
],
|
|
336
|
+
ember: [
|
|
337
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
338
|
+
/\{\{t\s+["']([^"']{2,99})["']\s*\}\}/g,
|
|
339
|
+
/\{\{intl\.t\s+["']([^"']{2,99})["']\s*\}\}/g
|
|
340
|
+
],
|
|
341
|
+
gatsby: [
|
|
342
|
+
/children:\s*["']([^"']{2,99})["']/g,
|
|
343
|
+
/>([^<{][^<>{]*[^}>])</g,
|
|
344
|
+
/<(?:Trans)[\s\S]*?\bi18nKey\s*=\s*(?:\{|)(['"`])([^'"`}]+)\1[^>]*>/g
|
|
289
345
|
],
|
|
290
346
|
django: [
|
|
291
|
-
// Django template patterns
|
|
292
347
|
/\{\%\s*trans\s+["']([^"']{2,99})["']\s*%\}/g,
|
|
293
348
|
/\{\%\s*blocktrans\s*%\}([^%]{2,99})\{\%\s*endblocktrans\s*%\}/g,
|
|
294
349
|
/{{\s*_["']([^"']{2,99})["']\s*}}/g,
|
|
295
350
|
/{{\s*gettext\(["']([^"']{2,99})["']\)\s*}}/g
|
|
296
351
|
],
|
|
297
352
|
flask: [
|
|
298
|
-
// Flask/Jinja2 template patterns
|
|
299
353
|
/\{\{\s*_["']([^"']{2,99})["']\s*}}/g,
|
|
300
354
|
/\{\{\s*gettext\(["']([^"']{2,99})["']\)\s*}}/g,
|
|
301
355
|
/\{\{\s*lazy_gettext\(["']([^"']{2,99})["']\)\s*}}/g
|
|
302
356
|
],
|
|
303
357
|
python: [
|
|
304
|
-
// Python source patterns
|
|
305
358
|
/gettext\(["']([^"']{2,99})["']\)/g,
|
|
306
359
|
/_\(["']([^"']{2,99})["']\)/g,
|
|
307
360
|
/gettext_lazy\(["']([^"']{2,99})["']\)/g,
|
|
308
361
|
/lazy_gettext\(["']([^"']{2,99})["']\)/g
|
|
362
|
+
],
|
|
363
|
+
rust: [
|
|
364
|
+
/bundle\.get_message\(\s*["']([^"']{2,99})["']\)/g,
|
|
365
|
+
/\.get_message\s*\(\s*["']([^"']{2,99})["']\)/g,
|
|
366
|
+
/ts!\s*\(\s*["']([^"']{2,99})["']/g,
|
|
367
|
+
/fluent!\s*\(\s*["']([^"']{2,99})["']/g
|
|
368
|
+
],
|
|
369
|
+
go: [
|
|
370
|
+
/i18n\.Translate\([^,]+,\s*["']([^"']{2,99})["']\)/g,
|
|
371
|
+
/i18n\.NewMessage\([^,]+,\s*["']([^"']{2,99})["']\)/g,
|
|
372
|
+
/t\.Get\([^,]+,\s*["']([^"']{2,99})["']\)/g
|
|
373
|
+
],
|
|
374
|
+
vanilla: [
|
|
375
|
+
/t\(["']([^"']{2,99})["']\)/g,
|
|
376
|
+
/i18n\.t\(["']([^"']{2,99})["']\)/g,
|
|
377
|
+
/translate\(["']([^"']{2,99})["']\)/g
|
|
309
378
|
]
|
|
310
379
|
};
|
|
311
380
|
|
|
@@ -518,7 +587,7 @@ class I18nTextScanner {
|
|
|
518
587
|
key: `ui.${key}`,
|
|
519
588
|
original: text,
|
|
520
589
|
translationKey: `t('ui.${key}')`,
|
|
521
|
-
frameworkSpecific: this.
|
|
590
|
+
frameworkSpecific: getFrameworkSuggestions(this.framework, text)
|
|
522
591
|
};
|
|
523
592
|
}
|
|
524
593
|
|
|
@@ -526,16 +595,51 @@ class I18nTextScanner {
|
|
|
526
595
|
const frameworks = {
|
|
527
596
|
react: {
|
|
528
597
|
hook: `const { t } = useTranslation();`,
|
|
529
|
-
usage: `{t('ui.${
|
|
530
|
-
component: `<Trans i18nKey="ui.${
|
|
598
|
+
usage: `{t('ui.${this._keySnippet(text)}')}`,
|
|
599
|
+
component: `<Trans i18nKey="ui.${this._keySnippet(text)}">${text}</Trans>`
|
|
600
|
+
},
|
|
601
|
+
next: {
|
|
602
|
+
hook: `const t = useTranslations();`,
|
|
603
|
+
usage: `{t('ui.${this._keySnippet(text)}')}`,
|
|
604
|
+
component: `<Trans i18nKey="ui.${this._keySnippet(text)}">${text}</Trans>`
|
|
531
605
|
},
|
|
532
606
|
vue: {
|
|
533
|
-
directive: `{{ $t('ui.${
|
|
534
|
-
method: `this.$t('ui.${
|
|
607
|
+
directive: `{{ $t('ui.${this._keySnippet(text)}') }}`,
|
|
608
|
+
method: `this.$t('ui.${this._keySnippet(text)}')`
|
|
535
609
|
},
|
|
536
610
|
angular: {
|
|
537
611
|
pipe: `{{ '${text}' | translate }}`,
|
|
538
|
-
service: `this.translateService.instant('ui.${
|
|
612
|
+
service: `this.translateService.instant('ui.${this._keySnippet(text)}')`
|
|
613
|
+
},
|
|
614
|
+
svelte: {
|
|
615
|
+
store: `$_(('ui.${this._keySnippet(text)}'))`,
|
|
616
|
+
method: `t.set('ui.${this._keySnippet(text)}', '${text}')`
|
|
617
|
+
},
|
|
618
|
+
astro: {
|
|
619
|
+
import: `import { t } from 'astro-i18next';`,
|
|
620
|
+
usage: `{t('ui.${this._keySnippet(text)}')}`
|
|
621
|
+
},
|
|
622
|
+
remix: {
|
|
623
|
+
hook: `const { t } = useTranslation();`,
|
|
624
|
+
usage: `{t('ui.${this._keySnippet(text)}')}`,
|
|
625
|
+
server: `export const handle = i18next.handle;`
|
|
626
|
+
},
|
|
627
|
+
qwik: {
|
|
628
|
+
hook: `const t = useTranslate();`,
|
|
629
|
+
usage: `{t('ui.${this._keySnippet(text)}')}`
|
|
630
|
+
},
|
|
631
|
+
solid: {
|
|
632
|
+
hook: `const [t] = useI18n();`,
|
|
633
|
+
usage: `{t('ui.${this._keySnippet(text)}')}`
|
|
634
|
+
},
|
|
635
|
+
ember: {
|
|
636
|
+
template: `{{t 'ui.${this._keySnippet(text)}'}}`,
|
|
637
|
+
helper: `this.intl.t('ui.${this._keySnippet(text)}')`
|
|
638
|
+
},
|
|
639
|
+
gatsby: {
|
|
640
|
+
hook: `const { t } = useTranslation();`,
|
|
641
|
+
usage: `{t('ui.${this._keySnippet(text)}')}`,
|
|
642
|
+
plugin: `'gatsby-plugin-react-i18next'`
|
|
539
643
|
},
|
|
540
644
|
django: {
|
|
541
645
|
template: `{% trans '${text}' %}`,
|
|
@@ -552,14 +656,29 @@ class I18nTextScanner {
|
|
|
552
656
|
underscore: `from gettext import gettext as _\n_('${text}')`,
|
|
553
657
|
lazy: `from gettext import gettext_lazy as _\n_('${text}')`
|
|
554
658
|
},
|
|
659
|
+
rust: {
|
|
660
|
+
fluent: `bundle.get_message("ui_${this._keySnippet(text)}")`,
|
|
661
|
+
gettext: `gettext("ui_${this._keySnippet(text)}")`
|
|
662
|
+
},
|
|
663
|
+
go: {
|
|
664
|
+
translate: `i18n.Translate("ui_${this._keySnippet(text)}")`,
|
|
665
|
+
message: `i18n.NewMessage("ui_${this._keySnippet(text)}")`
|
|
666
|
+
},
|
|
555
667
|
vanilla: {
|
|
556
|
-
generic: `t('ui.${
|
|
668
|
+
generic: `t('ui.${this._keySnippet(text)}')`
|
|
557
669
|
}
|
|
558
670
|
};
|
|
559
671
|
|
|
560
672
|
return frameworks[this.framework] || frameworks.vanilla;
|
|
561
673
|
}
|
|
562
674
|
|
|
675
|
+
_keySnippet(text) {
|
|
676
|
+
return text.toLowerCase()
|
|
677
|
+
.replace(/[^a-z0-9\s]/g, '')
|
|
678
|
+
.replace(/\s+/g, '_')
|
|
679
|
+
.substring(0, 40);
|
|
680
|
+
}
|
|
681
|
+
|
|
563
682
|
async scanDirectory(dir, options = {}) {
|
|
564
683
|
const {
|
|
565
684
|
patterns = [],
|
|
@@ -574,7 +693,7 @@ class I18nTextScanner {
|
|
|
574
693
|
}
|
|
575
694
|
|
|
576
695
|
const allResults = [];
|
|
577
|
-
const extensions = [
|
|
696
|
+
const extensions = [...SCANNER_EXTENSIONS];
|
|
578
697
|
|
|
579
698
|
const scanRecursive = (currentDir) => {
|
|
580
699
|
const items = SecurityUtils.safeReaddirSync(currentDir, path.dirname(currentDir), { withFileTypes: true });
|
|
@@ -736,7 +855,7 @@ class I18nTextScanner {
|
|
|
736
855
|
} else if (fwPref && fwPref !== 'auto') {
|
|
737
856
|
this.framework = fwPref;
|
|
738
857
|
} else if (fwDetectEnabled) {
|
|
739
|
-
const detected =
|
|
858
|
+
const detected = detectProjectFramework(process.cwd());
|
|
740
859
|
this.framework = detected || fwFallback;
|
|
741
860
|
} else {
|
|
742
861
|
this.framework = fwFallback;
|
|
@@ -762,7 +881,7 @@ class I18nTextScanner {
|
|
|
762
881
|
console.log(this.t('scanner.starting', { framework: this.framework }));
|
|
763
882
|
console.log(this.t('scanner.sourceDirectory', { sourceDir: this.sourceDir }));
|
|
764
883
|
|
|
765
|
-
const patterns =
|
|
884
|
+
const patterns = getFrameworkPatterns(this.framework);
|
|
766
885
|
const exclusions = this.config.exclude || ['node_modules', '.git', 'dist', 'build'];
|
|
767
886
|
const minLength = this.config.minLength || 3;
|
|
768
887
|
const maxLength = this.config.maxLength || 100;
|
package/main/i18ntk-translate.js
CHANGED
|
@@ -1131,10 +1131,13 @@ async function run(args) {
|
|
|
1131
1131
|
}
|
|
1132
1132
|
}
|
|
1133
1133
|
|
|
1134
|
+
const hadRealErrors = grandTranslated === 0 && grandTotal > 0;
|
|
1135
|
+
const hasResiduals = allResidualUntranslated.length > 0;
|
|
1136
|
+
|
|
1134
1137
|
return {
|
|
1135
|
-
success:
|
|
1136
|
-
exitCode:
|
|
1137
|
-
error:
|
|
1138
|
+
success: !hadRealErrors,
|
|
1139
|
+
exitCode: hadRealErrors ? ExitCodes.VALIDATION_FAILED : ExitCodes.SUCCESS,
|
|
1140
|
+
error: hadRealErrors ? 'Auto Translate failed to translate any values' : undefined,
|
|
1138
1141
|
total: grandTotal,
|
|
1139
1142
|
translated: grandTranslated,
|
|
1140
1143
|
skipped: grandSkipped,
|