poly-lexis 0.3.2 → 0.4.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/README.md +87 -17
- package/dist/cli/translations.js +527 -125
- package/dist/cli/translations.js.map +1 -1
- package/dist/index.d.ts +189 -113
- package/dist/index.js +531 -123
- package/dist/index.js.map +1 -1
- package/dist/scripts/generate-schema.js +5 -0
- package/dist/scripts/generate-schema.js.map +1 -1
- package/dist/scripts/verify-translations.js +73 -4
- package/dist/scripts/verify-translations.js.map +1 -1
- package/dist/translations/core/translations-config.schema.json +5 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8,87 +8,6 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/translations/utils/utils.ts
|
|
12
|
-
import * as fs from "fs";
|
|
13
|
-
import * as path from "path";
|
|
14
|
-
function readTranslations(translationsPath, language) {
|
|
15
|
-
const langPath = path.join(translationsPath, language);
|
|
16
|
-
if (!fs.existsSync(langPath)) {
|
|
17
|
-
return {};
|
|
18
|
-
}
|
|
19
|
-
const files = fs.readdirSync(langPath).filter((f) => f.endsWith(".json"));
|
|
20
|
-
const translations = {};
|
|
21
|
-
for (const file of files) {
|
|
22
|
-
const namespace = path.basename(file, ".json");
|
|
23
|
-
const filePath = path.join(langPath, file);
|
|
24
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
25
|
-
translations[namespace] = JSON.parse(content);
|
|
26
|
-
}
|
|
27
|
-
return translations;
|
|
28
|
-
}
|
|
29
|
-
function writeTranslation(translationsPath, language, namespace, translations) {
|
|
30
|
-
const langPath = path.join(translationsPath, language);
|
|
31
|
-
if (!fs.existsSync(langPath)) {
|
|
32
|
-
fs.mkdirSync(langPath, { recursive: true });
|
|
33
|
-
}
|
|
34
|
-
const filePath = path.join(langPath, `${namespace}.json`);
|
|
35
|
-
fs.writeFileSync(filePath, `${JSON.stringify(translations, null, 2)}
|
|
36
|
-
`, "utf-8");
|
|
37
|
-
}
|
|
38
|
-
function getAvailableLanguages(translationsPath) {
|
|
39
|
-
if (!fs.existsSync(translationsPath)) {
|
|
40
|
-
return [];
|
|
41
|
-
}
|
|
42
|
-
return fs.readdirSync(translationsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
43
|
-
}
|
|
44
|
-
function getNamespaces(translationsPath, language) {
|
|
45
|
-
const langPath = path.join(translationsPath, language);
|
|
46
|
-
if (!fs.existsSync(langPath)) {
|
|
47
|
-
return [];
|
|
48
|
-
}
|
|
49
|
-
return fs.readdirSync(langPath).filter((f) => f.endsWith(".json")).map((f) => path.basename(f, ".json"));
|
|
50
|
-
}
|
|
51
|
-
function hasInterpolation(text) {
|
|
52
|
-
return /\{\{[^}]+\}\}/g.test(text);
|
|
53
|
-
}
|
|
54
|
-
function extractVariables(text) {
|
|
55
|
-
const matches = text.match(/\{\{([^}]+)\}\}/g);
|
|
56
|
-
if (!matches) return [];
|
|
57
|
-
return matches.map((match) => match.replace(/\{\{|\}\}/g, "").trim());
|
|
58
|
-
}
|
|
59
|
-
function validateVariables(sourceText, translatedText) {
|
|
60
|
-
const sourceVars = extractVariables(sourceText).sort();
|
|
61
|
-
const translatedVars = extractVariables(translatedText).sort();
|
|
62
|
-
if (sourceVars.length !== translatedVars.length) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
return sourceVars.every((v, i) => v === translatedVars[i]);
|
|
66
|
-
}
|
|
67
|
-
function sortKeys(obj) {
|
|
68
|
-
const sorted = {};
|
|
69
|
-
const keys = Object.keys(obj).sort();
|
|
70
|
-
for (const key of keys) {
|
|
71
|
-
sorted[key] = obj[key];
|
|
72
|
-
}
|
|
73
|
-
return sorted;
|
|
74
|
-
}
|
|
75
|
-
function ensureTranslationsStructure(translationsPath, languages) {
|
|
76
|
-
if (!fs.existsSync(translationsPath)) {
|
|
77
|
-
fs.mkdirSync(translationsPath, { recursive: true });
|
|
78
|
-
}
|
|
79
|
-
for (const lang of languages) {
|
|
80
|
-
const langPath = path.join(translationsPath, lang);
|
|
81
|
-
if (!fs.existsSync(langPath)) {
|
|
82
|
-
fs.mkdirSync(langPath, { recursive: true });
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
var init_utils = __esm({
|
|
87
|
-
"src/translations/utils/utils.ts"() {
|
|
88
|
-
"use strict";
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
11
|
// src/translations/core/schema.ts
|
|
93
12
|
function isValidLanguage(lang) {
|
|
94
13
|
return SUPPORTED_LANGUAGES.includes(lang);
|
|
@@ -112,14 +31,14 @@ function isValidLanguageForProvider(lang, provider) {
|
|
|
112
31
|
function validateLanguages(languages) {
|
|
113
32
|
const invalid = languages.filter((lang) => !isValidLanguage(lang));
|
|
114
33
|
return {
|
|
115
|
-
valid: invalid.length
|
|
34
|
+
valid: !invalid.length,
|
|
116
35
|
invalid
|
|
117
36
|
};
|
|
118
37
|
}
|
|
119
38
|
function validateLanguagesForProvider(languages, provider) {
|
|
120
39
|
const invalid = languages.filter((lang) => !isValidLanguageForProvider(lang, provider));
|
|
121
40
|
return {
|
|
122
|
-
valid: invalid.length
|
|
41
|
+
valid: !invalid.length,
|
|
123
42
|
invalid
|
|
124
43
|
};
|
|
125
44
|
}
|
|
@@ -137,6 +56,7 @@ var TRANSLATION_PROVIDERS, DEEPL_LANGUAGES, GOOGLE_LANGUAGES, SUPPORTED_LANGUAGE
|
|
|
137
56
|
var init_schema = __esm({
|
|
138
57
|
"src/translations/core/schema.ts"() {
|
|
139
58
|
"use strict";
|
|
59
|
+
init_language_fallback();
|
|
140
60
|
TRANSLATION_PROVIDERS = ["deepl", "google"];
|
|
141
61
|
DEEPL_LANGUAGES = [
|
|
142
62
|
"ar",
|
|
@@ -490,6 +410,11 @@ var init_schema = __esm({
|
|
|
490
410
|
description: "Translation provider to use (deepl or google)",
|
|
491
411
|
enum: TRANSLATION_PROVIDERS,
|
|
492
412
|
default: "deepl"
|
|
413
|
+
},
|
|
414
|
+
useFallbackLanguages: {
|
|
415
|
+
type: "boolean",
|
|
416
|
+
description: "Enable automatic language fallback for unsupported regional variants (e.g., de_at -> de)",
|
|
417
|
+
default: false
|
|
493
418
|
}
|
|
494
419
|
},
|
|
495
420
|
required: ["translationsPath", "languages", "sourceLanguage"],
|
|
@@ -498,6 +423,314 @@ var init_schema = __esm({
|
|
|
498
423
|
}
|
|
499
424
|
});
|
|
500
425
|
|
|
426
|
+
// src/translations/utils/language-fallback.ts
|
|
427
|
+
function resolveLanguageWithFallback(language, provider, enableFallback = true) {
|
|
428
|
+
const normalizedLanguage = language.toLowerCase();
|
|
429
|
+
const supportedLanguages = getSupportedLanguagesForProvider(provider);
|
|
430
|
+
if (isLanguageSupported(normalizedLanguage, supportedLanguages)) {
|
|
431
|
+
return {
|
|
432
|
+
resolvedLanguage: normalizedLanguage,
|
|
433
|
+
usedFallback: false,
|
|
434
|
+
originalLanguage: language
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (!enableFallback) {
|
|
438
|
+
return {
|
|
439
|
+
resolvedLanguage: normalizedLanguage,
|
|
440
|
+
usedFallback: false,
|
|
441
|
+
originalLanguage: language
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const fallbackChain = LANGUAGE_FALLBACK_MAP[normalizedLanguage] || [];
|
|
445
|
+
for (const fallbackLang of fallbackChain) {
|
|
446
|
+
if (isLanguageSupported(fallbackLang, supportedLanguages)) {
|
|
447
|
+
return {
|
|
448
|
+
resolvedLanguage: fallbackLang,
|
|
449
|
+
usedFallback: true,
|
|
450
|
+
originalLanguage: language,
|
|
451
|
+
fallbackChain: [normalizedLanguage, ...fallbackChain]
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const baseLang = normalizedLanguage.split("_")[0];
|
|
456
|
+
if (baseLang !== normalizedLanguage && isLanguageSupported(baseLang, supportedLanguages)) {
|
|
457
|
+
return {
|
|
458
|
+
resolvedLanguage: baseLang,
|
|
459
|
+
usedFallback: true,
|
|
460
|
+
originalLanguage: language,
|
|
461
|
+
fallbackChain: [normalizedLanguage, baseLang]
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
resolvedLanguage: normalizedLanguage,
|
|
466
|
+
usedFallback: false,
|
|
467
|
+
originalLanguage: language
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function getSupportedLanguagesForProvider(provider) {
|
|
471
|
+
switch (provider) {
|
|
472
|
+
case "deepl":
|
|
473
|
+
return DEEPL_LANGUAGES;
|
|
474
|
+
case "google":
|
|
475
|
+
return GOOGLE_LANGUAGES;
|
|
476
|
+
default:
|
|
477
|
+
return [];
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function isLanguageSupported(language, supportedLanguages) {
|
|
481
|
+
return supportedLanguages.includes(language);
|
|
482
|
+
}
|
|
483
|
+
function logLanguageFallback(result, provider) {
|
|
484
|
+
if (result.usedFallback) {
|
|
485
|
+
console.warn(
|
|
486
|
+
`\u26A0\uFE0F Language fallback: '${result.originalLanguage}' is not supported by ${provider}, using '${result.resolvedLanguage}' instead`
|
|
487
|
+
);
|
|
488
|
+
if (result.fallbackChain && result.fallbackChain.length > 2) {
|
|
489
|
+
console.warn(` Fallback chain: ${result.fallbackChain.join(" \u2192 ")}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function getFallbackMappings() {
|
|
494
|
+
return { ...LANGUAGE_FALLBACK_MAP };
|
|
495
|
+
}
|
|
496
|
+
var LANGUAGE_FALLBACK_MAP;
|
|
497
|
+
var init_language_fallback = __esm({
|
|
498
|
+
"src/translations/utils/language-fallback.ts"() {
|
|
499
|
+
"use strict";
|
|
500
|
+
init_schema();
|
|
501
|
+
LANGUAGE_FALLBACK_MAP = {
|
|
502
|
+
// German variants
|
|
503
|
+
de_at: ["de"],
|
|
504
|
+
// Austrian German -> German
|
|
505
|
+
de_ch: ["de"],
|
|
506
|
+
// Swiss German -> German
|
|
507
|
+
de_de: ["de"],
|
|
508
|
+
// Standard German -> German
|
|
509
|
+
// English variants
|
|
510
|
+
en_gb: ["en"],
|
|
511
|
+
// British English -> English
|
|
512
|
+
en_us: ["en"],
|
|
513
|
+
// American English -> English
|
|
514
|
+
en_au: ["en"],
|
|
515
|
+
// Australian English -> English
|
|
516
|
+
en_ca: ["en"],
|
|
517
|
+
// Canadian English -> English
|
|
518
|
+
en_nz: ["en"],
|
|
519
|
+
// New Zealand English -> English
|
|
520
|
+
// Chinese variants (Hong Kong, Taiwan -> Traditional)
|
|
521
|
+
zh_hk: ["zh_hant", "zh"],
|
|
522
|
+
// Hong Kong Chinese -> Traditional Chinese -> Chinese
|
|
523
|
+
zh_tw: ["zh_hant", "zh"],
|
|
524
|
+
// Taiwan Chinese -> Traditional Chinese -> Chinese
|
|
525
|
+
zh_mo: ["zh_hant", "zh"],
|
|
526
|
+
// Macau Chinese -> Traditional Chinese -> Chinese
|
|
527
|
+
// Chinese variants (Mainland, Singapore -> Simplified)
|
|
528
|
+
zh_cn: ["zh_hans", "zh"],
|
|
529
|
+
// Mainland Chinese -> Simplified Chinese -> Chinese
|
|
530
|
+
zh_sg: ["zh_hans", "zh"],
|
|
531
|
+
// Singapore Chinese -> Simplified Chinese -> Chinese
|
|
532
|
+
// Portuguese variants
|
|
533
|
+
pt_pt: ["pt"],
|
|
534
|
+
// European Portuguese -> Portuguese
|
|
535
|
+
pt_ao: ["pt"],
|
|
536
|
+
// Angolan Portuguese -> Portuguese
|
|
537
|
+
pt_mz: ["pt"],
|
|
538
|
+
// Mozambican Portuguese -> Portuguese
|
|
539
|
+
// Spanish variants (Latin America)
|
|
540
|
+
es_mx: ["es_419", "es"],
|
|
541
|
+
// Mexican Spanish -> Latin American Spanish -> Spanish
|
|
542
|
+
es_ar: ["es_419", "es"],
|
|
543
|
+
// Argentine Spanish -> Latin American Spanish -> Spanish
|
|
544
|
+
es_co: ["es_419", "es"],
|
|
545
|
+
// Colombian Spanish -> Latin American Spanish -> Spanish
|
|
546
|
+
es_cl: ["es_419", "es"],
|
|
547
|
+
// Chilean Spanish -> Latin American Spanish -> Spanish
|
|
548
|
+
es_pe: ["es_419", "es"],
|
|
549
|
+
// Peruvian Spanish -> Latin American Spanish -> Spanish
|
|
550
|
+
es_ve: ["es_419", "es"],
|
|
551
|
+
// Venezuelan Spanish -> Latin American Spanish -> Spanish
|
|
552
|
+
es_ec: ["es_419", "es"],
|
|
553
|
+
// Ecuadorian Spanish -> Latin American Spanish -> Spanish
|
|
554
|
+
es_gt: ["es_419", "es"],
|
|
555
|
+
// Guatemalan Spanish -> Latin American Spanish -> Spanish
|
|
556
|
+
es_cu: ["es_419", "es"],
|
|
557
|
+
// Cuban Spanish -> Latin American Spanish -> Spanish
|
|
558
|
+
es_do: ["es_419", "es"],
|
|
559
|
+
// Dominican Spanish -> Latin American Spanish -> Spanish
|
|
560
|
+
es_hn: ["es_419", "es"],
|
|
561
|
+
// Honduran Spanish -> Latin American Spanish -> Spanish
|
|
562
|
+
es_ni: ["es_419", "es"],
|
|
563
|
+
// Nicaraguan Spanish -> Latin American Spanish -> Spanish
|
|
564
|
+
es_sv: ["es_419", "es"],
|
|
565
|
+
// Salvadoran Spanish -> Latin American Spanish -> Spanish
|
|
566
|
+
es_cr: ["es_419", "es"],
|
|
567
|
+
// Costa Rican Spanish -> Latin American Spanish -> Spanish
|
|
568
|
+
es_pa: ["es_419", "es"],
|
|
569
|
+
// Panamanian Spanish -> Latin American Spanish -> Spanish
|
|
570
|
+
es_uy: ["es_419", "es"],
|
|
571
|
+
// Uruguayan Spanish -> Latin American Spanish -> Spanish
|
|
572
|
+
es_py: ["es_419", "es"],
|
|
573
|
+
// Paraguayan Spanish -> Latin American Spanish -> Spanish
|
|
574
|
+
es_bo: ["es_419", "es"],
|
|
575
|
+
// Bolivian Spanish -> Latin American Spanish -> Spanish
|
|
576
|
+
// Spanish (European)
|
|
577
|
+
es_es: ["es"],
|
|
578
|
+
// European Spanish -> Spanish
|
|
579
|
+
// French variants
|
|
580
|
+
fr_ca: ["fr"],
|
|
581
|
+
// Canadian French -> French
|
|
582
|
+
fr_ch: ["fr"],
|
|
583
|
+
// Swiss French -> French
|
|
584
|
+
fr_be: ["fr"],
|
|
585
|
+
// Belgian French -> French
|
|
586
|
+
fr_fr: ["fr"],
|
|
587
|
+
// Standard French -> French
|
|
588
|
+
// Norwegian variants
|
|
589
|
+
no: ["nb"],
|
|
590
|
+
// Norwegian -> Norwegian Bokmål
|
|
591
|
+
nn: ["nb"],
|
|
592
|
+
// Norwegian Nynorsk -> Norwegian Bokmål
|
|
593
|
+
// Other regional variants
|
|
594
|
+
it_ch: ["it"],
|
|
595
|
+
// Swiss Italian -> Italian
|
|
596
|
+
nl_be: ["nl"],
|
|
597
|
+
// Belgian Dutch (Flemish) -> Dutch
|
|
598
|
+
sv_fi: ["sv"],
|
|
599
|
+
// Finland Swedish -> Swedish
|
|
600
|
+
ar_ae: ["ar"],
|
|
601
|
+
// UAE Arabic -> Arabic
|
|
602
|
+
ar_sa: ["ar"],
|
|
603
|
+
// Saudi Arabic -> Arabic
|
|
604
|
+
ar_eg: ["ar"]
|
|
605
|
+
// Egyptian Arabic -> Arabic
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// src/translations/utils/utils.ts
|
|
611
|
+
import * as fs from "fs";
|
|
612
|
+
import * as path from "path";
|
|
613
|
+
function readTranslations(translationsPath, language) {
|
|
614
|
+
const langPath = path.join(translationsPath, language);
|
|
615
|
+
if (!fs.existsSync(langPath)) {
|
|
616
|
+
return {};
|
|
617
|
+
}
|
|
618
|
+
const files = fs.readdirSync(langPath).filter((f) => f.endsWith(".json"));
|
|
619
|
+
const translations = {};
|
|
620
|
+
for (const file of files) {
|
|
621
|
+
const namespace = path.basename(file, ".json");
|
|
622
|
+
const filePath = path.join(langPath, file);
|
|
623
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
624
|
+
translations[namespace] = JSON.parse(content);
|
|
625
|
+
}
|
|
626
|
+
return translations;
|
|
627
|
+
}
|
|
628
|
+
function writeTranslation(translationsPath, language, namespace, translations) {
|
|
629
|
+
const langPath = path.join(translationsPath, language);
|
|
630
|
+
if (!fs.existsSync(langPath)) {
|
|
631
|
+
fs.mkdirSync(langPath, { recursive: true });
|
|
632
|
+
}
|
|
633
|
+
const filePath = path.join(langPath, `${namespace}.json`);
|
|
634
|
+
fs.writeFileSync(filePath, `${JSON.stringify(translations, null, 2)}
|
|
635
|
+
`, "utf-8");
|
|
636
|
+
}
|
|
637
|
+
function getAvailableLanguages(translationsPath) {
|
|
638
|
+
if (!fs.existsSync(translationsPath)) {
|
|
639
|
+
return [];
|
|
640
|
+
}
|
|
641
|
+
return fs.readdirSync(translationsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
642
|
+
}
|
|
643
|
+
function getNamespaces(translationsPath, language) {
|
|
644
|
+
const langPath = path.join(translationsPath, language);
|
|
645
|
+
if (!fs.existsSync(langPath)) {
|
|
646
|
+
return [];
|
|
647
|
+
}
|
|
648
|
+
return fs.readdirSync(langPath).filter((f) => f.endsWith(".json")).map((f) => path.basename(f, ".json"));
|
|
649
|
+
}
|
|
650
|
+
function hasInterpolation(text) {
|
|
651
|
+
return /\{\{[^}]+\}\}/g.test(text);
|
|
652
|
+
}
|
|
653
|
+
function extractVariables(text) {
|
|
654
|
+
const matches = text.match(/\{\{([^}]+)\}\}/g);
|
|
655
|
+
if (!matches) return [];
|
|
656
|
+
return matches.map((match) => match.replace(/\{\{|\}\}/g, "").trim());
|
|
657
|
+
}
|
|
658
|
+
function validateVariables(sourceText, translatedText) {
|
|
659
|
+
const sourceVars = extractVariables(sourceText).sort();
|
|
660
|
+
const translatedVars = extractVariables(translatedText).sort();
|
|
661
|
+
if (sourceVars.length !== translatedVars.length) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
return sourceVars.every((v, i) => v === translatedVars[i]);
|
|
665
|
+
}
|
|
666
|
+
function sortKeys(obj) {
|
|
667
|
+
const sorted = {};
|
|
668
|
+
const keys = Object.keys(obj).sort();
|
|
669
|
+
for (const key of keys) {
|
|
670
|
+
sorted[key] = obj[key];
|
|
671
|
+
}
|
|
672
|
+
return sorted;
|
|
673
|
+
}
|
|
674
|
+
function ensureTranslationsStructure(translationsPath, languages) {
|
|
675
|
+
if (!fs.existsSync(translationsPath)) {
|
|
676
|
+
fs.mkdirSync(translationsPath, { recursive: true });
|
|
677
|
+
}
|
|
678
|
+
for (const lang of languages) {
|
|
679
|
+
const langPath = path.join(translationsPath, lang);
|
|
680
|
+
if (!fs.existsSync(langPath)) {
|
|
681
|
+
fs.mkdirSync(langPath, { recursive: true });
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function createEmptyTranslationStructure(sourceFile) {
|
|
686
|
+
const result = {};
|
|
687
|
+
for (const key of Object.keys(sourceFile)) {
|
|
688
|
+
result[key] = "";
|
|
689
|
+
}
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
692
|
+
function syncTranslationStructure(translationsPath, languages, sourceLanguage) {
|
|
693
|
+
const result = {
|
|
694
|
+
createdFolders: [],
|
|
695
|
+
createdFiles: [],
|
|
696
|
+
skippedFiles: []
|
|
697
|
+
};
|
|
698
|
+
ensureTranslationsStructure(translationsPath, languages);
|
|
699
|
+
const sourceNamespaces = getNamespaces(translationsPath, sourceLanguage);
|
|
700
|
+
if (!sourceNamespaces.length) {
|
|
701
|
+
return result;
|
|
702
|
+
}
|
|
703
|
+
const sourceTranslations = readTranslations(translationsPath, sourceLanguage);
|
|
704
|
+
const targetLanguages = languages.filter((lang) => lang !== sourceLanguage);
|
|
705
|
+
for (const language of targetLanguages) {
|
|
706
|
+
for (const namespace of sourceNamespaces) {
|
|
707
|
+
const filePath = path.join(translationsPath, language, `${namespace}.json`);
|
|
708
|
+
if (fs.existsSync(filePath)) {
|
|
709
|
+
result.skippedFiles.push({
|
|
710
|
+
language,
|
|
711
|
+
namespace,
|
|
712
|
+
reason: "already exists"
|
|
713
|
+
});
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
const sourceFile = sourceTranslations[namespace] || {};
|
|
717
|
+
const emptyStructure = createEmptyTranslationStructure(sourceFile);
|
|
718
|
+
writeTranslation(translationsPath, language, namespace, emptyStructure);
|
|
719
|
+
result.createdFiles.push({
|
|
720
|
+
language,
|
|
721
|
+
namespace,
|
|
722
|
+
path: filePath
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return result;
|
|
727
|
+
}
|
|
728
|
+
var init_utils = __esm({
|
|
729
|
+
"src/translations/utils/utils.ts"() {
|
|
730
|
+
"use strict";
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
|
|
501
734
|
// src/translations/core/types.ts
|
|
502
735
|
var DEFAULT_CONFIG, DEFAULT_LANGUAGES;
|
|
503
736
|
var init_types = __esm({
|
|
@@ -507,24 +740,11 @@ var init_types = __esm({
|
|
|
507
740
|
translationsPath: "public/static/locales",
|
|
508
741
|
languages: ["en"],
|
|
509
742
|
sourceLanguage: "en",
|
|
510
|
-
typesOutputPath: "src/types/i18nTypes.ts"
|
|
743
|
+
typesOutputPath: "src/types/i18nTypes.ts",
|
|
744
|
+
provider: "google",
|
|
745
|
+
useFallbackLanguages: true
|
|
511
746
|
};
|
|
512
|
-
DEFAULT_LANGUAGES = [
|
|
513
|
-
"en",
|
|
514
|
-
"fr",
|
|
515
|
-
"it",
|
|
516
|
-
"pl",
|
|
517
|
-
"es",
|
|
518
|
-
"pt",
|
|
519
|
-
"de",
|
|
520
|
-
"de_at",
|
|
521
|
-
"nl",
|
|
522
|
-
"sv",
|
|
523
|
-
"hu",
|
|
524
|
-
"cs",
|
|
525
|
-
"ja",
|
|
526
|
-
"zh_hk"
|
|
527
|
-
];
|
|
747
|
+
DEFAULT_LANGUAGES = ["en", "fr", "it", "pl", "es", "pt", "de", "nl", "sv", "hu", "cs", "ja"];
|
|
528
748
|
}
|
|
529
749
|
});
|
|
530
750
|
|
|
@@ -598,12 +818,14 @@ function initTranslations(projectRoot, config = {}) {
|
|
|
598
818
|
`, "utf-8");
|
|
599
819
|
console.log(`Created sample file: ${commonPath}`);
|
|
600
820
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
821
|
+
console.log("\nSynchronizing translation structure...");
|
|
822
|
+
const syncResult = syncTranslationStructure(translationsPath, languages, sourceLanguage);
|
|
823
|
+
if (syncResult.createdFiles.length > 0) {
|
|
824
|
+
console.log(`Created ${syncResult.createdFiles.length} namespace files in target languages`);
|
|
825
|
+
const languageGroups = new Set(syncResult.createdFiles.map((f) => f.language));
|
|
826
|
+
for (const lang of languageGroups) {
|
|
827
|
+
const langFiles = syncResult.createdFiles.filter((f) => f.language === lang);
|
|
828
|
+
console.log(` ${lang}: ${langFiles.map((f) => f.namespace).join(", ")}`);
|
|
607
829
|
}
|
|
608
830
|
}
|
|
609
831
|
const configPath = path2.join(projectRoot, ".translationsrc.json");
|
|
@@ -613,7 +835,8 @@ function initTranslations(projectRoot, config = {}) {
|
|
|
613
835
|
translationsPath: finalConfig.translationsPath,
|
|
614
836
|
languages,
|
|
615
837
|
sourceLanguage,
|
|
616
|
-
typesOutputPath: finalConfig.typesOutputPath
|
|
838
|
+
typesOutputPath: finalConfig.typesOutputPath,
|
|
839
|
+
provider: finalConfig.provider
|
|
617
840
|
};
|
|
618
841
|
fs2.writeFileSync(configPath, `${JSON.stringify(configContent, null, 2)}
|
|
619
842
|
`, "utf-8");
|
|
@@ -661,6 +884,7 @@ var init_init = __esm({
|
|
|
661
884
|
import * as path3 from "path";
|
|
662
885
|
|
|
663
886
|
// src/translations/utils/google-translate-provider.ts
|
|
887
|
+
init_language_fallback();
|
|
664
888
|
function preserveVariables(text) {
|
|
665
889
|
const variableMap = /* @__PURE__ */ new Map();
|
|
666
890
|
let placeholderIndex = 0;
|
|
@@ -681,14 +905,24 @@ function restoreVariables(text, variableMap) {
|
|
|
681
905
|
}
|
|
682
906
|
var GoogleTranslateProvider = class {
|
|
683
907
|
async translate(options) {
|
|
684
|
-
const { text, sourceLang, targetLang, apiKey } = options;
|
|
908
|
+
const { text, sourceLang, targetLang, apiKey, useFallbackLanguages = true } = options;
|
|
685
909
|
if (!apiKey) {
|
|
686
910
|
throw new Error(
|
|
687
911
|
"Google Translate API key is required. Set GOOGLE_TRANSLATE_API_KEY environment variable or provide apiKey in options."
|
|
688
912
|
);
|
|
689
913
|
}
|
|
914
|
+
const targetLangResult = resolveLanguageWithFallback(targetLang, "google", useFallbackLanguages);
|
|
915
|
+
logLanguageFallback(targetLangResult, "google");
|
|
916
|
+
let resolvedSourceLang;
|
|
917
|
+
if (sourceLang) {
|
|
918
|
+
const sourceLangResult = resolveLanguageWithFallback(sourceLang, "google", useFallbackLanguages);
|
|
919
|
+
logLanguageFallback(sourceLangResult, "google");
|
|
920
|
+
resolvedSourceLang = sourceLangResult.resolvedLanguage;
|
|
921
|
+
}
|
|
690
922
|
const { textWithPlaceholders, variableMap } = preserveVariables(text);
|
|
691
923
|
const url = `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`;
|
|
924
|
+
const sourceForGoogle = resolvedSourceLang?.includes("_") ? resolvedSourceLang.split("_")[0] : resolvedSourceLang;
|
|
925
|
+
const targetForGoogle = targetLangResult.resolvedLanguage.includes("_") ? targetLangResult.resolvedLanguage.split("_")[0] : targetLangResult.resolvedLanguage;
|
|
692
926
|
const response = await fetch(url, {
|
|
693
927
|
method: "POST",
|
|
694
928
|
headers: {
|
|
@@ -696,9 +930,8 @@ var GoogleTranslateProvider = class {
|
|
|
696
930
|
},
|
|
697
931
|
body: JSON.stringify({
|
|
698
932
|
q: textWithPlaceholders,
|
|
699
|
-
source:
|
|
700
|
-
target:
|
|
701
|
-
// Convert 'pt_BR' to 'pt'
|
|
933
|
+
source: sourceForGoogle,
|
|
934
|
+
target: targetForGoogle,
|
|
702
935
|
format: "text"
|
|
703
936
|
})
|
|
704
937
|
});
|
|
@@ -743,13 +976,14 @@ function getTranslationProvider() {
|
|
|
743
976
|
function resetTranslationProvider() {
|
|
744
977
|
customProvider = null;
|
|
745
978
|
}
|
|
746
|
-
async function translateText(text, targetLang, sourceLang = "en", apiKey) {
|
|
979
|
+
async function translateText(text, targetLang, sourceLang = "en", apiKey, useFallbackLanguages = true) {
|
|
747
980
|
const provider = getTranslationProvider();
|
|
748
981
|
return provider.translate({
|
|
749
982
|
text,
|
|
750
983
|
sourceLang,
|
|
751
984
|
targetLang,
|
|
752
|
-
apiKey
|
|
985
|
+
apiKey,
|
|
986
|
+
useFallbackLanguages
|
|
753
987
|
});
|
|
754
988
|
}
|
|
755
989
|
async function translateBatch(texts, targetLang, sourceLang = "en", apiKey, delayMs = 100) {
|
|
@@ -845,6 +1079,101 @@ async function addTranslationKeys(projectRoot, entries, autoTranslate = false, a
|
|
|
845
1079
|
|
|
846
1080
|
// src/translations/cli/auto-fill.ts
|
|
847
1081
|
import * as path5 from "path";
|
|
1082
|
+
|
|
1083
|
+
// src/translations/utils/deepl-translate-provider.ts
|
|
1084
|
+
init_language_fallback();
|
|
1085
|
+
function preserveVariables2(text) {
|
|
1086
|
+
const variableMap = /* @__PURE__ */ new Map();
|
|
1087
|
+
let placeholderIndex = 0;
|
|
1088
|
+
const textWithPlaceholders = text.replace(/\{\{([^}]+)\}\}/g, (match) => {
|
|
1089
|
+
const placeholder = `XXX_${placeholderIndex}_XXX`;
|
|
1090
|
+
variableMap.set(placeholder, match);
|
|
1091
|
+
placeholderIndex++;
|
|
1092
|
+
return placeholder;
|
|
1093
|
+
});
|
|
1094
|
+
return { textWithPlaceholders, variableMap };
|
|
1095
|
+
}
|
|
1096
|
+
function restoreVariables2(text, variableMap) {
|
|
1097
|
+
let result = text;
|
|
1098
|
+
for (const [placeholder, original] of variableMap) {
|
|
1099
|
+
result = result.replace(new RegExp(placeholder, "g"), original);
|
|
1100
|
+
}
|
|
1101
|
+
return result;
|
|
1102
|
+
}
|
|
1103
|
+
function normalizeLanguageCode(langCode) {
|
|
1104
|
+
return langCode.replace("_", "-").toUpperCase();
|
|
1105
|
+
}
|
|
1106
|
+
var DeepLTranslateProvider = class {
|
|
1107
|
+
isFreeApi;
|
|
1108
|
+
constructor(isFreeApi = false) {
|
|
1109
|
+
this.isFreeApi = isFreeApi;
|
|
1110
|
+
}
|
|
1111
|
+
getApiEndpoint() {
|
|
1112
|
+
return this.isFreeApi ? "https://api-free.deepl.com/v2/translate" : "https://api.deepl.com/v2/translate";
|
|
1113
|
+
}
|
|
1114
|
+
async translate(options) {
|
|
1115
|
+
const { text, sourceLang, targetLang, apiKey, useFallbackLanguages = true } = options;
|
|
1116
|
+
if (!apiKey) {
|
|
1117
|
+
throw new Error(
|
|
1118
|
+
"DeepL API key is required. Set DEEPL_API_KEY environment variable or provide apiKey in options."
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
const targetLangResult = resolveLanguageWithFallback(targetLang, "deepl", useFallbackLanguages);
|
|
1122
|
+
logLanguageFallback(targetLangResult, "deepl");
|
|
1123
|
+
let resolvedSourceLang;
|
|
1124
|
+
if (sourceLang) {
|
|
1125
|
+
const sourceLangResult = resolveLanguageWithFallback(sourceLang, "deepl", useFallbackLanguages);
|
|
1126
|
+
logLanguageFallback(sourceLangResult, "deepl");
|
|
1127
|
+
resolvedSourceLang = sourceLangResult.resolvedLanguage;
|
|
1128
|
+
}
|
|
1129
|
+
const { textWithPlaceholders, variableMap } = preserveVariables2(text);
|
|
1130
|
+
const body = {
|
|
1131
|
+
text: [textWithPlaceholders],
|
|
1132
|
+
target_lang: normalizeLanguageCode(targetLangResult.resolvedLanguage),
|
|
1133
|
+
...resolvedSourceLang && { source_lang: normalizeLanguageCode(resolvedSourceLang) }
|
|
1134
|
+
};
|
|
1135
|
+
const response = await fetch(this.getApiEndpoint(), {
|
|
1136
|
+
method: "POST",
|
|
1137
|
+
headers: {
|
|
1138
|
+
Authorization: `DeepL-Auth-Key ${apiKey}`,
|
|
1139
|
+
"Content-Type": "application/json"
|
|
1140
|
+
},
|
|
1141
|
+
body: JSON.stringify(body)
|
|
1142
|
+
});
|
|
1143
|
+
if (!response.ok) {
|
|
1144
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1145
|
+
throw new Error(`DeepL API error: ${errorData.message || response.statusText} (${response.status})`);
|
|
1146
|
+
}
|
|
1147
|
+
const data = await response.json();
|
|
1148
|
+
if (!data.translations || data.translations.length === 0) {
|
|
1149
|
+
throw new Error("DeepL API returned no translations");
|
|
1150
|
+
}
|
|
1151
|
+
const translatedText = data.translations[0].text;
|
|
1152
|
+
return restoreVariables2(translatedText, variableMap);
|
|
1153
|
+
}
|
|
1154
|
+
async translateBatch(texts, sourceLang, targetLang, apiKey, delayMs = 100) {
|
|
1155
|
+
const results = [];
|
|
1156
|
+
for (const text of texts) {
|
|
1157
|
+
const translated = await this.translate({
|
|
1158
|
+
text,
|
|
1159
|
+
sourceLang,
|
|
1160
|
+
targetLang,
|
|
1161
|
+
apiKey
|
|
1162
|
+
});
|
|
1163
|
+
results.push(translated);
|
|
1164
|
+
if (delayMs > 0) {
|
|
1165
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
return results;
|
|
1169
|
+
}
|
|
1170
|
+
async validateConfig() {
|
|
1171
|
+
const apiKey = process.env.DEEPL_API_KEY;
|
|
1172
|
+
return !!apiKey;
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
// src/translations/cli/auto-fill.ts
|
|
848
1177
|
init_utils();
|
|
849
1178
|
init_init();
|
|
850
1179
|
|
|
@@ -860,7 +1189,11 @@ function validateTranslations(projectRoot = process.cwd()) {
|
|
|
860
1189
|
const empty = [];
|
|
861
1190
|
const sourceTranslations = readTranslations(translationsPath, sourceLanguage);
|
|
862
1191
|
const sourceNamespaces = getNamespaces(translationsPath, sourceLanguage);
|
|
863
|
-
const languages =
|
|
1192
|
+
const languages = config.languages.filter((lang) => lang !== sourceLanguage);
|
|
1193
|
+
const syncResult = syncTranslationStructure(translationsPath, config.languages, sourceLanguage);
|
|
1194
|
+
if (syncResult.createdFiles.length > 0) {
|
|
1195
|
+
console.log(`Created ${syncResult.createdFiles.length} missing namespace files during sync`);
|
|
1196
|
+
}
|
|
864
1197
|
console.log("=====");
|
|
865
1198
|
console.log("Validating translations");
|
|
866
1199
|
console.log("=====");
|
|
@@ -893,7 +1226,7 @@ function validateTranslations(projectRoot = process.cwd()) {
|
|
|
893
1226
|
}
|
|
894
1227
|
}
|
|
895
1228
|
}
|
|
896
|
-
const valid = missing.length
|
|
1229
|
+
const valid = !missing.length && !empty.length;
|
|
897
1230
|
if (valid) {
|
|
898
1231
|
console.log("\u2713 All translations are valid!");
|
|
899
1232
|
} else {
|
|
@@ -935,10 +1268,28 @@ async function autoFillTranslations(projectRoot = process.cwd(), options = {}) {
|
|
|
935
1268
|
const config = loadConfig(projectRoot);
|
|
936
1269
|
const translationsPath = path5.join(projectRoot, config.translationsPath);
|
|
937
1270
|
const { apiKey, limit = 1e3, delayMs = 100, dryRun = false } = options;
|
|
1271
|
+
const currentProvider = getTranslationProvider();
|
|
1272
|
+
const isDefaultGoogleProvider = currentProvider.constructor.name === "GoogleTranslateProvider";
|
|
1273
|
+
if (isDefaultGoogleProvider) {
|
|
1274
|
+
const provider = config.provider || "deepl";
|
|
1275
|
+
if (provider === "deepl") {
|
|
1276
|
+
setTranslationProvider(new DeepLTranslateProvider());
|
|
1277
|
+
} else {
|
|
1278
|
+
setTranslationProvider(new GoogleTranslateProvider());
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
938
1281
|
if (!apiKey) {
|
|
939
|
-
|
|
1282
|
+
const provider = config.provider || "deepl";
|
|
1283
|
+
const envVarName = provider === "google" ? "GOOGLE_TRANSLATE_API_KEY" : "DEEPL_API_KEY";
|
|
1284
|
+
throw new Error(`Translation API key is required. Set ${envVarName} or pass --api-key`);
|
|
940
1285
|
}
|
|
941
1286
|
const languagesToProcess = options.language ? [options.language] : config.languages.filter((lang) => lang !== config.sourceLanguage);
|
|
1287
|
+
console.log("\u{1F504} Synchronizing translation structure...");
|
|
1288
|
+
const syncResult = syncTranslationStructure(translationsPath, config.languages, config.sourceLanguage);
|
|
1289
|
+
if (syncResult.createdFiles.length > 0) {
|
|
1290
|
+
console.log(`Created ${syncResult.createdFiles.length} namespace files
|
|
1291
|
+
`);
|
|
1292
|
+
}
|
|
942
1293
|
console.log("=====");
|
|
943
1294
|
console.log("Auto-filling translations");
|
|
944
1295
|
console.log("=====");
|
|
@@ -957,7 +1308,7 @@ Reached limit of ${limit} translations`);
|
|
|
957
1308
|
console.log(`
|
|
958
1309
|
Processing language: ${language}`);
|
|
959
1310
|
const missing = getMissingForLanguage(projectRoot, language);
|
|
960
|
-
if (missing.length
|
|
1311
|
+
if (!missing.length) {
|
|
961
1312
|
console.log(" No missing or empty translations");
|
|
962
1313
|
continue;
|
|
963
1314
|
}
|
|
@@ -969,7 +1320,13 @@ Processing language: ${language}`);
|
|
|
969
1320
|
try {
|
|
970
1321
|
console.log(` [${totalProcessed}/${limit}] Translating ${item.namespace}.${item.key}`);
|
|
971
1322
|
console.log(` EN: "${item.sourceValue}"`);
|
|
972
|
-
const translated = await translateText(
|
|
1323
|
+
const translated = await translateText(
|
|
1324
|
+
item.sourceValue,
|
|
1325
|
+
language,
|
|
1326
|
+
config.sourceLanguage,
|
|
1327
|
+
apiKey,
|
|
1328
|
+
config.useFallbackLanguages ?? true
|
|
1329
|
+
);
|
|
973
1330
|
console.log(` ${language.toUpperCase()}: "${translated}"`);
|
|
974
1331
|
if (!dryRun) {
|
|
975
1332
|
const translations = readTranslations(translationsPath, language);
|
|
@@ -1003,6 +1360,16 @@ Processing language: ${language}`);
|
|
|
1003
1360
|
async function fillNamespace(projectRoot, language, namespace, apiKey) {
|
|
1004
1361
|
const config = loadConfig(projectRoot);
|
|
1005
1362
|
const translationsPath = path5.join(projectRoot, config.translationsPath);
|
|
1363
|
+
const currentProvider = getTranslationProvider();
|
|
1364
|
+
const isDefaultGoogleProvider = currentProvider.constructor.name === "GoogleTranslateProvider";
|
|
1365
|
+
if (isDefaultGoogleProvider) {
|
|
1366
|
+
const provider = config.provider || "deepl";
|
|
1367
|
+
if (provider === "deepl") {
|
|
1368
|
+
setTranslationProvider(new DeepLTranslateProvider());
|
|
1369
|
+
} else {
|
|
1370
|
+
setTranslationProvider(new GoogleTranslateProvider());
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1006
1373
|
console.log(`Filling translations for ${language}/${namespace}.json`);
|
|
1007
1374
|
const sourceTranslations = readTranslations(translationsPath, config.sourceLanguage);
|
|
1008
1375
|
const targetTranslations = readTranslations(translationsPath, language);
|
|
@@ -1015,7 +1382,13 @@ async function fillNamespace(projectRoot, language, namespace, apiKey) {
|
|
|
1015
1382
|
continue;
|
|
1016
1383
|
}
|
|
1017
1384
|
console.log(` Translating ${key}...`);
|
|
1018
|
-
const translated = await translateText(
|
|
1385
|
+
const translated = await translateText(
|
|
1386
|
+
sourceValue,
|
|
1387
|
+
language,
|
|
1388
|
+
config.sourceLanguage,
|
|
1389
|
+
apiKey,
|
|
1390
|
+
config.useFallbackLanguages ?? true
|
|
1391
|
+
);
|
|
1019
1392
|
targetKeys[key] = translated;
|
|
1020
1393
|
count++;
|
|
1021
1394
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -1056,7 +1429,7 @@ function generateTranslationTypes(projectRoot = process.cwd()) {
|
|
|
1056
1429
|
throw new Error(`Source language directory not found: ${dirPath}`);
|
|
1057
1430
|
}
|
|
1058
1431
|
const namespaces = getNamespaces(translationsPath, sourceLanguage);
|
|
1059
|
-
if (namespaces.length
|
|
1432
|
+
if (!namespaces.length) {
|
|
1060
1433
|
throw new Error(`No translation files found in ${dirPath}`);
|
|
1061
1434
|
}
|
|
1062
1435
|
const translations = readTranslations(translationsPath, sourceLanguage);
|
|
@@ -1094,7 +1467,7 @@ init_types();
|
|
|
1094
1467
|
init_init();
|
|
1095
1468
|
import * as fs4 from "fs";
|
|
1096
1469
|
import * as path7 from "path";
|
|
1097
|
-
import { checkbox, confirm, input } from "@inquirer/prompts";
|
|
1470
|
+
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
1098
1471
|
async function initTranslationsInteractive(projectRoot = process.cwd()) {
|
|
1099
1472
|
console.log("\n\u{1F30D} Translation System Setup\n");
|
|
1100
1473
|
const configPath = path7.join(projectRoot, ".translationsrc.json");
|
|
@@ -1173,11 +1546,28 @@ async function initTranslationsInteractive(projectRoot = process.cwd()) {
|
|
|
1173
1546
|
message: "Where should TypeScript types be generated?",
|
|
1174
1547
|
default: DEFAULT_CONFIG.typesOutputPath
|
|
1175
1548
|
});
|
|
1549
|
+
const provider = await select({
|
|
1550
|
+
message: "Which translation provider would you like to use?",
|
|
1551
|
+
choices: [
|
|
1552
|
+
{
|
|
1553
|
+
name: "DeepL (recommended)",
|
|
1554
|
+
value: "deepl",
|
|
1555
|
+
description: "High-quality translations, requires DEEPL_API_KEY"
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
name: "Google Translate",
|
|
1559
|
+
value: "google",
|
|
1560
|
+
description: "Google Cloud Translation API, requires GOOGLE_TRANSLATE_API_KEY"
|
|
1561
|
+
}
|
|
1562
|
+
],
|
|
1563
|
+
default: "deepl"
|
|
1564
|
+
});
|
|
1176
1565
|
console.log("\n\u{1F4CB} Configuration Summary:");
|
|
1177
1566
|
console.log(` Translations: ${translationsPath}`);
|
|
1178
1567
|
console.log(` Languages: ${languages.join(", ")}`);
|
|
1179
1568
|
console.log(` Source: ${sourceLanguage}`);
|
|
1180
1569
|
console.log(` Types: ${typesOutputPath}`);
|
|
1570
|
+
console.log(` Provider: ${provider}`);
|
|
1181
1571
|
const confirmInit = await confirm({
|
|
1182
1572
|
message: "\nProceed with initialization?",
|
|
1183
1573
|
default: true
|
|
@@ -1190,7 +1580,8 @@ async function initTranslationsInteractive(projectRoot = process.cwd()) {
|
|
|
1190
1580
|
translationsPath,
|
|
1191
1581
|
languages,
|
|
1192
1582
|
sourceLanguage,
|
|
1193
|
-
typesOutputPath
|
|
1583
|
+
typesOutputPath,
|
|
1584
|
+
provider
|
|
1194
1585
|
};
|
|
1195
1586
|
console.log();
|
|
1196
1587
|
initTranslations(projectRoot, config);
|
|
@@ -1247,6 +1638,7 @@ function getLanguageName(code) {
|
|
|
1247
1638
|
}
|
|
1248
1639
|
|
|
1249
1640
|
// src/translations/cli/manage.ts
|
|
1641
|
+
init_utils();
|
|
1250
1642
|
import * as fs5 from "fs";
|
|
1251
1643
|
import * as path8 from "path";
|
|
1252
1644
|
init_init();
|
|
@@ -1272,6 +1664,14 @@ async function manageTranslations(projectRoot = process.cwd(), options = {}) {
|
|
|
1272
1664
|
console.log("Please add translation files to the source language directory.\n");
|
|
1273
1665
|
return false;
|
|
1274
1666
|
}
|
|
1667
|
+
console.log("\u{1F504} Synchronizing translation structure...\n");
|
|
1668
|
+
const syncResult = syncTranslationStructure(translationsPath, config.languages, config.sourceLanguage);
|
|
1669
|
+
if (syncResult.createdFiles.length > 0) {
|
|
1670
|
+
console.log(`\u2713 Created ${syncResult.createdFiles.length} namespace files
|
|
1671
|
+
`);
|
|
1672
|
+
} else {
|
|
1673
|
+
console.log("\u2713 Translation structure is already synchronized\n");
|
|
1674
|
+
}
|
|
1275
1675
|
console.log("\u{1F50D} Validating translations...\n");
|
|
1276
1676
|
const validationResult = validateTranslations(projectRoot);
|
|
1277
1677
|
if (validationResult.valid) {
|
|
@@ -1280,8 +1680,11 @@ async function manageTranslations(projectRoot = process.cwd(), options = {}) {
|
|
|
1280
1680
|
const totalMissing = validationResult.missing.length + validationResult.empty.length;
|
|
1281
1681
|
if (autoFill) {
|
|
1282
1682
|
if (!apiKey) {
|
|
1683
|
+
const provider = config.provider || "deepl";
|
|
1684
|
+
const envVarName = provider === "google" ? "GOOGLE_TRANSLATE_API_KEY" : "DEEPL_API_KEY";
|
|
1283
1685
|
console.log("\n\u26A0\uFE0F Auto-fill requested but no API key provided.");
|
|
1284
|
-
console.log(
|
|
1686
|
+
console.log(`Set ${envVarName} or pass --api-key to enable auto-fill.
|
|
1687
|
+
`);
|
|
1285
1688
|
} else {
|
|
1286
1689
|
console.log(`
|
|
1287
1690
|
\u{1F916} Auto-filling ${totalMissing} missing translations...
|
|
@@ -1357,12 +1760,14 @@ export {
|
|
|
1357
1760
|
addTranslationKey,
|
|
1358
1761
|
addTranslationKeys,
|
|
1359
1762
|
autoFillTranslations,
|
|
1763
|
+
createEmptyTranslationStructure,
|
|
1360
1764
|
detectExistingTranslations,
|
|
1361
1765
|
ensureTranslationsStructure,
|
|
1362
1766
|
extractVariables,
|
|
1363
1767
|
fillNamespace,
|
|
1364
1768
|
generateTranslationTypes,
|
|
1365
1769
|
getAvailableLanguages,
|
|
1770
|
+
getFallbackMappings,
|
|
1366
1771
|
getMissingForLanguage,
|
|
1367
1772
|
getNamespaces,
|
|
1368
1773
|
getSupportedLanguages,
|
|
@@ -1375,11 +1780,14 @@ export {
|
|
|
1375
1780
|
isValidLanguage,
|
|
1376
1781
|
isValidLanguageForProvider,
|
|
1377
1782
|
loadConfig,
|
|
1783
|
+
logLanguageFallback,
|
|
1378
1784
|
manageTranslations,
|
|
1379
1785
|
readTranslations,
|
|
1380
1786
|
resetTranslationProvider,
|
|
1787
|
+
resolveLanguageWithFallback,
|
|
1381
1788
|
setTranslationProvider,
|
|
1382
1789
|
sortKeys,
|
|
1790
|
+
syncTranslationStructure,
|
|
1383
1791
|
translateBatch,
|
|
1384
1792
|
translateText,
|
|
1385
1793
|
validateLanguages,
|