@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 @@
|
|
|
1
|
+
export { finalizeProject } from './finalize';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.finalizeProject = void 0;
|
|
4
|
+
var finalize_1 = require("./finalize");
|
|
5
|
+
Object.defineProperty(exports, "finalizeProject", { enumerable: true, get: function () { return finalize_1.finalizeProject; } });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { WorkspaceInfo } from '../../runtime/types';
|
|
2
|
+
/**
|
|
3
|
+
* Next.js finalization for next-intl.
|
|
4
|
+
*
|
|
5
|
+
* Injection rewrites Next.js components to next-intl (`getTranslations` in
|
|
6
|
+
* Server Components, `useTranslations` in Client Components — see
|
|
7
|
+
* NextIntlStrategy), so the project must be wired for next-intl or every
|
|
8
|
+
* page throws at runtime. This sets up next-intl's "without i18n routing"
|
|
9
|
+
* mode: the locale comes from a NEXT_LOCALE cookie or the browser's
|
|
10
|
+
* Accept-Language header, and messages are the generated
|
|
11
|
+
* `public/locales/<locale>.json` files. URLs don't change.
|
|
12
|
+
*
|
|
13
|
+
* Every edit is a minimal text splice (the user's formatting is kept), and
|
|
14
|
+
* anything that can't be wired automatically becomes a warning — never a
|
|
15
|
+
* silently half-configured project.
|
|
16
|
+
*/
|
|
17
|
+
export declare const NEXT_INTL_DEPENDENCIES: Record<string, string>;
|
|
18
|
+
export interface NextIntlFinalizeResult {
|
|
19
|
+
configFileCreated: boolean;
|
|
20
|
+
entryPointModified: boolean;
|
|
21
|
+
warnings: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare function buildRequestConfig(opts: {
|
|
24
|
+
sourceLocale: string;
|
|
25
|
+
locales: string[];
|
|
26
|
+
/** Import path from the request config file to a locale file, with `__LOCALE__` as placeholder. */
|
|
27
|
+
messagesImportPath: string;
|
|
28
|
+
typescript: boolean;
|
|
29
|
+
}): string;
|
|
30
|
+
/** Wrap the next.config default export / module.exports with next-intl's plugin. */
|
|
31
|
+
export declare function wrapNextConfig(content: string, requestConfigPath: string, typescript: boolean): string | null;
|
|
32
|
+
/** Wrap `{children}` inside the root layout's <body> with <NextIntlClientProvider>. */
|
|
33
|
+
export declare function wrapRootLayout(content: string, typescript: boolean): string | null;
|
|
34
|
+
export declare function finalizeNextIntl(workspace: WorkspaceInfo, dryRun: boolean, options: {
|
|
35
|
+
sourceLocale?: string;
|
|
36
|
+
targetLocales?: string[];
|
|
37
|
+
}): Promise<NextIntlFinalizeResult>;
|
|
@@ -0,0 +1,329 @@
|
|
|
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.NEXT_INTL_DEPENDENCIES = void 0;
|
|
37
|
+
exports.buildRequestConfig = buildRequestConfig;
|
|
38
|
+
exports.wrapNextConfig = wrapNextConfig;
|
|
39
|
+
exports.wrapRootLayout = wrapRootLayout;
|
|
40
|
+
exports.finalizeNextIntl = finalizeNextIntl;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const file_utils_1 = require("../../utils/file-utils");
|
|
44
|
+
const ast_helpers_1 = require("../../utils/ast-helpers");
|
|
45
|
+
/**
|
|
46
|
+
* Next.js finalization for next-intl.
|
|
47
|
+
*
|
|
48
|
+
* Injection rewrites Next.js components to next-intl (`getTranslations` in
|
|
49
|
+
* Server Components, `useTranslations` in Client Components — see
|
|
50
|
+
* NextIntlStrategy), so the project must be wired for next-intl or every
|
|
51
|
+
* page throws at runtime. This sets up next-intl's "without i18n routing"
|
|
52
|
+
* mode: the locale comes from a NEXT_LOCALE cookie or the browser's
|
|
53
|
+
* Accept-Language header, and messages are the generated
|
|
54
|
+
* `public/locales/<locale>.json` files. URLs don't change.
|
|
55
|
+
*
|
|
56
|
+
* Every edit is a minimal text splice (the user's formatting is kept), and
|
|
57
|
+
* anything that can't be wired automatically becomes a warning — never a
|
|
58
|
+
* silently half-configured project.
|
|
59
|
+
*/
|
|
60
|
+
exports.NEXT_INTL_DEPENDENCIES = {
|
|
61
|
+
'next-intl': '^4.0.0',
|
|
62
|
+
};
|
|
63
|
+
const GENERATED_MARKER = '@babelize-generated next-intl request config';
|
|
64
|
+
const NEXT_CONFIG_FILES = ['next.config.ts', 'next.config.mjs', 'next.config.js', 'next.config.cjs'];
|
|
65
|
+
const LAYOUT_FILES = ['layout.tsx', 'layout.jsx', 'layout.js', 'layout.ts'];
|
|
66
|
+
function applySplices(content, splices) {
|
|
67
|
+
let out = content;
|
|
68
|
+
for (const s of [...splices].sort((a, b) => b.at - a.at)) {
|
|
69
|
+
out = out.slice(0, s.at) + s.insert + out.slice(s.at + (s.remove ?? 0));
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** Offset right after the last top-level import (or after leading directives), plus a newline prefix if needed. */
|
|
74
|
+
function importInsertionPoint(ast) {
|
|
75
|
+
let at = 0;
|
|
76
|
+
for (const node of ast.program.body) {
|
|
77
|
+
if (ast_helpers_1.t.isImportDeclaration(node) && node.end != null)
|
|
78
|
+
at = node.end;
|
|
79
|
+
}
|
|
80
|
+
if (at === 0 && ast.program.directives.length > 0) {
|
|
81
|
+
at = ast.program.directives[ast.program.directives.length - 1].end ?? 0;
|
|
82
|
+
}
|
|
83
|
+
return at;
|
|
84
|
+
}
|
|
85
|
+
function insertTopLevel(ast, lines) {
|
|
86
|
+
const at = importInsertionPoint(ast);
|
|
87
|
+
return at === 0 ? { at: 0, insert: `${lines}\n` } : { at, insert: `\n${lines}` };
|
|
88
|
+
}
|
|
89
|
+
function toPosix(p) {
|
|
90
|
+
return p.split(path.sep).join('/');
|
|
91
|
+
}
|
|
92
|
+
async function findFirst(dir, names) {
|
|
93
|
+
for (const name of names) {
|
|
94
|
+
const candidate = path.join(dir, name);
|
|
95
|
+
if (await (0, file_utils_1.pathExists)(candidate))
|
|
96
|
+
return candidate;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function buildRequestConfig(opts) {
|
|
101
|
+
const locales = [opts.sourceLocale, ...opts.locales.filter((l) => l !== opts.sourceLocale)];
|
|
102
|
+
const ts = opts.typescript;
|
|
103
|
+
const importPath = opts.messagesImportPath.replace('__LOCALE__', '${locale}');
|
|
104
|
+
return `// ${GENERATED_MARKER}
|
|
105
|
+
// Babelize regenerates this file (to update the locale list) while the line
|
|
106
|
+
// above is present. Remove that line to take it over.
|
|
107
|
+
import { getRequestConfig } from 'next-intl/server';
|
|
108
|
+
import { cookies, headers } from 'next/headers';
|
|
109
|
+
|
|
110
|
+
export const sourceLocale = ${JSON.stringify(opts.sourceLocale)};
|
|
111
|
+
export const locales = ${JSON.stringify(locales)};
|
|
112
|
+
|
|
113
|
+
function pickLocale(candidates${ts ? ': Array<string | undefined>' : ''})${ts ? ': string' : ''} {
|
|
114
|
+
for (const candidate of candidates) {
|
|
115
|
+
if (!candidate) continue;
|
|
116
|
+
const wanted = candidate.toLowerCase();
|
|
117
|
+
const exact = locales.find((l) => l.toLowerCase() === wanted);
|
|
118
|
+
if (exact) return exact;
|
|
119
|
+
const base = locales.find((l) => l.split('-')[0].toLowerCase() === wanted.split('-')[0]);
|
|
120
|
+
if (base) return base;
|
|
121
|
+
}
|
|
122
|
+
return sourceLocale;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default getRequestConfig(async () => {
|
|
126
|
+
// Locale preference: the NEXT_LOCALE cookie, then the browser's Accept-Language.
|
|
127
|
+
const cookieLocale = (await cookies()).get('NEXT_LOCALE')?.value;
|
|
128
|
+
const acceptLanguage = ((await headers()).get('accept-language') ?? '')
|
|
129
|
+
.split(',')
|
|
130
|
+
.map((part) => part.split(';')[0].trim());
|
|
131
|
+
const locale = pickLocale([cookieLocale, ...acceptLanguage]);
|
|
132
|
+
return {
|
|
133
|
+
locale,
|
|
134
|
+
messages: (await import(\`${importPath}\`)).default,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
/** Wrap the next.config default export / module.exports with next-intl's plugin. */
|
|
140
|
+
function wrapNextConfig(content, requestConfigPath, typescript) {
|
|
141
|
+
if (content.includes('next-intl/plugin'))
|
|
142
|
+
return content;
|
|
143
|
+
let ast;
|
|
144
|
+
try {
|
|
145
|
+
ast = (0, ast_helpers_1.parseCode)(content, { isTypeScript: typescript, isJSX: false });
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const pluginArg = JSON.stringify(requestConfigPath);
|
|
151
|
+
for (const node of ast.program.body) {
|
|
152
|
+
if (ast_helpers_1.t.isExportDefaultDeclaration(node) &&
|
|
153
|
+
ast_helpers_1.t.isExpression(node.declaration) &&
|
|
154
|
+
node.declaration.start != null &&
|
|
155
|
+
node.declaration.end != null) {
|
|
156
|
+
return applySplices(content, [
|
|
157
|
+
insertTopLevel(ast, `import createNextIntlPlugin from 'next-intl/plugin';\n\nconst withNextIntl = createNextIntlPlugin(${pluginArg});`),
|
|
158
|
+
{ at: node.declaration.start, insert: 'withNextIntl(' },
|
|
159
|
+
{ at: node.declaration.end, insert: ')' },
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
if (ast_helpers_1.t.isExpressionStatement(node) &&
|
|
163
|
+
ast_helpers_1.t.isAssignmentExpression(node.expression) &&
|
|
164
|
+
ast_helpers_1.t.isMemberExpression(node.expression.left) &&
|
|
165
|
+
ast_helpers_1.t.isIdentifier(node.expression.left.object, { name: 'module' }) &&
|
|
166
|
+
ast_helpers_1.t.isIdentifier(node.expression.left.property, { name: 'exports' }) &&
|
|
167
|
+
node.expression.right.start != null &&
|
|
168
|
+
node.expression.right.end != null) {
|
|
169
|
+
return applySplices(content, [
|
|
170
|
+
{
|
|
171
|
+
at: 0,
|
|
172
|
+
insert: `const createNextIntlPlugin = require('next-intl/plugin');\n\nconst withNextIntl = createNextIntlPlugin(${pluginArg});\n\n`,
|
|
173
|
+
},
|
|
174
|
+
{ at: node.expression.right.start, insert: 'withNextIntl(' },
|
|
175
|
+
{ at: node.expression.right.end, insert: ')' },
|
|
176
|
+
]);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
/** Wrap `{children}` inside the root layout's <body> with <NextIntlClientProvider>. */
|
|
182
|
+
function wrapRootLayout(content, typescript) {
|
|
183
|
+
if (content.includes('NextIntlClientProvider'))
|
|
184
|
+
return content;
|
|
185
|
+
let ast;
|
|
186
|
+
try {
|
|
187
|
+
ast = (0, ast_helpers_1.parseCode)(content, { isTypeScript: typescript });
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
let target = null;
|
|
193
|
+
(0, ast_helpers_1.traverse)(ast, {
|
|
194
|
+
JSXElement(p) {
|
|
195
|
+
const name = p.node.openingElement.name;
|
|
196
|
+
if (!ast_helpers_1.t.isJSXIdentifier(name, { name: 'body' }))
|
|
197
|
+
return;
|
|
198
|
+
p.traverse({
|
|
199
|
+
JSXExpressionContainer(inner) {
|
|
200
|
+
if (ast_helpers_1.t.isIdentifier(inner.node.expression, { name: 'children' }) && inner.node.start != null && inner.node.end != null) {
|
|
201
|
+
target = { start: inner.node.start, end: inner.node.end };
|
|
202
|
+
inner.stop();
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
p.stop();
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
if (!target)
|
|
210
|
+
return null;
|
|
211
|
+
const { start, end } = target;
|
|
212
|
+
return applySplices(content, [
|
|
213
|
+
insertTopLevel(ast, `import { NextIntlClientProvider } from 'next-intl';`),
|
|
214
|
+
{ at: start, insert: '<NextIntlClientProvider>' },
|
|
215
|
+
{ at: end, insert: '</NextIntlClientProvider>' },
|
|
216
|
+
]);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Locales the request config should offer: every locale file on disk (this
|
|
220
|
+
* run's and earlier runs') plus this run's source and targets, so a later run
|
|
221
|
+
* with a different `--to` never hides locales that still have files.
|
|
222
|
+
*/
|
|
223
|
+
async function availableLocales(workspace, options) {
|
|
224
|
+
const found = new Set(options.targetLocales ?? []);
|
|
225
|
+
const dir = path.dirname((0, file_utils_1.localeFilePath)(workspace.path, workspace.publicDir, 'json', '__LOCALE__').file);
|
|
226
|
+
const entries = await fs.promises.readdir(dir).catch(() => []);
|
|
227
|
+
for (const entry of entries) {
|
|
228
|
+
if (entry.endsWith('.json'))
|
|
229
|
+
found.add(entry.slice(0, -'.json'.length));
|
|
230
|
+
}
|
|
231
|
+
const source = options.sourceLocale ?? 'en';
|
|
232
|
+
found.delete(source);
|
|
233
|
+
// This run's targets in the order given, then the other locales found on disk.
|
|
234
|
+
const targets = (options.targetLocales ?? []).filter((l) => l !== source);
|
|
235
|
+
return [...new Set([...targets, ...[...found].sort()])];
|
|
236
|
+
}
|
|
237
|
+
async function finalizeNextIntl(workspace, dryRun, options) {
|
|
238
|
+
const warnings = [];
|
|
239
|
+
let configFileCreated = false;
|
|
240
|
+
let entryPointModified = false;
|
|
241
|
+
const root = workspace.path;
|
|
242
|
+
const typescript = await (0, file_utils_1.pathExists)(path.join(root, 'tsconfig.json'));
|
|
243
|
+
const appDir = (await (0, file_utils_1.pathExists)(path.join(root, 'app')))
|
|
244
|
+
? path.join(root, 'app')
|
|
245
|
+
: (await (0, file_utils_1.pathExists)(path.join(root, 'src', 'app')))
|
|
246
|
+
? path.join(root, 'src', 'app')
|
|
247
|
+
: null;
|
|
248
|
+
if (!appDir) {
|
|
249
|
+
const pagesRouter = (await (0, file_utils_1.pathExists)(path.join(root, 'pages'))) || (await (0, file_utils_1.pathExists)(path.join(root, 'src', 'pages')));
|
|
250
|
+
warnings.push(pagesRouter
|
|
251
|
+
? 'Next.js Pages Router detected: Babelize injected next-intl calls but cannot wire next-intl into pages/_app automatically yet. ' +
|
|
252
|
+
'Add NextIntlClientProvider to _app and load messages from public/locales — see https://next-intl.dev/docs/getting-started/pages-router.'
|
|
253
|
+
: 'No Next.js app/ directory found: next-intl was not wired up. See https://next-intl.dev/docs/getting-started/app-router.');
|
|
254
|
+
return { configFileCreated, entryPointModified, warnings };
|
|
255
|
+
}
|
|
256
|
+
// 1. i18n/request.ts — next-intl's request config (next to app/, i.e. in src/ when app/ is).
|
|
257
|
+
// An existing request file (ours or the user's) decides the file name, so
|
|
258
|
+
// the content's syntax always matches its extension and next.config points
|
|
259
|
+
// at a file that exists.
|
|
260
|
+
const requestDir = path.join(path.dirname(appDir), 'i18n');
|
|
261
|
+
const existingRequest = await findFirst(requestDir, ['request.ts', 'request.tsx', 'request.js', 'request.jsx', 'request.mjs']);
|
|
262
|
+
const requestFile = existingRequest ?? path.join(requestDir, `request.${typescript ? 'ts' : 'js'}`);
|
|
263
|
+
const requestIsTs = /\.tsx?$/.test(requestFile);
|
|
264
|
+
const existingContent = existingRequest ? await (0, file_utils_1.readFile)(existingRequest).catch(() => '') : null;
|
|
265
|
+
if (existingRequest && existingContent !== null && !existingContent.includes(GENERATED_MARKER)) {
|
|
266
|
+
warnings.push(`${toPosix(path.relative(root, existingRequest))} already exists and was not generated by Babelize; left untouched. ` +
|
|
267
|
+
`Make sure it loads messages from ${toPosix(workspace.publicDir)}/locales/<locale>.json.`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
const sampleLocale = (0, file_utils_1.localeFilePath)(root, workspace.publicDir, 'json', '__LOCALE__').file;
|
|
271
|
+
let messagesImportPath = toPosix(path.relative(requestDir, sampleLocale));
|
|
272
|
+
if (!messagesImportPath.startsWith('.'))
|
|
273
|
+
messagesImportPath = `./${messagesImportPath}`;
|
|
274
|
+
const content = buildRequestConfig({
|
|
275
|
+
sourceLocale: options.sourceLocale ?? 'en',
|
|
276
|
+
locales: await availableLocales(workspace, options),
|
|
277
|
+
messagesImportPath,
|
|
278
|
+
typescript: requestIsTs,
|
|
279
|
+
});
|
|
280
|
+
if (content !== existingContent) {
|
|
281
|
+
if (!dryRun)
|
|
282
|
+
await (0, file_utils_1.writeFile)(requestFile, content);
|
|
283
|
+
configFileCreated = true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// 2. next.config — wrap with the next-intl plugin.
|
|
287
|
+
const pluginPath = `./${toPosix(path.relative(root, requestFile))}`;
|
|
288
|
+
const nextConfigPath = await findFirst(root, NEXT_CONFIG_FILES);
|
|
289
|
+
if (!nextConfigPath) {
|
|
290
|
+
const created = `import createNextIntlPlugin from 'next-intl/plugin';\n\n` +
|
|
291
|
+
`const withNextIntl = createNextIntlPlugin(${JSON.stringify(pluginPath)});\n\n` +
|
|
292
|
+
`/** @type {import('next').NextConfig} */\nconst nextConfig = {};\n\nexport default withNextIntl(nextConfig);\n`;
|
|
293
|
+
if (!dryRun)
|
|
294
|
+
await (0, file_utils_1.writeFile)(path.join(root, 'next.config.mjs'), created);
|
|
295
|
+
configFileCreated = true;
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
const content = await (0, file_utils_1.readFile)(nextConfigPath);
|
|
299
|
+
const wrapped = wrapNextConfig(content, pluginPath, nextConfigPath.endsWith('.ts'));
|
|
300
|
+
if (wrapped === null) {
|
|
301
|
+
warnings.push(`Could not wire next-intl into ${path.basename(nextConfigPath)} automatically. Wrap your config with ` +
|
|
302
|
+
`createNextIntlPlugin(${JSON.stringify(pluginPath)}) from 'next-intl/plugin'.`);
|
|
303
|
+
}
|
|
304
|
+
else if (wrapped !== content) {
|
|
305
|
+
if (!dryRun)
|
|
306
|
+
await (0, file_utils_1.writeFile)(nextConfigPath, wrapped);
|
|
307
|
+
configFileCreated = true;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// 3. Root layout — NextIntlClientProvider so Client Components can use useTranslations().
|
|
311
|
+
const layoutPath = await findFirst(appDir, LAYOUT_FILES);
|
|
312
|
+
if (!layoutPath) {
|
|
313
|
+
warnings.push('No root layout found in app/: wrap your app in <NextIntlClientProvider> from next-intl.');
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
const content = await (0, file_utils_1.readFile)(layoutPath);
|
|
317
|
+
const wrapped = wrapRootLayout(content, /\.tsx?$/.test(layoutPath));
|
|
318
|
+
if (wrapped === null) {
|
|
319
|
+
warnings.push(`Could not find {children} inside <body> in ${toPosix(path.relative(root, layoutPath))}: ` +
|
|
320
|
+
'wrap it in <NextIntlClientProvider> from next-intl so Client Components can translate.');
|
|
321
|
+
}
|
|
322
|
+
else if (wrapped !== content) {
|
|
323
|
+
if (!dryRun)
|
|
324
|
+
await (0, file_utils_1.writeFile)(layoutPath, wrapped);
|
|
325
|
+
entryPointModified = true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return { configFileCreated, entryPointModified, warnings };
|
|
329
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const STATIC_RUNTIME_VERSION = "1.0.0";
|
|
2
|
+
export declare const RUNTIME_HEADER = "/* babelize:static-runtime v1.0.0 */";
|
|
3
|
+
export declare function buildStaticRuntime(config: {
|
|
4
|
+
sourceLocale: string;
|
|
5
|
+
targetLocales: string[];
|
|
6
|
+
baseDir?: string;
|
|
7
|
+
}): string;
|
|
8
|
+
export declare function buildSwitcherPlaceholder(): string;
|
|
9
|
+
export declare function checkRuntimeNeedsRegeneration(existingContent: string): 'missing' | 'ours' | 'user';
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RUNTIME_HEADER = exports.STATIC_RUNTIME_VERSION = void 0;
|
|
4
|
+
exports.buildStaticRuntime = buildStaticRuntime;
|
|
5
|
+
exports.buildSwitcherPlaceholder = buildSwitcherPlaceholder;
|
|
6
|
+
exports.checkRuntimeNeedsRegeneration = checkRuntimeNeedsRegeneration;
|
|
7
|
+
exports.STATIC_RUNTIME_VERSION = '1.0.0';
|
|
8
|
+
exports.RUNTIME_HEADER = `/* babelize:static-runtime v${exports.STATIC_RUNTIME_VERSION} */`;
|
|
9
|
+
function buildStaticRuntime(config) {
|
|
10
|
+
const locales = JSON.stringify([config.sourceLocale, ...config.targetLocales]);
|
|
11
|
+
const baseDir = config.baseDir ? JSON.stringify(config.baseDir) : 'null';
|
|
12
|
+
return `${exports.RUNTIME_HEADER}
|
|
13
|
+
(function () {
|
|
14
|
+
'use strict';
|
|
15
|
+
var SOURCE = ${JSON.stringify(config.sourceLocale)};
|
|
16
|
+
var LOCALES = ${locales};
|
|
17
|
+
var BASE_DIR = ${baseDir};
|
|
18
|
+
|
|
19
|
+
var loadedLocales = {};
|
|
20
|
+
var fetching = {};
|
|
21
|
+
var lang = null;
|
|
22
|
+
|
|
23
|
+
function resolveBase() {
|
|
24
|
+
var script = document.currentScript;
|
|
25
|
+
if (script && script.src) {
|
|
26
|
+
var url = new URL(script.src, window.location.href);
|
|
27
|
+
return url.href.replace(/\\/[^/]*$/, '/');
|
|
28
|
+
}
|
|
29
|
+
var el = document.querySelector('[data-babelize-base]');
|
|
30
|
+
if (el) {
|
|
31
|
+
var attr = el.getAttribute('data-babelize-base');
|
|
32
|
+
if (attr) return attr;
|
|
33
|
+
}
|
|
34
|
+
if (BASE_DIR) return BASE_DIR;
|
|
35
|
+
return './';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var BASE = resolveBase();
|
|
39
|
+
|
|
40
|
+
function fetchLocale(locale, cb) {
|
|
41
|
+
if (loadedLocales[locale]) {
|
|
42
|
+
cb(null, loadedLocales[locale]);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (fetching[locale]) {
|
|
46
|
+
fetching[locale].push(cb);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
fetching[locale] = [cb];
|
|
50
|
+
|
|
51
|
+
var url = BASE + 'locales/' + locale + '.json';
|
|
52
|
+
|
|
53
|
+
function done(err, data) {
|
|
54
|
+
if (!err && data) {
|
|
55
|
+
loadedLocales[locale] = data;
|
|
56
|
+
}
|
|
57
|
+
var cbs = fetching[locale];
|
|
58
|
+
delete fetching[locale];
|
|
59
|
+
for (var i = 0; i < cbs.length; i++) {
|
|
60
|
+
cbs[i](err, data);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof window.fetch === 'function') {
|
|
65
|
+
window.fetch(url).then(function (res) {
|
|
66
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
67
|
+
return res.json();
|
|
68
|
+
}).then(function (data) {
|
|
69
|
+
done(null, data);
|
|
70
|
+
}).catch(function (e) {
|
|
71
|
+
fallbackXhr(url, done);
|
|
72
|
+
});
|
|
73
|
+
} else {
|
|
74
|
+
fallbackXhr(url, done);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function fallbackXhr(url, cb) {
|
|
79
|
+
try {
|
|
80
|
+
var xhr = new XMLHttpRequest();
|
|
81
|
+
xhr.open('GET', url, true);
|
|
82
|
+
xhr.onreadystatechange = function () {
|
|
83
|
+
if (xhr.readyState === 4) {
|
|
84
|
+
if (xhr.status === 200) {
|
|
85
|
+
try {
|
|
86
|
+
cb(null, JSON.parse(xhr.responseText));
|
|
87
|
+
} catch (e) {
|
|
88
|
+
cb(e, null);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
cb(new Error('HTTP ' + xhr.status), null);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
xhr.onerror = function () { cb(new Error('Network error'), null); };
|
|
96
|
+
xhr.send();
|
|
97
|
+
} catch (e) {
|
|
98
|
+
cb(e, null);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getTranslation(key) {
|
|
103
|
+
var parts = key.split('.');
|
|
104
|
+
var current = loadedLocales[lang] || {};
|
|
105
|
+
for (var i = 0; i < parts.length; i++) {
|
|
106
|
+
if (current === null || current === undefined) break;
|
|
107
|
+
current = current[parts[i]];
|
|
108
|
+
}
|
|
109
|
+
if (current !== undefined) return current;
|
|
110
|
+
current = loadedLocales[lang] || {};
|
|
111
|
+
if (current[key] !== undefined) return current[key];
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function applyTranslations() {
|
|
116
|
+
var walk = document.createTreeWalker(
|
|
117
|
+
document.body,
|
|
118
|
+
NodeFilter.SHOW_ELEMENT,
|
|
119
|
+
null,
|
|
120
|
+
false
|
|
121
|
+
);
|
|
122
|
+
var node;
|
|
123
|
+
while ((node = walk.nextNode())) {
|
|
124
|
+
var key = node.getAttribute('data-i18n');
|
|
125
|
+
if (key) {
|
|
126
|
+
var val = getTranslation(key);
|
|
127
|
+
if (val === undefined) continue;
|
|
128
|
+
var argsStr = node.getAttribute('data-i18n-args');
|
|
129
|
+
if (argsStr) {
|
|
130
|
+
try {
|
|
131
|
+
var args = JSON.parse(argsStr);
|
|
132
|
+
val = String(val).split('{').map(function(part, i) {
|
|
133
|
+
if (i === 0) return part;
|
|
134
|
+
var closeIdx = part.indexOf('}');
|
|
135
|
+
if (closeIdx === -1) return '{' + part;
|
|
136
|
+
var name = part.substring(0, closeIdx);
|
|
137
|
+
var rest = part.substring(closeIdx + 1);
|
|
138
|
+
return (args[name] !== undefined ? args[name] : '{' + name + '}') + rest;
|
|
139
|
+
}).join('');
|
|
140
|
+
} catch (e) {}
|
|
141
|
+
}
|
|
142
|
+
if (node.getAttribute('data-i18n-html') === '1') {
|
|
143
|
+
node.innerHTML = val;
|
|
144
|
+
} else {
|
|
145
|
+
var tw = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, null, false);
|
|
146
|
+
var textNodes = [];
|
|
147
|
+
while (tw.nextNode()) textNodes.push(tw.currentNode);
|
|
148
|
+
for (var i = 0; i < textNodes.length; i++) {
|
|
149
|
+
textNodes[i].textContent = val;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
var attrNames = ['alt', 'title', 'placeholder', 'aria-label', 'aria-description'];
|
|
154
|
+
for (var a = 0; a < attrNames.length; a++) {
|
|
155
|
+
var attrKey = node.getAttribute('data-i18n-' + attrNames[a]);
|
|
156
|
+
if (attrKey) {
|
|
157
|
+
var attrVal = getTranslation(attrKey);
|
|
158
|
+
if (attrVal !== undefined) node.setAttribute(attrNames[a], attrVal);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function updateHtmlAttributes() {
|
|
165
|
+
var meta = (loadedLocales[lang] || {})._meta;
|
|
166
|
+
if (!meta) return;
|
|
167
|
+
var html = document.documentElement;
|
|
168
|
+
if (meta.language) html.setAttribute('lang', meta.language);
|
|
169
|
+
if (meta.dir) html.setAttribute('dir', meta.dir);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function resolveLanguage() {
|
|
173
|
+
var params = new URLSearchParams(window.location.search);
|
|
174
|
+
var fromParam = params.get('lang') || params.get('hl');
|
|
175
|
+
if (fromParam) return fromParam;
|
|
176
|
+
try {
|
|
177
|
+
var stored = window.localStorage.getItem('babelize-lang');
|
|
178
|
+
if (stored) return stored;
|
|
179
|
+
} catch (e) {}
|
|
180
|
+
var match = document.cookie.match(/(?:^|;\\s*)lang=([^;]*)/);
|
|
181
|
+
if (match) return match[1];
|
|
182
|
+
return (navigator.language || 'en').split('-')[0];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function switchLanguage(locale) {
|
|
186
|
+
lang = locale;
|
|
187
|
+
document.cookie = 'lang=' + locale + ';path=/;max-age=31536000';
|
|
188
|
+
try { window.localStorage.setItem('babelize-lang', locale); } catch (e) {}
|
|
189
|
+
fetchLocale(locale, function (err) {
|
|
190
|
+
if (!err) {
|
|
191
|
+
updateHtmlAttributes();
|
|
192
|
+
applyTranslations();
|
|
193
|
+
}
|
|
194
|
+
document.dispatchEvent(new CustomEvent('babelize:languagechange', {
|
|
195
|
+
detail: { language: locale }
|
|
196
|
+
}));
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function init() {
|
|
201
|
+
lang = resolveLanguage();
|
|
202
|
+
fetchLocale(lang, function (err) {
|
|
203
|
+
if (err) {
|
|
204
|
+
if (lang !== SOURCE) {
|
|
205
|
+
lang = SOURCE;
|
|
206
|
+
fetchLocale(SOURCE, function () {
|
|
207
|
+
updateHtmlAttributes();
|
|
208
|
+
applyTranslations();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
updateHtmlAttributes();
|
|
214
|
+
applyTranslations();
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
window.switchLanguage = switchLanguage;
|
|
219
|
+
|
|
220
|
+
window.babelize = {
|
|
221
|
+
switchLanguage: switchLanguage,
|
|
222
|
+
t: getTranslation,
|
|
223
|
+
apply: applyTranslations,
|
|
224
|
+
getLanguage: function () { return lang; },
|
|
225
|
+
locales: LOCALES
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
var switcher = document.querySelector('[data-babelize-switcher]');
|
|
229
|
+
if (switcher) {
|
|
230
|
+
var sel = document.createElement('select');
|
|
231
|
+
sel.setAttribute('data-babelize-switcher-select', '');
|
|
232
|
+
for (var i = 0; i < LOCALES.length; i++) {
|
|
233
|
+
var opt = document.createElement('option');
|
|
234
|
+
opt.value = LOCALES[i];
|
|
235
|
+
opt.textContent = LOCALES[i];
|
|
236
|
+
sel.appendChild(opt);
|
|
237
|
+
}
|
|
238
|
+
sel.addEventListener('change', function () { switchLanguage(this.value); });
|
|
239
|
+
switcher.appendChild(sel);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (document.readyState === 'loading') {
|
|
243
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
244
|
+
} else {
|
|
245
|
+
init();
|
|
246
|
+
}
|
|
247
|
+
})();
|
|
248
|
+
`;
|
|
249
|
+
}
|
|
250
|
+
function buildSwitcherPlaceholder() {
|
|
251
|
+
return '<div data-babelize-switcher></div>';
|
|
252
|
+
}
|
|
253
|
+
function checkRuntimeNeedsRegeneration(existingContent) {
|
|
254
|
+
if (!existingContent || existingContent.trim().length === 0) {
|
|
255
|
+
return 'missing';
|
|
256
|
+
}
|
|
257
|
+
if (existingContent.includes(exports.RUNTIME_HEADER)) {
|
|
258
|
+
return 'ours';
|
|
259
|
+
}
|
|
260
|
+
return 'user';
|
|
261
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ExtractedString, FileInjection, SkippedFile, WorkspaceInfo } from '../../runtime/types';
|
|
2
|
+
import type { FormatLocaleConfig } from '@babelize/types';
|
|
3
|
+
export interface FormatInjectionResult {
|
|
4
|
+
modifiedFiles: FileInjection[];
|
|
5
|
+
skippedFiles: SkippedFile[];
|
|
6
|
+
totalInjections: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Run format injection for all format strings.
|
|
10
|
+
*
|
|
11
|
+
* Replaces hardcoded currency, date, and number values in source files
|
|
12
|
+
* with Intl.* API calls (e.g., Intl.NumberFormat, Intl.DateTimeFormat).
|
|
13
|
+
*/
|
|
14
|
+
export declare function injectFormats(workspace: WorkspaceInfo, formatStrings: ExtractedString[], formatConfig: FormatLocaleConfig, dryRun?: boolean): Promise<FormatInjectionResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Filter format strings out of an array of extracted strings.
|
|
17
|
+
* Returns [translatableStrings, formatStrings].
|
|
18
|
+
*/
|
|
19
|
+
export declare function partitionFormatStrings(strings: ExtractedString[]): [translatable: ExtractedString[], format: ExtractedString[]];
|