@babelize/engine 0.1.0 → 0.1.3
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/dist/analysis/analyze.js +1 -1
- 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 +5 -0
- package/dist/index.js +16 -2
- package/dist/local/pseudo-provider.js +5 -4
- package/dist/local/translate-core.d.ts +5 -2
- package/dist/local/translate-core.js +43 -5
- package/dist/local/translate-messages.js +7 -9
- package/dist/modules/extractor/extract.js +22 -14
- package/dist/modules/finalization/django-finalize.d.ts +15 -0
- package/dist/modules/finalization/django-finalize.js +156 -19
- package/dist/modules/finalization/finalize.d.ts +2 -0
- package/dist/modules/finalization/finalize.js +58 -30
- package/dist/modules/finalization/nextjs-finalize.d.ts +4 -1
- package/dist/modules/finalization/nextjs-finalize.js +8 -9
- package/dist/modules/finalization/writes.d.ts +29 -0
- package/dist/modules/finalization/writes.js +78 -0
- package/dist/modules/format-injection/index.js +3 -2
- package/dist/modules/glossary/index.d.ts +1 -2
- package/dist/modules/glossary/index.js +14 -6
- package/dist/modules/glossary/rules.d.ts +81 -0
- package/dist/modules/glossary/rules.js +244 -0
- package/dist/modules/injection/inject.js +14 -30
- package/dist/modules/keygen/keygen.js +7 -0
- package/dist/modules/locale-builder/locales.js +23 -0
- package/dist/modules/lockfile/index.d.ts +1 -1
- package/dist/modules/lockfile/index.js +2 -1
- package/dist/modules/lockfile/lockfile-manager.d.ts +9 -0
- package/dist/modules/lockfile/lockfile-manager.js +16 -1
- package/dist/modules/lockfile/schema.d.ts +42 -0
- package/dist/modules/lockfile/schema.js +11 -0
- package/dist/modules/translator/icu-branches.d.ts +138 -0
- package/dist/modules/translator/icu-branches.js +393 -0
- package/dist/modules/translator/translate.d.ts +40 -4
- package/dist/modules/translator/translate.js +297 -173
- package/dist/modules/workspace/validate.js +67 -32
- package/dist/orchestrator.d.ts +5 -0
- package/dist/orchestrator.js +125 -14
- package/dist/providers/types.d.ts +10 -2
- package/dist/qa/check-project.d.ts +2 -1
- package/dist/qa/check-project.js +50 -7
- package/dist/qa/gettext-files.d.ts +15 -0
- package/dist/qa/gettext-files.js +115 -0
- package/dist/qa/locale-qa.js +20 -3
- package/dist/runtime/config.js +1 -0
- package/dist/runtime/types.d.ts +64 -0
- package/dist/utils/extraction-options.d.ts +9 -3
- package/dist/utils/extraction-options.js +13 -3
- package/dist/utils/file-utils.js +7 -2
- package/dist/utils/icu-message.d.ts +2 -2
- package/dist/utils/icu-message.js +43 -7
- package/dist/utils/jsx-message.js +7 -0
- package/dist/utils/metadata.d.ts +4 -2
- package/dist/utils/metadata.js +16 -10
- package/dist/utils/not-injected.js +2 -0
- package/dist/utils/placeholder-protection.d.ts +18 -1
- package/dist/utils/placeholder-protection.js +58 -13
- package/dist/utils/translation-sanitizer.d.ts +1 -0
- package/dist/utils/translation-sanitizer.js +23 -9
- package/dist/utils/translator-load-balancer.js +5 -1
- package/dist/utils/untranslated.d.ts +15 -0
- package/dist/utils/untranslated.js +34 -0
- package/package.json +2 -6
- package/dist/injection/django/django-template-injector.d.ts +0 -29
- package/dist/injection/django/django-template-injector.js +0 -216
- package/dist/modules/glossary/glossary.d.ts +0 -9
- package/dist/modules/glossary/glossary.js +0 -27
- package/dist/modules/glossary/masker.d.ts +0 -23
- package/dist/modules/glossary/masker.js +0 -56
- package/dist/modules/locale-builder/po.d.ts +0 -33
- package/dist/modules/locale-builder/po.js +0 -131
- package/dist/modules/translator/translator.d.ts +0 -5
- package/dist/modules/translator/translator.js +0 -46
- package/dist/utils/dtl-mask.d.ts +0 -25
- package/dist/utils/dtl-mask.js +0 -58
- package/dist/utils/py-extractor.d.ts +0 -14
- package/dist/utils/py-extractor.js +0 -285
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Shared types for the Django (gettext) pipeline. */
|
|
2
|
+
export interface GettextMessage {
|
|
3
|
+
msgid: string;
|
|
4
|
+
msgidPlural?: string;
|
|
5
|
+
msgctxt?: string;
|
|
6
|
+
/** PO flags such as python-format / python-brace-format. */
|
|
7
|
+
flags: string[];
|
|
8
|
+
/** Translator comments (`#.` in the PO file). */
|
|
9
|
+
comments: string[];
|
|
10
|
+
}
|
|
11
|
+
/** A hardcoded string the injector can wrap. */
|
|
12
|
+
export interface PythonEdit {
|
|
13
|
+
kind: 'python';
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
/** Evaluated at import time (module/class level): needs gettext_lazy. */
|
|
17
|
+
lazy: boolean;
|
|
18
|
+
/** Argument text placed inside the gettext call. */
|
|
19
|
+
inner: string;
|
|
20
|
+
/** Text appended after the call, e.g. ` % {"count": count}`. */
|
|
21
|
+
suffix: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TemplateEdit {
|
|
24
|
+
kind: 'template';
|
|
25
|
+
start: number;
|
|
26
|
+
end: number;
|
|
27
|
+
replacement: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DjangoFinding {
|
|
30
|
+
message: GettextMessage;
|
|
31
|
+
/** 1-based line of the string in the original file. */
|
|
32
|
+
line: number;
|
|
33
|
+
/** Already marked for translation in the code (gettext call, trans tag). */
|
|
34
|
+
wrapped: boolean;
|
|
35
|
+
/** How to mark a hardcoded string (absent for wrapped strings). */
|
|
36
|
+
edit?: PythonEdit | TemplateEdit;
|
|
37
|
+
}
|
|
38
|
+
export interface DjangoUnwired {
|
|
39
|
+
line: number;
|
|
40
|
+
text: string;
|
|
41
|
+
reason: string;
|
|
42
|
+
}
|
|
43
|
+
export interface DjangoFileAnalysis {
|
|
44
|
+
findings: DjangoFinding[];
|
|
45
|
+
/** User-facing text found but deliberately not wrapped, with why. */
|
|
46
|
+
unwired: DjangoUnwired[];
|
|
47
|
+
}
|
|
48
|
+
/** PO format flags for a message, the way xgettext would mark it. */
|
|
49
|
+
export declare function formatFlags(...texts: Array<string | undefined>): string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Shared types for the Django (gettext) pipeline. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.formatFlags = formatFlags;
|
|
5
|
+
const PYTHON_FORMAT = /%(?:\(\w+\))?[sdif]/;
|
|
6
|
+
const BRACE_FORMAT = /\{(?:\w+(?:\.\w+|\[\w+\])*)?(?:![rsa])?(?::[^{}]*)?\}/;
|
|
7
|
+
/** PO format flags for a message, the way xgettext would mark it. */
|
|
8
|
+
function formatFlags(...texts) {
|
|
9
|
+
const all = texts.filter((t) => !!t).map((t) => t.replace(/%%/g, ''));
|
|
10
|
+
if (all.some((t) => PYTHON_FORMAT.test(t)))
|
|
11
|
+
return ['python-format'];
|
|
12
|
+
if (all.some((t) => BRACE_FORMAT.test(t.replace(/\{\{|\}\}/g, ''))))
|
|
13
|
+
return ['python-brace-format'];
|
|
14
|
+
return [];
|
|
15
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { BabelizeEngine } from './BabelizeEngine';
|
|
|
9
9
|
export type { BabelizeConfig, BabelizeEngineDependencies, BabelRunResult, ExtractedString, TranslationResult, TranslationUnit, } from './types';
|
|
10
10
|
export { loadBabelizeConfig } from './config';
|
|
11
11
|
export { runEngineV2, validateEngineConfigV2 } from './runtime';
|
|
12
|
+
export { RunAbortedError } from './orchestrator';
|
|
12
13
|
export type { EngineConfigV2, EngineResultV2 } from './runtime';
|
|
13
14
|
export type { KeyUsageReport, ProcessingStats } from './runtime/types';
|
|
14
15
|
export { scanKeyReferences } from './utils/key-usage';
|
|
@@ -25,6 +26,8 @@ export type { SyncDetectionResult, SyncDetectedString, SyncMissingLocaleKey } fr
|
|
|
25
26
|
export type { TranslationEntry, WorkspaceInfo } from './runtime/types';
|
|
26
27
|
export { addBabelToHistory, detectProjectInfo, getBabelizeSummary, getProjectInfo, saveCachedStrings, saveProjectConfig, } from './utils/metadata';
|
|
27
28
|
export type { BabelHistoryEntry, ExtractedStringEntry, ProjectInfo } from './utils/metadata';
|
|
29
|
+
export { detectStack, localRepoFiles, FRAMEWORK_DISPLAY_NAMES } from './detection/stack';
|
|
30
|
+
export type { DetectedStack, RepoFiles, StackFramework, PackageManagerId } from './detection/stack';
|
|
28
31
|
export { parseCode } from './utils/ast-helpers';
|
|
29
32
|
export { extractPlaceholders, hasICUSyntax, hashString, protectPlaceholders, restorePlaceholders, } from './utils/string-utils';
|
|
30
33
|
export { sanitizeTranslation, validateRawTokens } from './utils/translation-sanitizer';
|
|
@@ -43,6 +46,8 @@ export { localeExtension } from './utils/file-utils';
|
|
|
43
46
|
export { analyzeProject, analyzeWorkspace, frameworkSupport, } from './analysis/analyze';
|
|
44
47
|
export type { AnalyzeOptions, AnalyzedString, FrameworkSupport, ProjectAnalysis } from './analysis/analyze';
|
|
45
48
|
export { checkLocalePair, flattenLocaleMessages, tallyLocaleIssues } from './qa/locale-qa';
|
|
49
|
+
export { canonicalLocale, canonicalizeLocaleKeys, containsTerm, localeValue, normalizeGlossary, glossaryFingerprint, } from './modules/glossary';
|
|
50
|
+
export type { GlossaryRules, GlossaryRuleInput } from './modules/glossary';
|
|
46
51
|
export type { LocaleGlossary, LocaleIssue, LocaleIssueCode, LocaleIssueSeverity, LocalePairOptions, LocalePairReport, } from './qa/locale-qa';
|
|
47
52
|
export { DEFAULT_LOCALE_IGNORE_DIRS, findLocaleFileSets, groupLocaleFiles, isIgnoredLocalePath, isLocaleCode, loadLocale } from './qa/locale-files';
|
|
48
53
|
export type { FindLocaleOptions, LoadedLocale, LocaleFileSet, ProjectFileReader } from './qa/locale-files';
|
package/dist/index.js
CHANGED
|
@@ -21,8 +21,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
21
21
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.
|
|
25
|
-
exports.TRANSLATION_CACHE_TTL_SECONDS = exports.translationCacheKey = exports.hashText = exports.readProjectLocaleConfig = exports.localeIssueFile = exports.checkProjectLocales = exports.ReviewError = exports.reviewLocaleChanges = exports.loadLocale = exports.isLocaleCode = exports.isIgnoredLocalePath = exports.groupLocaleFiles = exports.findLocaleFileSets = exports.DEFAULT_LOCALE_IGNORE_DIRS = void 0;
|
|
24
|
+
exports.analyzeWorkspace = exports.analyzeProject = exports.localeExtension = exports.getCircuitBreaker = exports.CircuitState = exports.CircuitBreaker = exports.validateBatchQuality = exports.validateTranslationQuality = exports.translatorLoadBalancer = exports.warmupTranslator = exports.checkTranslatorHealth = exports.callTranslator = exports.requiredPluralCategories = exports.parseIcu = exports.isIcuMessage = exports.compareIcuMessages = exports.sameProtectedSpans = exports.restoreText = exports.protectText = exports.hasNothingToTranslate = exports.checkTokens = exports.validateRawTokens = exports.sanitizeTranslation = exports.restorePlaceholders = exports.protectPlaceholders = exports.hashString = exports.hasICUSyntax = exports.extractPlaceholders = exports.parseCode = exports.FRAMEWORK_DISPLAY_NAMES = exports.localRepoFiles = exports.detectStack = exports.saveProjectConfig = exports.saveCachedStrings = exports.getProjectInfo = exports.getBabelizeSummary = exports.detectProjectInfo = exports.addBabelToHistory = exports.formatSyncNotificationMessage = exports.detectSyncIssues = exports.matchFromMemory = exports.setEngineLogger = exports.runWithEngineLogger = exports.validateWorkspace = exports.scanKeyReferences = exports.RunAbortedError = exports.validateEngineConfigV2 = exports.runEngineV2 = exports.loadBabelizeConfig = exports.BabelizeEngine = void 0;
|
|
25
|
+
exports.TRANSLATION_CACHE_TTL_SECONDS = exports.translationCacheKey = exports.hashText = exports.readProjectLocaleConfig = exports.localeIssueFile = exports.checkProjectLocales = exports.ReviewError = exports.reviewLocaleChanges = exports.loadLocale = exports.isLocaleCode = exports.isIgnoredLocalePath = exports.groupLocaleFiles = exports.findLocaleFileSets = exports.DEFAULT_LOCALE_IGNORE_DIRS = exports.glossaryFingerprint = exports.normalizeGlossary = exports.localeValue = exports.containsTerm = exports.canonicalizeLocaleKeys = exports.canonicalLocale = exports.tallyLocaleIssues = exports.flattenLocaleMessages = exports.checkLocalePair = exports.frameworkSupport = void 0;
|
|
26
26
|
var BabelizeEngine_1 = require("./BabelizeEngine");
|
|
27
27
|
Object.defineProperty(exports, "BabelizeEngine", { enumerable: true, get: function () { return BabelizeEngine_1.BabelizeEngine; } });
|
|
28
28
|
var config_1 = require("./config");
|
|
@@ -31,6 +31,8 @@ Object.defineProperty(exports, "loadBabelizeConfig", { enumerable: true, get: fu
|
|
|
31
31
|
var runtime_1 = require("./runtime");
|
|
32
32
|
Object.defineProperty(exports, "runEngineV2", { enumerable: true, get: function () { return runtime_1.runEngineV2; } });
|
|
33
33
|
Object.defineProperty(exports, "validateEngineConfigV2", { enumerable: true, get: function () { return runtime_1.validateEngineConfigV2; } });
|
|
34
|
+
var orchestrator_1 = require("./orchestrator");
|
|
35
|
+
Object.defineProperty(exports, "RunAbortedError", { enumerable: true, get: function () { return orchestrator_1.RunAbortedError; } });
|
|
34
36
|
var key_usage_1 = require("./utils/key-usage");
|
|
35
37
|
Object.defineProperty(exports, "scanKeyReferences", { enumerable: true, get: function () { return key_usage_1.scanKeyReferences; } });
|
|
36
38
|
// Framework detection without running the pipeline (CLI `translate` uses it
|
|
@@ -58,6 +60,10 @@ Object.defineProperty(exports, "getBabelizeSummary", { enumerable: true, get: fu
|
|
|
58
60
|
Object.defineProperty(exports, "getProjectInfo", { enumerable: true, get: function () { return metadata_1.getProjectInfo; } });
|
|
59
61
|
Object.defineProperty(exports, "saveCachedStrings", { enumerable: true, get: function () { return metadata_1.saveCachedStrings; } });
|
|
60
62
|
Object.defineProperty(exports, "saveProjectConfig", { enumerable: true, get: function () { return metadata_1.saveProjectConfig; } });
|
|
63
|
+
var stack_1 = require("./detection/stack");
|
|
64
|
+
Object.defineProperty(exports, "detectStack", { enumerable: true, get: function () { return stack_1.detectStack; } });
|
|
65
|
+
Object.defineProperty(exports, "localRepoFiles", { enumerable: true, get: function () { return stack_1.localRepoFiles; } });
|
|
66
|
+
Object.defineProperty(exports, "FRAMEWORK_DISPLAY_NAMES", { enumerable: true, get: function () { return stack_1.FRAMEWORK_DISPLAY_NAMES; } });
|
|
61
67
|
var ast_helpers_1 = require("./utils/ast-helpers");
|
|
62
68
|
Object.defineProperty(exports, "parseCode", { enumerable: true, get: function () { return ast_helpers_1.parseCode; } });
|
|
63
69
|
var string_utils_1 = require("./utils/string-utils");
|
|
@@ -104,6 +110,14 @@ var locale_qa_1 = require("./qa/locale-qa");
|
|
|
104
110
|
Object.defineProperty(exports, "checkLocalePair", { enumerable: true, get: function () { return locale_qa_1.checkLocalePair; } });
|
|
105
111
|
Object.defineProperty(exports, "flattenLocaleMessages", { enumerable: true, get: function () { return locale_qa_1.flattenLocaleMessages; } });
|
|
106
112
|
Object.defineProperty(exports, "tallyLocaleIssues", { enumerable: true, get: function () { return locale_qa_1.tallyLocaleIssues; } });
|
|
113
|
+
// Glossary rules: term matching, locale lookup/canonicalization, conflicts.
|
|
114
|
+
var glossary_1 = require("./modules/glossary");
|
|
115
|
+
Object.defineProperty(exports, "canonicalLocale", { enumerable: true, get: function () { return glossary_1.canonicalLocale; } });
|
|
116
|
+
Object.defineProperty(exports, "canonicalizeLocaleKeys", { enumerable: true, get: function () { return glossary_1.canonicalizeLocaleKeys; } });
|
|
117
|
+
Object.defineProperty(exports, "containsTerm", { enumerable: true, get: function () { return glossary_1.containsTerm; } });
|
|
118
|
+
Object.defineProperty(exports, "localeValue", { enumerable: true, get: function () { return glossary_1.localeValue; } });
|
|
119
|
+
Object.defineProperty(exports, "normalizeGlossary", { enumerable: true, get: function () { return glossary_1.normalizeGlossary; } });
|
|
120
|
+
Object.defineProperty(exports, "glossaryFingerprint", { enumerable: true, get: function () { return glossary_1.glossaryFingerprint; } });
|
|
107
121
|
var locale_files_1 = require("./qa/locale-files");
|
|
108
122
|
Object.defineProperty(exports, "DEFAULT_LOCALE_IGNORE_DIRS", { enumerable: true, get: function () { return locale_files_1.DEFAULT_LOCALE_IGNORE_DIRS; } });
|
|
109
123
|
Object.defineProperty(exports, "findLocaleFileSets", { enumerable: true, get: function () { return locale_files_1.findLocaleFileSets; } });
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PseudoTranslationProvider = exports.PSEUDO_OPEN = exports.PSEUDO_CLOSE = exports.isPseudoLocalized = void 0;
|
|
4
4
|
const icu_message_1 = require("../utils/icu-message");
|
|
5
|
+
const rules_1 = require("../modules/glossary/rules");
|
|
5
6
|
/**
|
|
6
7
|
* Pseudo-localization: a deterministic, offline TranslationProvider for tests
|
|
7
8
|
* and for checking i18n wiring without an API key (`babelize translate
|
|
@@ -27,6 +28,9 @@ const ACCENTS = {
|
|
|
27
28
|
/** Spans that must survive untouched: engine tokens, args, tags, URLs, emails, code, identifiers. */
|
|
28
29
|
const PROTECTED = new RegExp([
|
|
29
30
|
String.raw `\[\s*#\s*VAR_\d+\s*#\s*\]`,
|
|
31
|
+
// printf / gettext placeholders and HTML entities: %(name)s, %d, %%, —
|
|
32
|
+
String.raw `%\(\w+\)[-#0 +]*\d*(?:\.\d+)?[sdifr]|%%|%[sdif]`,
|
|
33
|
+
String.raw `&(?:[a-zA-Z][a-zA-Z0-9]{1,31}|#\d{1,7}|#x[0-9a-fA-F]{1,6});`,
|
|
30
34
|
String.raw `\{[^{}]*\}`,
|
|
31
35
|
String.raw `<[^<>]+>`,
|
|
32
36
|
String.raw `https?:\/\/\S+`,
|
|
@@ -40,9 +44,6 @@ const PROTECTED = new RegExp([
|
|
|
40
44
|
function accent(text) {
|
|
41
45
|
return text.replace(/[aeiouycnAEIOUYCN]/g, (ch) => ACCENTS[ch] ?? ch);
|
|
42
46
|
}
|
|
43
|
-
function escapeRegExp(s) {
|
|
44
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
45
|
-
}
|
|
46
47
|
/** Accent everything outside protected spans; glossary terms become their required translation. */
|
|
47
48
|
function pseudoText(text, glossary = []) {
|
|
48
49
|
const terms = [...glossary].sort((a, b) => b.term.length - a.term.length);
|
|
@@ -53,7 +54,7 @@ function pseudoText(text, glossary = []) {
|
|
|
53
54
|
while (rest) {
|
|
54
55
|
let best = null;
|
|
55
56
|
for (const g of terms) {
|
|
56
|
-
const m = new RegExp(
|
|
57
|
+
const m = new RegExp((0, rules_1.termPattern)(g.term), 'iu').exec(rest);
|
|
57
58
|
if (m && (!best || m.index < best.index))
|
|
58
59
|
best = { index: m.index, length: m[0].length, translation: g.translation };
|
|
59
60
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { EngineConfigV2, EngineResultV2 } from '../runtime/types';
|
|
2
|
-
import type { TranslationFailure, TranslationProvider } from '../providers/types';
|
|
2
|
+
import type { ProjectRulesStore, TranslationFailure, TranslationProvider } from '../providers/types';
|
|
3
|
+
import type { LocaleGlossary } from '../qa/locale-qa';
|
|
4
|
+
/** A rules store serving a `.babelize/config.json` glossary (no brand voice locally). */
|
|
5
|
+
export declare function configRulesStore(glossary: LocaleGlossary): ProjectRulesStore;
|
|
3
6
|
import { type ByokProvider } from '@babelize/types';
|
|
4
7
|
import type { HostedTranslationProvider } from './hosted-provider';
|
|
5
8
|
import { type FormatResult } from './formatter';
|
|
@@ -7,7 +10,7 @@ import { type FileDiffStat, type ApplyIo, type WorkspaceChanges } from './worksp
|
|
|
7
10
|
/** Lockfile scope for local runs: `.babelize/local/babelize-lock.json` in the project root. */
|
|
8
11
|
export declare const LOCAL_PROJECT_ID = "local";
|
|
9
12
|
export declare const LOCAL_LOCKFILE = ".babelize/local/babelize-lock.json";
|
|
10
|
-
export declare const SUPPORTED_LOCAL_FRAMEWORKS: readonly ["react", "nextjs"];
|
|
13
|
+
export declare const SUPPORTED_LOCAL_FRAMEWORKS: readonly ["react", "nextjs", "django"];
|
|
11
14
|
export declare const BETA_LOCAL_FRAMEWORKS: readonly ["vue", "static"];
|
|
12
15
|
export interface LocalTranslateOptions {
|
|
13
16
|
projectDir: string;
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.LocalTranslateError = exports.BETA_LOCAL_FRAMEWORKS = exports.SUPPORTED_LOCAL_FRAMEWORKS = exports.LOCAL_LOCKFILE = exports.LOCAL_PROJECT_ID = void 0;
|
|
37
|
+
exports.configRulesStore = configRulesStore;
|
|
37
38
|
exports.parseLocales = parseLocales;
|
|
38
39
|
exports.validateLocales = validateLocales;
|
|
39
40
|
exports.sentence = sentence;
|
|
@@ -45,10 +46,23 @@ const promises_1 = require("node:fs/promises");
|
|
|
45
46
|
const path = __importStar(require("node:path"));
|
|
46
47
|
const node_util_1 = require("node:util");
|
|
47
48
|
const language_codes_1 = require("../modules/translator/language-codes");
|
|
49
|
+
const locale_names_1 = require("../django/locale-names");
|
|
48
50
|
const lockfile_1 = require("../modules/lockfile");
|
|
49
51
|
const orchestrator_1 = require("../orchestrator");
|
|
50
52
|
const logger_1 = require("../utils/logger");
|
|
51
53
|
const workspace_1 = require("../modules/workspace");
|
|
54
|
+
const check_project_1 = require("../qa/check-project");
|
|
55
|
+
/** A rules store serving a `.babelize/config.json` glossary (no brand voice locally). */
|
|
56
|
+
function configRulesStore(glossary) {
|
|
57
|
+
const rules = [
|
|
58
|
+
...(glossary.neverTranslate ?? []).map((term) => ({ term, type: 'never_translate', translations: null })),
|
|
59
|
+
...(glossary.alwaysTranslateAs ?? []).map((e) => ({ term: e.term, type: 'always_translate_as', translations: e.translations })),
|
|
60
|
+
];
|
|
61
|
+
return {
|
|
62
|
+
getGlossary: async () => rules,
|
|
63
|
+
getBrandVoice: async () => null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
52
66
|
const types_1 = require("@babelize/types");
|
|
53
67
|
const git_1 = require("./git");
|
|
54
68
|
const formatter_1 = require("./formatter");
|
|
@@ -58,7 +72,7 @@ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
|
58
72
|
/** Lockfile scope for local runs: `.babelize/local/babelize-lock.json` in the project root. */
|
|
59
73
|
exports.LOCAL_PROJECT_ID = 'local';
|
|
60
74
|
exports.LOCAL_LOCKFILE = `.babelize/${exports.LOCAL_PROJECT_ID}/babelize-lock.json`;
|
|
61
|
-
exports.SUPPORTED_LOCAL_FRAMEWORKS = ['react', 'nextjs'];
|
|
75
|
+
exports.SUPPORTED_LOCAL_FRAMEWORKS = ['react', 'nextjs', 'django'];
|
|
62
76
|
exports.BETA_LOCAL_FRAMEWORKS = ['vue', 'static'];
|
|
63
77
|
class LocalTranslateError extends Error {
|
|
64
78
|
constructor(message, code,
|
|
@@ -96,7 +110,15 @@ async function listProjectFiles(dir) {
|
|
|
96
110
|
/** Locales this project was already translated into: locale files plus lockfile entries. */
|
|
97
111
|
async function existingTargetLocales(workspaceDir, publicDir, localeFormat, sourceLocale) {
|
|
98
112
|
const found = new Set();
|
|
99
|
-
if (localeFormat
|
|
113
|
+
if (localeFormat === 'po') {
|
|
114
|
+
// Django: <locale dir>/<pt_BR>/LC_MESSAGES/django.po
|
|
115
|
+
for (const entry of await (0, promises_1.readdir)(path.join(workspaceDir, publicDir)).catch(() => [])) {
|
|
116
|
+
const po = path.join(workspaceDir, publicDir, entry, 'LC_MESSAGES', 'django.po');
|
|
117
|
+
if (await (0, promises_1.readFile)(po, 'utf8').then(() => true, () => false))
|
|
118
|
+
found.add((0, locale_names_1.localeFromDjangoName)(entry));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
100
122
|
const ext = localeFormat === 'arb' ? '.arb' : '.json';
|
|
101
123
|
for (const entry of await (0, promises_1.readdir)(path.join(workspaceDir, publicDir, 'locales')).catch(() => [])) {
|
|
102
124
|
if (entry.endsWith(ext))
|
|
@@ -286,7 +308,7 @@ async function runScoped(opts, deps, engineLog, state) {
|
|
|
286
308
|
}
|
|
287
309
|
const frameworkStatus = frameworkStatusOf(framework);
|
|
288
310
|
if (!frameworkStatus) {
|
|
289
|
-
throw new LocalTranslateError(`Detected framework "${framework}", which \`babelize translate\` does not support. Supported: React
|
|
311
|
+
throw new LocalTranslateError(`Detected framework "${framework}", which \`babelize translate\` does not support. Supported: React, Next.js and Django (Vue and static HTML in beta).`, 'unsupported_framework');
|
|
290
312
|
}
|
|
291
313
|
deps.onFramework?.(framework, frameworkStatus);
|
|
292
314
|
// The engine deletes locale files that aren't in its target list, so
|
|
@@ -299,6 +321,10 @@ async function runScoped(opts, deps, engineLog, state) {
|
|
|
299
321
|
const targetLocales = [...opts.targetLocales, ...keptLocales];
|
|
300
322
|
const before = await (0, workspace_copy_1.snapshot)(temp.dir);
|
|
301
323
|
state.collecting = true;
|
|
324
|
+
// The project's glossary from .babelize/config.json — the same file and
|
|
325
|
+
// schema `babelize check` reads — applied like a cloud project's.
|
|
326
|
+
const { glossary } = await (0, check_project_1.readProjectLocaleConfig)(projectDir);
|
|
327
|
+
const rules = glossary ? configRulesStore(glossary) : undefined;
|
|
302
328
|
const runEngine = deps.runEngine ?? orchestrator_1.runEngineV2;
|
|
303
329
|
const result = await runEngine({
|
|
304
330
|
workspacePath: temp.dir,
|
|
@@ -314,7 +340,7 @@ async function runScoped(opts, deps, engineLog, state) {
|
|
|
314
340
|
// and the updated one comes back with the other changes — only on success.
|
|
315
341
|
dataDir: temp.dir,
|
|
316
342
|
byok: translator ? undefined : opts.byok,
|
|
317
|
-
providers: translator ? { translator } : undefined,
|
|
343
|
+
providers: translator || rules ? { ...(translator ? { translator } : {}), ...(rules ? { rules } : {}) } : undefined,
|
|
318
344
|
installDependencies: false,
|
|
319
345
|
onProgress: (message, step, status) => deps.onProgress?.(message, step, status),
|
|
320
346
|
});
|
|
@@ -348,9 +374,21 @@ async function runScoped(opts, deps, engineLog, state) {
|
|
|
348
374
|
// cloud backend stages locale files for a PR.
|
|
349
375
|
const lost = [];
|
|
350
376
|
for (const locale of result.locales) {
|
|
351
|
-
if (locale.locale === opts.sourceLocale)
|
|
377
|
+
if (locale.locale === opts.sourceLocale || locale.noFile)
|
|
352
378
|
continue;
|
|
353
379
|
const rel = locale.path.split(path.sep).join('/');
|
|
380
|
+
if (locale.content !== undefined) {
|
|
381
|
+
// A merged catalogue (Django .po): written exactly as the engine
|
|
382
|
+
// produced it. The merge never drops existing translations.
|
|
383
|
+
for (const f of [{ path: rel, content: locale.content, encoding: 'utf8' }, ...(locale.extraFiles ?? [])]) {
|
|
384
|
+
const abs = path.resolve(temp.dir, f.path.split(path.sep).join('/'));
|
|
385
|
+
if (path.relative(temp.dir, abs).startsWith('..'))
|
|
386
|
+
continue;
|
|
387
|
+
await (0, promises_1.mkdir)(path.dirname(abs), { recursive: true });
|
|
388
|
+
await (0, promises_1.writeFile)(abs, f.encoding === 'base64' ? Buffer.from(f.content, 'base64') : f.content);
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
354
392
|
const previous = await readJsonIfExists(path.join(projectDir, rel));
|
|
355
393
|
const next = flattenData(locale.data);
|
|
356
394
|
const failedHere = new Set(translationFailures.filter((f) => f.locale === locale.locale).map((f) => f.key));
|
|
@@ -39,6 +39,7 @@ const node_os_1 = require("node:os");
|
|
|
39
39
|
const path = __importStar(require("node:path"));
|
|
40
40
|
const keygen_1 = require("../modules/keygen");
|
|
41
41
|
const translate_1 = require("../modules/translator/translate");
|
|
42
|
+
const glossary_1 = require("../modules/glossary");
|
|
42
43
|
const logger_1 = require("../utils/logger");
|
|
43
44
|
async function translateMessages(opts) {
|
|
44
45
|
const logger = {
|
|
@@ -54,14 +55,11 @@ async function run(opts) {
|
|
|
54
55
|
throw new Error('translateMessages needs a byok key or a translator.');
|
|
55
56
|
const dir = await (0, promises_1.mkdtemp)(path.join((0, node_os_1.tmpdir)(), 'babelize-messages-'));
|
|
56
57
|
try {
|
|
57
|
-
// The
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
customTranslations[e.term] = { translations: e.translations };
|
|
63
|
-
await (0, promises_1.mkdir)(path.join(dir, '.babelize'), { recursive: true });
|
|
64
|
-
await (0, promises_1.writeFile)(path.join(dir, '.babelize', 'config.json'), JSON.stringify({ customTranslations }));
|
|
58
|
+
// The glossary goes in as already-loaded rules, normalized like a project's.
|
|
59
|
+
const glossary = (0, glossary_1.normalizeGlossary)([
|
|
60
|
+
...(opts.glossary?.neverTranslate ?? []).map((term) => ({ term, type: 'never_translate' })),
|
|
61
|
+
...(opts.glossary?.alwaysTranslateAs ?? []).map((e) => ({ term: e.term, type: 'always_translate_as', translations: e.translations })),
|
|
62
|
+
]);
|
|
65
63
|
const workspace = {
|
|
66
64
|
path: dir,
|
|
67
65
|
framework: 'react',
|
|
@@ -72,7 +70,7 @@ async function run(opts) {
|
|
|
72
70
|
localeFormat: 'json',
|
|
73
71
|
};
|
|
74
72
|
const result = await (0, translate_1.translateLocales)(workspace, { locale: opts.sourceLocale, path: '', data: { ...opts.messages }, isNew: false }, opts.targetLocales, false, true, // dry run: nothing written
|
|
75
|
-
undefined, opts.onProgress, undefined, undefined, opts.translator ? undefined : opts.byok, undefined, undefined, opts.translator ? { translator: opts.translator } : {}, { targetPlurals: opts.targetPlurals ?? true });
|
|
73
|
+
undefined, opts.onProgress, undefined, undefined, opts.translator ? undefined : opts.byok, undefined, undefined, opts.translator ? { translator: opts.translator } : {}, { targetPlurals: opts.targetPlurals ?? true, rules: { glossary, brandVoice: null, complete: true } });
|
|
76
74
|
const targets = {};
|
|
77
75
|
for (const file of result.translation.locales) {
|
|
78
76
|
const flat = (0, keygen_1.flattenLocaleData)(file.data);
|
|
@@ -42,8 +42,8 @@ const ast_helpers_1 = require("../../utils/ast-helpers");
|
|
|
42
42
|
const file_utils_1 = require("../../utils/file-utils");
|
|
43
43
|
const string_utils_1 = require("../../utils/string-utils");
|
|
44
44
|
const detect_format_1 = require("../../utils/detect-format");
|
|
45
|
-
const
|
|
46
|
-
const
|
|
45
|
+
const extract_1 = require("../../django/extract");
|
|
46
|
+
const types_2 = require("@babelize/types");
|
|
47
47
|
const jsx_message_1 = require("../../utils/jsx-message");
|
|
48
48
|
const ui_copy_1 = require("../../utils/ui-copy");
|
|
49
49
|
const module_constants_1 = require("../../utils/module-constants");
|
|
@@ -64,10 +64,14 @@ async function extractStrings(workspace, filesToProcess, extractOptions = {}) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
else {
|
|
67
|
-
// Otherwise scan all source directories
|
|
67
|
+
// Otherwise scan all source directories. Django scans .py/.html and
|
|
68
|
+
// skips virtualenvs, migrations, static files and the like.
|
|
69
|
+
const fwConfig = workspace.framework === 'django' ? types_2.FRAMEWORK_CONFIGS.django : undefined;
|
|
70
|
+
const extensions = fwConfig ? [...fwConfig.extensions] : types_1.SUPPORTED_EXTENSIONS;
|
|
71
|
+
const excluded = fwConfig ? [...types_1.EXCLUDED_PATTERNS, ...(fwConfig.excludedPatterns ?? [])] : types_1.EXCLUDED_PATTERNS;
|
|
68
72
|
for (const sourceDir of workspace.sourceDirs) {
|
|
69
73
|
const dirPath = path.join(workspace.path, sourceDir);
|
|
70
|
-
const files = await (0, file_utils_1.findFiles)(dirPath,
|
|
74
|
+
const files = await (0, file_utils_1.findFiles)(dirPath, extensions, excluded);
|
|
71
75
|
sourceFiles.push(...files);
|
|
72
76
|
}
|
|
73
77
|
}
|
|
@@ -104,17 +108,21 @@ async function extractStrings(workspace, filesToProcess, extractOptions = {}) {
|
|
|
104
108
|
const extraction = { strings, filesAnalyzed, skippedFiles, warnings, notInjected };
|
|
105
109
|
return { extraction };
|
|
106
110
|
}
|
|
111
|
+
const GENERATED_FILE_MARKERS = ['@babelize-generated', 'babelize:i18n-config'];
|
|
112
|
+
/** True for files Babelize wrote itself; the marker sits in the header comment. */
|
|
113
|
+
function isBabelizeGenerated(content) {
|
|
114
|
+
const head = content.slice(0, 500);
|
|
115
|
+
return GENERATED_FILE_MARKERS.some((marker) => head.includes(marker));
|
|
116
|
+
}
|
|
107
117
|
async function extractStringsFromFile(content, filePath, detectFormats, options) {
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (filePath.endsWith('.py')) {
|
|
117
|
-
return (0, py_extractor_1.extractStringsFromPython)(content, filePath);
|
|
118
|
+
// Files Babelize generated itself (i18n config, next-intl request config)
|
|
119
|
+
// hold config values such as bindI18n: 'languageChanged loaded'. A re-run
|
|
120
|
+
// used to extract those and inject t() into the i18n config, breaking it.
|
|
121
|
+
if (isBabelizeGenerated(content))
|
|
122
|
+
return { strings: [], warnings: [] };
|
|
123
|
+
// Django: gettext calls, translate tags and hardcoded text in templates/forms/models/views.
|
|
124
|
+
if (options?.framework === 'django') {
|
|
125
|
+
return (0, extract_1.extractDjangoFile)(content, filePath, { icu: options.plurals === true });
|
|
118
126
|
}
|
|
119
127
|
if (filePath.endsWith('.html') || filePath.endsWith('.htm')) {
|
|
120
128
|
return (0, html_extractor_1.extractStringsFromHtml)(content, filePath, detectFormats);
|
|
@@ -4,10 +4,25 @@ export interface DjangoFinalizeResult {
|
|
|
4
4
|
/** Human-readable description of each edit applied. */
|
|
5
5
|
changes: string[];
|
|
6
6
|
warnings: string[];
|
|
7
|
+
/** Settings file content before and after, when it changed. */
|
|
8
|
+
original?: string;
|
|
9
|
+
updated?: string;
|
|
7
10
|
}
|
|
8
11
|
export declare function resolveSettingsPath(workspacePath: string): Promise<string | null>;
|
|
12
|
+
/**
|
|
13
|
+
* The locale directory Django reads (LOCALE_PATHS[0]), relative to the
|
|
14
|
+
* workspace, '/'-separated. Falls back to <BASE_DIR>/locale — what finalize
|
|
15
|
+
* adds — when LOCALE_PATHS is absent or not a plain BASE_DIR path.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveDjangoLocaleDir(workspacePath: string, djangoRoot: string): Promise<string>;
|
|
18
|
+
/** First LOCALE_PATHS entry as a path relative to BASE_DIR, if it is one. */
|
|
19
|
+
export declare function parseLocalePaths(source: string): string | null;
|
|
9
20
|
export declare function finalizeDjangoSettings(workspacePath: string, options?: {
|
|
10
21
|
targetLocales?: string[];
|
|
11
22
|
sourceLocale?: string;
|
|
12
23
|
dryRun?: boolean;
|
|
24
|
+
/** Django project root (BASE_DIR), relative to the workspace. */
|
|
25
|
+
djangoRoot?: string;
|
|
26
|
+
/** Locale directory the catalogues were written to, relative to the workspace. */
|
|
27
|
+
localeDir?: string;
|
|
13
28
|
}): Promise<DjangoFinalizeResult>;
|