@menapo/vi18n 0.0.0-stage → 0.1.1
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 +49 -0
- package/README.md +110 -2
- package/dist/apply-Bre-lY9J.js +228 -0
- package/dist/captured-Bj8bO6vd.js +1034 -0
- package/dist/check-BIKEuu6B.js +201 -0
- package/dist/check-HsmmpJZg.js +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +41 -0
- package/dist/codec-DuvU2VQy.js +207 -0
- package/dist/collect-Cx4_SKOe.js +13 -0
- package/dist/collisions-CfpZU8e4.js +217 -0
- package/dist/compile-BaDh0cHG.js +245 -0
- package/dist/compile-Cc97JaW2.js +2502 -0
- package/dist/concurrency-C9UUWuDC.js +96 -0
- package/dist/concurrency-DONL7t4V.js +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.js +39 -0
- package/dist/dir-zw6bGMKQ.js +290 -0
- package/dist/duplicates-yuRljQ6h.js +185 -0
- package/dist/engine-C2ENBRJe.js +531 -0
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +1241 -0
- package/dist/exclude-Du-Zd_qI.js +26 -0
- package/dist/export-C9Sy6UWv.js +103 -0
- package/dist/extract-C3S8btx0.js +298 -0
- package/dist/formats-9unqj_fZ.js +781 -0
- package/dist/i18next-9HAoxwUI.js +1 -0
- package/dist/i18next-BEJgv_S8.js +1596 -0
- package/dist/import-nANvQutw.js +225 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.js +239 -0
- package/dist/init-C2ScuW6a.js +792 -0
- package/dist/library-D5CWvTQs.js +1882 -0
- package/dist/lingui-zUiCl9Um.js +833 -0
- package/dist/lint.d.ts +105 -0
- package/dist/lint.js +971 -0
- package/dist/load-CQRz4PpM.js +2950 -0
- package/dist/load-gW-oJt1Q.js +747 -0
- package/dist/locales-DX4MOBFE.js +827 -0
- package/dist/lsp-RMMMc88P.js +31 -0
- package/dist/match-BGr8EXB3.js +89 -0
- package/dist/mcp-CHVwBoRP.js +28 -0
- package/dist/merge-driver-BTX87aVm.js +422 -0
- package/dist/messages-BfjIExyB.js +166 -0
- package/dist/migrate-IpLDMXmq.js +1484 -0
- package/dist/migrate-rCQuOGsf.js +1 -0
- package/dist/openai-CTaMDcl-.js +346 -0
- package/dist/output-DWE3SIUA.js +164 -0
- package/dist/parse.d.ts +62 -0
- package/dist/parse.js +156 -0
- package/dist/pipeline-C9Dbotkh.js +2031 -0
- package/dist/pipeline-DbX6rlo5.js +1 -0
- package/dist/project-Bw9UPIdx.js +1046 -0
- package/dist/project-C4GpaN-n.js +381 -0
- package/dist/pseudo-DRv30iN5.js +145 -0
- package/dist/raw-C4Y6vyGY.js +4708 -0
- package/dist/react-intl-BP0u-D5m.js +519 -0
- package/dist/restore-CiYv-jxS.js +175 -0
- package/dist/rewrite-EsIw_lsq.js +803 -0
- package/dist/rich-DCUKl5HM.js +97 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/run-BEcnBbu-.js +694 -0
- package/dist/runtime-DTMmliT1.js +64 -0
- package/dist/scan-worker.d.ts +62 -0
- package/dist/scan-worker.js +1 -0
- package/dist/scoped-BMYpSthj.js +44 -0
- package/dist/server-Da1Ot9IN.js +10485 -0
- package/dist/server-deBOq2vq.js +11823 -0
- package/dist/server.d.ts +169 -0
- package/dist/server.js +1234 -0
- package/dist/sitemap-DJXzKoza.js +159 -0
- package/dist/sources-D19czDSA.js +2242 -0
- package/dist/status-zr4jY59Y.js +149 -0
- package/dist/stdio-CVaJfxA7.js +29 -0
- package/dist/stdio-FPOKQAgu.js +95 -0
- package/dist/sync-B9V2KFwb.js +658 -0
- package/dist/t-DC-c9zE5.js +454 -0
- package/dist/test.d.ts +116 -0
- package/dist/test.js +319 -0
- package/dist/translate-CQnJfLjO.js +299 -0
- package/dist/ts-BaU5yOf0.js +4863 -0
- package/dist/types-CgUCkFU1.d.ts +707 -0
- package/dist/types-D3QGM8XO.js +146 -0
- package/dist/types-DsdWBG0b.d.ts +558 -0
- package/dist/types-DtvQ7UdD.js +1948 -0
- package/dist/types-wSh9fCBY.js +11 -0
- package/dist/units-CQ5Q99RC.js +150 -0
- package/dist/url-CRfCOhUI.js +172 -0
- package/dist/url-CsBkRaJy.js +225 -0
- package/dist/validate-BxpupdAf.js +2615 -0
- package/dist/validate-D6DJepZv.js +365 -0
- package/dist/version-Byqi2guX.js +28 -0
- package/dist/vite-scan-CT_vsp4g.js +242 -0
- package/dist/vite.d.ts +34 -0
- package/dist/vite.js +7558 -0
- package/dist/vue-i18n-g0iGy6CZ.js +651 -0
- package/dist/which-BOemZ5Q8.js +116 -0
- package/dist/worker-84U6uYB3.js +1656 -0
- package/package.json +97 -3
- package/schemas/catalog.json +57 -0
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
import { o as isDiagnosticError } from "./types-D3QGM8XO.js";
|
|
2
|
+
import { n as printICU } from "./codec-DuvU2VQy.js";
|
|
3
|
+
import { h as parsePo } from "./library-D5CWvTQs.js";
|
|
4
|
+
import { l as langFromId, s as decodeEntities, u as parseModule } from "./worker-84U6uYB3.js";
|
|
5
|
+
import { _ as keyName, a as attributeExpression, b as stringValue, g as jsxNameNodes, h as jsxName, i as analyzeModule, l as identifierName, o as attributeString, p as jsxAttributes, s as childEntries, t as ModuleBuilder, u as inJsxChildren, v as memberParts, x as unwrap, y as objectProperties } from "./rewrite-EsIw_lsq.js";
|
|
6
|
+
import { _ as linguiId, c as newNaming, d as richCall, f as selfClosingTags, l as reactTypes, o as elementHandler, p as siteHandlers, s as entry, t as MODULE_EXTENSIONS, u as renameTags, v as parseJson } from "./i18next-BEJgv_S8.js";
|
|
7
|
+
import { extname } from "node:path";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
//#region src/cli/commands/migrate/lingui.ts
|
|
10
|
+
/**
|
|
11
|
+
* `vi18n migrate --from lingui` (INTERNALS §2.14): Lingui 3–5 macros and
|
|
12
|
+
* runtime.
|
|
13
|
+
*
|
|
14
|
+
* The source text is what the macros compile to, built the way Lingui
|
|
15
|
+
* builds it (`${name}` → `{name}`, other expressions → `{0}`, `plural()` /
|
|
16
|
+
* `select()` / `selectOrdinal()` and `<Plural>`… as ICU, JSX elements as
|
|
17
|
+
* `<0>…</0>`), so translations are found under the id Lingui gives it
|
|
18
|
+
* (`generateMessageId(message, context)`, or the explicit `id`). Catalogs
|
|
19
|
+
* are PO (`#. js-lingui-explicit-id` marks an explicit id), Lingui JSON or
|
|
20
|
+
* minimal JSON; a fuzzy translation is kept for review, not shipped. A
|
|
21
|
+
* `context` is a translator note and, when two messages share a text but
|
|
22
|
+
* not their translations, the stem of their manual ids (PLAN §5.6).
|
|
23
|
+
* `t`, `msg`/`defineMessage`, `<Trans>`, `<Plural>`, `i18n._()` become
|
|
24
|
+
* `t()`, `msg()`, `rich()`; macro imports, `useLingui()` bindings and
|
|
25
|
+
* `<I18nProvider>` go once unused.
|
|
26
|
+
*/
|
|
27
|
+
function isOldSource(source) {
|
|
28
|
+
return source.startsWith("@lingui/");
|
|
29
|
+
}
|
|
30
|
+
const MACRO_SOURCES = /* @__PURE__ */ new Set([
|
|
31
|
+
"@lingui/macro",
|
|
32
|
+
"@lingui/core/macro",
|
|
33
|
+
"@lingui/react/macro"
|
|
34
|
+
]);
|
|
35
|
+
var MessageText = class MessageText {
|
|
36
|
+
text = "";
|
|
37
|
+
shared;
|
|
38
|
+
constructor(shared) {
|
|
39
|
+
this.shared = shared ?? {
|
|
40
|
+
values: [],
|
|
41
|
+
elements: [],
|
|
42
|
+
consumed: [],
|
|
43
|
+
positional: 0,
|
|
44
|
+
problem: null
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
get values() {
|
|
48
|
+
return this.shared.values;
|
|
49
|
+
}
|
|
50
|
+
get elements() {
|
|
51
|
+
return this.shared.elements;
|
|
52
|
+
}
|
|
53
|
+
get problem() {
|
|
54
|
+
return this.shared.problem;
|
|
55
|
+
}
|
|
56
|
+
/** A part of the same message (a plural case): same values and counters, its own text. */
|
|
57
|
+
nested() {
|
|
58
|
+
return new MessageText(this.shared);
|
|
59
|
+
}
|
|
60
|
+
/** `{name}` for an identifier, `{0}`, `{1}`… for other expressions, as Lingui names them. */
|
|
61
|
+
argument(expression) {
|
|
62
|
+
const n = unwrap(expression);
|
|
63
|
+
if (n.type === "Identifier") {
|
|
64
|
+
const name = n.name;
|
|
65
|
+
if (!this.values.some((v) => v.name === name)) this.values.push({
|
|
66
|
+
name,
|
|
67
|
+
expression: n
|
|
68
|
+
});
|
|
69
|
+
return name;
|
|
70
|
+
}
|
|
71
|
+
const name = String(this.shared.positional++);
|
|
72
|
+
this.values.push({
|
|
73
|
+
name,
|
|
74
|
+
expression: n
|
|
75
|
+
});
|
|
76
|
+
return name;
|
|
77
|
+
}
|
|
78
|
+
fail(problem) {
|
|
79
|
+
this.shared.problem ??= problem;
|
|
80
|
+
}
|
|
81
|
+
built() {
|
|
82
|
+
return {
|
|
83
|
+
message: this.text,
|
|
84
|
+
values: this.values,
|
|
85
|
+
elements: this.elements,
|
|
86
|
+
consumed: this.shared.consumed,
|
|
87
|
+
problem: this.problem
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/** Lingui's whitespace rule for JSX messages: a line break and the indentation after it are one space. */
|
|
92
|
+
function normalizeWhitespace(text) {
|
|
93
|
+
return text.replace(/(?:\\(?:\r\n|\r|\n))+\s+/g, " ").replace(/(?:\r\n|\r|\n)+\s+/g, " ").trim();
|
|
94
|
+
}
|
|
95
|
+
/** `=N` for numeric choice keys (`0`, `_0`), CLDR categories as they are, select values without `_`. */
|
|
96
|
+
function choiceKey(key) {
|
|
97
|
+
if (/^\d+$/.test(key)) return `=${key}`;
|
|
98
|
+
if (/^_\d+$/.test(key)) return `=${key.slice(1)}`;
|
|
99
|
+
if (key.startsWith("_")) return key.slice(1);
|
|
100
|
+
return key;
|
|
101
|
+
}
|
|
102
|
+
function createLinguiAdapter() {
|
|
103
|
+
/** locale → Lingui id → entry. */
|
|
104
|
+
const catalogs = /* @__PURE__ */ new Map();
|
|
105
|
+
/** v3 minimal JSON keys catalogs by message text: locale → text → translation. */
|
|
106
|
+
const byText = /* @__PURE__ */ new Map();
|
|
107
|
+
const namings = /* @__PURE__ */ new Map();
|
|
108
|
+
/** Source-catalog entries some call site found, for the unused-key report. */
|
|
109
|
+
const found = /* @__PURE__ */ new Set();
|
|
110
|
+
let sourceLocale = "";
|
|
111
|
+
const put = (locale, id, entryValue) => {
|
|
112
|
+
let map = catalogs.get(locale);
|
|
113
|
+
if (map === void 0) catalogs.set(locale, map = /* @__PURE__ */ new Map());
|
|
114
|
+
map.set(id, entryValue);
|
|
115
|
+
let texts = byText.get(locale);
|
|
116
|
+
if (texts === void 0) byText.set(locale, texts = /* @__PURE__ */ new Map());
|
|
117
|
+
if (entryValue.message !== null && !texts.has(entryValue.message)) texts.set(entryValue.message, entryValue);
|
|
118
|
+
};
|
|
119
|
+
const readCatalog = (file, env) => {
|
|
120
|
+
let text;
|
|
121
|
+
try {
|
|
122
|
+
text = readFileSync(file.file, "utf8");
|
|
123
|
+
} catch (error) {
|
|
124
|
+
env.report("catalog-parse", `cannot read: ${error.message}`, { file: file.file }, "error");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (extname(file.file) === ".po") {
|
|
128
|
+
const parsed = parsePo(text, {
|
|
129
|
+
locale: file.locale,
|
|
130
|
+
layout: "source",
|
|
131
|
+
isSourceLocale: false
|
|
132
|
+
});
|
|
133
|
+
for (const d of parsed.diagnostics) if (d.severity === "error") env.diagnostics.push({
|
|
134
|
+
...d,
|
|
135
|
+
file: env.rel(file.file)
|
|
136
|
+
});
|
|
137
|
+
for (const e of parsed.catalog.entries) {
|
|
138
|
+
const explicit = e.contexts.some((c) => c.trim() === "js-lingui-explicit-id");
|
|
139
|
+
const idNote = e.contexts.map((c) => /^js-lingui-id:\s*(\S+)/.exec(c.trim())?.[1]).find((x) => x !== void 0);
|
|
140
|
+
const context = e.manualId ?? e.foreignContext;
|
|
141
|
+
const msgid = e.source ?? "";
|
|
142
|
+
const id = explicit ? msgid : idNote ?? linguiId(msgid, context);
|
|
143
|
+
put(file.locale, id, {
|
|
144
|
+
message: explicit ? file.locale === sourceLocale && e.translation !== "" ? e.translation : null : msgid,
|
|
145
|
+
translation: e.translation,
|
|
146
|
+
fuzzy: e.fuzzy,
|
|
147
|
+
context,
|
|
148
|
+
file: file.file,
|
|
149
|
+
line: e.line
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const parsed = parseJson(text);
|
|
155
|
+
if (parsed.problem !== null) {
|
|
156
|
+
env.report("catalog-parse", parsed.problem.message, {
|
|
157
|
+
file: file.file,
|
|
158
|
+
line: parsed.problem.line
|
|
159
|
+
}, "error");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (typeof parsed.value !== "object" || parsed.value === null || Array.isArray(parsed.value)) {
|
|
163
|
+
env.report("catalog-parse", "a Lingui JSON catalog is an object of messages by id", { file: file.file }, "error");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const lines = text.split("\n");
|
|
167
|
+
for (const [id, value] of Object.entries(parsed.value)) {
|
|
168
|
+
const line = lines.findIndex((l) => l.includes(`${JSON.stringify(id)}:`)) + 1;
|
|
169
|
+
if (typeof value === "string") {
|
|
170
|
+
put(file.locale, id, {
|
|
171
|
+
message: null,
|
|
172
|
+
translation: value,
|
|
173
|
+
fuzzy: false,
|
|
174
|
+
context: null,
|
|
175
|
+
file: file.file,
|
|
176
|
+
line
|
|
177
|
+
});
|
|
178
|
+
let texts = byText.get(file.locale);
|
|
179
|
+
if (texts === void 0) byText.set(file.locale, texts = /* @__PURE__ */ new Map());
|
|
180
|
+
texts.set(id, {
|
|
181
|
+
message: id,
|
|
182
|
+
translation: value,
|
|
183
|
+
fuzzy: false,
|
|
184
|
+
context: null,
|
|
185
|
+
file: file.file,
|
|
186
|
+
line
|
|
187
|
+
});
|
|
188
|
+
} else if (typeof value === "object" && value !== null) {
|
|
189
|
+
const record = value;
|
|
190
|
+
put(file.locale, id, {
|
|
191
|
+
message: typeof record.message === "string" ? record.message : null,
|
|
192
|
+
translation: typeof record.translation === "string" ? record.translation : "",
|
|
193
|
+
fuzzy: record.fuzzy === true || Array.isArray(record.flags) && record.flags.includes("fuzzy"),
|
|
194
|
+
context: typeof record.context === "string" ? record.context : null,
|
|
195
|
+
file: file.file,
|
|
196
|
+
line
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
/** The catalog entry of a message in a locale: by id, else (v3) by its text. */
|
|
202
|
+
const lookup = (locale, id, message, context) => {
|
|
203
|
+
let entryFound = catalogs.get(locale)?.get(id);
|
|
204
|
+
if (entryFound === void 0 && message !== null) {
|
|
205
|
+
const byMessage = byText.get(locale)?.get(message);
|
|
206
|
+
if (byMessage !== void 0 && (byMessage.context ?? null) === context) entryFound = byMessage;
|
|
207
|
+
}
|
|
208
|
+
if (entryFound !== void 0 && locale === sourceLocale) {
|
|
209
|
+
found.add(entryFound);
|
|
210
|
+
const other = catalogs.get(locale)?.get(entryFound.message ?? "");
|
|
211
|
+
if (other !== void 0) found.add(other);
|
|
212
|
+
}
|
|
213
|
+
return entryFound;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Registers a message. `message` is the macro's text (null for a runtime
|
|
217
|
+
* lookup by explicit id, whose text comes from the source catalog).
|
|
218
|
+
*/
|
|
219
|
+
const define = (env, input, origin, tags) => {
|
|
220
|
+
const id = input.id ?? linguiId(input.message ?? "", input.context);
|
|
221
|
+
if (env.message(id) !== void 0) {
|
|
222
|
+
lookup(sourceLocale, id, input.message, input.context);
|
|
223
|
+
return { key: id };
|
|
224
|
+
}
|
|
225
|
+
const sourceEntry = lookup(sourceLocale, id, input.message, input.context);
|
|
226
|
+
let source = input.message;
|
|
227
|
+
if (source === null) {
|
|
228
|
+
source = sourceEntry?.message ?? (sourceEntry?.translation || null);
|
|
229
|
+
if (source === null) return { problem: `${id} has no message in the code or the ${sourceLocale} catalog` };
|
|
230
|
+
}
|
|
231
|
+
const naming = newNaming();
|
|
232
|
+
const rename = (text, isSource) => {
|
|
233
|
+
if (tags === null) return text;
|
|
234
|
+
let nodes;
|
|
235
|
+
try {
|
|
236
|
+
nodes = env.codec.parse(text);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (!isDiagnosticError(error)) throw error;
|
|
239
|
+
return { error: error.diagnostic.message };
|
|
240
|
+
}
|
|
241
|
+
return printICU(renameTags(nodes, naming, tags.pathOf, tags.elementAt, isSource));
|
|
242
|
+
};
|
|
243
|
+
const numbered = (text) => text.replace(/<(\/?)(\d+)(\/?)>/g, (_, a, n, b) => `<${a}t${n}${b}>`);
|
|
244
|
+
const sourceText = rename(numbered(source), true);
|
|
245
|
+
if (typeof sourceText !== "string") return { problem: `${id}: ${sourceText.error}` };
|
|
246
|
+
const translations = /* @__PURE__ */ new Map();
|
|
247
|
+
for (const locale of catalogs.keys()) {
|
|
248
|
+
if (locale === sourceLocale) continue;
|
|
249
|
+
const found = lookup(locale, id, input.message, input.context);
|
|
250
|
+
if (found === void 0 || found.translation === "") continue;
|
|
251
|
+
if (found.fuzzy) {
|
|
252
|
+
translations.set(locale, {
|
|
253
|
+
unconverted: found.translation,
|
|
254
|
+
reason: "Lingui marked it fuzzy",
|
|
255
|
+
file: found.file,
|
|
256
|
+
line: found.line
|
|
257
|
+
});
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const converted = rename(numbered(found.translation), false);
|
|
261
|
+
translations.set(locale, typeof converted === "string" ? { icu: converted } : {
|
|
262
|
+
unconverted: found.translation,
|
|
263
|
+
reason: converted.error,
|
|
264
|
+
file: found.file,
|
|
265
|
+
line: found.line
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const notes = [input.comment, input.context].filter((n) => n !== null && n !== "");
|
|
269
|
+
if (!env.define({
|
|
270
|
+
key: id,
|
|
271
|
+
named: input.id !== null,
|
|
272
|
+
source: sourceText,
|
|
273
|
+
context: notes.length === 0 ? null : notes.join("; "),
|
|
274
|
+
hint: input.context,
|
|
275
|
+
translations,
|
|
276
|
+
file: origin.file,
|
|
277
|
+
line: origin.line
|
|
278
|
+
})) return { problem: `${id} could not be converted` };
|
|
279
|
+
if (tags !== null) namings.set(id, naming);
|
|
280
|
+
return { key: id };
|
|
281
|
+
};
|
|
282
|
+
const planModule = (file, text, env) => {
|
|
283
|
+
let program;
|
|
284
|
+
try {
|
|
285
|
+
program = parseModule(text, langFromId(file), file).program;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
if (!isDiagnosticError(error)) throw error;
|
|
288
|
+
env.diagnostics.push({
|
|
289
|
+
...error.diagnostic,
|
|
290
|
+
file: env.rel(file)
|
|
291
|
+
});
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const info = analyzeModule(program);
|
|
295
|
+
const builder = new ModuleBuilder({
|
|
296
|
+
file,
|
|
297
|
+
text,
|
|
298
|
+
info,
|
|
299
|
+
isOldSource
|
|
300
|
+
});
|
|
301
|
+
const tsx = extname(file) === ".tsx";
|
|
302
|
+
const lineOf = (offset) => text.slice(0, offset).split("\n").length;
|
|
303
|
+
const imported = (name, macro = null) => {
|
|
304
|
+
const locals = /* @__PURE__ */ new Set();
|
|
305
|
+
for (const declaration of info.imports) {
|
|
306
|
+
if (!isOldSource(declaration.source)) continue;
|
|
307
|
+
if (macro !== null && MACRO_SOURCES.has(declaration.source) !== macro) continue;
|
|
308
|
+
for (const s of declaration.specifiers) if (s.imported === name) locals.add(s.local);
|
|
309
|
+
}
|
|
310
|
+
return locals;
|
|
311
|
+
};
|
|
312
|
+
const macroT = imported("t", true);
|
|
313
|
+
const macroMsg = /* @__PURE__ */ new Set([...imported("msg", true), ...imported("defineMessage", true)]);
|
|
314
|
+
const choiceMacros = /* @__PURE__ */ new Map();
|
|
315
|
+
for (const [name, format] of [
|
|
316
|
+
["plural", "plural"],
|
|
317
|
+
["select", "select"],
|
|
318
|
+
["selectOrdinal", "selectordinal"]
|
|
319
|
+
]) for (const local of imported(name, true)) choiceMacros.set(local, format);
|
|
320
|
+
const transMacro = imported("Trans", true);
|
|
321
|
+
const choiceComponents = /* @__PURE__ */ new Map();
|
|
322
|
+
for (const [name, format] of [
|
|
323
|
+
["Plural", "plural"],
|
|
324
|
+
["Select", "select"],
|
|
325
|
+
["SelectOrdinal", "selectordinal"]
|
|
326
|
+
]) for (const local of imported(name, true)) choiceComponents.set(local, format);
|
|
327
|
+
const providers = imported("I18nProvider");
|
|
328
|
+
const runtimeI18n = imported("i18n");
|
|
329
|
+
const linguiT = /* @__PURE__ */ new Set();
|
|
330
|
+
const linguiUnderscore = /* @__PURE__ */ new Set();
|
|
331
|
+
const linguiInstances = /* @__PURE__ */ new Set();
|
|
332
|
+
const useLingui = imported("useLingui");
|
|
333
|
+
for (const binding of info.bindings) {
|
|
334
|
+
const init = binding.declarator?.init;
|
|
335
|
+
if (init === null || init === void 0) continue;
|
|
336
|
+
const call = unwrap(init);
|
|
337
|
+
const callee = call.type === "CallExpression" ? identifierName(call.callee) : null;
|
|
338
|
+
if (callee === null || !useLingui.has(callee)) continue;
|
|
339
|
+
builder.hooks.push(binding);
|
|
340
|
+
const slot = binding.slot;
|
|
341
|
+
const key = slot !== null && slot.type === "Property" ? keyName(slot) : null;
|
|
342
|
+
if (key === "t") linguiT.add(binding);
|
|
343
|
+
else if (key === "_") linguiUnderscore.add(binding);
|
|
344
|
+
else if (key === "i18n") linguiInstances.add(binding);
|
|
345
|
+
}
|
|
346
|
+
/** Locals holding `msg`/`defineMessage` results: their members are `msg()` refs now. */
|
|
347
|
+
const messageTables = /* @__PURE__ */ new Set();
|
|
348
|
+
for (const binding of info.bindings) {
|
|
349
|
+
const init = binding.declarator?.init;
|
|
350
|
+
if (init === null || init === void 0) continue;
|
|
351
|
+
let found = false;
|
|
352
|
+
const look = (n) => {
|
|
353
|
+
if (found) return;
|
|
354
|
+
if (n.type === "TaggedTemplateExpression" || n.type === "CallExpression") {
|
|
355
|
+
const callee = identifierName(n.type === "CallExpression" ? n.callee : n.tag);
|
|
356
|
+
if (callee !== null && macroMsg.has(callee)) found = true;
|
|
357
|
+
}
|
|
358
|
+
for (const [, child] of childEntries(n)) look(child);
|
|
359
|
+
};
|
|
360
|
+
look(init);
|
|
361
|
+
if (found) messageTables.add(binding.name);
|
|
362
|
+
}
|
|
363
|
+
const relativeImports = /* @__PURE__ */ new Set();
|
|
364
|
+
for (const declaration of info.imports) {
|
|
365
|
+
if (!declaration.source.startsWith(".")) continue;
|
|
366
|
+
for (const s of declaration.specifiers) relativeImports.add(s.local);
|
|
367
|
+
}
|
|
368
|
+
const resolve = (name, offset) => {
|
|
369
|
+
let best = null;
|
|
370
|
+
for (const b of info.bindings) {
|
|
371
|
+
if (b.name !== name || offset < b.scope.start || offset >= b.scope.end) continue;
|
|
372
|
+
if (best === null || b.scope.end - b.scope.start < best.scope.end - best.scope.start) best = b;
|
|
373
|
+
}
|
|
374
|
+
return best;
|
|
375
|
+
};
|
|
376
|
+
const isMacroT = (callee) => {
|
|
377
|
+
const n = unwrap(callee);
|
|
378
|
+
if (n.type === "CallExpression") return isMacroT(n.callee);
|
|
379
|
+
if (n.type !== "Identifier") return false;
|
|
380
|
+
const binding = resolve(n.name, n.start);
|
|
381
|
+
return binding !== null && (macroT.has(n.name) ? binding.kind === "import" : linguiT.has(binding));
|
|
382
|
+
};
|
|
383
|
+
const calleeNodes = (callee) => {
|
|
384
|
+
const n = unwrap(callee);
|
|
385
|
+
if (n.type === "CallExpression") return calleeNodes(n.callee);
|
|
386
|
+
return n.type === "Identifier" ? [n] : [];
|
|
387
|
+
};
|
|
388
|
+
/** Choice macros and components a message absorbed: not sites of their own. */
|
|
389
|
+
const absorbed = /* @__PURE__ */ new Set();
|
|
390
|
+
const template = (quasi, out) => {
|
|
391
|
+
const quasis = quasi.quasis;
|
|
392
|
+
const expressions = quasi.expressions;
|
|
393
|
+
for (let i = 0; i < quasis.length; i++) {
|
|
394
|
+
out.text += quasis[i].value.cooked ?? "";
|
|
395
|
+
const expression = expressions[i];
|
|
396
|
+
if (expression !== void 0) placeholder(expression, out);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const placeholder = (expression, out) => {
|
|
400
|
+
const n = unwrap(expression);
|
|
401
|
+
if (n.type === "CallExpression") {
|
|
402
|
+
const callee = identifierName(n.callee);
|
|
403
|
+
const format = callee === null ? void 0 : choiceMacros.get(callee);
|
|
404
|
+
if (format !== void 0) {
|
|
405
|
+
absorbed.add(n);
|
|
406
|
+
out.shared.consumed.push(unwrap(n.callee));
|
|
407
|
+
choice(n, format, out);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
out.text += `{${out.argument(n)}}`;
|
|
412
|
+
};
|
|
413
|
+
/** `plural(value, { one: "…", other: \`…\` })` as `{value, plural, one {…} other {…}}`. */
|
|
414
|
+
const choice = (call, format, out) => {
|
|
415
|
+
const [value, options] = call.arguments;
|
|
416
|
+
const props = options === void 0 ? null : objectProperties(options);
|
|
417
|
+
if (value === void 0 || props === null || props.spreads.length > 0) {
|
|
418
|
+
out.fail(`${format}() needs a value and an object literal of cases`);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const name = out.argument(value);
|
|
422
|
+
const cases = [];
|
|
423
|
+
for (const [key, property] of props.properties) {
|
|
424
|
+
const v = unwrap(property.value);
|
|
425
|
+
if (key === "offset") {
|
|
426
|
+
cases.unshift(`offset:${String(v.value)}`);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
const inner = out.nested();
|
|
430
|
+
const literal = stringValue(v);
|
|
431
|
+
if (literal !== null) inner.text = literal;
|
|
432
|
+
else if (v.type === "TemplateLiteral") template(v, inner);
|
|
433
|
+
else placeholder(v, inner);
|
|
434
|
+
cases.push(`${choiceKey(key)} {${inner.text}}`);
|
|
435
|
+
}
|
|
436
|
+
out.text += `{${name}, ${format}, ${cases.join(" ")}}`;
|
|
437
|
+
};
|
|
438
|
+
/** JSX children of `<Trans>` (or a choice component's JSX option). */
|
|
439
|
+
const jsx = (children, out) => {
|
|
440
|
+
for (const child of children) if (child.type === "JSXText") out.text += decodeEntities(child.value);
|
|
441
|
+
else if (child.type === "JSXExpressionContainer") {
|
|
442
|
+
const expression = unwrap(child.expression);
|
|
443
|
+
if (expression.type === "JSXEmptyExpression") continue;
|
|
444
|
+
const literal = stringValue(expression);
|
|
445
|
+
if (literal !== null) out.text += literal;
|
|
446
|
+
else if (expression.type === "TemplateLiteral") template(expression, out);
|
|
447
|
+
else placeholder(expression, out);
|
|
448
|
+
} else if (child.type === "JSXElement") {
|
|
449
|
+
const name = jsxName(child);
|
|
450
|
+
const format = choiceComponents.get(name);
|
|
451
|
+
if (format !== void 0) {
|
|
452
|
+
absorbed.add(child);
|
|
453
|
+
out.shared.consumed.push(...jsxNameNodes(child));
|
|
454
|
+
choiceComponent(child, format, out);
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
const index = out.elements.length;
|
|
458
|
+
out.elements.push(child);
|
|
459
|
+
if (child.children.filter((c) => !(c.type === "JSXText" && c.value.trim() === "" && c.value.includes("\n"))).length === 0) out.text += `<${index}/>`;
|
|
460
|
+
else {
|
|
461
|
+
out.text += `<${index}>`;
|
|
462
|
+
jsx(child.children, out);
|
|
463
|
+
out.text += `</${index}>`;
|
|
464
|
+
}
|
|
465
|
+
} else if (child.type === "JSXFragment") jsx(child.children, out);
|
|
466
|
+
};
|
|
467
|
+
/** `<Plural value={n} one="…" other={<b>…</b>} />` as an ICU plural. */
|
|
468
|
+
const choiceComponent = (element, format, out) => {
|
|
469
|
+
const attributes = jsxAttributes(element);
|
|
470
|
+
const value = attributeExpression(attributes.named.get("value")?.value ?? null);
|
|
471
|
+
if (value === null || attributes.spreads.length > 0) {
|
|
472
|
+
out.fail(`<${jsxName(element)}> needs a value={…} and no spread props`);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const name = out.argument(value);
|
|
476
|
+
const cases = [];
|
|
477
|
+
for (const [key, { value: attribute }] of attributes.named) {
|
|
478
|
+
if (key === "value" || key === "id" || key === "comment" || key === "context" || key === "render") continue;
|
|
479
|
+
if (key === "offset") {
|
|
480
|
+
cases.unshift(`offset:${attributeString(attribute) ?? String(attributeExpression(attribute)?.value)}`);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
const inner = out.nested();
|
|
484
|
+
const literal = attributeString(attribute);
|
|
485
|
+
if (literal !== null) inner.text = literal;
|
|
486
|
+
else {
|
|
487
|
+
const expression = attributeExpression(attribute);
|
|
488
|
+
if (expression === null) continue;
|
|
489
|
+
const n = unwrap(expression);
|
|
490
|
+
if (n.type === "JSXElement" || n.type === "JSXFragment") jsx([n], inner);
|
|
491
|
+
else if (n.type === "TemplateLiteral") template(n, inner);
|
|
492
|
+
else placeholder(n, inner);
|
|
493
|
+
}
|
|
494
|
+
cases.push(`${choiceKey(key)} {${inner.text}}`);
|
|
495
|
+
}
|
|
496
|
+
out.text += `{${name}, ${format}, ${cases.join(" ")}}`;
|
|
497
|
+
};
|
|
498
|
+
/** `{ id, message, context, comment }` of a macro descriptor (message a string or template). */
|
|
499
|
+
const descriptor = (node, out) => {
|
|
500
|
+
const props = objectProperties(node);
|
|
501
|
+
if (props === null || props.spreads.length > 0) return null;
|
|
502
|
+
const field = (name) => {
|
|
503
|
+
const property = props.properties.get(name);
|
|
504
|
+
if (property === void 0) return null;
|
|
505
|
+
return stringValue(property.value) ?? void 0;
|
|
506
|
+
};
|
|
507
|
+
const id = field("id");
|
|
508
|
+
const context = field("context");
|
|
509
|
+
const comment = field("comment");
|
|
510
|
+
if (id === void 0 || context === void 0 || comment === void 0) return null;
|
|
511
|
+
const messageProperty = props.properties.get("message");
|
|
512
|
+
if (messageProperty === void 0) return {
|
|
513
|
+
id,
|
|
514
|
+
message: null,
|
|
515
|
+
context,
|
|
516
|
+
comment
|
|
517
|
+
};
|
|
518
|
+
const value = unwrap(messageProperty.value);
|
|
519
|
+
const literal = stringValue(value);
|
|
520
|
+
if (literal !== null) out.text = literal;
|
|
521
|
+
else if (value.type === "TemplateLiteral") template(value, out);
|
|
522
|
+
else return null;
|
|
523
|
+
return {
|
|
524
|
+
id,
|
|
525
|
+
message: out.text,
|
|
526
|
+
context,
|
|
527
|
+
comment
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
const valuesCode = (r, values, extra = []) => {
|
|
531
|
+
const entries = [...extra, ...values.map(({ name, expression }) => entry(name, r.src(expression.start, expression.end)))];
|
|
532
|
+
return entries.length === 0 ? "" : `, { ${entries.join(", ")} }`;
|
|
533
|
+
};
|
|
534
|
+
/** A `t` macro (template or descriptor) or a standalone choice macro, as `t()`. */
|
|
535
|
+
const tSite = (node, built, input, consume) => {
|
|
536
|
+
if (built.problem !== null) {
|
|
537
|
+
builder.note(node.start, `${built.problem}; not rewritten`);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const defined = define(env, {
|
|
541
|
+
...input,
|
|
542
|
+
message: built.message
|
|
543
|
+
}, {
|
|
544
|
+
file,
|
|
545
|
+
line: lineOf(node.start)
|
|
546
|
+
}, null);
|
|
547
|
+
if ("problem" in defined) {
|
|
548
|
+
builder.note(node.start, `${defined.problem}; not rewritten`);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
builder.site(node, (r) => `${r.fn.t}(${r.message(defined.key)}${valuesCode(r, built.values)})`, {
|
|
552
|
+
keys: [defined.key],
|
|
553
|
+
uses: ["t"],
|
|
554
|
+
consume: [...consume, ...built.consumed]
|
|
555
|
+
});
|
|
556
|
+
};
|
|
557
|
+
/** `<Trans>` (or a choice component) whose message is `built`, as `rich()` or `t()`. */
|
|
558
|
+
const jsxSite = (element, built, input) => {
|
|
559
|
+
if (built.problem !== null) {
|
|
560
|
+
builder.note(element.start, `<${jsxName(element)}>: ${built.problem}; not rewritten`);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const elements = built.elements;
|
|
564
|
+
const pathOf = (name) => /^t\d+$/.test(name) ? name.slice(1) : null;
|
|
565
|
+
const elementAt = (path) => elements[Number(path)] ?? null;
|
|
566
|
+
const defined = define(env, {
|
|
567
|
+
...input,
|
|
568
|
+
message: built.message
|
|
569
|
+
}, {
|
|
570
|
+
file,
|
|
571
|
+
line: lineOf(element.start)
|
|
572
|
+
}, {
|
|
573
|
+
pathOf,
|
|
574
|
+
elementAt
|
|
575
|
+
});
|
|
576
|
+
if ("problem" in defined) {
|
|
577
|
+
builder.note(element.start, `${defined.problem}; <${jsxName(element)}> was not rewritten`);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const message = env.message(defined.key);
|
|
581
|
+
const naming = namings.get(defined.key);
|
|
582
|
+
if (message === void 0 || naming === void 0) return;
|
|
583
|
+
const { handlers, missing } = siteHandlers(naming, elementAt);
|
|
584
|
+
if (missing.length > 0) {
|
|
585
|
+
builder.note(element.start, `<${jsxName(element)}>: a tag maps to no element here; not rewritten`, [defined.key]);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const selfClosing = selfClosingTags(env.codec.parse(message.canonical));
|
|
589
|
+
const rich = handlers.size > 0;
|
|
590
|
+
const types = rich && tsx ? reactTypes(builder) : null;
|
|
591
|
+
builder.site(element, (r) => {
|
|
592
|
+
const handlerEntries = [...handlers].map(([name, handler]) => entry(name, elementHandler(handler, selfClosing.has(name), r, types)));
|
|
593
|
+
const values = built.values.map(({ name, expression }) => entry(name, r.src(expression.start, expression.end)));
|
|
594
|
+
const call = rich ? richCall(r, defined.key, [...handlerEntries, ...values], types) : `${r.fn.t}(${r.message(defined.key)}${values.length === 0 ? "" : `, { ${values.join(", ")} }`})`;
|
|
595
|
+
return inJsxChildren(info, element) ? `{${call}}` : call;
|
|
596
|
+
}, {
|
|
597
|
+
keys: [defined.key],
|
|
598
|
+
uses: [rich ? "rich" : "t"],
|
|
599
|
+
consume: [...jsxNameNodes(element), ...built.consumed]
|
|
600
|
+
});
|
|
601
|
+
};
|
|
602
|
+
const transElement = (element) => {
|
|
603
|
+
const attributes = jsxAttributes(element);
|
|
604
|
+
if (attributes.spreads.length > 0 || attributes.named.has("render") || attributes.named.has("component")) {
|
|
605
|
+
builder.note(element.start, "<Trans> with spread props, render or component was not rewritten");
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const read = (name) => attributes.named.has(name) ? attributeString(attributes.named.get(name)?.value ?? null) : null;
|
|
609
|
+
const out = new MessageText();
|
|
610
|
+
jsx(element.children, out);
|
|
611
|
+
out.text = normalizeWhitespace(out.text);
|
|
612
|
+
jsxSite(element, out.built(), {
|
|
613
|
+
id: read("id"),
|
|
614
|
+
context: read("context"),
|
|
615
|
+
comment: read("comment")
|
|
616
|
+
});
|
|
617
|
+
};
|
|
618
|
+
const choiceElement = (element, format) => {
|
|
619
|
+
const attributes = jsxAttributes(element);
|
|
620
|
+
const read = (name) => attributes.named.has(name) ? attributeString(attributes.named.get(name)?.value ?? null) : null;
|
|
621
|
+
const out = new MessageText();
|
|
622
|
+
choiceComponent(element, format, out);
|
|
623
|
+
jsxSite(element, out.built(), {
|
|
624
|
+
id: read("id"),
|
|
625
|
+
context: read("context"),
|
|
626
|
+
comment: read("comment")
|
|
627
|
+
});
|
|
628
|
+
};
|
|
629
|
+
/** `msg`/`defineMessage` as `msg()`: only without placeholders (a vi18n descriptor holds no values). */
|
|
630
|
+
const msgSite = (node, out, input) => {
|
|
631
|
+
if (out.problem !== null || out.values.length > 0) {
|
|
632
|
+
builder.note(node.start, out.problem ?? "msg/defineMessage with placeholders captures values, which a vi18n msg() descriptor does not: pass them to t() where it is rendered; not rewritten");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const defined = define(env, {
|
|
636
|
+
...input,
|
|
637
|
+
message: out.text
|
|
638
|
+
}, {
|
|
639
|
+
file,
|
|
640
|
+
line: lineOf(node.start)
|
|
641
|
+
}, null);
|
|
642
|
+
if ("problem" in defined) {
|
|
643
|
+
builder.note(node.start, `${defined.problem}; not rewritten`);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const callee = node.type === "CallExpression" ? node.callee : node.tag;
|
|
647
|
+
builder.site(node, (r) => `${r.fn.msg}(${r.message(defined.key)})`, {
|
|
648
|
+
keys: [defined.key],
|
|
649
|
+
uses: ["msg"],
|
|
650
|
+
consume: calleeNodes(callee)
|
|
651
|
+
});
|
|
652
|
+
};
|
|
653
|
+
/** `i18n._(…)` / `i18n.t(…)` / `_(…)` at runtime. */
|
|
654
|
+
const runtimeCall = (node) => {
|
|
655
|
+
const callee = unwrap(node.callee);
|
|
656
|
+
let consume = [];
|
|
657
|
+
if (callee.type === "Identifier") {
|
|
658
|
+
const binding = resolve(callee.name, callee.start);
|
|
659
|
+
if (binding === null || !linguiUnderscore.has(binding)) return false;
|
|
660
|
+
consume = [callee];
|
|
661
|
+
} else {
|
|
662
|
+
const member = memberParts(callee);
|
|
663
|
+
if (member === null || member.property !== "_" && member.property !== "t") return false;
|
|
664
|
+
let root = unwrap(member.object);
|
|
665
|
+
while (root.type === "MemberExpression") root = unwrap(root.object);
|
|
666
|
+
const name = identifierName(root);
|
|
667
|
+
if (name === null) return false;
|
|
668
|
+
const binding = resolve(name, root.start);
|
|
669
|
+
if (!(runtimeI18n.has(name) || binding !== null && linguiInstances.has(binding))) return false;
|
|
670
|
+
if (unwrap(member.object).type === "Identifier") consume = [unwrap(member.object)];
|
|
671
|
+
}
|
|
672
|
+
const args = node.arguments;
|
|
673
|
+
const first = args[0];
|
|
674
|
+
if (first === void 0 || first.type === "SpreadElement") return false;
|
|
675
|
+
const values = args[1] ?? null;
|
|
676
|
+
const n = unwrap(first);
|
|
677
|
+
const id = stringValue(n);
|
|
678
|
+
let key = null;
|
|
679
|
+
if (id !== null) {
|
|
680
|
+
const defined = define(env, {
|
|
681
|
+
id,
|
|
682
|
+
message: null,
|
|
683
|
+
context: null,
|
|
684
|
+
comment: null
|
|
685
|
+
}, {
|
|
686
|
+
file,
|
|
687
|
+
line: lineOf(node.start)
|
|
688
|
+
}, null);
|
|
689
|
+
if ("problem" in defined) {
|
|
690
|
+
builder.note(node.start, `${defined.problem}; not rewritten`);
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
key = defined.key;
|
|
694
|
+
} else if (n.type === "ObjectExpression") {
|
|
695
|
+
const out = new MessageText();
|
|
696
|
+
const d = descriptor(n, out);
|
|
697
|
+
if (d === null || out.values.length > 0) {
|
|
698
|
+
builder.note(node.start, "a runtime descriptor vi18n migrate cannot read was not rewritten");
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
const defined = define(env, d, {
|
|
702
|
+
file,
|
|
703
|
+
line: lineOf(node.start)
|
|
704
|
+
}, null);
|
|
705
|
+
if ("problem" in defined) {
|
|
706
|
+
builder.note(node.start, `${defined.problem}; not rewritten`);
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
key = defined.key;
|
|
710
|
+
} else {
|
|
711
|
+
let root = n;
|
|
712
|
+
while (root.type === "MemberExpression") root = unwrap(root.object);
|
|
713
|
+
const name = identifierName(root);
|
|
714
|
+
if (!(name !== null && (messageTables.has(name) || relativeImports.has(name)) || isMsgMacro(n))) {
|
|
715
|
+
builder.note(node.start, "i18n._() with a message vi18n migrate cannot trace was not rewritten: pass a msg() descriptor to t()");
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const resolvedKey = key;
|
|
720
|
+
builder.site(node, (r) => {
|
|
721
|
+
const message = resolvedKey === null ? r.src(first.start, first.end) : r.message(resolvedKey);
|
|
722
|
+
return `${r.fn.t}(${message}${values === null ? "" : `, ${r.src(values.start, values.end)}`})`;
|
|
723
|
+
}, {
|
|
724
|
+
keys: resolvedKey === null ? [] : [resolvedKey],
|
|
725
|
+
uses: ["t"],
|
|
726
|
+
consume
|
|
727
|
+
});
|
|
728
|
+
return true;
|
|
729
|
+
};
|
|
730
|
+
const isMsgMacro = (n) => {
|
|
731
|
+
const callee = n.type === "TaggedTemplateExpression" ? n.tag : n.type === "CallExpression" ? n.callee : null;
|
|
732
|
+
const name = callee === null ? null : identifierName(callee);
|
|
733
|
+
return name !== null && macroMsg.has(name);
|
|
734
|
+
};
|
|
735
|
+
const unwrapProvider = (element) => {
|
|
736
|
+
const opening = element.openingElement;
|
|
737
|
+
const closing = element.closingElement;
|
|
738
|
+
const children = element.children.filter((c) => !(c.type === "JSXText" && c.value.trim() === ""));
|
|
739
|
+
builder.consumeRange(opening);
|
|
740
|
+
builder.consume(jsxNameNodes(element));
|
|
741
|
+
const nested = inJsxChildren(info, element);
|
|
742
|
+
builder.replace(element, (r) => {
|
|
743
|
+
if (closing === null) return nested ? "" : "null";
|
|
744
|
+
if (nested) return r.src(opening.end, closing.start).trim();
|
|
745
|
+
if (children.length === 1 && children[0].type === "JSXElement") return r.src(children[0].start, children[0].end);
|
|
746
|
+
return `<>${r.src(opening.end, closing.start)}</>`;
|
|
747
|
+
});
|
|
748
|
+
};
|
|
749
|
+
const visit = (node) => {
|
|
750
|
+
if (node.type === "TaggedTemplateExpression") {
|
|
751
|
+
const tag = node.tag;
|
|
752
|
+
const name = identifierName(tag);
|
|
753
|
+
if (isMacroT(tag)) {
|
|
754
|
+
const out = new MessageText();
|
|
755
|
+
template(node.quasi, out);
|
|
756
|
+
tSite(node, out.built(), {
|
|
757
|
+
id: null,
|
|
758
|
+
context: null,
|
|
759
|
+
comment: null
|
|
760
|
+
}, calleeNodes(tag));
|
|
761
|
+
} else if (name !== null && macroMsg.has(name)) {
|
|
762
|
+
const out = new MessageText();
|
|
763
|
+
template(node.quasi, out);
|
|
764
|
+
msgSite(node, out, {
|
|
765
|
+
id: null,
|
|
766
|
+
context: null,
|
|
767
|
+
comment: null
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
} else if (node.type === "CallExpression") {
|
|
771
|
+
const callee = node.callee;
|
|
772
|
+
const name = identifierName(callee);
|
|
773
|
+
const arg = node.arguments[0];
|
|
774
|
+
if (isMacroT(callee) && unwrap(callee).type === "Identifier" && arg !== void 0 && unwrap(arg).type === "ObjectExpression") {
|
|
775
|
+
const out = new MessageText();
|
|
776
|
+
const d = descriptor(unwrap(arg), out);
|
|
777
|
+
if (d === null) builder.note(node.start, "t({…}) with a computed descriptor was not rewritten");
|
|
778
|
+
else tSite(node, {
|
|
779
|
+
...out.built(),
|
|
780
|
+
message: d.message ?? ""
|
|
781
|
+
}, d, calleeNodes(callee));
|
|
782
|
+
} else if (name !== null && macroMsg.has(name) && arg !== void 0) {
|
|
783
|
+
const out = new MessageText();
|
|
784
|
+
const d = unwrap(arg).type === "ObjectExpression" ? descriptor(unwrap(arg), out) : null;
|
|
785
|
+
if (d === null) builder.note(node.start, `${name}() with a computed descriptor was not rewritten`);
|
|
786
|
+
else msgSite(node, out, d);
|
|
787
|
+
} else if (name !== null && choiceMacros.has(name) && !absorbed.has(node)) {
|
|
788
|
+
const out = new MessageText();
|
|
789
|
+
choice(node, choiceMacros.get(name), out);
|
|
790
|
+
tSite(node, out.built(), {
|
|
791
|
+
id: null,
|
|
792
|
+
context: null,
|
|
793
|
+
comment: null
|
|
794
|
+
}, calleeNodes(callee));
|
|
795
|
+
} else if (!runtimeCall(node)) {
|
|
796
|
+
const member = memberParts(callee);
|
|
797
|
+
if (member !== null && (member.property === "activate" || member.property === "load" || member.property === "loadAndActivate")) {
|
|
798
|
+
const object = identifierName(member.object);
|
|
799
|
+
if (object !== null && (runtimeI18n.has(object) || /^i18n$/.test(object))) builder.note(node.start, member.property === "activate" ? "i18n.activate(locale): use setLocale(locale) from @menapo/vi18n" : `i18n.${member.property}(): vi18n loads catalogs itself; delete this once nothing uses Lingui`);
|
|
800
|
+
} else if (name !== null && imported("setupI18n").has(name)) builder.note(node.start, "setupI18n(): use createTranslator(locale) or withLocale() from @menapo/vi18n/server");
|
|
801
|
+
}
|
|
802
|
+
} else if (node.type === "JSXElement") {
|
|
803
|
+
const name = jsxName(node);
|
|
804
|
+
if (transMacro.has(name)) transElement(node);
|
|
805
|
+
else if (choiceComponents.has(name) && !absorbed.has(node)) choiceElement(node, choiceComponents.get(name));
|
|
806
|
+
else if (providers.has(name)) unwrapProvider(node);
|
|
807
|
+
else if (imported("Trans", false).has(name)) builder.note(node.start, "<Trans id> from @lingui/react (the runtime component) was not rewritten: use t() or rich() with the message");
|
|
808
|
+
}
|
|
809
|
+
for (const [, child] of childEntries(node)) visit(child);
|
|
810
|
+
};
|
|
811
|
+
visit(program);
|
|
812
|
+
return builder.finish();
|
|
813
|
+
};
|
|
814
|
+
return {
|
|
815
|
+
catalogExtensions: [".po", ".json"],
|
|
816
|
+
catalogsRequired: false,
|
|
817
|
+
extensions: MODULE_EXTENSIONS,
|
|
818
|
+
mentions: (text) => text.includes("@lingui/"),
|
|
819
|
+
load(files, env) {
|
|
820
|
+
sourceLocale = env.config.sourceLocale;
|
|
821
|
+
const sorted = [...files].sort((a, b) => Number(b.locale === sourceLocale) - Number(a.locale === sourceLocale));
|
|
822
|
+
for (const file of sorted) readCatalog(file, env);
|
|
823
|
+
},
|
|
824
|
+
planFile: planModule,
|
|
825
|
+
unusedKeys() {
|
|
826
|
+
const out = [];
|
|
827
|
+
for (const [id, e] of catalogs.get(sourceLocale) ?? []) if (!found.has(e)) out.push(e.message !== null && e.message !== id ? `${id} (${e.message})` : id);
|
|
828
|
+
return out;
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
//#endregion
|
|
833
|
+
export { createLinguiAdapter };
|