@lingo.dev/compiler 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.md +201 -0
- package/README.md +192 -0
- package/build/_virtual/rolldown_runtime.cjs +29 -0
- package/build/_virtual/rolldown_runtime.mjs +7 -0
- package/build/index.cjs +0 -0
- package/build/index.d.cts +2 -0
- package/build/index.d.mts +2 -0
- package/build/index.mjs +1 -0
- package/build/metadata/manager.cjs +131 -0
- package/build/metadata/manager.mjs +123 -0
- package/build/metadata/manager.mjs.map +1 -0
- package/build/plugin/build-translator.cjs +198 -0
- package/build/plugin/build-translator.mjs +196 -0
- package/build/plugin/build-translator.mjs.map +1 -0
- package/build/plugin/cleanup.cjs +20 -0
- package/build/plugin/cleanup.mjs +20 -0
- package/build/plugin/cleanup.mjs.map +1 -0
- package/build/plugin/next-compiler-loader.cjs +41 -0
- package/build/plugin/next-compiler-loader.d.cts +12 -0
- package/build/plugin/next-compiler-loader.d.cts.map +1 -0
- package/build/plugin/next-compiler-loader.d.mts +13 -0
- package/build/plugin/next-compiler-loader.d.mts.map +1 -0
- package/build/plugin/next-compiler-loader.mjs +42 -0
- package/build/plugin/next-compiler-loader.mjs.map +1 -0
- package/build/plugin/next-config-loader.cjs +13 -0
- package/build/plugin/next-config-loader.d.cts +8 -0
- package/build/plugin/next-config-loader.d.cts.map +1 -0
- package/build/plugin/next-config-loader.d.mts +9 -0
- package/build/plugin/next-config-loader.d.mts.map +1 -0
- package/build/plugin/next-config-loader.mjs +14 -0
- package/build/plugin/next-config-loader.mjs.map +1 -0
- package/build/plugin/next-locale-client-loader.cjs +9 -0
- package/build/plugin/next-locale-client-loader.d.cts +8 -0
- package/build/plugin/next-locale-client-loader.d.cts.map +1 -0
- package/build/plugin/next-locale-client-loader.d.mts +9 -0
- package/build/plugin/next-locale-client-loader.d.mts.map +1 -0
- package/build/plugin/next-locale-client-loader.mjs +10 -0
- package/build/plugin/next-locale-client-loader.mjs.map +1 -0
- package/build/plugin/next-locale-server-loader.cjs +9 -0
- package/build/plugin/next-locale-server-loader.d.cts +8 -0
- package/build/plugin/next-locale-server-loader.d.cts.map +1 -0
- package/build/plugin/next-locale-server-loader.d.mts +9 -0
- package/build/plugin/next-locale-server-loader.d.mts.map +1 -0
- package/build/plugin/next-locale-server-loader.mjs +10 -0
- package/build/plugin/next-locale-server-loader.mjs.map +1 -0
- package/build/plugin/next.cjs +220 -0
- package/build/plugin/next.d.cts +9 -0
- package/build/plugin/next.d.cts.map +1 -0
- package/build/plugin/next.d.mts +9 -0
- package/build/plugin/next.d.mts.map +1 -0
- package/build/plugin/next.mjs +222 -0
- package/build/plugin/next.mjs.map +1 -0
- package/build/plugin/transform/babel-compat.cjs +13 -0
- package/build/plugin/transform/babel-compat.mjs +10 -0
- package/build/plugin/transform/babel-compat.mjs.map +1 -0
- package/build/plugin/transform/index.cjs +44 -0
- package/build/plugin/transform/index.mjs +42 -0
- package/build/plugin/transform/index.mjs.map +1 -0
- package/build/plugin/transform/metadata.cjs +142 -0
- package/build/plugin/transform/metadata.mjs +141 -0
- package/build/plugin/transform/metadata.mjs.map +1 -0
- package/build/plugin/transform/parse-override.cjs +145 -0
- package/build/plugin/transform/parse-override.mjs +144 -0
- package/build/plugin/transform/parse-override.mjs.map +1 -0
- package/build/plugin/transform/process-file.cjs +391 -0
- package/build/plugin/transform/process-file.mjs +390 -0
- package/build/plugin/transform/process-file.mjs.map +1 -0
- package/build/plugin/transform/use-i18n.cjs +8 -0
- package/build/plugin/transform/use-i18n.mjs +7 -0
- package/build/plugin/transform/use-i18n.mjs.map +1 -0
- package/build/plugin/transform/utils.cjs +205 -0
- package/build/plugin/transform/utils.mjs +192 -0
- package/build/plugin/transform/utils.mjs.map +1 -0
- package/build/plugin/unplugin.cjs +188 -0
- package/build/plugin/unplugin.d.cts +8 -0
- package/build/plugin/unplugin.d.cts.map +1 -0
- package/build/plugin/unplugin.d.mts +8 -0
- package/build/plugin/unplugin.d.mts.map +1 -0
- package/build/plugin/unplugin.mjs +186 -0
- package/build/plugin/unplugin.mjs.map +1 -0
- package/build/plugin/vite.cjs +28 -0
- package/build/plugin/vite.d.cts +9 -0
- package/build/plugin/vite.d.cts.map +1 -0
- package/build/plugin/vite.d.mts +9 -0
- package/build/plugin/vite.d.mts.map +1 -0
- package/build/plugin/vite.mjs +29 -0
- package/build/plugin/vite.mjs.map +1 -0
- package/build/plugin/webpack.cjs +27 -0
- package/build/plugin/webpack.d.cts +8 -0
- package/build/plugin/webpack.d.cts.map +1 -0
- package/build/plugin/webpack.d.mts +8 -0
- package/build/plugin/webpack.d.mts.map +1 -0
- package/build/plugin/webpack.mjs +28 -0
- package/build/plugin/webpack.mjs.map +1 -0
- package/build/react/client/index.cjs +9 -0
- package/build/react/client/index.d.cts +5 -0
- package/build/react/client/index.d.mts +5 -0
- package/build/react/client/index.mjs +6 -0
- package/build/react/client/useTranslation.cjs +71 -0
- package/build/react/client/useTranslation.d.cts +42 -0
- package/build/react/client/useTranslation.d.cts.map +1 -0
- package/build/react/client/useTranslation.d.mts +42 -0
- package/build/react/client/useTranslation.d.mts.map +1 -0
- package/build/react/client/useTranslation.mjs +71 -0
- package/build/react/client/useTranslation.mjs.map +1 -0
- package/build/react/next/client.cjs +25 -0
- package/build/react/next/client.d.cts +9 -0
- package/build/react/next/client.d.cts.map +1 -0
- package/build/react/next/client.d.mts +9 -0
- package/build/react/next/client.d.mts.map +1 -0
- package/build/react/next/client.mjs +24 -0
- package/build/react/next/client.mjs.map +1 -0
- package/build/react/next/cookie-locale-resolver.cjs +29 -0
- package/build/react/next/cookie-locale-resolver.d.cts +33 -0
- package/build/react/next/cookie-locale-resolver.d.cts.map +1 -0
- package/build/react/next/cookie-locale-resolver.d.mts +33 -0
- package/build/react/next/cookie-locale-resolver.d.mts.map +1 -0
- package/build/react/next/cookie-locale-resolver.mjs +29 -0
- package/build/react/next/cookie-locale-resolver.mjs.map +1 -0
- package/build/react/next/server.cjs +21 -0
- package/build/react/next/server.d.cts +13 -0
- package/build/react/next/server.d.cts.map +1 -0
- package/build/react/next/server.d.mts +14 -0
- package/build/react/next/server.d.mts.map +1 -0
- package/build/react/next/server.mjs +20 -0
- package/build/react/next/server.mjs.map +1 -0
- package/build/react/server/ServerLingoProvider.cjs +19 -0
- package/build/react/server/ServerLingoProvider.d.cts +12 -0
- package/build/react/server/ServerLingoProvider.d.cts.map +1 -0
- package/build/react/server/ServerLingoProvider.d.mts +12 -0
- package/build/react/server/ServerLingoProvider.d.mts.map +1 -0
- package/build/react/server/ServerLingoProvider.mjs +19 -0
- package/build/react/server/ServerLingoProvider.mjs.map +1 -0
- package/build/react/server/index.cjs +7 -0
- package/build/react/server/index.d.cts +4 -0
- package/build/react/server/index.d.mts +4 -0
- package/build/react/server/index.mjs +5 -0
- package/build/react/server/useTranslation.cjs +60 -0
- package/build/react/server/useTranslation.d.cts +36 -0
- package/build/react/server/useTranslation.d.cts.map +1 -0
- package/build/react/server/useTranslation.d.mts +36 -0
- package/build/react/server/useTranslation.d.mts.map +1 -0
- package/build/react/server/useTranslation.mjs +60 -0
- package/build/react/server/useTranslation.mjs.map +1 -0
- package/build/react/server-only/index.cjs +42 -0
- package/build/react/server-only/index.d.cts +38 -0
- package/build/react/server-only/index.d.cts.map +1 -0
- package/build/react/server-only/index.d.mts +38 -0
- package/build/react/server-only/index.d.mts.map +1 -0
- package/build/react/server-only/index.mjs +42 -0
- package/build/react/server-only/index.mjs.map +1 -0
- package/build/react/server-only/translations.cjs +85 -0
- package/build/react/server-only/translations.mjs +85 -0
- package/build/react/server-only/translations.mjs.map +1 -0
- package/build/react/shared/LingoContext.cjs +14 -0
- package/build/react/shared/LingoContext.d.cts +41 -0
- package/build/react/shared/LingoContext.d.cts.map +1 -0
- package/build/react/shared/LingoContext.d.mts +41 -0
- package/build/react/shared/LingoContext.d.mts.map +1 -0
- package/build/react/shared/LingoContext.mjs +13 -0
- package/build/react/shared/LingoContext.mjs.map +1 -0
- package/build/react/shared/LingoProvider.cjs +274 -0
- package/build/react/shared/LingoProvider.d.cts +76 -0
- package/build/react/shared/LingoProvider.d.cts.map +1 -0
- package/build/react/shared/LingoProvider.d.mts +76 -0
- package/build/react/shared/LingoProvider.d.mts.map +1 -0
- package/build/react/shared/LingoProvider.mjs +274 -0
- package/build/react/shared/LingoProvider.mjs.map +1 -0
- package/build/react/shared/LocaleSwitcher.cjs +61 -0
- package/build/react/shared/LocaleSwitcher.d.cts +71 -0
- package/build/react/shared/LocaleSwitcher.d.cts.map +1 -0
- package/build/react/shared/LocaleSwitcher.d.mts +71 -0
- package/build/react/shared/LocaleSwitcher.d.mts.map +1 -0
- package/build/react/shared/LocaleSwitcher.mjs +61 -0
- package/build/react/shared/LocaleSwitcher.mjs.map +1 -0
- package/build/react/shared/render-rich-text.cjs +55 -0
- package/build/react/shared/render-rich-text.d.cts +17 -0
- package/build/react/shared/render-rich-text.d.cts.map +1 -0
- package/build/react/shared/render-rich-text.d.mts +17 -0
- package/build/react/shared/render-rich-text.d.mts.map +1 -0
- package/build/react/shared/render-rich-text.mjs +54 -0
- package/build/react/shared/render-rich-text.mjs.map +1 -0
- package/build/react/shared/utils.cjs +34 -0
- package/build/react/shared/utils.mjs +35 -0
- package/build/react/shared/utils.mjs.map +1 -0
- package/build/react/types.d.cts +16 -0
- package/build/react/types.d.cts.map +1 -0
- package/build/react/types.d.mts +16 -0
- package/build/react/types.d.mts.map +1 -0
- package/build/translation-server/logger.cjs +37 -0
- package/build/translation-server/logger.mjs +37 -0
- package/build/translation-server/logger.mjs.map +1 -0
- package/build/translation-server/translation-server.cjs +547 -0
- package/build/translation-server/translation-server.mjs +544 -0
- package/build/translation-server/translation-server.mjs.map +1 -0
- package/build/translation-server/ws-events.cjs +15 -0
- package/build/translation-server/ws-events.mjs +15 -0
- package/build/translation-server/ws-events.mjs.map +1 -0
- package/build/translators/api.cjs +12 -0
- package/build/translators/api.mjs +12 -0
- package/build/translators/api.mjs.map +1 -0
- package/build/translators/cache-factory.cjs +26 -0
- package/build/translators/cache-factory.mjs +27 -0
- package/build/translators/cache-factory.mjs.map +1 -0
- package/build/translators/lingo/model-factory.cjs +179 -0
- package/build/translators/lingo/model-factory.mjs +174 -0
- package/build/translators/lingo/model-factory.mjs.map +1 -0
- package/build/translators/lingo/prompt.cjs +43 -0
- package/build/translators/lingo/prompt.mjs +43 -0
- package/build/translators/lingo/prompt.mjs.map +1 -0
- package/build/translators/lingo/service.cjs +152 -0
- package/build/translators/lingo/service.mjs +152 -0
- package/build/translators/lingo/service.mjs.map +1 -0
- package/build/translators/lingo/shots.cjs +28 -0
- package/build/translators/lingo/shots.mjs +28 -0
- package/build/translators/lingo/shots.mjs.map +1 -0
- package/build/translators/local-cache.cjs +115 -0
- package/build/translators/local-cache.mjs +113 -0
- package/build/translators/local-cache.mjs.map +1 -0
- package/build/translators/parse-xml.cjs +109 -0
- package/build/translators/parse-xml.mjs +108 -0
- package/build/translators/parse-xml.mjs.map +1 -0
- package/build/translators/pluralization/icu-validator.cjs +36 -0
- package/build/translators/pluralization/icu-validator.mjs +36 -0
- package/build/translators/pluralization/icu-validator.mjs.map +1 -0
- package/build/translators/pluralization/pattern-detector.cjs +25 -0
- package/build/translators/pluralization/pattern-detector.mjs +25 -0
- package/build/translators/pluralization/pattern-detector.mjs.map +1 -0
- package/build/translators/pluralization/prompt.cjs +98 -0
- package/build/translators/pluralization/prompt.mjs +98 -0
- package/build/translators/pluralization/prompt.mjs.map +1 -0
- package/build/translators/pluralization/service.cjs +247 -0
- package/build/translators/pluralization/service.mjs +247 -0
- package/build/translators/pluralization/service.mjs.map +1 -0
- package/build/translators/pluralization/shots.cjs +53 -0
- package/build/translators/pluralization/shots.mjs +53 -0
- package/build/translators/pluralization/shots.mjs.map +1 -0
- package/build/translators/pluralization/types.d.cts +17 -0
- package/build/translators/pluralization/types.d.cts.map +1 -0
- package/build/translators/pluralization/types.d.mts +17 -0
- package/build/translators/pluralization/types.d.mts.map +1 -0
- package/build/translators/pseudotranslator/index.cjs +129 -0
- package/build/translators/pseudotranslator/index.mjs +129 -0
- package/build/translators/pseudotranslator/index.mjs.map +1 -0
- package/build/translators/translation-service.cjs +182 -0
- package/build/translators/translation-service.mjs +183 -0
- package/build/translators/translation-service.mjs.map +1 -0
- package/build/translators/translator-factory.cjs +49 -0
- package/build/translators/translator-factory.mjs +50 -0
- package/build/translators/translator-factory.mjs.map +1 -0
- package/build/types.d.cts +161 -0
- package/build/types.d.cts.map +1 -0
- package/build/types.d.mts +161 -0
- package/build/types.d.mts.map +1 -0
- package/build/utils/config-factory.cjs +58 -0
- package/build/utils/config-factory.mjs +58 -0
- package/build/utils/config-factory.mjs.map +1 -0
- package/build/utils/hash.cjs +17 -0
- package/build/utils/hash.mjs +16 -0
- package/build/utils/hash.mjs.map +1 -0
- package/build/utils/is-valid-locale.cjs +14 -0
- package/build/utils/is-valid-locale.mjs +14 -0
- package/build/utils/is-valid-locale.mjs.map +1 -0
- package/build/utils/logger.cjs +51 -0
- package/build/utils/logger.mjs +50 -0
- package/build/utils/logger.mjs.map +1 -0
- package/build/utils/path-helpers.cjs +49 -0
- package/build/utils/path-helpers.mjs +47 -0
- package/build/utils/path-helpers.mjs.map +1 -0
- package/build/utils/timeout.cjs +42 -0
- package/build/utils/timeout.mjs +41 -0
- package/build/utils/timeout.mjs.map +1 -0
- package/build/virtual/code-generator.cjs +54 -0
- package/build/virtual/code-generator.mjs +53 -0
- package/build/virtual/code-generator.mjs.map +1 -0
- package/build/virtual/config.cjs +10 -0
- package/build/virtual/config.d.cts +9 -0
- package/build/virtual/config.d.cts.map +1 -0
- package/build/virtual/config.d.mts +9 -0
- package/build/virtual/config.d.mts.map +1 -0
- package/build/virtual/config.mjs +8 -0
- package/build/virtual/config.mjs.map +1 -0
- package/build/virtual/locale/client.cjs +23 -0
- package/build/virtual/locale/client.d.cts +19 -0
- package/build/virtual/locale/client.d.cts.map +1 -0
- package/build/virtual/locale/client.d.mts +19 -0
- package/build/virtual/locale/client.d.mts.map +1 -0
- package/build/virtual/locale/client.mjs +22 -0
- package/build/virtual/locale/client.mjs.map +1 -0
- package/build/virtual/locale/server.cjs +13 -0
- package/build/virtual/locale/server.d.cts +13 -0
- package/build/virtual/locale/server.d.cts.map +1 -0
- package/build/virtual/locale/server.d.mts +13 -0
- package/build/virtual/locale/server.d.mts.map +1 -0
- package/build/virtual/locale/server.mjs +13 -0
- package/build/virtual/locale/server.mjs.map +1 -0
- package/build/widget/lingo-dev-widget.cjs +228 -0
- package/build/widget/lingo-dev-widget.mjs +229 -0
- package/build/widget/lingo-dev-widget.mjs.map +1 -0
- package/package.json +189 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_logger = require('../../utils/logger.cjs');
|
|
3
|
+
const require_utils = require('./utils.cjs');
|
|
4
|
+
const require_metadata = require('./metadata.cjs');
|
|
5
|
+
const require_babel_compat = require('./babel-compat.cjs');
|
|
6
|
+
const require_parse_override = require('./parse-override.cjs');
|
|
7
|
+
let _babel_types = require("@babel/types");
|
|
8
|
+
_babel_types = require_rolldown_runtime.__toESM(_babel_types);
|
|
9
|
+
|
|
10
|
+
//#region src/plugin/transform/process-file.ts
|
|
11
|
+
/**
|
|
12
|
+
* Keep mutations to the current node and children. Do not mutate program inserting imports when processing the component.
|
|
13
|
+
* This should be done in the end.
|
|
14
|
+
* The overall idea is that state is used for side effect tracking. When we exit relevant points, we check if we need to make extra changes at this level.
|
|
15
|
+
* e.g. when we transform elements to insert translations, we record their hashes and add the translation hook call when leaving the component.
|
|
16
|
+
*/
|
|
17
|
+
function updateWithMetadataState(state, newState) {
|
|
18
|
+
state.newEntries = [...state.newEntries, ...newState.newEntries];
|
|
19
|
+
state.needsAsyncImport = state.needsAsyncImport || newState.needsAsyncImport;
|
|
20
|
+
return state;
|
|
21
|
+
}
|
|
22
|
+
const SKIP_ATTRIBUTE = "data-lingo-skip";
|
|
23
|
+
/**
|
|
24
|
+
* Elements whose content should not be translated by default.
|
|
25
|
+
* These are typically code-related or technical elements where translation
|
|
26
|
+
* would break functionality or meaning.
|
|
27
|
+
*/
|
|
28
|
+
const NON_TRANSLATABLE_ELEMENTS = new Set([
|
|
29
|
+
"code",
|
|
30
|
+
"pre",
|
|
31
|
+
"script",
|
|
32
|
+
"style",
|
|
33
|
+
"kbd",
|
|
34
|
+
"samp",
|
|
35
|
+
"var"
|
|
36
|
+
]);
|
|
37
|
+
const TRANSLATABLE_ATTRIBUTES = new Set([
|
|
38
|
+
"title",
|
|
39
|
+
"aria-label",
|
|
40
|
+
"aria-description",
|
|
41
|
+
"alt",
|
|
42
|
+
"label",
|
|
43
|
+
"description",
|
|
44
|
+
"placeholder",
|
|
45
|
+
"content",
|
|
46
|
+
"subtitle"
|
|
47
|
+
]);
|
|
48
|
+
/**
|
|
49
|
+
* Check if a JSX element should skip translation based on:
|
|
50
|
+
* 1. Element type (code, pre, script, style, etc.)
|
|
51
|
+
* 2. translate="no" attribute (HTML standard)
|
|
52
|
+
* 3. data-lingo-skip attribute
|
|
53
|
+
*/
|
|
54
|
+
function shouldSkipTranslationForElement(element) {
|
|
55
|
+
if (element.type === "JSXFragment") return false;
|
|
56
|
+
const openingElement = element.openingElement;
|
|
57
|
+
if (openingElement.name.type === "JSXIdentifier") {
|
|
58
|
+
if (NON_TRANSLATABLE_ELEMENTS.has(openingElement.name.name)) return true;
|
|
59
|
+
}
|
|
60
|
+
for (const attr of openingElement.attributes) if (attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier") {
|
|
61
|
+
if (attr.name.name === "translate") {
|
|
62
|
+
if (attr.value?.type === "StringLiteral" && attr.value.value === "no") return true;
|
|
63
|
+
}
|
|
64
|
+
if (attr.name.name === SKIP_ATTRIBUTE) return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
function translateAttributes(node, state) {
|
|
69
|
+
const openingElement = node.openingElement;
|
|
70
|
+
const component = state.componentsStack.at(-1);
|
|
71
|
+
if (!component) return;
|
|
72
|
+
for (const attr of openingElement.attributes) if (attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && TRANSLATABLE_ATTRIBUTES.has(attr.name.name) && attr.value?.type === "StringLiteral") {
|
|
73
|
+
const text = require_utils.normalizeWhitespace(attr.value.value);
|
|
74
|
+
if (text.length == 0) continue;
|
|
75
|
+
const entry = require_utils.createTranslationEntry("attribute", text, {
|
|
76
|
+
attributeName: attr.name.name,
|
|
77
|
+
componentName: component.name
|
|
78
|
+
}, state.filePath, node.loc?.start.line, node.loc?.start.column);
|
|
79
|
+
registerEntry(entry, state, component.name);
|
|
80
|
+
attr.value = require_utils.constructTranslationCall(entry.hash, text);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if a JSX element has mixed content that needs rich text translation
|
|
85
|
+
* Mixed content = text nodes + expressions + nested JSX elements (excluding void elements)
|
|
86
|
+
*/
|
|
87
|
+
function hasMixedContent(element) {
|
|
88
|
+
const children = element.children;
|
|
89
|
+
if (children.length === 0) return false;
|
|
90
|
+
let state = 0;
|
|
91
|
+
for (const child of children) if (child.type === "JSXText") {
|
|
92
|
+
if (child.value.trim().length > 0) switch (state) {
|
|
93
|
+
case 0:
|
|
94
|
+
state = 1;
|
|
95
|
+
break;
|
|
96
|
+
case 1: break;
|
|
97
|
+
case 2: return true;
|
|
98
|
+
case 3: return true;
|
|
99
|
+
}
|
|
100
|
+
} else if (child.type === "JSXExpressionContainer") switch (state) {
|
|
101
|
+
case 0:
|
|
102
|
+
state = 2;
|
|
103
|
+
break;
|
|
104
|
+
case 1: return true;
|
|
105
|
+
case 2: break;
|
|
106
|
+
case 3: return true;
|
|
107
|
+
}
|
|
108
|
+
else if (child.type === "JSXElement") if (!require_utils.isVoidElement(child)) switch (state) {
|
|
109
|
+
case 0:
|
|
110
|
+
state = 2;
|
|
111
|
+
break;
|
|
112
|
+
case 1: return true;
|
|
113
|
+
case 2: break;
|
|
114
|
+
case 3: return true;
|
|
115
|
+
}
|
|
116
|
+
else switch (state) {
|
|
117
|
+
case 0: break;
|
|
118
|
+
case 1:
|
|
119
|
+
state = 3;
|
|
120
|
+
break;
|
|
121
|
+
case 2: break;
|
|
122
|
+
case 3: break;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Serialize JSX children to a translation string with placeholders
|
|
128
|
+
*/
|
|
129
|
+
function serializeJSXChildren(children, state) {
|
|
130
|
+
let text = "";
|
|
131
|
+
const variables = [];
|
|
132
|
+
const expressions = /* @__PURE__ */ new Map();
|
|
133
|
+
const components = /* @__PURE__ */ new Map();
|
|
134
|
+
const elementCounts = /* @__PURE__ */ new Map();
|
|
135
|
+
for (const child of children) if (child.type === "JSXText") {
|
|
136
|
+
let normalized = require_utils.normalizeWhitespace(child.value);
|
|
137
|
+
if (text.length > 0 && child.value.match(/^\s/) && normalized.length > 0) {
|
|
138
|
+
if (!text.endsWith(" ")) normalized = ` ${normalized}`;
|
|
139
|
+
}
|
|
140
|
+
if (normalized.length > 0 && child.value.match(/\s$/)) {
|
|
141
|
+
if (!text.endsWith(" ")) normalized = `${normalized} `;
|
|
142
|
+
}
|
|
143
|
+
text += require_utils.escapeTextForICU(normalized);
|
|
144
|
+
} else if (child.type === "JSXExpressionContainer") {
|
|
145
|
+
const expr = child.expression;
|
|
146
|
+
if (expr.type === "Identifier") {
|
|
147
|
+
text += `{${expr.name}}`;
|
|
148
|
+
if (!variables.includes(expr.name)) variables.push(expr.name);
|
|
149
|
+
} else if (expr.type === "StringLiteral") text += require_utils.escapeTextForICU(expr.value);
|
|
150
|
+
else if (expr.type !== "JSXEmptyExpression") {
|
|
151
|
+
const name = `expression${expressions.size}`;
|
|
152
|
+
text += `{${name}}`;
|
|
153
|
+
expressions.set(name, expr);
|
|
154
|
+
}
|
|
155
|
+
} else if (child.type === "JSXElement") {
|
|
156
|
+
let shouldTranslate = true;
|
|
157
|
+
if (require_utils.isVoidElement(child)) shouldTranslate = false;
|
|
158
|
+
if (shouldSkipTranslationForElement(child)) shouldTranslate = false;
|
|
159
|
+
const openingElement = child.openingElement;
|
|
160
|
+
let elementName = "";
|
|
161
|
+
if (openingElement.name.type === "JSXIdentifier") elementName = openingElement.name.name;
|
|
162
|
+
else elementName = "element";
|
|
163
|
+
const count = elementCounts.get(elementName) || 0;
|
|
164
|
+
elementCounts.set(elementName, count + 1);
|
|
165
|
+
const tagName = `${elementName}${count}`;
|
|
166
|
+
translateAttributes(child, state);
|
|
167
|
+
if (shouldTranslate) {
|
|
168
|
+
const nested = serializeJSXChildren(child.children, state);
|
|
169
|
+
text += `<${tagName}>${nested.text}</${tagName}>`;
|
|
170
|
+
for (const v of nested.variables) if (!variables.includes(v)) variables.push(v);
|
|
171
|
+
for (const [nestedTag, nestedElement] of nested.components) components.set(`${tagName}_${nestedTag}`, nestedElement);
|
|
172
|
+
} else {
|
|
173
|
+
text += `<${tagName}></${tagName}>`;
|
|
174
|
+
child.extra = {
|
|
175
|
+
...child.extra,
|
|
176
|
+
shouldTranslate: false
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
components.set(tagName, child);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
text,
|
|
183
|
+
variables,
|
|
184
|
+
expressions,
|
|
185
|
+
components
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function transformVoidElement(node, state) {
|
|
189
|
+
translateAttributes(node, state);
|
|
190
|
+
}
|
|
191
|
+
function getTranslationScope(node, state) {
|
|
192
|
+
if (hasMixedContent(node)) {
|
|
193
|
+
const serialized = serializeJSXChildren(node.children, state);
|
|
194
|
+
const text$1 = serialized.text.trim();
|
|
195
|
+
if (text$1.length === 0) return null;
|
|
196
|
+
return {
|
|
197
|
+
kind: "mixed",
|
|
198
|
+
text: text$1,
|
|
199
|
+
args: {
|
|
200
|
+
variables: serialized.variables,
|
|
201
|
+
expressions: serialized.expressions,
|
|
202
|
+
components: serialized.components
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const textNodeIndex = node.children.findIndex((child) => child.type === "JSXText" && child.value.trim().length > 0);
|
|
207
|
+
if (textNodeIndex === -1) return null;
|
|
208
|
+
if (!node.children.every((child) => child.type === "JSXText" || child.type === "JSXElement" && require_utils.isVoidElement(child))) return null;
|
|
209
|
+
const textNode = node.children[textNodeIndex];
|
|
210
|
+
const text = require_utils.normalizeWhitespace(textNode.value);
|
|
211
|
+
if (text.length === 0) return null;
|
|
212
|
+
return {
|
|
213
|
+
kind: "text",
|
|
214
|
+
text,
|
|
215
|
+
textNodeIndex
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function registerEntry(entry, state, componentName) {
|
|
219
|
+
state.newEntries.push(entry);
|
|
220
|
+
const hashes = state.componentHashes.get(componentName) ?? [];
|
|
221
|
+
hashes.push(entry.hash);
|
|
222
|
+
state.componentHashes.set(componentName, hashes);
|
|
223
|
+
}
|
|
224
|
+
function rewriteChildren(path, state, translationScope, entryHash) {
|
|
225
|
+
if (translationScope.kind === "mixed") {
|
|
226
|
+
path.node.children = [require_utils.constructTranslationCall(entryHash, translationScope.text, translationScope.args)];
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const { textNodeIndex, text } = translationScope;
|
|
230
|
+
path.node.children = path.node.children.map((child, index) => {
|
|
231
|
+
if (index === textNodeIndex) return require_utils.constructTranslationCall(entryHash, text);
|
|
232
|
+
if (child.type === "JSXElement") transformVoidElement(child, state);
|
|
233
|
+
return child;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function processJSXElement(path, state) {
|
|
237
|
+
const component = state.componentsStack.at(-1);
|
|
238
|
+
if (!component) return;
|
|
239
|
+
const scope = getTranslationScope(path.node, state);
|
|
240
|
+
if (!scope) return;
|
|
241
|
+
const overrides = require_parse_override.processOverrideAttributes(path);
|
|
242
|
+
const entry = require_utils.createTranslationEntry("content", scope.text, { componentName: component.name }, state.filePath, path.node.loc?.start.line, path.node.loc?.start.column, overrides);
|
|
243
|
+
registerEntry(entry, state, component.name);
|
|
244
|
+
rewriteChildren(path, state, scope, entry.hash);
|
|
245
|
+
path.skip();
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Inject dynamic locale attribute into <html> elements
|
|
249
|
+
* Transforms: <html> → <html lang={locale}>
|
|
250
|
+
*
|
|
251
|
+
* Only injects if:
|
|
252
|
+
* 1. Element is <html>
|
|
253
|
+
* 2. No existing lang/language attribute
|
|
254
|
+
* 3. We're inside a React component (has locale context)
|
|
255
|
+
*/
|
|
256
|
+
function injectHtmlLangAttribute(node, state) {
|
|
257
|
+
const openingElement = node.openingElement;
|
|
258
|
+
if (openingElement.name.type !== "JSXIdentifier" || openingElement.name.name !== "html") return;
|
|
259
|
+
if (openingElement.attributes.some((attr) => {
|
|
260
|
+
return attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && (attr.name.name === "lang" || attr.name.name === "language");
|
|
261
|
+
})) return;
|
|
262
|
+
const component = state.componentsStack.at(-1);
|
|
263
|
+
if (!component) return;
|
|
264
|
+
const langAttr = _babel_types.jsxAttribute(_babel_types.jsxIdentifier("lang"), _babel_types.jsxExpressionContainer(_babel_types.identifier("locale")));
|
|
265
|
+
openingElement.attributes.push(langAttr);
|
|
266
|
+
require_logger.logger.debug(`Injected locale attribute into <html> element in component: ${component.name}`);
|
|
267
|
+
state.componentsNeedingLocale.add(component.name);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Inject translation hook into a component when it's done processing
|
|
271
|
+
* Called when popping component from stack
|
|
272
|
+
*/
|
|
273
|
+
function injectTranslationHook(path, component, state) {
|
|
274
|
+
const needsLocale = state.componentsNeedingLocale.has(component.name);
|
|
275
|
+
const hashes = state.componentHashes.get(component.name);
|
|
276
|
+
if ((!hashes || hashes.length === 0) && !needsLocale) return;
|
|
277
|
+
if (component.isAsync) {
|
|
278
|
+
require_utils.injectServerHook(path, hashes ?? [], needsLocale);
|
|
279
|
+
state.needsAsyncImport = true;
|
|
280
|
+
} else {
|
|
281
|
+
require_utils.injectUnifiedHook(path, hashes ?? [], needsLocale);
|
|
282
|
+
state.needsUnifiedImport = true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Add translation imports at the top of the file
|
|
287
|
+
* Called on Program exit after all components have been processed
|
|
288
|
+
*/
|
|
289
|
+
function addTranslationImports(programPath, state) {
|
|
290
|
+
if (state.needsAsyncImport) programPath.node.body.unshift(require_utils.constructServerImport());
|
|
291
|
+
if (state.needsUnifiedImport) programPath.node.body.unshift(require_utils.comstructUnifiedImport());
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Handle component function detection and state update
|
|
295
|
+
*/
|
|
296
|
+
function processComponentFunction(path, state) {
|
|
297
|
+
if (!require_utils.isReactComponent(path)) {
|
|
298
|
+
path.skip();
|
|
299
|
+
require_logger.logger.debug(`Skipping non-React component: ${path.node.type}`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const componentName = require_utils.inferComponentName(path);
|
|
303
|
+
if (!componentName) {
|
|
304
|
+
path.skip();
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (!state.componentHashes.has(componentName)) state.componentHashes.set(componentName, []);
|
|
308
|
+
const componentEntry = {
|
|
309
|
+
name: componentName,
|
|
310
|
+
isAsync: path.node.async
|
|
311
|
+
};
|
|
312
|
+
state.componentsStack.push(componentEntry);
|
|
313
|
+
require_logger.logger.debug(`Component ${JSON.stringify(componentEntry)} entered`);
|
|
314
|
+
path.setData("componentName", componentName);
|
|
315
|
+
path.traverse(componentVisitors, { visitorState: state });
|
|
316
|
+
const component = state.componentsStack.pop();
|
|
317
|
+
if (component) injectTranslationHook(path, component, state);
|
|
318
|
+
path.skip();
|
|
319
|
+
}
|
|
320
|
+
const componentVisitors = {
|
|
321
|
+
FunctionDeclaration(path) {
|
|
322
|
+
processComponentFunction(path, this.visitorState);
|
|
323
|
+
},
|
|
324
|
+
ArrowFunctionExpression(path) {
|
|
325
|
+
processComponentFunction(path, this.visitorState);
|
|
326
|
+
},
|
|
327
|
+
FunctionExpression(path) {
|
|
328
|
+
processComponentFunction(path, this.visitorState);
|
|
329
|
+
},
|
|
330
|
+
JSXFragment(path) {
|
|
331
|
+
processJSXElement(path, this.visitorState);
|
|
332
|
+
},
|
|
333
|
+
JSXElement(path) {
|
|
334
|
+
translateAttributes(path.node, this.visitorState);
|
|
335
|
+
injectHtmlLangAttribute(path.node, this.visitorState);
|
|
336
|
+
if (shouldSkipTranslationForElement(path.node)) {
|
|
337
|
+
path.skip();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
processJSXElement(path, this.visitorState);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
const fileVisitors = {
|
|
344
|
+
ExportNamedDeclaration(path) {
|
|
345
|
+
const metadataState = require_metadata.processNextStaticMetadata(path, { filePath: this.visitorState.filePath });
|
|
346
|
+
if (metadataState) updateWithMetadataState(this.visitorState, metadataState);
|
|
347
|
+
},
|
|
348
|
+
FunctionDeclaration(path) {
|
|
349
|
+
const metadataState = require_metadata.processNextDynamicMetadata(path, { filePath: this.visitorState.filePath });
|
|
350
|
+
if (metadataState) updateWithMetadataState(this.visitorState, metadataState);
|
|
351
|
+
processComponentFunction(path, this.visitorState);
|
|
352
|
+
},
|
|
353
|
+
ArrowFunctionExpression(path) {
|
|
354
|
+
processComponentFunction(path, this.visitorState);
|
|
355
|
+
},
|
|
356
|
+
FunctionExpression(path) {
|
|
357
|
+
processComponentFunction(path, this.visitorState);
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Create Babel visitors for auto-translation (single-pass transformation)
|
|
362
|
+
*/
|
|
363
|
+
const programVisitor = { Program: {
|
|
364
|
+
enter(path) {
|
|
365
|
+
if (this.visitorState.needsDirective && !require_utils.hasUseI18nDirective(path)) {
|
|
366
|
+
path.skip();
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
path.traverse(fileVisitors, { visitorState: this.visitorState });
|
|
370
|
+
},
|
|
371
|
+
exit(path) {
|
|
372
|
+
if (this.visitorState.needsUnifiedImport || this.visitorState.needsAsyncImport) addTranslationImports(path, this.visitorState);
|
|
373
|
+
}
|
|
374
|
+
} };
|
|
375
|
+
function processFile(ast, options) {
|
|
376
|
+
const state = {
|
|
377
|
+
needsDirective: options.needsDirective,
|
|
378
|
+
filePath: options.relativeFilePath,
|
|
379
|
+
newEntries: [],
|
|
380
|
+
componentsStack: [],
|
|
381
|
+
componentHashes: /* @__PURE__ */ new Map(),
|
|
382
|
+
needsUnifiedImport: false,
|
|
383
|
+
needsAsyncImport: false,
|
|
384
|
+
componentsNeedingLocale: /* @__PURE__ */ new Set()
|
|
385
|
+
};
|
|
386
|
+
require_babel_compat.traverse(ast, programVisitor, void 0, { visitorState: state });
|
|
387
|
+
return state.newEntries;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
//#endregion
|
|
391
|
+
exports.processFile = processFile;
|