@babelize/engine 0.0.0-stage → 0.1.0
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/index.d.ts +54 -0
- package/dist/index.js +126 -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/django/django-template-injector.d.ts +29 -0
- package/dist/injection/django/django-template-injector.js +216 -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 +207 -0
- package/dist/local/report.d.ts +177 -0
- package/dist/local/report.js +186 -0
- package/dist/local/translate-core.d.ts +215 -0
- package/dist/local/translate-core.js +461 -0
- package/dist/local/translate-messages.d.ts +49 -0
- package/dist/local/translate-messages.js +91 -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 +701 -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 +13 -0
- package/dist/modules/finalization/django-finalize.js +275 -0
- package/dist/modules/finalization/finalize.d.ts +36 -0
- package/dist/modules/finalization/finalize.js +717 -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 +37 -0
- package/dist/modules/finalization/nextjs-finalize.js +329 -0
- package/dist/modules/finalization/static-runtime.d.ts +9 -0
- package/dist/modules/finalization/static-runtime.js +261 -0
- package/dist/modules/format-injection/index.d.ts +19 -0
- package/dist/modules/format-injection/index.js +272 -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/glossary.d.ts +9 -0
- package/dist/modules/glossary/glossary.js +27 -0
- package/dist/modules/glossary/index.d.ts +3 -0
- package/dist/modules/glossary/index.js +8 -0
- package/dist/modules/glossary/masker.d.ts +23 -0
- package/dist/modules/glossary/masker.js +56 -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 +1066 -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 +116 -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 +107 -0
- package/dist/modules/locale-builder/po.d.ts +33 -0
- package/dist/modules/locale-builder/po.js +131 -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 +21 -0
- package/dist/modules/lockfile/lockfile-manager.d.ts +90 -0
- package/dist/modules/lockfile/lockfile-manager.js +482 -0
- package/dist/modules/lockfile/schema.d.ts +154 -0
- package/dist/modules/lockfile/schema.js +45 -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/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 +38 -0
- package/dist/modules/translator/translate.js +932 -0
- package/dist/modules/translator/translator.d.ts +5 -0
- package/dist/modules/translator/translator.js +46 -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 +298 -0
- package/dist/orchestrator.d.ts +8 -0
- package/dist/orchestrator.js +616 -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 +145 -0
- package/dist/providers/types.js +2 -0
- package/dist/qa/check-project.d.ts +49 -0
- package/dist/qa/check-project.js +150 -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 +325 -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 +36 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/types.d.ts +363 -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/dtl-mask.d.ts +25 -0
- package/dist/utils/dtl-mask.js +58 -0
- package/dist/utils/extraction-options.d.ts +23 -0
- package/dist/utils/extraction-options.js +23 -0
- package/dist/utils/file-utils.d.ts +33 -0
- package/dist/utils/file-utils.js +262 -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 +621 -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 +80 -0
- package/dist/utils/metadata.js +255 -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 +36 -0
- package/dist/utils/placeholder-protection.d.ts +57 -0
- package/dist/utils/placeholder-protection.js +136 -0
- package/dist/utils/pseudo-markers.d.ts +9 -0
- package/dist/utils/pseudo-markers.js +16 -0
- package/dist/utils/py-extractor.d.ts +14 -0
- package/dist/utils/py-extractor.js +285 -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 +10 -0
- package/dist/utils/translation-sanitizer.js +57 -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 +293 -0
- package/dist/utils/ui-copy.d.ts +32 -0
- package/dist/utils/ui-copy.js +126 -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,63 @@
|
|
|
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.extract = extract;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const file_utils_1 = require("../../utils/file-utils");
|
|
39
|
+
const types_1 = require("../../runtime/types");
|
|
40
|
+
const extract_1 = require("./extract");
|
|
41
|
+
async function extract(config) {
|
|
42
|
+
// For v2 bootstrapping we reuse existing phase1 extractor logic to guarantee
|
|
43
|
+
// identical behavior, while we later refactor adapters + parser in place.
|
|
44
|
+
const workspacePath = config.workspacePath;
|
|
45
|
+
const sourceDir = config.sourceDir ?? 'apps/frontend/app';
|
|
46
|
+
const dirPath = path.join(workspacePath, sourceDir);
|
|
47
|
+
const files = await (0, file_utils_1.findFiles)(dirPath, types_1.SUPPORTED_EXTENSIONS, types_1.EXCLUDED_PATTERNS);
|
|
48
|
+
const all = [];
|
|
49
|
+
for (const filePath of files) {
|
|
50
|
+
const rel = (0, file_utils_1.getRelativePath)(workspacePath, filePath);
|
|
51
|
+
const content = await (0, file_utils_1.readFile)(filePath);
|
|
52
|
+
const { strings } = await (0, extract_1.extractStringsFromFile)(content, rel);
|
|
53
|
+
all.push(...strings.map((s) => ({
|
|
54
|
+
id: s.id,
|
|
55
|
+
text: s.text,
|
|
56
|
+
filePath: s.file,
|
|
57
|
+
line: s.line,
|
|
58
|
+
column: s.column,
|
|
59
|
+
context: s.context,
|
|
60
|
+
})));
|
|
61
|
+
}
|
|
62
|
+
return all;
|
|
63
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DjangoFinalizeResult {
|
|
2
|
+
/** Absolute path to the settings file, or null when none could be resolved. */
|
|
3
|
+
settingsPath: string | null;
|
|
4
|
+
/** Human-readable description of each edit applied. */
|
|
5
|
+
changes: string[];
|
|
6
|
+
warnings: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function resolveSettingsPath(workspacePath: string): Promise<string | null>;
|
|
9
|
+
export declare function finalizeDjangoSettings(workspacePath: string, options?: {
|
|
10
|
+
targetLocales?: string[];
|
|
11
|
+
sourceLocale?: string;
|
|
12
|
+
dryRun?: boolean;
|
|
13
|
+
}): Promise<DjangoFinalizeResult>;
|
|
@@ -0,0 +1,275 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.resolveSettingsPath = resolveSettingsPath;
|
|
40
|
+
exports.finalizeDjangoSettings = finalizeDjangoSettings;
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
43
|
+
const file_utils_1 = require("../../utils/file-utils");
|
|
44
|
+
const logger_1 = require("../../utils/logger");
|
|
45
|
+
/**
|
|
46
|
+
* Django settings.py auto-configuration.
|
|
47
|
+
*
|
|
48
|
+
* Translating a Django project produces .po files under locale/<lang>/LC_MESSAGES/,
|
|
49
|
+
* but Django will not load them unless settings.py opts in: USE_I18N, LocaleMiddleware
|
|
50
|
+
* in the right position, LOCALE_PATHS pointing at the locale directory, and LANGUAGES
|
|
51
|
+
* listing what was translated. Without this the run "succeeds" and the site stays in
|
|
52
|
+
* English, which looks like a translation bug rather than a missing setting.
|
|
53
|
+
*
|
|
54
|
+
* settings.py is Python, and we have no Python parser here, so every edit is
|
|
55
|
+
* deliberately conservative:
|
|
56
|
+
*
|
|
57
|
+
* - Nothing is ever reordered or rewritten. We append settings that are absent and
|
|
58
|
+
* insert exactly one middleware line.
|
|
59
|
+
* - A setting the user has already written is left alone, even when its value differs
|
|
60
|
+
* from what we would have chosen. Their file, their call.
|
|
61
|
+
* - Anything we cannot do safely becomes a warning rather than a guess.
|
|
62
|
+
*
|
|
63
|
+
* Re-running is a no-op: every step checks for what it is about to add.
|
|
64
|
+
*/
|
|
65
|
+
const LOCALE_MIDDLEWARE = 'django.middleware.locale.LocaleMiddleware';
|
|
66
|
+
const SESSION_MIDDLEWARE = 'django.contrib.sessions.middleware.SessionMiddleware';
|
|
67
|
+
const COMMON_MIDDLEWARE = 'django.middleware.common.CommonMiddleware';
|
|
68
|
+
const MANAGED_BLOCK_HEADER = '# --- Added by Babelize ---';
|
|
69
|
+
/** Is this settings file using pathlib (Django >= 3.1) or os.path? */
|
|
70
|
+
function usesPathlibBaseDir(source) {
|
|
71
|
+
return /BASE_DIR\s*=\s*Path\s*\(/.test(source);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Does this setting already exist as a top-level assignment?
|
|
75
|
+
*
|
|
76
|
+
* Anchored to the start of a line so a mention inside a comment, a string, or a
|
|
77
|
+
* nested dict does not count as a definition.
|
|
78
|
+
*/
|
|
79
|
+
function hasSetting(source, name) {
|
|
80
|
+
return new RegExp(`^${name}\\s*=`, 'm').test(source);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Find settings.py by reading DJANGO_SETTINGS_MODULE out of manage.py.
|
|
84
|
+
*
|
|
85
|
+
* That is the project's own declaration of which settings module is real, which
|
|
86
|
+
* matters when a repo carries several (settings/base.py, settings/prod.py, or a
|
|
87
|
+
* second vendored Django app).
|
|
88
|
+
*/
|
|
89
|
+
async function resolveViaManagePy(workspacePath) {
|
|
90
|
+
const managePaths = await (0, fast_glob_1.default)('**/manage.py', {
|
|
91
|
+
cwd: workspacePath,
|
|
92
|
+
onlyFiles: true,
|
|
93
|
+
deep: 3,
|
|
94
|
+
ignore: ['**/node_modules/**', '**/.venv/**', '**/venv/**', '**/site-packages/**'],
|
|
95
|
+
});
|
|
96
|
+
for (const rel of managePaths) {
|
|
97
|
+
const source = await (0, file_utils_1.readFile)(path.join(workspacePath, rel)).catch(() => '');
|
|
98
|
+
// os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
|
|
99
|
+
const match = source.match(/DJANGO_SETTINGS_MODULE['"]\s*,\s*['"]([\w.]+)['"]/);
|
|
100
|
+
if (!match)
|
|
101
|
+
continue;
|
|
102
|
+
const moduleParts = match[1].split('.');
|
|
103
|
+
const manageDir = path.dirname(path.join(workspacePath, rel));
|
|
104
|
+
// A settings module is either a file (pkg/settings.py) or a package
|
|
105
|
+
// (pkg/settings/__init__.py). Try both, relative to manage.py.
|
|
106
|
+
const candidates = [
|
|
107
|
+
path.join(manageDir, ...moduleParts) + '.py',
|
|
108
|
+
path.join(manageDir, ...moduleParts, '__init__.py'),
|
|
109
|
+
];
|
|
110
|
+
for (const candidate of candidates) {
|
|
111
|
+
if (await (0, file_utils_1.pathExists)(candidate))
|
|
112
|
+
return candidate;
|
|
113
|
+
}
|
|
114
|
+
logger_1.engineLogger.warn('DJANGO_SETTINGS_MODULE did not resolve to a file', {
|
|
115
|
+
module: match[1],
|
|
116
|
+
managePath: rel,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
/** Fall back to the shallowest settings.py in the tree. */
|
|
122
|
+
async function resolveByGlob(workspacePath) {
|
|
123
|
+
const found = await (0, fast_glob_1.default)('**/settings.py', {
|
|
124
|
+
cwd: workspacePath,
|
|
125
|
+
onlyFiles: true,
|
|
126
|
+
deep: 4,
|
|
127
|
+
ignore: ['**/node_modules/**', '**/.venv/**', '**/venv/**', '**/site-packages/**'],
|
|
128
|
+
});
|
|
129
|
+
if (found.length === 0)
|
|
130
|
+
return null;
|
|
131
|
+
// Shallowest wins: a vendored or example settings.py is usually deeper than
|
|
132
|
+
// the project's own.
|
|
133
|
+
const shallowest = found.sort((a, b) => a.split('/').length - b.split('/').length || a.localeCompare(b))[0];
|
|
134
|
+
return path.join(workspacePath, shallowest);
|
|
135
|
+
}
|
|
136
|
+
async function resolveSettingsPath(workspacePath) {
|
|
137
|
+
return (await resolveViaManagePy(workspacePath)) ?? (await resolveByGlob(workspacePath));
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Insert LocaleMiddleware into the MIDDLEWARE list.
|
|
141
|
+
*
|
|
142
|
+
* Django requires it after SessionMiddleware (it reads the session for the active
|
|
143
|
+
* language) and before CommonMiddleware (which performs locale-aware URL redirects).
|
|
144
|
+
* Getting the order wrong fails quietly — language selection simply stops working.
|
|
145
|
+
*/
|
|
146
|
+
function insertLocaleMiddleware(source) {
|
|
147
|
+
if (source.includes(LOCALE_MIDDLEWARE)) {
|
|
148
|
+
return { source };
|
|
149
|
+
}
|
|
150
|
+
const middlewareMatch = source.match(/^MIDDLEWARE\s*=\s*\[/m);
|
|
151
|
+
if (!middlewareMatch) {
|
|
152
|
+
return {
|
|
153
|
+
source,
|
|
154
|
+
warning: 'Could not find a MIDDLEWARE list in settings.py — add ' +
|
|
155
|
+
`'${LOCALE_MIDDLEWARE}' after SessionMiddleware and before CommonMiddleware yourself.`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const listStart = middlewareMatch.index + middlewareMatch[0].length;
|
|
159
|
+
const listEnd = source.indexOf(']', listStart);
|
|
160
|
+
if (listEnd === -1) {
|
|
161
|
+
return { source, warning: 'MIDDLEWARE list in settings.py is not closed — left unchanged.' };
|
|
162
|
+
}
|
|
163
|
+
const list = source.slice(listStart, listEnd);
|
|
164
|
+
const lines = list.split('\n');
|
|
165
|
+
// Match the indentation and quote style already in the file.
|
|
166
|
+
const entryLine = lines.find((l) => l.trim().startsWith("'") || l.trim().startsWith('"'));
|
|
167
|
+
const indent = entryLine?.match(/^(\s*)/)?.[1] ?? ' ';
|
|
168
|
+
const quote = entryLine?.trim().startsWith('"') ? '"' : "'";
|
|
169
|
+
const newEntry = `${indent}${quote}${LOCALE_MIDDLEWARE}${quote},`;
|
|
170
|
+
const sessionIdx = lines.findIndex((l) => l.includes(SESSION_MIDDLEWARE));
|
|
171
|
+
const commonIdx = lines.findIndex((l) => l.includes(COMMON_MIDDLEWARE));
|
|
172
|
+
let insertAt;
|
|
173
|
+
if (sessionIdx !== -1) {
|
|
174
|
+
insertAt = sessionIdx + 1;
|
|
175
|
+
}
|
|
176
|
+
else if (commonIdx !== -1) {
|
|
177
|
+
insertAt = commonIdx;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Neither neighbour present. Append rather than guess a position.
|
|
181
|
+
insertAt = lines.length - 1;
|
|
182
|
+
}
|
|
183
|
+
// Appending after Session must still land before Common.
|
|
184
|
+
if (commonIdx !== -1 && insertAt > commonIdx) {
|
|
185
|
+
insertAt = commonIdx;
|
|
186
|
+
}
|
|
187
|
+
lines.splice(insertAt, 0, newEntry);
|
|
188
|
+
const updated = source.slice(0, listStart) + lines.join('\n') + source.slice(listEnd);
|
|
189
|
+
const position = sessionIdx !== -1 ? 'after SessionMiddleware' : commonIdx !== -1 ? 'before CommonMiddleware' : 'at the end of MIDDLEWARE';
|
|
190
|
+
return { source: updated, change: `Added LocaleMiddleware ${position}` };
|
|
191
|
+
}
|
|
192
|
+
async function finalizeDjangoSettings(workspacePath, options = {}) {
|
|
193
|
+
const { targetLocales = [], sourceLocale = 'en', dryRun = false } = options;
|
|
194
|
+
const changes = [];
|
|
195
|
+
const warnings = [];
|
|
196
|
+
const settingsPath = await resolveSettingsPath(workspacePath);
|
|
197
|
+
if (!settingsPath) {
|
|
198
|
+
warnings.push('Could not find settings.py — configure USE_I18N, LocaleMiddleware, LOCALE_PATHS and LANGUAGES manually.');
|
|
199
|
+
return { settingsPath: null, changes, warnings };
|
|
200
|
+
}
|
|
201
|
+
const original = await (0, file_utils_1.readFile)(settingsPath).catch(() => null);
|
|
202
|
+
if (original === null) {
|
|
203
|
+
warnings.push(`Found ${path.basename(settingsPath)} but could not read it — left unchanged.`);
|
|
204
|
+
return { settingsPath, changes, warnings };
|
|
205
|
+
}
|
|
206
|
+
let source = original;
|
|
207
|
+
// ── USE_I18N ────────────────────────────────────────────────
|
|
208
|
+
// Only flip an explicit False. Anything else the user wrote stays.
|
|
209
|
+
if (hasSetting(source, 'USE_I18N')) {
|
|
210
|
+
const falseMatch = source.match(/^USE_I18N\s*=\s*False\s*$/m);
|
|
211
|
+
if (falseMatch) {
|
|
212
|
+
source = source.replace(/^USE_I18N\s*=\s*False\s*$/m, 'USE_I18N = True');
|
|
213
|
+
changes.push('Set USE_I18N = True (was False)');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
source = appendSetting(source, 'USE_I18N = True');
|
|
218
|
+
changes.push('Added USE_I18N = True');
|
|
219
|
+
}
|
|
220
|
+
// ── LocaleMiddleware ────────────────────────────────────────
|
|
221
|
+
const middlewareResult = insertLocaleMiddleware(source);
|
|
222
|
+
source = middlewareResult.source;
|
|
223
|
+
if (middlewareResult.change)
|
|
224
|
+
changes.push(middlewareResult.change);
|
|
225
|
+
if (middlewareResult.warning)
|
|
226
|
+
warnings.push(middlewareResult.warning);
|
|
227
|
+
// ── LOCALE_PATHS ────────────────────────────────────────────
|
|
228
|
+
// Must match where the locale builder writes: <workspace>/locale/<lang>/LC_MESSAGES/
|
|
229
|
+
if (!hasSetting(source, 'LOCALE_PATHS')) {
|
|
230
|
+
const value = usesPathlibBaseDir(source)
|
|
231
|
+
? "LOCALE_PATHS = [BASE_DIR / 'locale']"
|
|
232
|
+
: "LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]";
|
|
233
|
+
source = appendSetting(source, value);
|
|
234
|
+
changes.push('Added LOCALE_PATHS');
|
|
235
|
+
}
|
|
236
|
+
// ── LANGUAGE_CODE ───────────────────────────────────────────
|
|
237
|
+
// Only added when missing; an existing value is a deliberate choice.
|
|
238
|
+
if (!hasSetting(source, 'LANGUAGE_CODE')) {
|
|
239
|
+
source = appendSetting(source, `LANGUAGE_CODE = '${sourceLocale}'`);
|
|
240
|
+
changes.push(`Added LANGUAGE_CODE = '${sourceLocale}'`);
|
|
241
|
+
}
|
|
242
|
+
// ── LANGUAGES ───────────────────────────────────────────────
|
|
243
|
+
if (!hasSetting(source, 'LANGUAGES') && targetLocales.length > 0) {
|
|
244
|
+
const all = [sourceLocale, ...targetLocales].filter((l, i, arr) => l && arr.indexOf(l) === i);
|
|
245
|
+
const entries = all.map((l) => ` ('${l}', '${l}'),`).join('\n');
|
|
246
|
+
source = appendSetting(source, `LANGUAGES = [\n${entries}\n]`);
|
|
247
|
+
changes.push(`Added LANGUAGES (${all.length} locales)`);
|
|
248
|
+
}
|
|
249
|
+
if (changes.length === 0) {
|
|
250
|
+
logger_1.engineLogger.info('Django settings already configured for i18n', { settingsPath });
|
|
251
|
+
return { settingsPath, changes, warnings };
|
|
252
|
+
}
|
|
253
|
+
if (!dryRun) {
|
|
254
|
+
await (0, file_utils_1.writeFile)(settingsPath, source);
|
|
255
|
+
}
|
|
256
|
+
logger_1.engineLogger.info('Django settings finalized', {
|
|
257
|
+
settingsPath,
|
|
258
|
+
changes,
|
|
259
|
+
dryRun,
|
|
260
|
+
});
|
|
261
|
+
return { settingsPath, changes, warnings };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Append a setting under a single Babelize block at the end of the file.
|
|
265
|
+
*
|
|
266
|
+
* Appending keeps every existing line exactly where the author put it — the
|
|
267
|
+
* alternative, inserting near related settings, means moving code around a file
|
|
268
|
+
* we cannot parse.
|
|
269
|
+
*/
|
|
270
|
+
function appendSetting(source, statement) {
|
|
271
|
+
const withBlock = source.includes(MANAGED_BLOCK_HEADER)
|
|
272
|
+
? source
|
|
273
|
+
: `${source.trimEnd()}\n\n${MANAGED_BLOCK_HEADER}\n`;
|
|
274
|
+
return `${withBlock.trimEnd()}\n${statement}\n`;
|
|
275
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { WorkspaceInfo } from '../../runtime/types';
|
|
2
|
+
interface PackageJson {
|
|
3
|
+
dependencies?: Record<string, string>;
|
|
4
|
+
devDependencies?: Record<string, string>;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function finalizeProject(workspace: WorkspaceInfo, dryRun: boolean, options?: {
|
|
8
|
+
targetLocales?: string[];
|
|
9
|
+
sourceLocale?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Run the package manager after adding missing i18n dependencies.
|
|
12
|
+
* Default true. Local runs (CLI on a temp copy) turn it off: installing
|
|
13
|
+
* into a throwaway copy is slow, runs install scripts, and the user's own
|
|
14
|
+
* install is what actually matters.
|
|
15
|
+
*/
|
|
16
|
+
installDependencies?: boolean;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
finalization: {
|
|
19
|
+
dependenciesInvalid: boolean;
|
|
20
|
+
configFileCreated: boolean;
|
|
21
|
+
entryPointModified: boolean;
|
|
22
|
+
packagesInstalled: boolean;
|
|
23
|
+
warnings: string[];
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* The i18n runtime the engine wires a project to, and which of its packages
|
|
28
|
+
* package.json doesn't list yet. Same choice as finalizeProject: Next.js gets
|
|
29
|
+
* next-intl unless it already uses react-i18next.
|
|
30
|
+
*/
|
|
31
|
+
export declare function i18nRuntimeFor(framework: string, pkg: PackageJson | null | undefined): {
|
|
32
|
+
library: string;
|
|
33
|
+
required: string[];
|
|
34
|
+
missing: string[];
|
|
35
|
+
};
|
|
36
|
+
export {};
|