@babelize/engine 0.0.0-stage → 0.1.2
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/LICENSE +202 -0
- package/dist/BabelizeEngine.d.ts +12 -0
- package/dist/BabelizeEngine.js +47 -0
- package/dist/analysis/analyze.d.ts +106 -0
- package/dist/analysis/analyze.js +323 -0
- package/dist/config/env.d.ts +9 -0
- package/dist/config/env.js +16 -0
- package/dist/config.d.ts +74 -0
- package/dist/config.js +47 -0
- package/dist/detection/stack.d.ts +42 -0
- package/dist/detection/stack.js +277 -0
- package/dist/django/catalog.d.ts +92 -0
- package/dist/django/catalog.js +677 -0
- package/dist/django/extract.d.ts +20 -0
- package/dist/django/extract.js +79 -0
- package/dist/django/inject-file.d.ts +6 -0
- package/dist/django/inject-file.js +52 -0
- package/dist/django/inject.d.ts +11 -0
- package/dist/django/inject.js +205 -0
- package/dist/django/keys.d.ts +21 -0
- package/dist/django/keys.js +63 -0
- package/dist/django/locale-names.d.ts +13 -0
- package/dist/django/locale-names.js +60 -0
- package/dist/django/mo-file.d.ts +6 -0
- package/dist/django/mo-file.js +60 -0
- package/dist/django/plural-forms.d.ts +34 -0
- package/dist/django/plural-forms.js +216 -0
- package/dist/django/po-file.d.ts +38 -0
- package/dist/django/po-file.js +273 -0
- package/dist/django/python-extract.d.ts +34 -0
- package/dist/django/python-extract.js +619 -0
- package/dist/django/python-lexer.d.ts +65 -0
- package/dist/django/python-lexer.js +398 -0
- package/dist/django/template-extract.d.ts +32 -0
- package/dist/django/template-extract.js +680 -0
- package/dist/django/types.d.ts +49 -0
- package/dist/django/types.js +15 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +140 -0
- package/dist/injection/InjectionStrategy.d.ts +62 -0
- package/dist/injection/InjectionStrategy.js +2 -0
- package/dist/injection/StrategyFactory.d.ts +4 -0
- package/dist/injection/StrategyFactory.js +29 -0
- package/dist/injection/html/html-injector.d.ts +11 -0
- package/dist/injection/html/html-injector.js +173 -0
- package/dist/injection/strategies/NextIntlStrategy.d.ts +25 -0
- package/dist/injection/strategies/NextIntlStrategy.js +141 -0
- package/dist/injection/strategies/ReactI18nextStrategy.d.ts +19 -0
- package/dist/injection/strategies/ReactI18nextStrategy.js +116 -0
- package/dist/injection/strategies/VanillaStrategy.d.ts +11 -0
- package/dist/injection/strategies/VanillaStrategy.js +73 -0
- package/dist/injection/strategies/VueI18nStrategy.d.ts +11 -0
- package/dist/injection/strategies/VueI18nStrategy.js +84 -0
- package/dist/local/formatter.d.ts +26 -0
- package/dist/local/formatter.js +105 -0
- package/dist/local/git.d.ts +23 -0
- package/dist/local/git.js +136 -0
- package/dist/local/hosted-provider.d.ts +68 -0
- package/dist/local/hosted-provider.js +215 -0
- package/dist/local/index.d.ts +23 -0
- package/dist/local/index.js +75 -0
- package/dist/local/provider.d.ts +56 -0
- package/dist/local/provider.js +119 -0
- package/dist/local/pseudo-provider.d.ts +18 -0
- package/dist/local/pseudo-provider.js +208 -0
- package/dist/local/report.d.ts +177 -0
- package/dist/local/report.js +186 -0
- package/dist/local/translate-core.d.ts +218 -0
- package/dist/local/translate-core.js +499 -0
- package/dist/local/translate-messages.d.ts +49 -0
- package/dist/local/translate-messages.js +89 -0
- package/dist/local/workspace-copy.d.ts +91 -0
- package/dist/local/workspace-copy.js +355 -0
- package/dist/modules/extractor/extract.d.ts +20 -0
- package/dist/modules/extractor/extract.js +709 -0
- package/dist/modules/extractor/index.d.ts +2 -0
- package/dist/modules/extractor/index.js +63 -0
- package/dist/modules/finalization/django-finalize.d.ts +28 -0
- package/dist/modules/finalization/django-finalize.js +412 -0
- package/dist/modules/finalization/finalize.d.ts +38 -0
- package/dist/modules/finalization/finalize.js +745 -0
- package/dist/modules/finalization/index.d.ts +1 -0
- package/dist/modules/finalization/index.js +5 -0
- package/dist/modules/finalization/nextjs-finalize.d.ts +40 -0
- package/dist/modules/finalization/nextjs-finalize.js +328 -0
- package/dist/modules/finalization/static-runtime.d.ts +9 -0
- package/dist/modules/finalization/static-runtime.js +261 -0
- package/dist/modules/finalization/writes.d.ts +29 -0
- package/dist/modules/finalization/writes.js +78 -0
- package/dist/modules/format-injection/index.d.ts +19 -0
- package/dist/modules/format-injection/index.js +273 -0
- package/dist/modules/format-injection/inject-formats.d.ts +31 -0
- package/dist/modules/format-injection/inject-formats.js +208 -0
- package/dist/modules/glossary/index.d.ts +2 -0
- package/dist/modules/glossary/index.js +16 -0
- package/dist/modules/glossary/rules.d.ts +81 -0
- package/dist/modules/glossary/rules.js +244 -0
- package/dist/modules/glossary/types.d.ts +22 -0
- package/dist/modules/glossary/types.js +2 -0
- package/dist/modules/injection/index.d.ts +1 -0
- package/dist/modules/injection/index.js +6 -0
- package/dist/modules/injection/inject.d.ts +29 -0
- package/dist/modules/injection/inject.js +1050 -0
- package/dist/modules/keygen/index.d.ts +1 -0
- package/dist/modules/keygen/index.js +11 -0
- package/dist/modules/keygen/keygen.d.ts +4 -0
- package/dist/modules/keygen/keygen.js +123 -0
- package/dist/modules/keygen/locale-data.d.ts +7 -0
- package/dist/modules/keygen/locale-data.js +73 -0
- package/dist/modules/keygen/public.d.ts +2 -0
- package/dist/modules/keygen/public.js +12 -0
- package/dist/modules/locale-builder/index.d.ts +1 -0
- package/dist/modules/locale-builder/index.js +7 -0
- package/dist/modules/locale-builder/locales.d.ts +13 -0
- package/dist/modules/locale-builder/locales.js +130 -0
- package/dist/modules/lockfile/hasher.d.ts +7 -0
- package/dist/modules/lockfile/hasher.js +13 -0
- package/dist/modules/lockfile/index.d.ts +3 -0
- package/dist/modules/lockfile/index.js +22 -0
- package/dist/modules/lockfile/lockfile-manager.d.ts +99 -0
- package/dist/modules/lockfile/lockfile-manager.js +497 -0
- package/dist/modules/lockfile/schema.d.ts +196 -0
- package/dist/modules/lockfile/schema.js +56 -0
- package/dist/modules/parser/index.d.ts +2 -0
- package/dist/modules/parser/index.js +9 -0
- package/dist/modules/summary/index.d.ts +1 -0
- package/dist/modules/summary/index.js +7 -0
- package/dist/modules/summary/summary.d.ts +9 -0
- package/dist/modules/summary/summary.js +195 -0
- package/dist/modules/sync-detector/index.d.ts +2 -0
- package/dist/modules/sync-detector/index.js +7 -0
- package/dist/modules/sync-detector/reporter.d.ts +3 -0
- package/dist/modules/sync-detector/reporter.js +16 -0
- package/dist/modules/sync-detector/sync-detector.d.ts +39 -0
- package/dist/modules/sync-detector/sync-detector.js +374 -0
- package/dist/modules/translator/batch-optimizer.d.ts +12 -0
- package/dist/modules/translator/batch-optimizer.js +30 -0
- package/dist/modules/translator/client/byok.provider.d.ts +64 -0
- package/dist/modules/translator/client/byok.provider.js +319 -0
- package/dist/modules/translator/client/direct-translator.client.d.ts +12 -0
- package/dist/modules/translator/client/direct-translator.client.js +71 -0
- package/dist/modules/translator/client/gemini.provider.d.ts +1 -0
- package/dist/modules/translator/client/gemini.provider.js +110 -0
- package/dist/modules/translator/client/groq.provider.d.ts +1 -0
- package/dist/modules/translator/client/groq.provider.js +111 -0
- package/dist/modules/translator/client/request.types.d.ts +1 -0
- package/dist/modules/translator/client/request.types.js +2 -0
- package/dist/modules/translator/client/retry.d.ts +29 -0
- package/dist/modules/translator/client/retry.js +94 -0
- package/dist/modules/translator/client/test-utils/mock.client.d.ts +31 -0
- package/dist/modules/translator/client/test-utils/mock.client.js +59 -0
- package/dist/modules/translator/client/translator.interface.d.ts +1 -0
- package/dist/modules/translator/client/translator.interface.js +2 -0
- package/dist/modules/translator/icu-branches.d.ts +138 -0
- package/dist/modules/translator/icu-branches.js +393 -0
- package/dist/modules/translator/language-codes/errors.d.ts +10 -0
- package/dist/modules/translator/language-codes/errors.js +18 -0
- package/dist/modules/translator/language-codes/flores-map.d.ts +1 -0
- package/dist/modules/translator/language-codes/flores-map.js +107 -0
- package/dist/modules/translator/language-codes/index.d.ts +11 -0
- package/dist/modules/translator/language-codes/index.js +24 -0
- package/dist/modules/translator/language-codes/language-mapper.d.ts +25 -0
- package/dist/modules/translator/language-codes/language-mapper.js +132 -0
- package/dist/modules/translator/language-codes/rtl-languages.d.ts +2 -0
- package/dist/modules/translator/language-codes/rtl-languages.js +9 -0
- package/dist/modules/translator/memory/memory-matcher.d.ts +8 -0
- package/dist/modules/translator/memory/memory-matcher.js +218 -0
- package/dist/modules/translator/memory/types.d.ts +22 -0
- package/dist/modules/translator/memory/types.js +2 -0
- package/dist/modules/translator/translate.d.ts +74 -0
- package/dist/modules/translator/translate.js +1047 -0
- package/dist/modules/vue/migrate.d.ts +13 -0
- package/dist/modules/vue/migrate.js +40 -0
- package/dist/modules/vue/script-inject.d.ts +16 -0
- package/dist/modules/vue/script-inject.js +115 -0
- package/dist/modules/vue/sfc.d.ts +11 -0
- package/dist/modules/vue/sfc.js +51 -0
- package/dist/modules/vue/template-rewrite.d.ts +25 -0
- package/dist/modules/vue/template-rewrite.js +76 -0
- package/dist/modules/vue/template-scan.d.ts +29 -0
- package/dist/modules/vue/template-scan.js +264 -0
- package/dist/modules/workspace/index.d.ts +1 -0
- package/dist/modules/workspace/index.js +5 -0
- package/dist/modules/workspace/validate.d.ts +13 -0
- package/dist/modules/workspace/validate.js +333 -0
- package/dist/orchestrator.d.ts +13 -0
- package/dist/orchestrator.js +727 -0
- package/dist/providers/default-translation-provider.d.ts +20 -0
- package/dist/providers/default-translation-provider.js +30 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +21 -0
- package/dist/providers/types.d.ts +153 -0
- package/dist/providers/types.js +2 -0
- package/dist/qa/check-project.d.ts +50 -0
- package/dist/qa/check-project.js +193 -0
- package/dist/qa/gettext-files.d.ts +15 -0
- package/dist/qa/gettext-files.js +115 -0
- package/dist/qa/locale-files.d.ts +44 -0
- package/dist/qa/locale-files.js +159 -0
- package/dist/qa/locale-qa.d.ts +61 -0
- package/dist/qa/locale-qa.js +342 -0
- package/dist/qa/review.d.ts +43 -0
- package/dist/qa/review.js +175 -0
- package/dist/runtime/config.d.ts +8 -0
- package/dist/runtime/config.js +37 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/types.d.ts +427 -0
- package/dist/runtime/types.js +38 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.js +12 -0
- package/dist/utils/ast-helpers.d.ts +57 -0
- package/dist/utils/ast-helpers.js +608 -0
- package/dist/utils/cache-keys.d.ts +8 -0
- package/dist/utils/cache-keys.js +18 -0
- package/dist/utils/circuit-breaker.d.ts +43 -0
- package/dist/utils/circuit-breaker.js +119 -0
- package/dist/utils/concurrent-map.d.ts +4 -0
- package/dist/utils/concurrent-map.js +21 -0
- package/dist/utils/detect-format.d.ts +5 -0
- package/dist/utils/detect-format.js +125 -0
- package/dist/utils/extraction-options.d.ts +29 -0
- package/dist/utils/extraction-options.js +33 -0
- package/dist/utils/file-utils.d.ts +33 -0
- package/dist/utils/file-utils.js +267 -0
- package/dist/utils/glossary-match.d.ts +9 -0
- package/dist/utils/glossary-match.js +25 -0
- package/dist/utils/html-extractor.d.ts +8 -0
- package/dist/utils/html-extractor.js +317 -0
- package/dist/utils/i18next-icu.d.ts +38 -0
- package/dist/utils/i18next-icu.js +159 -0
- package/dist/utils/icu-message.d.ts +65 -0
- package/dist/utils/icu-message.js +346 -0
- package/dist/utils/icu-spans.d.ts +10 -0
- package/dist/utils/icu-spans.js +126 -0
- package/dist/utils/jsx-message.d.ts +93 -0
- package/dist/utils/jsx-message.js +628 -0
- package/dist/utils/key-usage.d.ts +35 -0
- package/dist/utils/key-usage.js +247 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.js +33 -0
- package/dist/utils/metadata.d.ts +82 -0
- package/dist/utils/metadata.js +261 -0
- package/dist/utils/module-constants.d.ts +30 -0
- package/dist/utils/module-constants.js +265 -0
- package/dist/utils/not-injected.d.ts +6 -0
- package/dist/utils/not-injected.js +38 -0
- package/dist/utils/placeholder-protection.d.ts +74 -0
- package/dist/utils/placeholder-protection.js +181 -0
- package/dist/utils/pseudo-markers.d.ts +9 -0
- package/dist/utils/pseudo-markers.js +16 -0
- package/dist/utils/string-utils.d.ts +52 -0
- package/dist/utils/string-utils.js +270 -0
- package/dist/utils/translation-quality.d.ts +34 -0
- package/dist/utils/translation-quality.js +156 -0
- package/dist/utils/translation-sanitizer.d.ts +11 -0
- package/dist/utils/translation-sanitizer.js +71 -0
- package/dist/utils/translator-caller.d.ts +32 -0
- package/dist/utils/translator-caller.js +217 -0
- package/dist/utils/translator-load-balancer.d.ts +73 -0
- package/dist/utils/translator-load-balancer.js +297 -0
- package/dist/utils/ui-copy.d.ts +32 -0
- package/dist/utils/ui-copy.js +126 -0
- package/dist/utils/untranslated.d.ts +15 -0
- package/dist/utils/untranslated.js +34 -0
- package/dist/utils/workspace.d.ts +14 -0
- package/dist/utils/workspace.js +89 -0
- package/package.json +64 -4
- package/schemas/translate-report.schema.json +297 -0
- package/README.md +0 -3
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toFlores = toFlores;
|
|
4
|
+
exports.fromFlores = fromFlores;
|
|
5
|
+
exports.isSupported = isSupported;
|
|
6
|
+
exports.isNllbSupported = isNllbSupported;
|
|
7
|
+
exports.storageLangCode = storageLangCode;
|
|
8
|
+
exports.localeFromStorageCode = localeFromStorageCode;
|
|
9
|
+
exports.getSupportedLanguages = getSupportedLanguages;
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
const flores_map_1 = require("./flores-map");
|
|
12
|
+
/**
|
|
13
|
+
* Script variants that have their own FLORES-200 code (NLLB can translate
|
|
14
|
+
* them). Keys are `language-script` in lower case.
|
|
15
|
+
*/
|
|
16
|
+
const SCRIPT_FLORES = {
|
|
17
|
+
'zh-hans': 'zho_Hans',
|
|
18
|
+
'zh-hant': 'zho_Hant',
|
|
19
|
+
'pa-arab': 'pnb_Arab',
|
|
20
|
+
'az-arab': 'azb_Arab',
|
|
21
|
+
};
|
|
22
|
+
/** Chinese regions imply a script. */
|
|
23
|
+
const REGION_SCRIPT = {
|
|
24
|
+
'zh-tw': 'hant',
|
|
25
|
+
'zh-hk': 'hant',
|
|
26
|
+
'zh-mo': 'hant',
|
|
27
|
+
'zh-cn': 'hans',
|
|
28
|
+
'zh-sg': 'hans',
|
|
29
|
+
};
|
|
30
|
+
/** language[-Script][-REGION], e.g. pt-BR, sr-Latn, zh-Hant-TW, es-419. */
|
|
31
|
+
const LOCALE_TAG = /^([a-z]{2,3})(?:-([a-z]{4}))?(?:-([a-z]{2}|\d{3}))?$/;
|
|
32
|
+
function normalize(code) {
|
|
33
|
+
return code.trim().toLowerCase().replace(/_/g, '-');
|
|
34
|
+
}
|
|
35
|
+
function own(map, key) {
|
|
36
|
+
// Own keys only: `in` would also accept prototype keys like "constructor".
|
|
37
|
+
return Object.prototype.hasOwnProperty.call(map, key) ? map[key] : undefined;
|
|
38
|
+
}
|
|
39
|
+
function titleCase(script) {
|
|
40
|
+
return script.charAt(0).toUpperCase() + script.slice(1);
|
|
41
|
+
}
|
|
42
|
+
function resolve(isoCode) {
|
|
43
|
+
const n = normalize(isoCode);
|
|
44
|
+
const plain = own(flores_map_1.FLORES_MAP, n);
|
|
45
|
+
if (plain)
|
|
46
|
+
return { flores: plain, nllb: true, plain: true };
|
|
47
|
+
const m = LOCALE_TAG.exec(n);
|
|
48
|
+
if (!m)
|
|
49
|
+
return undefined;
|
|
50
|
+
const [, lang, explicitScript, region] = m;
|
|
51
|
+
const base = own(flores_map_1.FLORES_MAP, lang);
|
|
52
|
+
if (!base)
|
|
53
|
+
return undefined;
|
|
54
|
+
const script = explicitScript ?? (region ? own(REGION_SCRIPT, `${lang}-${region}`) : undefined);
|
|
55
|
+
if (!script)
|
|
56
|
+
return { flores: base, nllb: true, plain: false, region };
|
|
57
|
+
const variant = own(SCRIPT_FLORES, `${lang}-${script}`);
|
|
58
|
+
if (variant)
|
|
59
|
+
return { flores: variant, nllb: true, plain: false, region };
|
|
60
|
+
const [baseLang, baseScript] = base.split('_');
|
|
61
|
+
if (script === baseScript.toLowerCase())
|
|
62
|
+
return { flores: base, nllb: true, plain: false, region };
|
|
63
|
+
// A script FLORES-200 doesn't have for this language (sr-Latn, uz-Cyrl,
|
|
64
|
+
// az-Cyrl): LLM providers translate it from the ISO locale; NLLB can't.
|
|
65
|
+
return { flores: `${baseLang}_${titleCase(script)}`, nllb: false, plain: false, region };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* FLORES-200 code for an ISO locale.
|
|
69
|
+
* - Region variants map to their language (pt-BR → por_Latn); Chinese regions
|
|
70
|
+
* pick the script (zh-TW → zho_Hant, zh-CN → zho_Hans).
|
|
71
|
+
* - Script subtags are honoured: sr-Cyrl → srp_Cyrl, pa-Arab → pnb_Arab. A
|
|
72
|
+
* script FLORES-200 lacks gets a FLORES-style code (sr-Latn → srp_Latn) that
|
|
73
|
+
* only LLM providers can use — see isNllbSupported().
|
|
74
|
+
*/
|
|
75
|
+
function toFlores(isoCode) {
|
|
76
|
+
const r = resolve(isoCode);
|
|
77
|
+
if (!r) {
|
|
78
|
+
throw new errors_1.BabelizeError('UNSUPPORTED_LANGUAGE', `Unsupported language code: "${isoCode}". Use getSupportedLanguages() to see available codes.`);
|
|
79
|
+
}
|
|
80
|
+
return r.flores;
|
|
81
|
+
}
|
|
82
|
+
function fromFlores(floresCode) {
|
|
83
|
+
const entry = Object.entries(flores_map_1.FLORES_MAP).find(([, code]) => code === floresCode);
|
|
84
|
+
if (entry)
|
|
85
|
+
return entry[0];
|
|
86
|
+
const variant = Object.entries(SCRIPT_FLORES).find(([, code]) => code === floresCode);
|
|
87
|
+
if (variant) {
|
|
88
|
+
const [lang, script] = variant[0].split('-');
|
|
89
|
+
return `${lang}-${titleCase(script)}`;
|
|
90
|
+
}
|
|
91
|
+
const [langPart, script] = floresCode.split('_');
|
|
92
|
+
const base = Object.entries(flores_map_1.FLORES_MAP).find(([, code]) => code.split('_')[0] === langPart);
|
|
93
|
+
if (base && script)
|
|
94
|
+
return `${base[0]}-${script}`;
|
|
95
|
+
throw new errors_1.BabelizeError('UNSUPPORTED_LANGUAGE', `Flores code '${floresCode}' is not supported.`);
|
|
96
|
+
}
|
|
97
|
+
/** True when any translator can handle the locale (LLM providers use the ISO tag). */
|
|
98
|
+
function isSupported(isoCode) {
|
|
99
|
+
return resolve(isoCode) !== undefined;
|
|
100
|
+
}
|
|
101
|
+
/** True when the NLLB translator service can translate into the locale. */
|
|
102
|
+
function isNllbSupported(isoCode) {
|
|
103
|
+
return resolve(isoCode)?.nllb === true;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Key for storing translations of `isoCode` (translation cache, translation
|
|
107
|
+
* memory): the FLORES code including the script, plus the region when there
|
|
108
|
+
* is one, so variants never share translations — pt-BR → por_Latn-br,
|
|
109
|
+
* sr-Latn → srp_Latn, sr-Cyrl-RS → srp_Cyrl-rs. A plain language code keeps
|
|
110
|
+
* its FLORES code (pt → por_Latn), as before.
|
|
111
|
+
*/
|
|
112
|
+
function storageLangCode(isoCode) {
|
|
113
|
+
const r = resolve(isoCode);
|
|
114
|
+
if (!r)
|
|
115
|
+
return toFlores(isoCode); // throws the usual error
|
|
116
|
+
if (r.plain || !r.region)
|
|
117
|
+
return r.flores;
|
|
118
|
+
return `${r.flores}-${r.region}`;
|
|
119
|
+
}
|
|
120
|
+
/** BCP-47 tag for a storage code (por_Latn-br → pt-BR, srp_Latn → sr-Latn). */
|
|
121
|
+
function localeFromStorageCode(code) {
|
|
122
|
+
const [flores, region] = code.split('-');
|
|
123
|
+
if (!region)
|
|
124
|
+
return fromFlores(flores);
|
|
125
|
+
// Chinese regions imply the script: zho_Hant-tw → zh-TW, zho_Hans-cn → zh-CN.
|
|
126
|
+
if (flores === 'zho_Hant' || flores === 'zho_Hans')
|
|
127
|
+
return `zh-${region.toUpperCase()}`;
|
|
128
|
+
return `${fromFlores(flores)}-${/^\d+$/.test(region) ? region : region.toUpperCase()}`;
|
|
129
|
+
}
|
|
130
|
+
function getSupportedLanguages() {
|
|
131
|
+
return Object.keys(flores_map_1.FLORES_MAP);
|
|
132
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RTL_LANGUAGES = void 0;
|
|
4
|
+
exports.isRTL = isRTL;
|
|
5
|
+
exports.RTL_LANGUAGES = ['ar', 'he', 'fa', 'ur', 'ps'];
|
|
6
|
+
function isRTL(locale) {
|
|
7
|
+
const base = locale.split('-')[0];
|
|
8
|
+
return exports.RTL_LANGUAGES.includes(base);
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MemoryEntry, MemoryLookupResult } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Match from memory with exact and fuzzy matching.
|
|
4
|
+
* Exact matches are a map lookup; fuzzy matches (≥90% similar) are only
|
|
5
|
+
* computed when there is no exact match, and only against candidates whose
|
|
6
|
+
* length could possibly reach the threshold.
|
|
7
|
+
*/
|
|
8
|
+
export declare function matchFromMemory(entries: MemoryEntry[], sourceText: string, targetLocale: string): MemoryLookupResult;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchFromMemory = matchFromMemory;
|
|
4
|
+
const FUZZY_THRESHOLD = 0.9;
|
|
5
|
+
const MAX_FUZZY_MATCHES = 3;
|
|
6
|
+
const Q = 3;
|
|
7
|
+
/**
|
|
8
|
+
* Fuzzy results are suggestions only (never auto-applied) and only the top
|
|
9
|
+
* MAX_FUZZY_MATCHES are kept, so Levenshtein is run on at most this many
|
|
10
|
+
* candidates — those sharing the most q-grams with the query. Without a cap,
|
|
11
|
+
* a memory full of near-identical templated strings makes every entry a
|
|
12
|
+
* candidate.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_FUZZY_CANDIDATES = 50;
|
|
15
|
+
/** Largest edit distance that still scores ≥ FUZZY_THRESHOLD (float-safe). */
|
|
16
|
+
function maxDistanceFor(maxLength) {
|
|
17
|
+
return Math.floor(maxLength - FUZZY_THRESHOLD * maxLength + 1e-9);
|
|
18
|
+
}
|
|
19
|
+
/** Multiset of the string's q-grams: gram → occurrences. */
|
|
20
|
+
function qgrams(text) {
|
|
21
|
+
const grams = new Map();
|
|
22
|
+
for (let i = 0; i + Q <= text.length; i++) {
|
|
23
|
+
const g = text.slice(i, i + Q);
|
|
24
|
+
grams.set(g, (grams.get(g) ?? 0) + 1);
|
|
25
|
+
}
|
|
26
|
+
return grams;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Levenshtein distance, or `maxDistance + 1` as soon as it is known to exceed
|
|
30
|
+
* `maxDistance`. Only cells within `maxDistance` of the diagonal can lead to
|
|
31
|
+
* an answer under the bound, so each row computes that band only, and the
|
|
32
|
+
* loop exits once a whole band row is over the bound.
|
|
33
|
+
*/
|
|
34
|
+
function boundedLevenshtein(s1, s2, maxDistance) {
|
|
35
|
+
const m = s1.length;
|
|
36
|
+
const n = s2.length;
|
|
37
|
+
const over = maxDistance + 1;
|
|
38
|
+
if (Math.abs(m - n) > maxDistance)
|
|
39
|
+
return over;
|
|
40
|
+
let prev = new Array(n + 1).fill(over);
|
|
41
|
+
let curr = new Array(n + 1).fill(over);
|
|
42
|
+
for (let j = 0; j <= Math.min(n, maxDistance); j++)
|
|
43
|
+
prev[j] = j;
|
|
44
|
+
for (let i = 1; i <= m; i++) {
|
|
45
|
+
const from = Math.max(1, i - maxDistance);
|
|
46
|
+
const to = Math.min(n, i + maxDistance);
|
|
47
|
+
curr.fill(over);
|
|
48
|
+
curr[0] = i <= maxDistance ? i : over;
|
|
49
|
+
let rowMin = curr[0];
|
|
50
|
+
for (let j = from; j <= to; j++) {
|
|
51
|
+
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
|
|
52
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost, over);
|
|
53
|
+
if (curr[j] < rowMin)
|
|
54
|
+
rowMin = curr[j];
|
|
55
|
+
}
|
|
56
|
+
if (rowMin > maxDistance)
|
|
57
|
+
return over;
|
|
58
|
+
[prev, curr] = [curr, prev];
|
|
59
|
+
}
|
|
60
|
+
return Math.min(prev[n], over);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Indexes are built once per (memory array, target locale) and reused for
|
|
64
|
+
* every key in the run. The caller fetches one array per locale and matches
|
|
65
|
+
* every uncached key against it, so rebuilding (or re-hashing every entry)
|
|
66
|
+
* per key was O(keys × entries).
|
|
67
|
+
*/
|
|
68
|
+
const indexCache = new WeakMap();
|
|
69
|
+
function getIndex(entries, targetLocale) {
|
|
70
|
+
let byLocale = indexCache.get(entries);
|
|
71
|
+
if (!byLocale) {
|
|
72
|
+
byLocale = new Map();
|
|
73
|
+
indexCache.set(entries, byLocale);
|
|
74
|
+
}
|
|
75
|
+
let index = byLocale.get(targetLocale);
|
|
76
|
+
if (!index) {
|
|
77
|
+
const forLocale = entries.filter((e) => e.targetLocale === targetLocale);
|
|
78
|
+
const exact = new Map();
|
|
79
|
+
for (const e of forLocale) {
|
|
80
|
+
// Prefer a human-approved entry if the same text appears twice.
|
|
81
|
+
const existing = exact.get(e.sourceText);
|
|
82
|
+
if (!existing || (!existing.approvedAt && e.approvedAt))
|
|
83
|
+
exact.set(e.sourceText, e);
|
|
84
|
+
}
|
|
85
|
+
const lists = new Map();
|
|
86
|
+
forLocale.forEach((e, idx) => {
|
|
87
|
+
for (const [g, n] of qgrams(e.sourceText)) {
|
|
88
|
+
let list = lists.get(g);
|
|
89
|
+
if (!list)
|
|
90
|
+
lists.set(g, (list = []));
|
|
91
|
+
list.push(idx, n);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const postings = new Map();
|
|
95
|
+
for (const [g, list] of lists) {
|
|
96
|
+
const idx = new Int32Array(list.length / 2);
|
|
97
|
+
const count = new Uint16Array(list.length / 2);
|
|
98
|
+
for (let p = 0; p < idx.length; p++) {
|
|
99
|
+
idx[p] = list[2 * p];
|
|
100
|
+
count[p] = Math.min(list[2 * p + 1], 0xffff);
|
|
101
|
+
}
|
|
102
|
+
postings.set(g, { idx, count });
|
|
103
|
+
}
|
|
104
|
+
index = { exact, entries: forLocale, postings, shared: new Int32Array(forLocale.length) };
|
|
105
|
+
byLocale.set(targetLocale, index);
|
|
106
|
+
}
|
|
107
|
+
return index;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Match from memory with exact and fuzzy matching.
|
|
111
|
+
* Exact matches are a map lookup; fuzzy matches (≥90% similar) are only
|
|
112
|
+
* computed when there is no exact match, and only against candidates whose
|
|
113
|
+
* length could possibly reach the threshold.
|
|
114
|
+
*/
|
|
115
|
+
function matchFromMemory(entries, sourceText, targetLocale) {
|
|
116
|
+
if (!entries.length)
|
|
117
|
+
return { hit: false };
|
|
118
|
+
const index = getIndex(entries, targetLocale);
|
|
119
|
+
const exactMatch = index.exact.get(sourceText);
|
|
120
|
+
if (exactMatch) {
|
|
121
|
+
return {
|
|
122
|
+
hit: true,
|
|
123
|
+
translatedText: exactMatch.translatedText,
|
|
124
|
+
confidence: 1,
|
|
125
|
+
fromMemory: true
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const fuzzyMatches = [];
|
|
129
|
+
const consider = (i) => {
|
|
130
|
+
const e = index.entries[i];
|
|
131
|
+
const maxLength = Math.max(sourceText.length, e.sourceText.length);
|
|
132
|
+
if (maxLength === 0)
|
|
133
|
+
return;
|
|
134
|
+
const maxDistance = maxDistanceFor(maxLength);
|
|
135
|
+
if (Math.abs(sourceText.length - e.sourceText.length) > maxDistance)
|
|
136
|
+
return;
|
|
137
|
+
const distance = boundedLevenshtein(sourceText, e.sourceText, maxDistance);
|
|
138
|
+
if (distance > maxDistance)
|
|
139
|
+
return;
|
|
140
|
+
fuzzyMatches.push({
|
|
141
|
+
translatedText: e.translatedText,
|
|
142
|
+
sourceText: e.sourceText,
|
|
143
|
+
similarity: (maxLength - distance) / maxLength,
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
if (sourceText.length <= Q + 1) {
|
|
147
|
+
// Too short for the q-gram filter to prune safely; short strings are cheap.
|
|
148
|
+
for (let i = 0; i < index.entries.length; i++)
|
|
149
|
+
consider(i);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// q-gram lemma: strings within edit distance d share at least
|
|
153
|
+
// max(len) - Q + 1 - Q·d q-grams. Count shared grams via the inverted
|
|
154
|
+
// index and only run Levenshtein on entries that clear that bar.
|
|
155
|
+
const { shared } = index;
|
|
156
|
+
const touched = [];
|
|
157
|
+
for (const [g, qn] of qgrams(sourceText)) {
|
|
158
|
+
const posting = index.postings.get(g);
|
|
159
|
+
if (!posting)
|
|
160
|
+
continue;
|
|
161
|
+
const { idx: ids, count: counts } = posting;
|
|
162
|
+
for (let p = 0; p < ids.length; p++) {
|
|
163
|
+
const idx = ids[p];
|
|
164
|
+
if (shared[idx] === 0)
|
|
165
|
+
touched.push(idx);
|
|
166
|
+
shared[idx] += counts[p] < qn ? counts[p] : qn;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const candidates = [];
|
|
170
|
+
const candidateCounts = [];
|
|
171
|
+
let maxCount = 0;
|
|
172
|
+
for (const idx of touched) {
|
|
173
|
+
const count = shared[idx];
|
|
174
|
+
shared[idx] = 0;
|
|
175
|
+
const maxLength = Math.max(sourceText.length, index.entries[idx].sourceText.length);
|
|
176
|
+
if (count >= maxLength - Q + 1 - Q * maxDistanceFor(maxLength)) {
|
|
177
|
+
candidates.push(idx);
|
|
178
|
+
candidateCounts.push(count);
|
|
179
|
+
if (count > maxCount)
|
|
180
|
+
maxCount = count;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (candidates.length <= MAX_FUZZY_CANDIDATES) {
|
|
184
|
+
for (const idx of candidates)
|
|
185
|
+
consider(idx);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// Top-K by shared count in O(n): counts are small integers, so bucket them.
|
|
189
|
+
const buckets = new Int32Array(maxCount + 1);
|
|
190
|
+
for (const c of candidateCounts)
|
|
191
|
+
buckets[c]++;
|
|
192
|
+
let cutoff = maxCount;
|
|
193
|
+
for (let taken = 0; cutoff > 0 && taken + buckets[cutoff] < MAX_FUZZY_CANDIDATES; cutoff--)
|
|
194
|
+
taken += buckets[cutoff];
|
|
195
|
+
let budget = MAX_FUZZY_CANDIDATES;
|
|
196
|
+
for (let i = 0; i < candidates.length && budget > 0; i++) {
|
|
197
|
+
if (candidateCounts[i] > cutoff) {
|
|
198
|
+
consider(candidates[i]);
|
|
199
|
+
budget--;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
for (let i = 0; i < candidates.length && budget > 0; i++) {
|
|
203
|
+
if (candidateCounts[i] === cutoff) {
|
|
204
|
+
consider(candidates[i]);
|
|
205
|
+
budget--;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (fuzzyMatches.length > 0) {
|
|
211
|
+
fuzzyMatches.sort((a, b) => b.similarity - a.similarity);
|
|
212
|
+
return {
|
|
213
|
+
hit: false, // Don't auto-apply fuzzy matches
|
|
214
|
+
fuzzyMatches: fuzzyMatches.slice(0, MAX_FUZZY_MATCHES),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return { hit: false };
|
|
218
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface MemoryEntry {
|
|
2
|
+
sourceText: string;
|
|
3
|
+
sourceLocale: string;
|
|
4
|
+
targetLocale: string;
|
|
5
|
+
translatedText: string;
|
|
6
|
+
projectId?: string;
|
|
7
|
+
projectName?: string;
|
|
8
|
+
approvedAt?: string;
|
|
9
|
+
isoKey?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FuzzyMatch {
|
|
12
|
+
translatedText: string;
|
|
13
|
+
similarity: number;
|
|
14
|
+
sourceText: string;
|
|
15
|
+
}
|
|
16
|
+
export interface MemoryLookupResult {
|
|
17
|
+
hit: boolean;
|
|
18
|
+
translatedText?: string;
|
|
19
|
+
confidence?: number;
|
|
20
|
+
fromMemory?: boolean;
|
|
21
|
+
fuzzyMatches?: FuzzyMatch[];
|
|
22
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { LocaleFile, WorkspaceInfo } from '../../runtime/types';
|
|
2
|
+
import type { ByokProvider } from '@babelize/types';
|
|
3
|
+
import { type GlossaryRules } from '../glossary';
|
|
4
|
+
import type { EngineProviders, ProjectRulesStore, TranslationFailure, TranslationCache } from '../../providers/types';
|
|
5
|
+
/** QA note on keys the backup (NLLB) translator produced. */
|
|
6
|
+
export declare const FALLBACK_QA_WARNING = "translated with the backup translator; will be retried with the AI translator next run";
|
|
7
|
+
export interface TranslateOptions {
|
|
8
|
+
/**
|
|
9
|
+
* The project's runtime formats ICU (next-intl, react-i18next + i18next-icu):
|
|
10
|
+
* a translation may turn a count placeholder into an ICU plural. An LLM
|
|
11
|
+
* decides per message; for a plain-text translator (NLLB) the plural is
|
|
12
|
+
* built branch by branch for languages with more than one plural category.
|
|
13
|
+
* Otherwise a plain source must stay plain.
|
|
14
|
+
*/
|
|
15
|
+
targetPlurals?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Per key: literal text in an ICU plural's branches that stands for the
|
|
18
|
+
* plural's value besides `#` (a gettext plural turned into ICU keeps its
|
|
19
|
+
* `%(count)d`). Lets a plain-text translator inflect each branch.
|
|
20
|
+
*/
|
|
21
|
+
pluralValuePlaceholders?: Record<string, string>;
|
|
22
|
+
/**
|
|
23
|
+
* The project's glossary and brand voice, already loaded (the orchestrator
|
|
24
|
+
* loads them once to partition the lockfile). Loaded here when absent.
|
|
25
|
+
*/
|
|
26
|
+
rules?: LoadedTranslationRules;
|
|
27
|
+
/**
|
|
28
|
+
* Running count of fresh primary-translator translations, bumped as each
|
|
29
|
+
* batch lands (when it is also written to the cache and TM). Lets a run
|
|
30
|
+
* stopped mid-translation bill what it already produced.
|
|
31
|
+
*/
|
|
32
|
+
progress?: {
|
|
33
|
+
freshTranslations: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface LoadedTranslationRules {
|
|
37
|
+
glossary: GlossaryRules;
|
|
38
|
+
brandVoice: string | null;
|
|
39
|
+
/** False if the glossary or brand voice failed to load. */
|
|
40
|
+
complete: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load a project's glossary and brand voice. Never throws: a failure means
|
|
44
|
+
* translating without that rule, as before. The two lookups are independent,
|
|
45
|
+
* so a glossary failure doesn't also drop the brand voice. `complete` is false
|
|
46
|
+
* if either failed — callers that copy text into shared TM must then assume
|
|
47
|
+
* project-specific wording may be baked in.
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadProjectTranslationRules(projectId: string, rules?: ProjectRulesStore): Promise<LoadedTranslationRules>;
|
|
50
|
+
export declare function translateLocales(workspace: WorkspaceInfo, sourceLocale: LocaleFile, targetLocales: string[], forceTranslation?: boolean, dryRun?: boolean, projectId?: string, onProgress?: (message: string) => void, keysToTranslateByLocale?: Record<string, string[]>, cacheProvider?: TranslationCache, byokConfig?: {
|
|
51
|
+
provider: ByokProvider;
|
|
52
|
+
apiKey: string;
|
|
53
|
+
model?: string;
|
|
54
|
+
}, contextMap?: Record<string, string>, extractionContextMap?: Record<string, string>, providers?: EngineProviders, options?: TranslateOptions): Promise<{
|
|
55
|
+
translation: {
|
|
56
|
+
locales: LocaleFile[];
|
|
57
|
+
errors: {
|
|
58
|
+
locale: string;
|
|
59
|
+
error: string;
|
|
60
|
+
}[];
|
|
61
|
+
failures: TranslationFailure[];
|
|
62
|
+
};
|
|
63
|
+
stats: {
|
|
64
|
+
tmHits: number;
|
|
65
|
+
};
|
|
66
|
+
metadata: Record<string, Record<string, any>>;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Backfill TM from translations that were reused without calling the
|
|
70
|
+
* translator (lockfile hits). Existing TM rows are left untouched.
|
|
71
|
+
* Locales are ISO codes here; identity translations (source === target) are
|
|
72
|
+
* skipped since they mean a previous run had no working translator.
|
|
73
|
+
*/
|
|
74
|
+
export declare function backfillTranslationMemory(projectId: string, sourceLocale: string, flatSource: Record<string, string>, translationsByLocale: Record<string, Record<string, string>>, excludeKeys?: Set<string>, providers?: EngineProviders): Promise<Record<string, string[]>>;
|