@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,374 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.detectSyncIssues = detectSyncIssues;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs-extra"));
|
|
39
|
+
const logger_1 = require("../../utils/logger");
|
|
40
|
+
const extract_1 = require("../extractor/extract");
|
|
41
|
+
const lockfile_manager_1 = require("../lockfile/lockfile-manager");
|
|
42
|
+
const types_1 = require("../../runtime/types");
|
|
43
|
+
const extraction_options_1 = require("../../utils/extraction-options");
|
|
44
|
+
/**
|
|
45
|
+
* Extraction options the project's engine runs use (sentence grouping, ICU
|
|
46
|
+
* plurals, data-literal rules), so a sync check compares like with like and
|
|
47
|
+
* doesn't report every sentence as new.
|
|
48
|
+
*/
|
|
49
|
+
async function runExtractionOptions(workspacePath, options) {
|
|
50
|
+
let packageJson;
|
|
51
|
+
try {
|
|
52
|
+
packageJson = (await fs.readJson(path.join(workspacePath, 'package.json'))) ?? undefined;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
packageJson = undefined;
|
|
56
|
+
}
|
|
57
|
+
const deps = { ...packageJson?.dependencies, ...packageJson?.devDependencies };
|
|
58
|
+
const framework = options.framework ?? (deps.next ? 'nextjs' : deps.react ? 'react' : deps.vue ? 'vue' : 'unknown');
|
|
59
|
+
const workspace = {
|
|
60
|
+
path: workspacePath, framework, sourceDirs: ['.'], hasLocalesDir: false, existingLocales: [], publicDir: 'public',
|
|
61
|
+
localeFormat: 'json', packageJson,
|
|
62
|
+
};
|
|
63
|
+
const icu = await (0, extraction_options_1.resolveIcuOptions)(workspace, !!options.llmTranslator);
|
|
64
|
+
return { framework, sentences: icu.sentences, plurals: icu.plurals };
|
|
65
|
+
}
|
|
66
|
+
// ─────────────────────────────────────────────────────────────
|
|
67
|
+
// File filtering
|
|
68
|
+
// ─────────────────────────────────────────────────────────────
|
|
69
|
+
/**
|
|
70
|
+
* Patterns for files that should never trigger sync detection.
|
|
71
|
+
* Covers config, test, build, and documentation files.
|
|
72
|
+
*/
|
|
73
|
+
const SYNC_IGNORE_PATTERNS = [
|
|
74
|
+
...types_1.EXCLUDED_PATTERNS.map(p => typeof p === 'string' ? new RegExp(p.replace(/\*/g, '.*')) : p),
|
|
75
|
+
// Additional patterns specific to sync detection
|
|
76
|
+
/\.test\.(ts|tsx|js|jsx)$/,
|
|
77
|
+
/\.spec\.(ts|tsx|js|jsx)$/,
|
|
78
|
+
/\.stories\.(ts|tsx|js|jsx)$/,
|
|
79
|
+
/__tests__\//,
|
|
80
|
+
/__mocks__\//,
|
|
81
|
+
/\.config\.(ts|js|mjs|cjs)$/,
|
|
82
|
+
/jest\.config/,
|
|
83
|
+
/playwright\.config/,
|
|
84
|
+
/next\.config/,
|
|
85
|
+
/tailwind\.config/,
|
|
86
|
+
/postcss\.config/,
|
|
87
|
+
/tsconfig/,
|
|
88
|
+
/\.eslintrc/,
|
|
89
|
+
/\.prettierrc/,
|
|
90
|
+
/\.env/,
|
|
91
|
+
/package\.json$/,
|
|
92
|
+
/package-lock\.json$/,
|
|
93
|
+
/bun\.lock$/,
|
|
94
|
+
/yarn\.lock$/,
|
|
95
|
+
/README\.md$/i,
|
|
96
|
+
/CHANGELOG\.md$/i,
|
|
97
|
+
/LICENSE/i,
|
|
98
|
+
/\.d\.ts$/,
|
|
99
|
+
/\.map$/,
|
|
100
|
+
/dist\//,
|
|
101
|
+
/build\//,
|
|
102
|
+
/\.next\//,
|
|
103
|
+
/node_modules\//,
|
|
104
|
+
// Locale files themselves should not trigger detection
|
|
105
|
+
/locales?\//,
|
|
106
|
+
/i18n\//,
|
|
107
|
+
/translations?\//,
|
|
108
|
+
/lang\//,
|
|
109
|
+
/messages\//,
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* Check if a file path matches supported source extensions
|
|
113
|
+
* and is not excluded by ignore patterns.
|
|
114
|
+
*/
|
|
115
|
+
function isSourceFile(filePath) {
|
|
116
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
117
|
+
if (!types_1.SUPPORTED_EXTENSIONS.includes(ext))
|
|
118
|
+
return false;
|
|
119
|
+
for (const pattern of SYNC_IGNORE_PATTERNS) {
|
|
120
|
+
if (pattern.test(filePath))
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
// ─────────────────────────────────────────────────────────────
|
|
126
|
+
// Core detection
|
|
127
|
+
// ─────────────────────────────────────────────────────────────
|
|
128
|
+
/**
|
|
129
|
+
* Detect sync issues by comparing extracted strings from changed files
|
|
130
|
+
* against the lockfile and locale files.
|
|
131
|
+
*
|
|
132
|
+
* @param projectId The project identifier (for lockfile lookup)
|
|
133
|
+
* @param workspacePath Absolute path to the project workspace
|
|
134
|
+
* @param changedFiles List of file paths changed in the push (relative to repo root)
|
|
135
|
+
* @param commitSha The commit SHA that triggered this check
|
|
136
|
+
* @param targetLocales Target locales configured for this project
|
|
137
|
+
*/
|
|
138
|
+
async function detectSyncIssues(projectId, workspacePath, changedFiles, commitSha, targetLocales, options = {}) {
|
|
139
|
+
const now = new Date().toISOString();
|
|
140
|
+
const filesChecked = [];
|
|
141
|
+
const filesSkipped = [];
|
|
142
|
+
// 1. Filter changed files to only source files
|
|
143
|
+
const sourceFiles = [];
|
|
144
|
+
for (const file of changedFiles) {
|
|
145
|
+
if (isSourceFile(file)) {
|
|
146
|
+
sourceFiles.push(file);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
filesSkipped.push(file);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (sourceFiles.length === 0) {
|
|
153
|
+
logger_1.engineLogger.info('No source files in push, skipping sync check', {
|
|
154
|
+
projectId,
|
|
155
|
+
commitSha,
|
|
156
|
+
totalChanged: changedFiles.length,
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
newStrings: [],
|
|
160
|
+
changedStrings: [],
|
|
161
|
+
missingLocaleKeys: [],
|
|
162
|
+
totalIssues: 0,
|
|
163
|
+
commitSha,
|
|
164
|
+
checkedAt: now,
|
|
165
|
+
filesChecked: [],
|
|
166
|
+
filesSkipped,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// 2. Extract strings from changed source files, the way runs do
|
|
170
|
+
const extractedStrings = {};
|
|
171
|
+
const extractOptions = await runExtractionOptions(workspacePath, options);
|
|
172
|
+
for (const relativePath of sourceFiles) {
|
|
173
|
+
const absolutePath = path.join(workspacePath, relativePath);
|
|
174
|
+
try {
|
|
175
|
+
if (!(await fs.pathExists(absolutePath))) {
|
|
176
|
+
filesSkipped.push(relativePath);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const content = await fs.readFile(absolutePath, 'utf-8');
|
|
180
|
+
const { strings } = await (0, extract_1.extractStringsFromFile)(content, relativePath, false, extractOptions);
|
|
181
|
+
for (const s of strings) {
|
|
182
|
+
// Use the text as both key and value since we're doing pre-keygen detection
|
|
183
|
+
const key = s.id || `${relativePath}:${s.line}:${s.text}`;
|
|
184
|
+
extractedStrings[key] = s.text;
|
|
185
|
+
}
|
|
186
|
+
filesChecked.push(relativePath);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
logger_1.engineLogger.warn('Failed to extract strings from file during sync check', {
|
|
190
|
+
projectId,
|
|
191
|
+
file: relativePath,
|
|
192
|
+
error: err instanceof Error ? err.message : String(err),
|
|
193
|
+
});
|
|
194
|
+
filesSkipped.push(relativePath);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (Object.keys(extractedStrings).length === 0) {
|
|
198
|
+
logger_1.engineLogger.info('No translatable strings found in changed files', {
|
|
199
|
+
projectId,
|
|
200
|
+
commitSha,
|
|
201
|
+
filesChecked: filesChecked.length,
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
newStrings: [],
|
|
205
|
+
changedStrings: [],
|
|
206
|
+
missingLocaleKeys: [],
|
|
207
|
+
totalIssues: 0,
|
|
208
|
+
commitSha,
|
|
209
|
+
checkedAt: now,
|
|
210
|
+
filesChecked,
|
|
211
|
+
filesSkipped,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// 3. Load lockfile and partition keys
|
|
215
|
+
const lockfile = await (0, lockfile_manager_1.loadLockfile)(projectId);
|
|
216
|
+
const partition = (0, lockfile_manager_1.partitionKeys)(lockfile, extractedStrings, targetLocales);
|
|
217
|
+
// 4. Build detection results
|
|
218
|
+
const newStrings = partition.newKeys.map(k => ({
|
|
219
|
+
key: k.key,
|
|
220
|
+
value: k.value,
|
|
221
|
+
file: k.key.split(':')[0] || 'unknown',
|
|
222
|
+
line: parseInt(k.key.split(':')[1] || '0', 10),
|
|
223
|
+
}));
|
|
224
|
+
const changedStrings = partition.changed.map(k => ({
|
|
225
|
+
key: k.key,
|
|
226
|
+
value: k.value,
|
|
227
|
+
file: k.key.split(':')[0] || 'unknown',
|
|
228
|
+
line: parseInt(k.key.split(':')[1] || '0', 10),
|
|
229
|
+
}));
|
|
230
|
+
// 5. Detect missing locale keys
|
|
231
|
+
const missingLocaleKeys = partition.missingLocales.map(k => ({
|
|
232
|
+
key: k.key,
|
|
233
|
+
presentIn: targetLocales.filter(loc => !k.missingLocales.includes(loc)),
|
|
234
|
+
missingFrom: k.missingLocales,
|
|
235
|
+
}));
|
|
236
|
+
// 6. Also check locale files on disk for cross-locale missing keys
|
|
237
|
+
const diskMissing = await detectMissingLocaleKeysFromDisk(workspacePath, targetLocales);
|
|
238
|
+
for (const m of diskMissing) {
|
|
239
|
+
// Only add if not already detected via lockfile
|
|
240
|
+
if (!missingLocaleKeys.some(existing => existing.key === m.key)) {
|
|
241
|
+
missingLocaleKeys.push(m);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const totalIssues = newStrings.length + changedStrings.length + missingLocaleKeys.length;
|
|
245
|
+
logger_1.engineLogger.info('Sync detection complete', {
|
|
246
|
+
projectId,
|
|
247
|
+
commitSha,
|
|
248
|
+
newStrings: newStrings.length,
|
|
249
|
+
changedStrings: changedStrings.length,
|
|
250
|
+
missingLocaleKeys: missingLocaleKeys.length,
|
|
251
|
+
totalIssues,
|
|
252
|
+
filesChecked: filesChecked.length,
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
newStrings,
|
|
256
|
+
changedStrings,
|
|
257
|
+
missingLocaleKeys,
|
|
258
|
+
totalIssues,
|
|
259
|
+
commitSha,
|
|
260
|
+
checkedAt: now,
|
|
261
|
+
filesChecked,
|
|
262
|
+
filesSkipped,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
// ─────────────────────────────────────────────────────────────
|
|
266
|
+
// Locale file disk check
|
|
267
|
+
// ─────────────────────────────────────────────────────────────
|
|
268
|
+
/**
|
|
269
|
+
* Scan locale JSON files on disk to detect keys present in the
|
|
270
|
+
* base locale (en) but missing from target locales.
|
|
271
|
+
*/
|
|
272
|
+
async function detectMissingLocaleKeysFromDisk(workspacePath, targetLocales) {
|
|
273
|
+
const missing = [];
|
|
274
|
+
// Common locale directory patterns
|
|
275
|
+
const localeDirCandidates = [
|
|
276
|
+
'locales',
|
|
277
|
+
'locale',
|
|
278
|
+
'i18n',
|
|
279
|
+
'translations',
|
|
280
|
+
'lang',
|
|
281
|
+
'messages',
|
|
282
|
+
'public/locales',
|
|
283
|
+
'src/locales',
|
|
284
|
+
'src/i18n',
|
|
285
|
+
];
|
|
286
|
+
let localeDir = null;
|
|
287
|
+
for (const candidate of localeDirCandidates) {
|
|
288
|
+
const dirPath = path.join(workspacePath, candidate);
|
|
289
|
+
if (await fs.pathExists(dirPath)) {
|
|
290
|
+
localeDir = dirPath;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!localeDir)
|
|
295
|
+
return missing;
|
|
296
|
+
try {
|
|
297
|
+
// Load the base locale file (en.json)
|
|
298
|
+
const baseLocalePath = path.join(localeDir, 'en.json');
|
|
299
|
+
if (!(await fs.pathExists(baseLocalePath)))
|
|
300
|
+
return missing;
|
|
301
|
+
const baseLocale = await fs.readJson(baseLocalePath);
|
|
302
|
+
const baseKeys = flattenKeys(baseLocale);
|
|
303
|
+
// Check each target locale
|
|
304
|
+
for (const locale of targetLocales) {
|
|
305
|
+
if (locale === 'en')
|
|
306
|
+
continue;
|
|
307
|
+
const localeFilePath = path.join(localeDir, `${locale}.json`);
|
|
308
|
+
if (!(await fs.pathExists(localeFilePath))) {
|
|
309
|
+
// Entire locale file missing — all keys are missing
|
|
310
|
+
for (const key of Object.keys(baseKeys)) {
|
|
311
|
+
const existing = missing.find(m => m.key === key);
|
|
312
|
+
if (existing) {
|
|
313
|
+
if (!existing.missingFrom.includes(locale)) {
|
|
314
|
+
existing.missingFrom.push(locale);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
missing.push({
|
|
319
|
+
key,
|
|
320
|
+
presentIn: ['en'],
|
|
321
|
+
missingFrom: [locale],
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const localeData = await fs.readJson(localeFilePath);
|
|
328
|
+
const localeKeys = flattenKeys(localeData);
|
|
329
|
+
for (const key of Object.keys(baseKeys)) {
|
|
330
|
+
if (!(key in localeKeys)) {
|
|
331
|
+
const existing = missing.find(m => m.key === key);
|
|
332
|
+
if (existing) {
|
|
333
|
+
if (!existing.missingFrom.includes(locale)) {
|
|
334
|
+
existing.missingFrom.push(locale);
|
|
335
|
+
}
|
|
336
|
+
if (!existing.presentIn.includes('en')) {
|
|
337
|
+
existing.presentIn.push('en');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
missing.push({
|
|
342
|
+
key,
|
|
343
|
+
presentIn: ['en'],
|
|
344
|
+
missingFrom: [locale],
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch (err) {
|
|
352
|
+
logger_1.engineLogger.warn('Failed to check locale files on disk', {
|
|
353
|
+
error: err instanceof Error ? err.message : String(err),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
return missing;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Flatten a nested JSON object into a flat key map.
|
|
360
|
+
* e.g., { "a": { "b": "hello" } } → { "a.b": "hello" }
|
|
361
|
+
*/
|
|
362
|
+
function flattenKeys(obj, prefix = '') {
|
|
363
|
+
const result = {};
|
|
364
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
365
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
366
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
367
|
+
Object.assign(result, flattenKeys(value, fullKey));
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
result[fullKey] = String(value);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TranslationUnit } from '../../types';
|
|
2
|
+
export interface Batch {
|
|
3
|
+
units: TranslationUnit[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Groups translation units into batches that respect a rough token limit and
|
|
7
|
+
* ensures we do NOT make parallel HTTP calls to the HF Space.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createBatches(units: TranslationUnit[], options?: {
|
|
10
|
+
maxBatchSize?: number;
|
|
11
|
+
maxCharsPerBatch?: number;
|
|
12
|
+
}): Batch[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createBatches = createBatches;
|
|
4
|
+
/**
|
|
5
|
+
* Groups translation units into batches that respect a rough token limit and
|
|
6
|
+
* ensures we do NOT make parallel HTTP calls to the HF Space.
|
|
7
|
+
*/
|
|
8
|
+
function createBatches(units, options = {}) {
|
|
9
|
+
const maxBatchSize = options.maxBatchSize ?? 50;
|
|
10
|
+
const maxCharsPerBatch = options.maxCharsPerBatch ?? 2000;
|
|
11
|
+
const batches = [];
|
|
12
|
+
let current = [];
|
|
13
|
+
let currentChars = 0;
|
|
14
|
+
for (const unit of units) {
|
|
15
|
+
const textLen = unit.sourceText.length;
|
|
16
|
+
const wouldExceedSize = current.length >= maxBatchSize;
|
|
17
|
+
const wouldExceedChars = currentChars + textLen > maxCharsPerBatch;
|
|
18
|
+
if (current.length > 0 && (wouldExceedSize || wouldExceedChars)) {
|
|
19
|
+
batches.push({ units: current });
|
|
20
|
+
current = [];
|
|
21
|
+
currentChars = 0;
|
|
22
|
+
}
|
|
23
|
+
current.push(unit);
|
|
24
|
+
currentChars += textLen;
|
|
25
|
+
}
|
|
26
|
+
if (current.length > 0) {
|
|
27
|
+
batches.push({ units: current });
|
|
28
|
+
}
|
|
29
|
+
return batches;
|
|
30
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BYOK (Bring Your Own Key) Translation Provider
|
|
3
|
+
*
|
|
4
|
+
* Direct integration with Anthropic, OpenAI, Gemini, Groq and OpenRouter for
|
|
5
|
+
* users who provide their own API keys. Zero Babelize AI credits consumed.
|
|
6
|
+
*
|
|
7
|
+
* Contract: returns a translation only for entries the model actually
|
|
8
|
+
* translated. An entry it skipped or garbled is simply absent from the
|
|
9
|
+
* response, and the engine reports it as a failed key — never as the source
|
|
10
|
+
* text.
|
|
11
|
+
*/
|
|
12
|
+
import type { HFTranslateRequest, HFTranslateResponse } from './request.types';
|
|
13
|
+
import type { TranslatorClient } from './translator.interface';
|
|
14
|
+
import type { ByokProvider } from '@babelize/types';
|
|
15
|
+
interface BYOKProviderConfig {
|
|
16
|
+
provider: ByokProvider;
|
|
17
|
+
apiKey: string;
|
|
18
|
+
/** Optional model override */
|
|
19
|
+
model?: string;
|
|
20
|
+
}
|
|
21
|
+
interface PromptEntry {
|
|
22
|
+
id: string;
|
|
23
|
+
text: string;
|
|
24
|
+
context?: string;
|
|
25
|
+
element?: string;
|
|
26
|
+
note?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class BYOKTranslatorClient implements TranslatorClient {
|
|
29
|
+
private readonly config;
|
|
30
|
+
constructor(config: BYOKProviderConfig);
|
|
31
|
+
health(): Promise<boolean>;
|
|
32
|
+
warmup(): Promise<boolean>;
|
|
33
|
+
translate(req: HFTranslateRequest, signal?: AbortSignal): Promise<HFTranslateResponse>;
|
|
34
|
+
private getModel;
|
|
35
|
+
private getBaseUrl;
|
|
36
|
+
private getProviderDisplayName;
|
|
37
|
+
/** Anthropic: force a tool call so the output is schema-shaped JSON. */
|
|
38
|
+
private callAnthropic;
|
|
39
|
+
private callOpenAICompatible;
|
|
40
|
+
private httpError;
|
|
41
|
+
private formatApiError;
|
|
42
|
+
private redactApiKey;
|
|
43
|
+
/**
|
|
44
|
+
* Estimate token count and cost for a translation request
|
|
45
|
+
*/
|
|
46
|
+
static estimateCost(entries: Array<{
|
|
47
|
+
value: string;
|
|
48
|
+
}>, provider: ByokProvider): {
|
|
49
|
+
estimatedTokens: number;
|
|
50
|
+
estimatedCost: number | null;
|
|
51
|
+
currency: 'USD';
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export declare function buildSystemPrompt(req: HFTranslateRequest, entries: PromptEntry[]): string;
|
|
55
|
+
/**
|
|
56
|
+
* Pull `{translations:[{id, value}]}` out of a tool input object or a text
|
|
57
|
+
* reply (tolerating code fences and surrounding prose). Returns only entries
|
|
58
|
+
* whose id was requested and whose value is a string; nothing is invented.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseTranslations(content: unknown, requestedIds: Set<string>): Array<{
|
|
61
|
+
key: string;
|
|
62
|
+
value: string;
|
|
63
|
+
}>;
|
|
64
|
+
export {};
|