@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,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Circuit Breaker Pattern
|
|
4
|
+
*
|
|
5
|
+
* Prevents cascading failures by stopping requests to failing services
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.CircuitBreaker = exports.CircuitState = void 0;
|
|
9
|
+
exports.getCircuitBreaker = getCircuitBreaker;
|
|
10
|
+
var CircuitState;
|
|
11
|
+
(function (CircuitState) {
|
|
12
|
+
CircuitState["CLOSED"] = "closed";
|
|
13
|
+
CircuitState["OPEN"] = "open";
|
|
14
|
+
CircuitState["HALF_OPEN"] = "half-open";
|
|
15
|
+
})(CircuitState || (exports.CircuitState = CircuitState = {}));
|
|
16
|
+
class CircuitBreaker {
|
|
17
|
+
constructor(name, options = {
|
|
18
|
+
failureThreshold: 5,
|
|
19
|
+
resetTimeout: 60000, // 1 minute
|
|
20
|
+
halfOpenMaxCalls: 3,
|
|
21
|
+
}) {
|
|
22
|
+
this.name = name;
|
|
23
|
+
this.options = options;
|
|
24
|
+
this.state = CircuitState.CLOSED;
|
|
25
|
+
this.failureCount = 0;
|
|
26
|
+
this.lastFailureTime = 0;
|
|
27
|
+
this.halfOpenCalls = 0;
|
|
28
|
+
this.successCount = 0;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Execute a function with circuit breaker protection
|
|
32
|
+
*/
|
|
33
|
+
async execute(fn) {
|
|
34
|
+
if (this.state === CircuitState.OPEN) {
|
|
35
|
+
if (Date.now() - this.lastFailureTime > this.options.resetTimeout) {
|
|
36
|
+
this.state = CircuitState.HALF_OPEN;
|
|
37
|
+
this.halfOpenCalls = 0;
|
|
38
|
+
this.successCount = 0;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
throw new Error(`Circuit breaker ${this.name} is OPEN`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
45
|
+
if (this.halfOpenCalls >= this.options.halfOpenMaxCalls) {
|
|
46
|
+
// Too many calls in half-open, open the circuit
|
|
47
|
+
this.state = CircuitState.OPEN;
|
|
48
|
+
this.lastFailureTime = Date.now();
|
|
49
|
+
throw new Error(`Circuit breaker ${this.name} is OPEN`);
|
|
50
|
+
}
|
|
51
|
+
this.halfOpenCalls++;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const result = await fn();
|
|
55
|
+
this.onSuccess();
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
this.onFailure();
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
onSuccess() {
|
|
64
|
+
this.failureCount = 0;
|
|
65
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
66
|
+
this.successCount++;
|
|
67
|
+
if (this.successCount >= this.options.halfOpenMaxCalls) {
|
|
68
|
+
// Service recovered
|
|
69
|
+
this.state = CircuitState.CLOSED;
|
|
70
|
+
this.halfOpenCalls = 0;
|
|
71
|
+
this.successCount = 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
onFailure() {
|
|
76
|
+
this.failureCount++;
|
|
77
|
+
this.lastFailureTime = Date.now();
|
|
78
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
79
|
+
// Failed in half-open, open the circuit
|
|
80
|
+
this.state = CircuitState.OPEN;
|
|
81
|
+
this.halfOpenCalls = 0;
|
|
82
|
+
this.successCount = 0;
|
|
83
|
+
}
|
|
84
|
+
else if (this.failureCount >= this.options.failureThreshold) {
|
|
85
|
+
// Too many failures, open the circuit
|
|
86
|
+
this.state = CircuitState.OPEN;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
getState() {
|
|
90
|
+
return this.state;
|
|
91
|
+
}
|
|
92
|
+
getStats() {
|
|
93
|
+
return {
|
|
94
|
+
name: this.name,
|
|
95
|
+
state: this.state,
|
|
96
|
+
failureCount: this.failureCount,
|
|
97
|
+
lastFailureTime: this.lastFailureTime,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
reset() {
|
|
101
|
+
this.state = CircuitState.CLOSED;
|
|
102
|
+
this.failureCount = 0;
|
|
103
|
+
this.lastFailureTime = 0;
|
|
104
|
+
this.halfOpenCalls = 0;
|
|
105
|
+
this.successCount = 0;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.CircuitBreaker = CircuitBreaker;
|
|
109
|
+
// Global circuit breakers
|
|
110
|
+
const circuitBreakers = new Map();
|
|
111
|
+
/**
|
|
112
|
+
* Get or create a circuit breaker
|
|
113
|
+
*/
|
|
114
|
+
function getCircuitBreaker(name, options) {
|
|
115
|
+
if (!circuitBreakers.has(name)) {
|
|
116
|
+
circuitBreakers.set(name, new CircuitBreaker(name, options));
|
|
117
|
+
}
|
|
118
|
+
return circuitBreakers.get(name);
|
|
119
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.concurrentMap = concurrentMap;
|
|
4
|
+
async function concurrentMap(items, fn, concurrency) {
|
|
5
|
+
const results = new Array(items.length);
|
|
6
|
+
const errors = [];
|
|
7
|
+
let nextIndex = 0;
|
|
8
|
+
const worker = async () => {
|
|
9
|
+
while (nextIndex < items.length) {
|
|
10
|
+
const i = nextIndex++;
|
|
11
|
+
try {
|
|
12
|
+
results[i] = await fn(items[i], i);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
|
|
20
|
+
return { results, errors };
|
|
21
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DetectedFormat } from '../runtime/types';
|
|
2
|
+
export declare function extractCurrency(raw: string): DetectedFormat | null;
|
|
3
|
+
export declare function extractDate(raw: string): DetectedFormat | null;
|
|
4
|
+
export declare function extractNumber(raw: string): DetectedFormat | null;
|
|
5
|
+
export declare function detectFormat(text: string): DetectedFormat | null;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractCurrency = extractCurrency;
|
|
4
|
+
exports.extractDate = extractDate;
|
|
5
|
+
exports.extractNumber = extractNumber;
|
|
6
|
+
exports.detectFormat = detectFormat;
|
|
7
|
+
// ─────────────────────────────────────────────────────────────
|
|
8
|
+
// Currency Detection
|
|
9
|
+
// ─────────────────────────────────────────────────────────────
|
|
10
|
+
const CURRENCY_CODES = ['USD', 'EUR', 'GBP', 'JPY', 'CNY', 'INR', 'KRW', 'RUB', 'TRY', 'VND', 'UAH', 'NGN', 'PHP', 'GHS', 'ARS', 'BRL', 'MXN', 'CAD', 'AUD', 'CHF', 'SEK', 'NOK', 'DKK', 'NZD', 'SGD', 'HKD', 'TWD', 'THB', 'MYR', 'IDR'];
|
|
11
|
+
const CURRENCY_PATTERNS = [
|
|
12
|
+
/^[€$£¥₹₩₽₺\u20B9\u20A8]?\s?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d{2})?\s?[€$£¥₹₩₽₺A-Z]*$/,
|
|
13
|
+
/^[A-Z]{3}\s?\d{1,3}(?:[.,]\d{3})*(?:[.,]\d{2})?$/,
|
|
14
|
+
/^\d{1,3}(?:[.,]\d{3})*(?:[.,]\d{2})?\s?[€$£¥€]$/,
|
|
15
|
+
];
|
|
16
|
+
function extractCurrency(raw) {
|
|
17
|
+
const trimmed = raw.trim();
|
|
18
|
+
if (!CURRENCY_PATTERNS.some((p) => p.test(trimmed)))
|
|
19
|
+
return null;
|
|
20
|
+
const match = trimmed.match(/[\d,.\s]+(?:\.[0-9]{2})?/);
|
|
21
|
+
if (!match)
|
|
22
|
+
return null;
|
|
23
|
+
const numericPart = match[0].replace(/[^\d,.]/g, '').trim();
|
|
24
|
+
if (!numericPart)
|
|
25
|
+
return null;
|
|
26
|
+
const symbolMatch = trimmed.match(/[€$£¥₹₩₽₺\u20B9\u20A8]/);
|
|
27
|
+
const codeMatch = trimmed.match(/\b([A-Z]{3})\b/);
|
|
28
|
+
let detectedCurrency;
|
|
29
|
+
if (symbolMatch) {
|
|
30
|
+
const symbolToCode = {
|
|
31
|
+
'$': 'USD', '€': 'EUR', '£': 'GBP', '¥': 'JPY',
|
|
32
|
+
'₹': 'INR', '₩': 'KRW', '₽': 'RUB', '₺': 'TRY',
|
|
33
|
+
'₫': 'VND', '₴': 'UAH', '₦': 'NGN', '₱': 'PHP',
|
|
34
|
+
'₵': 'GHS', '₿': 'BTC',
|
|
35
|
+
};
|
|
36
|
+
detectedCurrency = symbolToCode[symbolMatch[0]] || symbolMatch[0];
|
|
37
|
+
}
|
|
38
|
+
else if (codeMatch && CURRENCY_CODES.includes(codeMatch[1])) {
|
|
39
|
+
detectedCurrency = codeMatch[1];
|
|
40
|
+
}
|
|
41
|
+
const rawNumber = numericPart.replace(/,/g, '');
|
|
42
|
+
const parsed = parseFloat(rawNumber);
|
|
43
|
+
if (isNaN(parsed))
|
|
44
|
+
return null;
|
|
45
|
+
return {
|
|
46
|
+
class: 'currency',
|
|
47
|
+
rawValue: numericPart,
|
|
48
|
+
originalString: trimmed,
|
|
49
|
+
confidence: 0.85,
|
|
50
|
+
detectedCurrency,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// ─────────────────────────────────────────────────────────────
|
|
54
|
+
// Date Detection
|
|
55
|
+
// ─────────────────────────────────────────────────────────────
|
|
56
|
+
const DATE_PATTERNS = [
|
|
57
|
+
{ regex: /^(0[1-9]|1[012])[-/](0[1-9]|[12]\d|3[01])[-/](\d{4})$/, options: { month: '2-digit', day: '2-digit', year: 'numeric' }, source: 'MM/DD/YYYY' },
|
|
58
|
+
{ regex: /^(0[1-9]|[12]\d|3[01])[-/](0[1-9]|1[012])[-/](\d{4})$/, options: { day: '2-digit', month: '2-digit', year: 'numeric' }, source: 'DD/MM/YYYY' },
|
|
59
|
+
{ regex: /^(\d{4})[-/](0[1-9]|1[012])[-/](0[1-9]|[12]\d|3[01])$/, options: { year: 'numeric', month: '2-digit', day: '2-digit' }, source: 'YYYY-MM-DD' },
|
|
60
|
+
{ regex: /^(0[1-9]|[12]\d|3[01])\.(0[1-9]|1[012])\.(\d{4})$/, options: { day: '2-digit', month: '2-digit', year: 'numeric' }, source: 'DD.MM.YYYY' },
|
|
61
|
+
{ regex: /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+(0[1-9]|[12]\d|3[01]),\s+(\d{4})$/, options: { month: 'short', day: '2-digit', year: 'numeric' }, source: 'MMM DD, YYYY' },
|
|
62
|
+
{ regex: /^(0[1-9]|[12]\d|3[01])\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+(\d{4})$/, options: { day: '2-digit', month: 'short', year: 'numeric' }, source: 'DD MMM YYYY' },
|
|
63
|
+
];
|
|
64
|
+
function extractDate(raw) {
|
|
65
|
+
const trimmed = raw.trim();
|
|
66
|
+
for (const { regex, options, source } of DATE_PATTERNS) {
|
|
67
|
+
const match = trimmed.match(regex);
|
|
68
|
+
if (match) {
|
|
69
|
+
return {
|
|
70
|
+
class: 'date',
|
|
71
|
+
rawValue: trimmed,
|
|
72
|
+
originalString: trimmed,
|
|
73
|
+
confidence: 0.9,
|
|
74
|
+
dateOptions: options,
|
|
75
|
+
dateSource: source,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
// ─────────────────────────────────────────────────────────────
|
|
82
|
+
// Number Detection
|
|
83
|
+
// ─────────────────────────────────────────────────────────────
|
|
84
|
+
const NUMBER_PATTERNS = [
|
|
85
|
+
/^-?\d{1,3}(?:,\d{3})+(?:\.\d+)?$/,
|
|
86
|
+
/^-?\d{1,3}(?:\.\d{3})+(?:,\d+)?$/,
|
|
87
|
+
/^-?\d+(?:\.\d+)?$/,
|
|
88
|
+
];
|
|
89
|
+
function extractNumber(raw) {
|
|
90
|
+
const trimmed = raw.trim();
|
|
91
|
+
if (!NUMBER_PATTERNS.some((p) => p.test(trimmed)))
|
|
92
|
+
return null;
|
|
93
|
+
const hasDotDecimal = /\.\d+$/.test(trimmed);
|
|
94
|
+
const hasCommaDecimal = /,\d+$/.test(trimmed);
|
|
95
|
+
const numberOptions = {};
|
|
96
|
+
if (hasDotDecimal || hasCommaDecimal) {
|
|
97
|
+
numberOptions.minimumFractionDigits = 0;
|
|
98
|
+
numberOptions.maximumFractionDigits = 10;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
class: 'number',
|
|
102
|
+
rawValue: trimmed,
|
|
103
|
+
originalString: trimmed,
|
|
104
|
+
confidence: 0.75,
|
|
105
|
+
numberOptions: Object.keys(numberOptions).length > 0 ? numberOptions : undefined,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// ─────────────────────────────────────────────────────────────
|
|
109
|
+
// Dispatch
|
|
110
|
+
// ─────────────────────────────────────────────────────────────
|
|
111
|
+
function detectFormat(text) {
|
|
112
|
+
if (!text || text.length < 2)
|
|
113
|
+
return null;
|
|
114
|
+
const trimmed = text.trim();
|
|
115
|
+
const dateResult = extractDate(trimmed);
|
|
116
|
+
if (dateResult)
|
|
117
|
+
return dateResult;
|
|
118
|
+
const currencyResult = extractCurrency(trimmed);
|
|
119
|
+
if (currencyResult)
|
|
120
|
+
return currencyResult;
|
|
121
|
+
const numberResult = extractNumber(trimmed);
|
|
122
|
+
if (numberResult)
|
|
123
|
+
return numberResult;
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { WorkspaceInfo } from '../runtime/types';
|
|
2
|
+
/**
|
|
3
|
+
* How a run treats ICU, resolved once from the project and the translator
|
|
4
|
+
* and shared by extraction, translation and the sync detector, so they agree.
|
|
5
|
+
*/
|
|
6
|
+
export interface ResolvedIcuOptions {
|
|
7
|
+
/** Group JSX sentences into one message (needs ICU placeholders at runtime for react-i18next). */
|
|
8
|
+
sentences: boolean;
|
|
9
|
+
/** Generate ICU plurals in source sentences ("{count, plural, …}"): ICU runtime + LLM translator. */
|
|
10
|
+
plurals: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The project's runtime formats ICU messages: next-intl, or react-i18next
|
|
13
|
+
* with i18next-icu. Not vue-i18n, the vanilla runtime or static HTML.
|
|
14
|
+
*/
|
|
15
|
+
icuRuntime: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* A translation may add an ICU plural over a count (`{files} files`): ICU
|
|
18
|
+
* runtime. An LLM writes the plural; for NLLB it is built branch by branch.
|
|
19
|
+
*/
|
|
20
|
+
targetPlurals: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @param llm the run's translator is an LLM (BYOK or an injected provider).
|
|
24
|
+
* Only then do JSX sentences get ICU plurals in the source (that
|
|
25
|
+
* changes the source messages, and the sync check must agree).
|
|
26
|
+
* Translating ICU works with every translator: NLLB gets it
|
|
27
|
+
* branch by branch.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveIcuOptions(workspace: WorkspaceInfo, llm: boolean): Promise<ResolvedIcuOptions>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveIcuOptions = resolveIcuOptions;
|
|
4
|
+
const StrategyFactory_1 = require("../injection/StrategyFactory");
|
|
5
|
+
const i18next_icu_1 = require("./i18next-icu");
|
|
6
|
+
/**
|
|
7
|
+
* @param llm the run's translator is an LLM (BYOK or an injected provider).
|
|
8
|
+
* Only then do JSX sentences get ICU plurals in the source (that
|
|
9
|
+
* changes the source messages, and the sync check must agree).
|
|
10
|
+
* Translating ICU works with every translator: NLLB gets it
|
|
11
|
+
* branch by branch.
|
|
12
|
+
*/
|
|
13
|
+
async function resolveIcuOptions(workspace, llm) {
|
|
14
|
+
if (workspace.framework === 'django') {
|
|
15
|
+
// gettext has its own plural forms (msgstr[n]). A plural message is
|
|
16
|
+
// translated as one ICU plural (an LLM whole, NLLB branch by branch with
|
|
17
|
+
// a number of each category) and mapped onto them. The keys are internal:
|
|
18
|
+
// the project's source never gains ICU.
|
|
19
|
+
return { sentences: false, plurals: true, icuRuntime: false, targetPlurals: false };
|
|
20
|
+
}
|
|
21
|
+
const deps = { ...workspace.packageJson?.dependencies, ...workspace.packageJson?.devDependencies };
|
|
22
|
+
const strategy = ['react', 'nextjs'].includes(workspace.framework) ? StrategyFactory_1.StrategyFactory.getStrategy(workspace.framework, deps).id : 'other';
|
|
23
|
+
let icuRuntime = false;
|
|
24
|
+
let sentences = true;
|
|
25
|
+
if (strategy === 'next-intl') {
|
|
26
|
+
icuRuntime = true;
|
|
27
|
+
}
|
|
28
|
+
else if (strategy === 'react-i18next') {
|
|
29
|
+
icuRuntime = (await (0, i18next_icu_1.detectI18nextIcu)(workspace)).available;
|
|
30
|
+
sentences = icuRuntime;
|
|
31
|
+
}
|
|
32
|
+
return { sentences, plurals: icuRuntime && llm, icuRuntime, targetPlurals: icuRuntime };
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare function clearFindFilesCache(): void;
|
|
2
|
+
export declare function readFile(filePath: string): Promise<string>;
|
|
3
|
+
export declare function writeFile(filePath: string, content: string, allowedBaseDir?: string): Promise<void>;
|
|
4
|
+
export declare function pathExists(filePath: string): Promise<boolean>;
|
|
5
|
+
export declare function isDirectory(filePath: string): Promise<boolean>;
|
|
6
|
+
export declare function findFiles(dir: string, extensions: string[], excludePatterns?: string[]): Promise<string[]>;
|
|
7
|
+
export declare function getRelativePath(workspacePath: string, filePath: string): string;
|
|
8
|
+
export declare function readJsonFile<T = any>(filePath: string): Promise<T | null>;
|
|
9
|
+
export declare function writeJsonFile(filePath: string, data: any, sortKeys?: boolean): Promise<void>;
|
|
10
|
+
export declare function localeExtension(format: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Compute the locale directory and file path for a given target locale.
|
|
13
|
+
* Django uses `locale/<lang>/LC_MESSAGES/django.po`,
|
|
14
|
+
* other frameworks use `<publicDir>/locales/<lang>.json`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function localeFilePath(workspacePath: string, publicDir: string, localeFormat: string, targetLocale: string): {
|
|
17
|
+
dir: string;
|
|
18
|
+
file: string;
|
|
19
|
+
relativeFile: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function ensureDir(dirPath: string): Promise<void>;
|
|
22
|
+
export declare function copyFile(src: string, dest: string): Promise<void>;
|
|
23
|
+
export declare function deleteFile(filePath: string): Promise<void>;
|
|
24
|
+
export declare function readDir(dirPath: string): Promise<string[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Process items with bounded concurrency.
|
|
27
|
+
* Unlike Promise.all, this limits the number of concurrent operations.
|
|
28
|
+
* Errors are collected and returned alongside results.
|
|
29
|
+
*/
|
|
30
|
+
export declare function concurrentMap<T, R>(items: T[], fn: (item: T, index: number) => Promise<R>, concurrency: number): Promise<{
|
|
31
|
+
results: R[];
|
|
32
|
+
errors: Error[];
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,267 @@
|
|
|
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.clearFindFilesCache = clearFindFilesCache;
|
|
37
|
+
exports.readFile = readFile;
|
|
38
|
+
exports.writeFile = writeFile;
|
|
39
|
+
exports.pathExists = pathExists;
|
|
40
|
+
exports.isDirectory = isDirectory;
|
|
41
|
+
exports.findFiles = findFiles;
|
|
42
|
+
exports.getRelativePath = getRelativePath;
|
|
43
|
+
exports.readJsonFile = readJsonFile;
|
|
44
|
+
exports.writeJsonFile = writeJsonFile;
|
|
45
|
+
exports.localeExtension = localeExtension;
|
|
46
|
+
exports.localeFilePath = localeFilePath;
|
|
47
|
+
exports.ensureDir = ensureDir;
|
|
48
|
+
exports.copyFile = copyFile;
|
|
49
|
+
exports.deleteFile = deleteFile;
|
|
50
|
+
exports.readDir = readDir;
|
|
51
|
+
exports.concurrentMap = concurrentMap;
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const string_utils_1 = require("./string-utils");
|
|
55
|
+
const locale_names_1 = require("../django/locale-names");
|
|
56
|
+
/**
|
|
57
|
+
* Engine v2 file utils.
|
|
58
|
+
*
|
|
59
|
+
* This is a direct carry-over of the v1 utilities to preserve behavior.
|
|
60
|
+
*/
|
|
61
|
+
const findFilesCache = new Map();
|
|
62
|
+
function clearFindFilesCache() {
|
|
63
|
+
findFilesCache.clear();
|
|
64
|
+
}
|
|
65
|
+
function findFilesCacheKey(dir, extensions, excludePatterns) {
|
|
66
|
+
return `${dir}::${extensions.sort().join(',')}::${excludePatterns.sort().join(',')}`;
|
|
67
|
+
}
|
|
68
|
+
async function readFile(filePath) {
|
|
69
|
+
return fs.promises.readFile(filePath, 'utf-8');
|
|
70
|
+
}
|
|
71
|
+
async function writeFile(filePath, content, allowedBaseDir) {
|
|
72
|
+
const resolved = path.resolve(filePath);
|
|
73
|
+
if (allowedBaseDir && !resolved.startsWith(path.resolve(allowedBaseDir))) {
|
|
74
|
+
throw new Error(`Path traversal detected: ${filePath} is outside ${allowedBaseDir}`);
|
|
75
|
+
}
|
|
76
|
+
const dir = path.dirname(resolved);
|
|
77
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
78
|
+
// Atomic write: write to tmp, then rename to prevent partial/corrupt files on crash
|
|
79
|
+
const tmpPath = resolved + '.tmp';
|
|
80
|
+
await fs.promises.writeFile(tmpPath, content, 'utf-8'); // codeql[js/http-to-file-access] — engine writes translation output to workspace; callers authenticate + authorize
|
|
81
|
+
await fs.promises.rename(tmpPath, resolved);
|
|
82
|
+
}
|
|
83
|
+
async function pathExists(filePath) {
|
|
84
|
+
try {
|
|
85
|
+
await fs.promises.access(filePath);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function isDirectory(filePath) {
|
|
93
|
+
try {
|
|
94
|
+
const stats = await fs.promises.stat(filePath);
|
|
95
|
+
return stats.isDirectory();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function findFiles(dir, extensions, excludePatterns = []) {
|
|
102
|
+
const key = findFilesCacheKey(dir, extensions, excludePatterns);
|
|
103
|
+
const cached = findFilesCache.get(key);
|
|
104
|
+
if (cached)
|
|
105
|
+
return cached;
|
|
106
|
+
const promise = findFilesUncached(dir, extensions, excludePatterns);
|
|
107
|
+
findFilesCache.set(key, promise);
|
|
108
|
+
return promise;
|
|
109
|
+
}
|
|
110
|
+
/** Exclusion names that usually mean generated output (but not inside SOURCE_TREE_DIRS). */
|
|
111
|
+
const OUTPUT_DIR_NAMES = new Set(['build', 'dist', 'coverage', 'lib', 'libs', 'vendor']);
|
|
112
|
+
/** Directories whose subdirectories are routes or components, never build output. */
|
|
113
|
+
const SOURCE_TREE_DIRS = new Set(['app', 'pages', 'components']);
|
|
114
|
+
function isInsideSourceTree(walkRoot, relativePath) {
|
|
115
|
+
const ancestors = [path.basename(walkRoot), ...relativePath.split(path.sep).slice(0, -1)];
|
|
116
|
+
return ancestors.some((segment) => SOURCE_TREE_DIRS.has(segment));
|
|
117
|
+
}
|
|
118
|
+
async function findFilesUncached(dir, extensions, excludePatterns = []) {
|
|
119
|
+
const results = [];
|
|
120
|
+
async function walk(currentDir) {
|
|
121
|
+
const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
124
|
+
const relativePath = path.relative(dir, fullPath);
|
|
125
|
+
const shouldExclude = excludePatterns.some((pattern) => {
|
|
126
|
+
if (pattern.includes('*')) {
|
|
127
|
+
const escaped = (0, string_utils_1.escapeRegex)(pattern).replace(/\*/g, '.*');
|
|
128
|
+
const regex = new RegExp(escaped);
|
|
129
|
+
return regex.test(entry.name) || regex.test(relativePath);
|
|
130
|
+
}
|
|
131
|
+
// Whole path segments only: a substring match excluded files like
|
|
132
|
+
// `components/Calibrate.tsx` ("lib") or `app/distance/page.tsx` ("dist").
|
|
133
|
+
if (pattern.includes('/')) {
|
|
134
|
+
const rel = relativePath.split(path.sep).join('/');
|
|
135
|
+
return rel === pattern || rel.startsWith(`${pattern}/`);
|
|
136
|
+
}
|
|
137
|
+
if (entry.name !== pattern)
|
|
138
|
+
return false;
|
|
139
|
+
// Build-output names are real source inside routing/component trees
|
|
140
|
+
// (a Next.js route `app/build/page.tsx`); output never lives there.
|
|
141
|
+
if (OUTPUT_DIR_NAMES.has(pattern) && isInsideSourceTree(dir, relativePath))
|
|
142
|
+
return false;
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
if (shouldExclude)
|
|
146
|
+
continue;
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
await walk(fullPath);
|
|
149
|
+
}
|
|
150
|
+
else if (entry.isFile()) {
|
|
151
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
152
|
+
if (extensions.includes(ext)) {
|
|
153
|
+
results.push(fullPath);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
await walk(dir);
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
function getRelativePath(workspacePath, filePath) {
|
|
162
|
+
return path.relative(workspacePath, filePath).replace(/\\/g, '/');
|
|
163
|
+
}
|
|
164
|
+
async function readJsonFile(filePath) {
|
|
165
|
+
try {
|
|
166
|
+
const content = await readFile(filePath);
|
|
167
|
+
return JSON.parse(content);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async function writeJsonFile(filePath, data, sortKeys = true) {
|
|
174
|
+
let content;
|
|
175
|
+
if (sortKeys && typeof data === 'object' && data !== null) {
|
|
176
|
+
content = JSON.stringify(sortObjectKeys(data), null, 2);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
content = JSON.stringify(data, null, 2);
|
|
180
|
+
}
|
|
181
|
+
await writeFile(filePath, content + '\n');
|
|
182
|
+
}
|
|
183
|
+
function sortObjectKeys(obj) {
|
|
184
|
+
if (Array.isArray(obj)) {
|
|
185
|
+
return obj.map(sortObjectKeys);
|
|
186
|
+
}
|
|
187
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
188
|
+
const sorted = {};
|
|
189
|
+
const keys = Object.keys(obj).sort();
|
|
190
|
+
for (const key of keys) {
|
|
191
|
+
sorted[key] = sortObjectKeys(obj[key]);
|
|
192
|
+
}
|
|
193
|
+
return sorted;
|
|
194
|
+
}
|
|
195
|
+
return obj;
|
|
196
|
+
}
|
|
197
|
+
function localeExtension(format) {
|
|
198
|
+
return format === 'po' ? '.po' : '.json';
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Compute the locale directory and file path for a given target locale.
|
|
202
|
+
* Django uses `locale/<lang>/LC_MESSAGES/django.po`,
|
|
203
|
+
* other frameworks use `<publicDir>/locales/<lang>.json`.
|
|
204
|
+
*/
|
|
205
|
+
function localeFilePath(workspacePath, publicDir, localeFormat, targetLocale) {
|
|
206
|
+
if (localeFormat === 'po') {
|
|
207
|
+
// publicDir is the locale directory (LOCALE_PATHS) for gettext projects;
|
|
208
|
+
// Django names locale directories pt_BR, zh_Hans, …
|
|
209
|
+
const localeDir = publicDir && publicDir !== 'public' ? publicDir : 'locale';
|
|
210
|
+
const name = (0, locale_names_1.djangoLocaleName)(targetLocale);
|
|
211
|
+
const dir = path.join(workspacePath, localeDir, name, 'LC_MESSAGES');
|
|
212
|
+
const file = path.join(dir, 'django.po');
|
|
213
|
+
const relativeFile = path.join(localeDir, name, 'LC_MESSAGES', 'django.po');
|
|
214
|
+
return { dir, file, relativeFile };
|
|
215
|
+
}
|
|
216
|
+
const ext = localeExtension(localeFormat);
|
|
217
|
+
const dir = path.join(workspacePath, publicDir, 'locales');
|
|
218
|
+
const file = path.join(dir, `${targetLocale}${ext}`);
|
|
219
|
+
const relativeFile = path.join(publicDir, 'locales', `${targetLocale}${ext}`);
|
|
220
|
+
return { dir, file, relativeFile };
|
|
221
|
+
}
|
|
222
|
+
async function ensureDir(dirPath) {
|
|
223
|
+
await fs.promises.mkdir(dirPath, { recursive: true });
|
|
224
|
+
}
|
|
225
|
+
async function copyFile(src, dest) {
|
|
226
|
+
await ensureDir(path.dirname(dest));
|
|
227
|
+
await fs.promises.copyFile(src, dest);
|
|
228
|
+
}
|
|
229
|
+
async function deleteFile(filePath) {
|
|
230
|
+
try {
|
|
231
|
+
await fs.promises.unlink(filePath);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// ignore
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function readDir(dirPath) {
|
|
238
|
+
try {
|
|
239
|
+
return await fs.promises.readdir(dirPath);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Process items with bounded concurrency.
|
|
247
|
+
* Unlike Promise.all, this limits the number of concurrent operations.
|
|
248
|
+
* Errors are collected and returned alongside results.
|
|
249
|
+
*/
|
|
250
|
+
async function concurrentMap(items, fn, concurrency) {
|
|
251
|
+
const results = new Array(items.length);
|
|
252
|
+
const errors = [];
|
|
253
|
+
let nextIndex = 0;
|
|
254
|
+
const worker = async () => {
|
|
255
|
+
while (nextIndex < items.length) {
|
|
256
|
+
const i = nextIndex++;
|
|
257
|
+
try {
|
|
258
|
+
results[i] = await fn(items[i], i);
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
|
|
266
|
+
return { results, errors };
|
|
267
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Shared by the translate phase and locale QA so both judge glossary use the same way. */
|
|
2
|
+
/**
|
|
3
|
+
* Whether the required glossary translation appears in the output, allowing
|
|
4
|
+
* for inflection: each of its words must match a word in the output on a
|
|
5
|
+
* shared stem (Polish "obszar roboczy" → "obszarem roboczym", German
|
|
6
|
+
* "Arbeitsbereich" → "Arbeitsbereichs"). Scripts without spaces fall back to a
|
|
7
|
+
* substring check.
|
|
8
|
+
*/
|
|
9
|
+
export declare function containsGlossaryTranslation(translated: string, required: string): boolean;
|