@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,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SKIP_TAGS = void 0;
|
|
4
|
+
exports.normalizeString = normalizeString;
|
|
5
|
+
exports.toCamelCase = toCamelCase;
|
|
6
|
+
exports.inferNamespace = inferNamespace;
|
|
7
|
+
exports.generateUniqueKey = generateUniqueKey;
|
|
8
|
+
exports.truncateForKey = truncateForKey;
|
|
9
|
+
exports.isTranslatableText = isTranslatableText;
|
|
10
|
+
exports.extractPlaceholders = extractPlaceholders;
|
|
11
|
+
exports.protectPlaceholders = protectPlaceholders;
|
|
12
|
+
exports.restorePlaceholders = restorePlaceholders;
|
|
13
|
+
exports.isLikelyPlural = isLikelyPlural;
|
|
14
|
+
exports.hasICUSyntax = hasICUSyntax;
|
|
15
|
+
exports.escapeRegex = escapeRegex;
|
|
16
|
+
exports.hashString = hashString;
|
|
17
|
+
exports.matchesCurrencyPattern = matchesCurrencyPattern;
|
|
18
|
+
exports.matchesDatePattern = matchesDatePattern;
|
|
19
|
+
exports.containsPII = containsPII;
|
|
20
|
+
exports.extractBalancedTag = extractBalancedTag;
|
|
21
|
+
const node_crypto_1 = require("node:crypto");
|
|
22
|
+
/**
|
|
23
|
+
* Engine v2 string utilities.
|
|
24
|
+
*
|
|
25
|
+
* Copied from v1 to preserve keygen/normalization behavior.
|
|
26
|
+
*/
|
|
27
|
+
function normalizeString(text) {
|
|
28
|
+
return text
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^\p{L}\p{N}\s']/gu, ' ')
|
|
31
|
+
.replace(/\s+/g, ' ')
|
|
32
|
+
.trim();
|
|
33
|
+
}
|
|
34
|
+
function toCamelCase(text) {
|
|
35
|
+
const normalized = normalizeString(text);
|
|
36
|
+
const words = normalized.split(' ').filter(Boolean);
|
|
37
|
+
if (words.length === 0)
|
|
38
|
+
return 'untitled';
|
|
39
|
+
const result = words
|
|
40
|
+
.map((word, index) => {
|
|
41
|
+
word = word.replace(/'/g, '');
|
|
42
|
+
if (index === 0)
|
|
43
|
+
return word.toLowerCase();
|
|
44
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
45
|
+
})
|
|
46
|
+
.join('');
|
|
47
|
+
if (!/[a-zA-Z]/.test(result))
|
|
48
|
+
return 'untitled';
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
function inferNamespace(filePath) {
|
|
52
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
53
|
+
const pathWithoutExt = normalizedPath.replace(/\.(ts|tsx|js|jsx)$/, '');
|
|
54
|
+
const parts = pathWithoutExt.split('/').filter(Boolean);
|
|
55
|
+
const prefixesToRemove = ['src', 'app', 'pages', 'components', 'features', 'modules'];
|
|
56
|
+
while (parts.length > 0 && prefixesToRemove.includes(parts[0].toLowerCase())) {
|
|
57
|
+
parts.shift();
|
|
58
|
+
}
|
|
59
|
+
const suffixesToRemove = ['index', 'page', 'component', 'container', 'view'];
|
|
60
|
+
while (parts.length > 1 && suffixesToRemove.includes(parts[parts.length - 1].toLowerCase())) {
|
|
61
|
+
parts.pop();
|
|
62
|
+
}
|
|
63
|
+
if (parts.length === 0)
|
|
64
|
+
return 'common';
|
|
65
|
+
const namespace = parts[parts.length - 1]
|
|
66
|
+
.replace(/([A-Z])/g, (match, p1, offset) => (offset > 0 ? '-' + p1 : p1))
|
|
67
|
+
.toLowerCase()
|
|
68
|
+
.replace(/^-/, '');
|
|
69
|
+
return namespace.replace(/[^a-z0-9-]/g, '') || 'common';
|
|
70
|
+
}
|
|
71
|
+
function generateUniqueKey(baseKey, existingKeys) {
|
|
72
|
+
if (!existingKeys.has(baseKey))
|
|
73
|
+
return baseKey;
|
|
74
|
+
let counter = 2;
|
|
75
|
+
let uniqueKey = `${baseKey}_${counter}`;
|
|
76
|
+
while (existingKeys.has(uniqueKey)) {
|
|
77
|
+
counter++;
|
|
78
|
+
uniqueKey = `${baseKey}_${counter}`;
|
|
79
|
+
}
|
|
80
|
+
return uniqueKey;
|
|
81
|
+
}
|
|
82
|
+
function truncateForKey(text, maxWords = 5) {
|
|
83
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
84
|
+
if (words.length <= maxWords)
|
|
85
|
+
return text;
|
|
86
|
+
return words.slice(0, maxWords).join(' ');
|
|
87
|
+
}
|
|
88
|
+
function isTranslatableText(text) {
|
|
89
|
+
const trimmed = text.trim();
|
|
90
|
+
if (!trimmed)
|
|
91
|
+
return false;
|
|
92
|
+
if (/^\s*$/.test(trimmed))
|
|
93
|
+
return false;
|
|
94
|
+
if (/^\d+$/.test(trimmed))
|
|
95
|
+
return false;
|
|
96
|
+
if (/^[^\p{L}\p{N}\s]+$/u.test(trimmed))
|
|
97
|
+
return false;
|
|
98
|
+
if (trimmed.length < 2)
|
|
99
|
+
return false;
|
|
100
|
+
if (/^\{.*\}$/.test(trimmed))
|
|
101
|
+
return false;
|
|
102
|
+
if (/^(https?:\/\/|\/|\.\/|\.\.\/)/.test(trimmed))
|
|
103
|
+
return false;
|
|
104
|
+
if (/^[.#][a-zA-Z]/.test(trimmed))
|
|
105
|
+
return false;
|
|
106
|
+
if (/^\p{Ll}[\p{L}\p{N}_]*$/u.test(trimmed) && trimmed.length > 20)
|
|
107
|
+
return false;
|
|
108
|
+
return /[\p{L}]/u.test(trimmed);
|
|
109
|
+
}
|
|
110
|
+
function extractPlaceholders(text) {
|
|
111
|
+
// Use a more specific regex that only matches simple variable names to avoid greedy ICU matching
|
|
112
|
+
// or use non-greedy matching if we expect alphanumeric+underscore names.
|
|
113
|
+
const matches = text.match(/\{(\w+)\}/g);
|
|
114
|
+
if (!matches)
|
|
115
|
+
return [];
|
|
116
|
+
return matches.map((m) => m.slice(1, -1));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Protect placeholders by replacing them with AI-safe tokens before translation
|
|
120
|
+
*/
|
|
121
|
+
function protectPlaceholders(text) {
|
|
122
|
+
// If it's complex ICU syntax, we don't protect simple placeholders as they might be part of the ICU structure
|
|
123
|
+
// This avoids destroying nested braces like {count, plural, one {# item} other {# items}}
|
|
124
|
+
if (hasICUSyntax(text)) {
|
|
125
|
+
return { protectedText: text, vars: [] };
|
|
126
|
+
}
|
|
127
|
+
const vars = [];
|
|
128
|
+
let protectedText = text;
|
|
129
|
+
// Refined regex to only match simple alphanumeric variable names in braces
|
|
130
|
+
const regex = /\{(\w+)\}/g;
|
|
131
|
+
let i = 0;
|
|
132
|
+
protectedText = text.replace(regex, (match, varName) => {
|
|
133
|
+
vars.push(varName);
|
|
134
|
+
const token = `[#VAR_${i}#]`;
|
|
135
|
+
i++;
|
|
136
|
+
return token;
|
|
137
|
+
});
|
|
138
|
+
return { protectedText, vars };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Restore placeholders from AI-safe tokens after translation
|
|
142
|
+
* Returns a diagnostic result to detect dropped variables
|
|
143
|
+
*/
|
|
144
|
+
function restorePlaceholders(text, vars) {
|
|
145
|
+
let restored = text;
|
|
146
|
+
let restoredCount = 0;
|
|
147
|
+
const missingTokens = [];
|
|
148
|
+
for (let i = 0; i < vars.length; i++) {
|
|
149
|
+
const regex = new RegExp(`\\[\\s*#\\s*VAR_${i}\\s*#\\s*\\]`, 'gi');
|
|
150
|
+
const before = restored;
|
|
151
|
+
restored = restored.replace(regex, `{${vars[i]}}`);
|
|
152
|
+
if (restored !== before) {
|
|
153
|
+
restoredCount++;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
missingTokens.push(`[#VAR_${i}#]`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
restoredText: restored,
|
|
161
|
+
restoredCount,
|
|
162
|
+
expectedCount: vars.length,
|
|
163
|
+
missingTokens
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check if text text and associated variables likely represent a pluralized string
|
|
168
|
+
* (e.g. "count files", "Total {length}")
|
|
169
|
+
*/
|
|
170
|
+
function isLikelyPlural(text, variables = []) {
|
|
171
|
+
const lowerText = text.toLowerCase().trim();
|
|
172
|
+
// Exclude question strings — plural generation produces nonsensical variants
|
|
173
|
+
// for interrogative sentences (e.g. "Do you support tracked shipping?").
|
|
174
|
+
if (/^(do|does|is|are|can|will|would|have|has|did|should|could|was|were)\s/i.test(lowerText))
|
|
175
|
+
return false;
|
|
176
|
+
// Exclude single-brand terms (e.g. "GIFTIFY")
|
|
177
|
+
if (/^[A-Z]{3,}$/.test(text.trim()))
|
|
178
|
+
return false;
|
|
179
|
+
const pluralVars = ['count', 'length', 'total', 'num', 'size'];
|
|
180
|
+
if (variables.some((v) => pluralVars.includes(v.toLowerCase())))
|
|
181
|
+
return true;
|
|
182
|
+
if (/\{\s*(count|length|total|num|size)\s*\}/.test(lowerText))
|
|
183
|
+
return true;
|
|
184
|
+
// Digit + plural noun pattern — exclude longer title-like phrases
|
|
185
|
+
// (5+ words, e.g. "15 reasons naive automation fails") to reduce false positives
|
|
186
|
+
if (/\b\d+\s+[a-z]+s\b/.test(lowerText)) {
|
|
187
|
+
const wordCount = lowerText.split(/\s+/).length;
|
|
188
|
+
if (wordCount >= 5)
|
|
189
|
+
return false;
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
function hasICUSyntax(text) {
|
|
195
|
+
return /\{[^}]+,\s*(plural|select|selectordinal)\s*,/.test(text);
|
|
196
|
+
}
|
|
197
|
+
function escapeRegex(text) {
|
|
198
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
199
|
+
}
|
|
200
|
+
function hashString(text) {
|
|
201
|
+
return (0, node_crypto_1.createHash)('sha256').update(text, 'utf8').digest('hex').slice(0, 16);
|
|
202
|
+
}
|
|
203
|
+
function matchesCurrencyPattern(text) {
|
|
204
|
+
return /[€$£¥]/.test(text) || /\d[.,]\d{2}\b/.test(text) || /\b\d{1,3}(,\d{3})+\b/.test(text);
|
|
205
|
+
}
|
|
206
|
+
function matchesDatePattern(text) {
|
|
207
|
+
return (/(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d/.test(text) ||
|
|
208
|
+
/\b(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])\b/.test(text));
|
|
209
|
+
}
|
|
210
|
+
function containsPII(text) {
|
|
211
|
+
const PATTERNS = {
|
|
212
|
+
email: /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/,
|
|
213
|
+
ipv4: /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/,
|
|
214
|
+
creditCard: /\b(?:\d[ -]*?){13,16}\b/,
|
|
215
|
+
secretToken: /\b(?:sk|pk|key|secret|token|auth|password)[-_=]\w{12,}\b/i,
|
|
216
|
+
hexKey: /\b[a-f0-9]{32,64}\b/i,
|
|
217
|
+
phone: /\b(?:\+\d{1,4}[- ]?)?\(?\d{2,5}\)?[- ]?\d{3,5}[- ]?\d{3,5}\b/,
|
|
218
|
+
};
|
|
219
|
+
return Object.values(PATTERNS).some((pattern) => pattern.test(text));
|
|
220
|
+
}
|
|
221
|
+
exports.SKIP_TAGS = new Set(['script', 'style', 'noscript', 'code', 'pre', 'svg']);
|
|
222
|
+
/**
|
|
223
|
+
* Extract content between balanced opening and closing tags, handling nested tags.
|
|
224
|
+
* Finds the first `<tagName>` and its matching `</tagName>`, correctly counting
|
|
225
|
+
* nested `<tagName ...>` elements.
|
|
226
|
+
*/
|
|
227
|
+
function extractBalancedTag(content, tagName, allowAttrs = false) {
|
|
228
|
+
const openPattern = allowAttrs ? `<${tagName}` : `<${tagName}>`;
|
|
229
|
+
const closeTag = `</${tagName}>`;
|
|
230
|
+
const startIdx = content.indexOf(openPattern);
|
|
231
|
+
if (startIdx === -1)
|
|
232
|
+
return null;
|
|
233
|
+
// For allowAttrs=true, find the end of the opening tag
|
|
234
|
+
let afterOpen;
|
|
235
|
+
let attrs = '';
|
|
236
|
+
if (allowAttrs) {
|
|
237
|
+
const gtIdx = content.indexOf('>', startIdx);
|
|
238
|
+
if (gtIdx === -1)
|
|
239
|
+
return null;
|
|
240
|
+
afterOpen = gtIdx + 1;
|
|
241
|
+
attrs = content.slice(startIdx + tagName.length + 1, gtIdx).trim();
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
afterOpen = startIdx + openPattern.length;
|
|
245
|
+
}
|
|
246
|
+
let depth = 1;
|
|
247
|
+
let i = afterOpen;
|
|
248
|
+
while (i < content.length && depth > 0) {
|
|
249
|
+
const nextOpen = content.indexOf(`<${tagName}`, i);
|
|
250
|
+
const nextClose = content.indexOf(closeTag, i);
|
|
251
|
+
if (nextClose === -1)
|
|
252
|
+
return null;
|
|
253
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
254
|
+
depth++;
|
|
255
|
+
i = nextOpen + tagName.length + 1;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
depth--;
|
|
259
|
+
if (depth === 0) {
|
|
260
|
+
return {
|
|
261
|
+
fullMatch: content.slice(startIdx, nextClose + closeTag.length),
|
|
262
|
+
body: content.slice(afterOpen, nextClose),
|
|
263
|
+
attrs,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
i = nextClose + closeTag.length;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translation Quality Validation
|
|
3
|
+
*
|
|
4
|
+
* Validates translation quality and detects potential issues
|
|
5
|
+
*/
|
|
6
|
+
export interface QualityIssue {
|
|
7
|
+
type: 'missing_placeholder' | 'extra_placeholder' | 'empty_translation' | 'suspicious_length' | 'untranslated_text' | 'html_tag_mismatch' | 'placeholder_order_mismatch';
|
|
8
|
+
severity: 'error' | 'warning';
|
|
9
|
+
message: string;
|
|
10
|
+
original: string;
|
|
11
|
+
translated: string;
|
|
12
|
+
}
|
|
13
|
+
export interface QualityResult {
|
|
14
|
+
isValid: boolean;
|
|
15
|
+
score: number;
|
|
16
|
+
issues: QualityIssue[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate translation quality
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateTranslationQuality(original: string, translated: string): QualityResult;
|
|
22
|
+
/**
|
|
23
|
+
* Validate batch of translations
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateBatchQuality(entries: Array<{
|
|
26
|
+
key: string;
|
|
27
|
+
original: string;
|
|
28
|
+
translated: string;
|
|
29
|
+
}>): {
|
|
30
|
+
valid: number;
|
|
31
|
+
invalid: number;
|
|
32
|
+
averageScore: number;
|
|
33
|
+
issues: QualityIssue[];
|
|
34
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Translation Quality Validation
|
|
4
|
+
*
|
|
5
|
+
* Validates translation quality and detects potential issues
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.validateTranslationQuality = validateTranslationQuality;
|
|
9
|
+
exports.validateBatchQuality = validateBatchQuality;
|
|
10
|
+
/**
|
|
11
|
+
* Validate translation quality
|
|
12
|
+
*/
|
|
13
|
+
function validateTranslationQuality(original, translated) {
|
|
14
|
+
const issues = [];
|
|
15
|
+
let score = 100;
|
|
16
|
+
const trimmedOriginal = original.trim();
|
|
17
|
+
const trimmedTranslated = (translated || '').trim();
|
|
18
|
+
// Check for empty translation
|
|
19
|
+
if (trimmedTranslated.length === 0) {
|
|
20
|
+
issues.push({
|
|
21
|
+
type: 'empty_translation',
|
|
22
|
+
severity: 'error',
|
|
23
|
+
message: 'Translation is empty',
|
|
24
|
+
original,
|
|
25
|
+
translated,
|
|
26
|
+
});
|
|
27
|
+
score -= 100; // Complete failure
|
|
28
|
+
}
|
|
29
|
+
// Check for untranslated text (identical to source)
|
|
30
|
+
// Skip for very short strings (e.g. "OK", "Save") where source might equal translation
|
|
31
|
+
if (trimmedTranslated.length > 4 && trimmedOriginal.toLowerCase() === trimmedTranslated.toLowerCase()) {
|
|
32
|
+
issues.push({
|
|
33
|
+
type: 'untranslated_text',
|
|
34
|
+
severity: 'warning',
|
|
35
|
+
message: 'Translation is identical to source text',
|
|
36
|
+
original,
|
|
37
|
+
translated,
|
|
38
|
+
});
|
|
39
|
+
score -= 20;
|
|
40
|
+
}
|
|
41
|
+
// Check placeholder preservation
|
|
42
|
+
const originalPlaceholders = original.match(/\{[^}]+\}/g) || [];
|
|
43
|
+
const translatedPlaceholders = translated.match(/\{[^}]+\}/g) || [];
|
|
44
|
+
// Missing placeholders
|
|
45
|
+
const sortedOriginal = [...originalPlaceholders].sort();
|
|
46
|
+
const sortedTranslated = [...translatedPlaceholders].sort();
|
|
47
|
+
const missingPlaceholders = sortedOriginal.filter((p) => !sortedTranslated.includes(p));
|
|
48
|
+
if (missingPlaceholders.length > 0) {
|
|
49
|
+
issues.push({
|
|
50
|
+
type: 'missing_placeholder',
|
|
51
|
+
severity: 'error',
|
|
52
|
+
message: `Missing placeholders: ${missingPlaceholders.join(', ')}`,
|
|
53
|
+
original,
|
|
54
|
+
translated,
|
|
55
|
+
});
|
|
56
|
+
score -= 30 * missingPlaceholders.length;
|
|
57
|
+
}
|
|
58
|
+
// Extra placeholders
|
|
59
|
+
const extraPlaceholders = sortedTranslated.filter((p) => !sortedOriginal.includes(p));
|
|
60
|
+
if (extraPlaceholders.length > 0) {
|
|
61
|
+
issues.push({
|
|
62
|
+
type: 'extra_placeholder',
|
|
63
|
+
severity: 'warning',
|
|
64
|
+
message: `Extra placeholders found: ${extraPlaceholders.join(', ')}`,
|
|
65
|
+
original,
|
|
66
|
+
translated,
|
|
67
|
+
});
|
|
68
|
+
score -= 10 * extraPlaceholders.length;
|
|
69
|
+
}
|
|
70
|
+
// Check placeholder ordering
|
|
71
|
+
// If the order of placeholders is fundamentally different, it might indicate grammar corruption
|
|
72
|
+
if (originalPlaceholders.length > 1 && originalPlaceholders.length === translatedPlaceholders.length) {
|
|
73
|
+
if (originalPlaceholders.join(',') !== translatedPlaceholders.join(',')) {
|
|
74
|
+
// Check if they are just reordered (which might be valid for some languages)
|
|
75
|
+
// vs. duplicated or mangled. If sorted they match, it's just a reorder.
|
|
76
|
+
if (sortedOriginal.join(',') === sortedTranslated.join(',')) {
|
|
77
|
+
// Reordering is often valid (Subject-Object-Verb vs Subject-Verb-Object)
|
|
78
|
+
// but drastic jumps might be worth a warning in some engines.
|
|
79
|
+
// For now, we allow reordering but log it if we wanted to.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Check HTML tag parity
|
|
84
|
+
const originalTags = original.match(/<[^>]+>/g) || [];
|
|
85
|
+
const translatedTags = translated.match(/<[^>]+>/g) || [];
|
|
86
|
+
if (originalTags.length !== translatedTags.length) {
|
|
87
|
+
issues.push({
|
|
88
|
+
type: 'html_tag_mismatch',
|
|
89
|
+
severity: 'error',
|
|
90
|
+
message: `HTML tag count mismatch: source has ${originalTags.length}, translation has ${translatedTags.length}`,
|
|
91
|
+
original,
|
|
92
|
+
translated,
|
|
93
|
+
});
|
|
94
|
+
score -= 25;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Even if count matches, check if tags themselves were mangled (e.g. <strong> becomes <fort>)
|
|
98
|
+
const sortedOrigTags = [...originalTags].map(t => t.toLowerCase().split(' ')[0]).sort();
|
|
99
|
+
const sortedTransTags = [...translatedTags].map(t => t.toLowerCase().split(' ')[0]).sort();
|
|
100
|
+
if (sortedOrigTags.join(',') !== sortedTransTags.join(',')) {
|
|
101
|
+
issues.push({
|
|
102
|
+
type: 'html_tag_mismatch',
|
|
103
|
+
severity: 'error',
|
|
104
|
+
message: 'HTML tags were altered or translated (e.g. <strong> became something else)',
|
|
105
|
+
original,
|
|
106
|
+
translated,
|
|
107
|
+
});
|
|
108
|
+
score -= 25;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Check for suspicious length differences
|
|
112
|
+
const lengthRatio = trimmedTranslated.length / (trimmedOriginal.length || 1);
|
|
113
|
+
if (lengthRatio < 0.2 || lengthRatio > 5) {
|
|
114
|
+
issues.push({
|
|
115
|
+
type: 'suspicious_length',
|
|
116
|
+
severity: 'warning',
|
|
117
|
+
message: `Translation length is ${(lengthRatio * 100).toFixed(0)}% of original (suspicious)`,
|
|
118
|
+
original,
|
|
119
|
+
translated,
|
|
120
|
+
});
|
|
121
|
+
score -= 15;
|
|
122
|
+
}
|
|
123
|
+
// Ensure score doesn't go below 0
|
|
124
|
+
score = Math.max(0, score);
|
|
125
|
+
return {
|
|
126
|
+
isValid: issues.filter((i) => i.severity === 'error').length === 0,
|
|
127
|
+
score,
|
|
128
|
+
issues,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validate batch of translations
|
|
133
|
+
*/
|
|
134
|
+
function validateBatchQuality(entries) {
|
|
135
|
+
let valid = 0;
|
|
136
|
+
let invalid = 0;
|
|
137
|
+
let totalScore = 0;
|
|
138
|
+
const allIssues = [];
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const result = validateTranslationQuality(entry.original, entry.translated);
|
|
141
|
+
totalScore += result.score;
|
|
142
|
+
if (result.isValid) {
|
|
143
|
+
valid++;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
invalid++;
|
|
147
|
+
}
|
|
148
|
+
allIssues.push(...result.issues);
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
valid,
|
|
152
|
+
invalid,
|
|
153
|
+
averageScore: entries.length > 0 ? totalScore / entries.length : 0,
|
|
154
|
+
issues: allIssues,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clean up translator output. Script injection is stripped for every
|
|
3
|
+
* provider; the NLLB-specific repairs (language-code echoes, unknown-token
|
|
4
|
+
* marks, whitespace and stutter collapsing) only run for NLLB output, since on
|
|
5
|
+
* LLM output they would rewrite legitimate text.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sanitizeTranslation(text: string, options?: {
|
|
8
|
+
nllb?: boolean;
|
|
9
|
+
source?: string;
|
|
10
|
+
}): string;
|
|
11
|
+
export declare function validateRawTokens(aiOutput: string, expectedCount: number): boolean;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeTranslation = sanitizeTranslation;
|
|
4
|
+
exports.validateRawTokens = validateRawTokens;
|
|
5
|
+
const NLLB_ARTIFACT_PATTERN = new RegExp(
|
|
6
|
+
// Sentence-initial echoes come back capitalized ("Eng_Latn Produktkatalog").
|
|
7
|
+
'\\b(?:__)?[A-Za-z][a-z]{2}_[A-Z][a-z]{3,4}(?:__)?\\b', 'g');
|
|
8
|
+
/**
|
|
9
|
+
* Collapse phrase-level stutter ("Il a Il a" -> "Il a"). NLLB occasionally
|
|
10
|
+
* repeats a short fragment twice after language tokens are stripped. Only
|
|
11
|
+
* collapse when BOTH halves contain >=2 words and match case-insensitively —
|
|
12
|
+
* this never touches legitimate one-word doublings like "no no" or "la la".
|
|
13
|
+
*/
|
|
14
|
+
function collapsePhraseStutter(text) {
|
|
15
|
+
const words = text.split(/\s+/);
|
|
16
|
+
// The whole output is one phrase of >=2 words repeated 2+ times ("Il a Il a",
|
|
17
|
+
// "Product catalog Product catalog Product catalog").
|
|
18
|
+
for (let size = 2; size <= words.length / 2; size++) {
|
|
19
|
+
if (words.length % size !== 0)
|
|
20
|
+
continue;
|
|
21
|
+
const first = words.slice(0, size).map((w) => w.toLowerCase());
|
|
22
|
+
let repeated = true;
|
|
23
|
+
for (let i = size; i < words.length && repeated; i++) {
|
|
24
|
+
if (words[i].toLowerCase() !== first[i % size])
|
|
25
|
+
repeated = false;
|
|
26
|
+
}
|
|
27
|
+
if (repeated)
|
|
28
|
+
return words.slice(0, size).join(' ');
|
|
29
|
+
}
|
|
30
|
+
return text;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Clean up translator output. Script injection is stripped for every
|
|
34
|
+
* provider; the NLLB-specific repairs (language-code echoes, unknown-token
|
|
35
|
+
* marks, whitespace and stutter collapsing) only run for NLLB output, since on
|
|
36
|
+
* LLM output they would rewrite legitimate text.
|
|
37
|
+
*/
|
|
38
|
+
function sanitizeTranslation(text, options = {}) {
|
|
39
|
+
if (!text)
|
|
40
|
+
return text;
|
|
41
|
+
let clean = text.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gim, '');
|
|
42
|
+
clean = clean.replace(/javascript:/gim, 'java_script:');
|
|
43
|
+
clean = clean.replace(/\s+on[a-z]+\s*=\s*['"][^'"]*['"]/gim, '');
|
|
44
|
+
if (options.nllb === false)
|
|
45
|
+
return clean;
|
|
46
|
+
// Strip FLORES-200 language-code artifacts (eng_Latn, __fra_Latn__, ...)
|
|
47
|
+
// anywhere in the output — NLLB echoes these at the end/middle of short
|
|
48
|
+
// fragments. Mirrors the fix in apps/translator/src/translator.rs.
|
|
49
|
+
clean = clean.replace(NLLB_ARTIFACT_PATTERN, '');
|
|
50
|
+
// Strip U+2047 (DOUBLE QUESTION MARK) — NLLB's unknown-token marker, emitted
|
|
51
|
+
// for tokens it can't translate (e.g. punctuation-only fragments). Never
|
|
52
|
+
// legitimate translated text.
|
|
53
|
+
clean = clean.replace(/\u2047/g, '');
|
|
54
|
+
clean = clean.replace(/ +/g, ' ').trim();
|
|
55
|
+
clean = collapsePhraseStutter(clean);
|
|
56
|
+
// A one-word source doubled in the output ("Product" → "Produkt Produkt").
|
|
57
|
+
if (options.source && !/\s/.test(options.source.trim())) {
|
|
58
|
+
const words = clean.split(/\s+/);
|
|
59
|
+
if (words.length > 1 && words.every((w) => w.toLowerCase() === words[0].toLowerCase()))
|
|
60
|
+
clean = words[0];
|
|
61
|
+
}
|
|
62
|
+
return clean;
|
|
63
|
+
}
|
|
64
|
+
function validateRawTokens(aiOutput, expectedCount) {
|
|
65
|
+
for (let i = 0; i < expectedCount; i++) {
|
|
66
|
+
const tokenRegex = new RegExp(`\\[\\s*#\\s*VAR_${i}\\s*#\\s*\\]`, 'gi');
|
|
67
|
+
if (!tokenRegex.test(aiOutput))
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TranslationEntry } from '../runtime/types';
|
|
2
|
+
import type { ByokProvider } from '@babelize/types';
|
|
3
|
+
/** Options only LLM (BYOK) providers use; the NLLB service ignores them. */
|
|
4
|
+
export interface LlmRequestOptions {
|
|
5
|
+
targetLocale?: string;
|
|
6
|
+
glossary?: Array<{
|
|
7
|
+
term: string;
|
|
8
|
+
translation: string;
|
|
9
|
+
}>;
|
|
10
|
+
hints?: Record<string, string>;
|
|
11
|
+
/** Aborts the request and any retries. */
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Translate a batch. Returns translations only for entries the translator
|
|
16
|
+
* actually produced — never the source text in their place. Entries missing
|
|
17
|
+
* from the result are reported as failed keys by the caller.
|
|
18
|
+
*/
|
|
19
|
+
export declare function callTranslator(entries: TranslationEntry[], sourceLang: string, targetLang: string, byokConfig?: {
|
|
20
|
+
provider: ByokProvider;
|
|
21
|
+
apiKey: string;
|
|
22
|
+
model?: string;
|
|
23
|
+
}, brandVoice?: string, llmOptions?: LlmRequestOptions): Promise<{
|
|
24
|
+
translations: TranslationEntry[];
|
|
25
|
+
metadata?: any;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function checkTranslatorHealth(byokConfig?: {
|
|
28
|
+
provider: ByokProvider;
|
|
29
|
+
apiKey: string;
|
|
30
|
+
model?: string;
|
|
31
|
+
}): Promise<boolean>;
|
|
32
|
+
export declare function warmupTranslator(): Promise<boolean>;
|