@formatjs/cli-lib 8.5.0 → 8.5.2
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/gts_extractor-Dc-C_f4R.js +14 -0
- package/gts_extractor-Dc-C_f4R.js.map +1 -0
- package/{src/hbs_extractor.js → hbs_extractor-Eiqkz4d7.js} +9 -8
- package/hbs_extractor-Eiqkz4d7.js.map +1 -0
- package/index.d.ts +144 -7
- package/index.js +633 -2
- package/index.js.map +1 -0
- package/package.json +5 -5
- package/parse_script-EspfGgzZ.js +93 -0
- package/parse_script-EspfGgzZ.js.map +1 -0
- package/{src/svelte_extractor.js → svelte_extractor-Fr1Z3JHX.js} +16 -37
- package/svelte_extractor-Fr1Z3JHX.js.map +1 -0
- package/vue_extractor-Btw05cfG.js +41 -0
- package/vue_extractor-Btw05cfG.js.map +1 -0
- package/main.d.ts +0 -1
- package/main.js +0 -3
- package/src/cli.d.ts +0 -2
- package/src/cli.js +0 -145
- package/src/compile.d.ts +0 -63
- package/src/compile.js +0 -95
- package/src/compile_folder.d.ts +0 -2
- package/src/compile_folder.js +0 -8
- package/src/console_utils.d.ts +0 -7
- package/src/console_utils.js +0 -62
- package/src/extract.d.ts +0 -87
- package/src/extract.js +0 -212
- package/src/formatters/crowdin.d.ts +0 -7
- package/src/formatters/crowdin.js +0 -21
- package/src/formatters/default.d.ts +0 -6
- package/src/formatters/default.js +0 -9
- package/src/formatters/index.d.ts +0 -9
- package/src/formatters/index.js +0 -31
- package/src/formatters/lokalise.d.ts +0 -9
- package/src/formatters/lokalise.js +0 -18
- package/src/formatters/simple.d.ts +0 -4
- package/src/formatters/simple.js +0 -8
- package/src/formatters/smartling.d.ts +0 -21
- package/src/formatters/smartling.js +0 -39
- package/src/formatters/transifex.d.ts +0 -9
- package/src/formatters/transifex.js +0 -18
- package/src/gts_extractor.d.ts +0 -1
- package/src/gts_extractor.js +0 -14
- package/src/hbs_extractor.d.ts +0 -1
- package/src/parse_script.d.ts +0 -7
- package/src/parse_script.js +0 -43
- package/src/pseudo_locale.d.ts +0 -22
- package/src/pseudo_locale.js +0 -231
- package/src/svelte_extractor.d.ts +0 -2
- package/src/verify/checkExtraKeys.d.ts +0 -1
- package/src/verify/checkExtraKeys.js +0 -32
- package/src/verify/checkMissingKeys.d.ts +0 -1
- package/src/verify/checkMissingKeys.js +0 -33
- package/src/verify/checkStructuralEquality.d.ts +0 -1
- package/src/verify/checkStructuralEquality.js +0 -71
- package/src/verify/index.d.ts +0 -13
- package/src/verify/index.js +0 -26
- package/src/vue_extractor.d.ts +0 -2
- package/src/vue_extractor.js +0 -58
package/index.js
CHANGED
|
@@ -1,2 +1,633 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { a as writeStdout, i as warn, n as debug, r as getStdinAsString, t as parseScript } from "./parse_script-EspfGgzZ.js";
|
|
2
|
+
import { interpolateName } from "@formatjs/ts-transformer";
|
|
3
|
+
import { outputFile } from "fs-extra/esm";
|
|
4
|
+
import * as stringifyNs from "json-stable-stringify";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
7
|
+
import { readFile } from "fs/promises";
|
|
8
|
+
import { TYPE, isLiteralElement, isPluralElement, isSelectElement, isTagElement, parse } from "@formatjs/icu-messageformat-parser";
|
|
9
|
+
//#region \0rolldown/runtime.js
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __exportAll = (all, no_symbols) => {
|
|
12
|
+
let target = {};
|
|
13
|
+
for (var name in all) __defProp(target, name, {
|
|
14
|
+
get: all[name],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
+
return target;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region packages/cli-lib/formatters/crowdin.ts
|
|
22
|
+
var crowdin_exports = /* @__PURE__ */ __exportAll({
|
|
23
|
+
compile: () => compile$6,
|
|
24
|
+
format: () => format$5
|
|
25
|
+
});
|
|
26
|
+
const format$5 = (msgs) => {
|
|
27
|
+
const results = {};
|
|
28
|
+
for (const [id, msg] of Object.entries(msgs)) results[id] = {
|
|
29
|
+
message: msg.defaultMessage,
|
|
30
|
+
description: typeof msg.description === "string" ? msg.description : JSON.stringify(msg.description)
|
|
31
|
+
};
|
|
32
|
+
return results;
|
|
33
|
+
};
|
|
34
|
+
const compile$6 = (msgs) => {
|
|
35
|
+
const results = {};
|
|
36
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
37
|
+
if (id === "smartling") continue;
|
|
38
|
+
results[id] = msg.message;
|
|
39
|
+
}
|
|
40
|
+
return results;
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region packages/cli-lib/formatters/default.ts
|
|
44
|
+
var default_exports = /* @__PURE__ */ __exportAll({
|
|
45
|
+
compile: () => compile$5,
|
|
46
|
+
format: () => format$4
|
|
47
|
+
});
|
|
48
|
+
const format$4 = (msgs) => msgs;
|
|
49
|
+
const compile$5 = (msgs) => {
|
|
50
|
+
const results = {};
|
|
51
|
+
for (const k in msgs) results[k] = msgs[k].defaultMessage;
|
|
52
|
+
return results;
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region packages/cli-lib/formatters/lokalise.ts
|
|
56
|
+
var lokalise_exports = /* @__PURE__ */ __exportAll({
|
|
57
|
+
compile: () => compile$4,
|
|
58
|
+
format: () => format$3
|
|
59
|
+
});
|
|
60
|
+
const format$3 = (msgs) => {
|
|
61
|
+
const results = {};
|
|
62
|
+
for (const [id, msg] of Object.entries(msgs)) results[id] = {
|
|
63
|
+
translation: msg.defaultMessage,
|
|
64
|
+
notes: typeof msg.description === "string" ? msg.description : JSON.stringify(msg.description)
|
|
65
|
+
};
|
|
66
|
+
return results;
|
|
67
|
+
};
|
|
68
|
+
const compile$4 = (msgs) => {
|
|
69
|
+
const results = {};
|
|
70
|
+
for (const [id, msg] of Object.entries(msgs)) results[id] = msg.translation;
|
|
71
|
+
return results;
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region packages/cli-lib/formatters/simple.ts
|
|
75
|
+
var simple_exports = /* @__PURE__ */ __exportAll({
|
|
76
|
+
compile: () => compile$3,
|
|
77
|
+
format: () => format$2
|
|
78
|
+
});
|
|
79
|
+
const format$2 = (msgs) => {
|
|
80
|
+
return Object.keys(msgs).reduce((all, k) => {
|
|
81
|
+
all[k] = msgs[k].defaultMessage;
|
|
82
|
+
return all;
|
|
83
|
+
}, {});
|
|
84
|
+
};
|
|
85
|
+
const compile$3 = (msgs) => msgs;
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region packages/cli-lib/formatters/smartling.ts
|
|
88
|
+
var smartling_exports = /* @__PURE__ */ __exportAll({
|
|
89
|
+
compareMessages: () => compareMessages,
|
|
90
|
+
compile: () => compile$2,
|
|
91
|
+
format: () => format$1
|
|
92
|
+
});
|
|
93
|
+
const format$1 = (msgs) => {
|
|
94
|
+
const results = { smartling: {
|
|
95
|
+
translate_paths: [{
|
|
96
|
+
path: "*/message",
|
|
97
|
+
key: "{*}/message",
|
|
98
|
+
instruction: "*/description"
|
|
99
|
+
}],
|
|
100
|
+
variants_enabled: true,
|
|
101
|
+
string_format: "icu"
|
|
102
|
+
} };
|
|
103
|
+
for (const [id, msg] of Object.entries(msgs)) results[id] = {
|
|
104
|
+
message: msg.defaultMessage,
|
|
105
|
+
description: typeof msg.description === "string" ? msg.description : JSON.stringify(msg.description)
|
|
106
|
+
};
|
|
107
|
+
return results;
|
|
108
|
+
};
|
|
109
|
+
const compareMessages = (el1, el2) => {
|
|
110
|
+
if (el1.key === "smartling") return -1;
|
|
111
|
+
if (el2.key === "smartling") return 1;
|
|
112
|
+
return el1.key < el2.key ? -1 : el1.key === el2.key ? 0 : 1;
|
|
113
|
+
};
|
|
114
|
+
const compile$2 = (msgs) => {
|
|
115
|
+
const results = {};
|
|
116
|
+
for (const [id, msg] of Object.entries(msgs)) {
|
|
117
|
+
if (id === "smartling") continue;
|
|
118
|
+
results[id] = msg.message;
|
|
119
|
+
}
|
|
120
|
+
return results;
|
|
121
|
+
};
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region packages/cli-lib/formatters/transifex.ts
|
|
124
|
+
var transifex_exports = /* @__PURE__ */ __exportAll({
|
|
125
|
+
compile: () => compile$1,
|
|
126
|
+
format: () => format
|
|
127
|
+
});
|
|
128
|
+
const format = (msgs) => {
|
|
129
|
+
const results = {};
|
|
130
|
+
for (const [id, msg] of Object.entries(msgs)) results[id] = {
|
|
131
|
+
string: msg.defaultMessage,
|
|
132
|
+
developer_comment: typeof msg.description === "string" ? msg.description : JSON.stringify(msg.description)
|
|
133
|
+
};
|
|
134
|
+
return results;
|
|
135
|
+
};
|
|
136
|
+
const compile$1 = (msgs) => {
|
|
137
|
+
const results = {};
|
|
138
|
+
for (const [id, msg] of Object.entries(msgs)) results[id] = msg.string;
|
|
139
|
+
return results;
|
|
140
|
+
};
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region packages/cli-lib/formatters/index.ts
|
|
143
|
+
async function resolveBuiltinFormatter(format) {
|
|
144
|
+
if (!format) return default_exports;
|
|
145
|
+
if (typeof format !== "string") return format;
|
|
146
|
+
switch (format) {
|
|
147
|
+
case "transifex": return transifex_exports;
|
|
148
|
+
case "smartling": return smartling_exports;
|
|
149
|
+
case "simple": return simple_exports;
|
|
150
|
+
case "lokalise": return lokalise_exports;
|
|
151
|
+
case "crowdin": return crowdin_exports;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
return import(pathToFileURL(resolve(process.cwd(), format)).href);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
console.error(`Cannot resolve formatter ${format}`);
|
|
157
|
+
throw e;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region packages/cli-lib/extract.ts
|
|
162
|
+
const stringify$1 = stringifyNs.default || stringifyNs;
|
|
163
|
+
function calculateLineColFromOffset(text, start) {
|
|
164
|
+
if (!start) return {
|
|
165
|
+
line: 1,
|
|
166
|
+
col: 1
|
|
167
|
+
};
|
|
168
|
+
const lines = text.slice(0, start).split("\n");
|
|
169
|
+
const lastLine = lines[lines.length - 1];
|
|
170
|
+
return {
|
|
171
|
+
line: lines.length,
|
|
172
|
+
col: lastLine.length
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function processFile(source, fn, { idInterpolationPattern, ...opts }) {
|
|
176
|
+
let messages = [];
|
|
177
|
+
let meta;
|
|
178
|
+
const onMsgExtracted = opts.onMsgExtracted;
|
|
179
|
+
const onMetaExtracted = opts.onMetaExtracted;
|
|
180
|
+
opts = {
|
|
181
|
+
...opts,
|
|
182
|
+
additionalComponentNames: ["$formatMessage", ...opts.additionalComponentNames || []],
|
|
183
|
+
onMsgExtracted(filePath, msgs) {
|
|
184
|
+
if (opts.extractSourceLocation) msgs = msgs.map((msg) => ({
|
|
185
|
+
...msg,
|
|
186
|
+
...calculateLineColFromOffset(source, msg.start)
|
|
187
|
+
}));
|
|
188
|
+
messages = messages.concat(msgs);
|
|
189
|
+
if (onMsgExtracted) onMsgExtracted(filePath, msgs);
|
|
190
|
+
},
|
|
191
|
+
onMetaExtracted(filePath, m) {
|
|
192
|
+
meta = m;
|
|
193
|
+
if (onMetaExtracted) onMetaExtracted(filePath, m);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
if (!opts.overrideIdFn && idInterpolationPattern) opts = {
|
|
197
|
+
...opts,
|
|
198
|
+
overrideIdFn: (id, defaultMessage, description, fileName) => id || interpolateName({ resourcePath: fileName }, idInterpolationPattern, { content: description ? `${defaultMessage}#${typeof description === "string" ? description : stringify$1(description)}` : defaultMessage })
|
|
199
|
+
};
|
|
200
|
+
debug("Processing opts for %s: %s", fn, opts);
|
|
201
|
+
const scriptParseFn = parseScript(opts, fn);
|
|
202
|
+
if (fn.endsWith(".vue")) {
|
|
203
|
+
debug("Processing %s using vue extractor", fn);
|
|
204
|
+
const { parseFile } = await import("./vue_extractor-Btw05cfG.js");
|
|
205
|
+
parseFile(source, fn, scriptParseFn);
|
|
206
|
+
} else if (fn.endsWith(".svelte")) {
|
|
207
|
+
debug("Processing %s using svelte extractor", fn);
|
|
208
|
+
const { parseFile } = await import("./svelte_extractor-Fr1Z3JHX.js");
|
|
209
|
+
parseFile(source, fn, scriptParseFn);
|
|
210
|
+
} else if (fn.endsWith(".hbs")) {
|
|
211
|
+
debug("Processing %s using hbs extractor", fn);
|
|
212
|
+
const { parseFile } = await import("./hbs_extractor-Eiqkz4d7.js");
|
|
213
|
+
parseFile(source, fn, opts);
|
|
214
|
+
} else if (fn.endsWith(".gts") || fn.endsWith(".gjs")) {
|
|
215
|
+
debug("Processing %s as gts/gjs file", fn);
|
|
216
|
+
const { parseFile } = await import("./gts_extractor-Dc-C_f4R.js");
|
|
217
|
+
parseFile(source, fn, opts);
|
|
218
|
+
} else {
|
|
219
|
+
debug("Processing %s using typescript extractor", fn);
|
|
220
|
+
scriptParseFn(source);
|
|
221
|
+
}
|
|
222
|
+
debug("Done extracting %s messages: %s", fn, messages);
|
|
223
|
+
if (meta) {
|
|
224
|
+
debug("Extracted meta:", meta);
|
|
225
|
+
messages.forEach((m) => m.meta = meta);
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
messages,
|
|
229
|
+
meta
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Extract strings from source files
|
|
234
|
+
* @param files list of files
|
|
235
|
+
* @param extractOpts extract options
|
|
236
|
+
* @returns messages serialized as JSON string since key order
|
|
237
|
+
* matters for some `format`
|
|
238
|
+
*/
|
|
239
|
+
async function extract(files, extractOpts) {
|
|
240
|
+
const { throws, readFromStdin, signal, ...opts } = extractOpts;
|
|
241
|
+
const shouldThrow = throws === true;
|
|
242
|
+
const optsWithThrows = {
|
|
243
|
+
...opts,
|
|
244
|
+
throws: shouldThrow,
|
|
245
|
+
onMsgError: !shouldThrow ? (_, e) => warn(e.message) : void 0
|
|
246
|
+
};
|
|
247
|
+
let rawResults = [];
|
|
248
|
+
try {
|
|
249
|
+
if (readFromStdin) {
|
|
250
|
+
debug(`Reading input from stdin`);
|
|
251
|
+
if (process.stdin.isTTY) warn("Reading source file from TTY.");
|
|
252
|
+
rawResults = [await processFile(await getStdinAsString(), "dummy", optsWithThrows)];
|
|
253
|
+
} else if (!shouldThrow) rawResults = (await Promise.allSettled(files.map(async (fn) => {
|
|
254
|
+
debug("Extracting file:", fn);
|
|
255
|
+
return processFile(await readFile(fn, {
|
|
256
|
+
encoding: "utf8",
|
|
257
|
+
signal
|
|
258
|
+
}), fn, optsWithThrows);
|
|
259
|
+
}))).map((result) => {
|
|
260
|
+
if (result.status === "fulfilled") return result.value;
|
|
261
|
+
else {
|
|
262
|
+
warn(String(result.reason));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
else rawResults = await Promise.all(files.map(async (fn) => {
|
|
267
|
+
debug("Extracting file:", fn);
|
|
268
|
+
return processFile(await readFile(fn, {
|
|
269
|
+
encoding: "utf8",
|
|
270
|
+
signal
|
|
271
|
+
}), fn, optsWithThrows);
|
|
272
|
+
}));
|
|
273
|
+
} catch (e) {
|
|
274
|
+
if (shouldThrow) throw e;
|
|
275
|
+
else warn(String(e));
|
|
276
|
+
}
|
|
277
|
+
const formatter = await resolveBuiltinFormatter(opts.format);
|
|
278
|
+
const extractionResults = rawResults.filter((r) => !!r);
|
|
279
|
+
const extractedMessages = /* @__PURE__ */ new Map();
|
|
280
|
+
for (const { messages } of extractionResults) for (const message of messages) {
|
|
281
|
+
const { id, description, defaultMessage } = message;
|
|
282
|
+
if (!id) {
|
|
283
|
+
const error = /* @__PURE__ */ new Error(`[FormatJS CLI] Missing message id for message:
|
|
284
|
+
${JSON.stringify(message, void 0, 2)}`);
|
|
285
|
+
if (throws) throw error;
|
|
286
|
+
else warn(error.message);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (extractedMessages.has(id)) {
|
|
290
|
+
const existing = extractedMessages.get(id);
|
|
291
|
+
if (stringify$1(description) !== stringify$1(existing.description) || defaultMessage !== existing.defaultMessage) {
|
|
292
|
+
const error = /* @__PURE__ */ new Error(`[FormatJS CLI] Duplicate message id: "${id}", but the \`description\` and/or \`defaultMessage\` are different.`);
|
|
293
|
+
if (throws) throw error;
|
|
294
|
+
else warn(error.message);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
extractedMessages.set(id, message);
|
|
298
|
+
}
|
|
299
|
+
const results = {};
|
|
300
|
+
const messages = Array.from(extractedMessages.values());
|
|
301
|
+
for (const { id, ...msg } of messages) results[id] = msg;
|
|
302
|
+
if (typeof formatter.serialize === "function") return formatter.serialize(formatter.format(results));
|
|
303
|
+
return stringify$1(formatter.format(results), {
|
|
304
|
+
space: 2,
|
|
305
|
+
cmp: formatter.compareMessages || void 0
|
|
306
|
+
}) ?? "";
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Extract strings from source files, also writes to a file.
|
|
310
|
+
* @param files list of files
|
|
311
|
+
* @param extractOpts extract options
|
|
312
|
+
* @returns A Promise that resolves if output file was written successfully
|
|
313
|
+
*/
|
|
314
|
+
async function extractAndWrite(files, extractOpts) {
|
|
315
|
+
const { outFile, ...opts } = extractOpts;
|
|
316
|
+
const serializedResult = await extract(files, opts) + "\n";
|
|
317
|
+
if (outFile) {
|
|
318
|
+
debug("Writing output file:", outFile);
|
|
319
|
+
return outputFile(outFile, serializedResult);
|
|
320
|
+
}
|
|
321
|
+
await writeStdout(serializedResult);
|
|
322
|
+
}
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region packages/cli-lib/pseudo_locale.ts
|
|
325
|
+
function forEachLiteralElement(ast, fn) {
|
|
326
|
+
ast.forEach((el) => {
|
|
327
|
+
if (isLiteralElement(el)) fn(el);
|
|
328
|
+
else if (isPluralElement(el) || isSelectElement(el)) for (const opt of Object.values(el.options)) forEachLiteralElement(opt.value, fn);
|
|
329
|
+
else if (isTagElement(el)) forEachLiteralElement(el.children, fn);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function generateXXLS(msg) {
|
|
333
|
+
const ast = typeof msg === "string" ? parse(msg) : msg;
|
|
334
|
+
const lastChunk = ast[ast.length - 1];
|
|
335
|
+
if (lastChunk && isLiteralElement(lastChunk)) {
|
|
336
|
+
lastChunk.value += "SSSSSSSSSSSSSSSSSSSSSSSSS";
|
|
337
|
+
return ast;
|
|
338
|
+
}
|
|
339
|
+
return [...ast, {
|
|
340
|
+
type: TYPE.literal,
|
|
341
|
+
value: "SSSSSSSSSSSSSSSSSSSSSSSSS"
|
|
342
|
+
}];
|
|
343
|
+
}
|
|
344
|
+
function generateXXAC(msg) {
|
|
345
|
+
const ast = typeof msg === "string" ? parse(msg) : msg;
|
|
346
|
+
forEachLiteralElement(ast, (el) => {
|
|
347
|
+
el.value = el.value.toUpperCase();
|
|
348
|
+
});
|
|
349
|
+
return ast;
|
|
350
|
+
}
|
|
351
|
+
function generateXXHA(msg) {
|
|
352
|
+
const ast = typeof msg === "string" ? parse(msg) : msg;
|
|
353
|
+
const [firstChunk, ...rest] = ast;
|
|
354
|
+
if (firstChunk && isLiteralElement(firstChunk)) {
|
|
355
|
+
firstChunk.value = "[javascript]" + firstChunk.value;
|
|
356
|
+
return [firstChunk, ...rest];
|
|
357
|
+
}
|
|
358
|
+
return [{
|
|
359
|
+
type: TYPE.literal,
|
|
360
|
+
value: "[javascript]"
|
|
361
|
+
}, ...ast];
|
|
362
|
+
}
|
|
363
|
+
const ACCENTED_MAP = {
|
|
364
|
+
"caps": [
|
|
365
|
+
550,
|
|
366
|
+
385,
|
|
367
|
+
391,
|
|
368
|
+
7698,
|
|
369
|
+
7702,
|
|
370
|
+
401,
|
|
371
|
+
403,
|
|
372
|
+
294,
|
|
373
|
+
298,
|
|
374
|
+
308,
|
|
375
|
+
310,
|
|
376
|
+
319,
|
|
377
|
+
7742,
|
|
378
|
+
544,
|
|
379
|
+
510,
|
|
380
|
+
420,
|
|
381
|
+
586,
|
|
382
|
+
344,
|
|
383
|
+
350,
|
|
384
|
+
358,
|
|
385
|
+
364,
|
|
386
|
+
7804,
|
|
387
|
+
7814,
|
|
388
|
+
7818,
|
|
389
|
+
7822,
|
|
390
|
+
7824
|
|
391
|
+
],
|
|
392
|
+
"small": [
|
|
393
|
+
551,
|
|
394
|
+
384,
|
|
395
|
+
392,
|
|
396
|
+
7699,
|
|
397
|
+
7703,
|
|
398
|
+
402,
|
|
399
|
+
608,
|
|
400
|
+
295,
|
|
401
|
+
299,
|
|
402
|
+
309,
|
|
403
|
+
311,
|
|
404
|
+
320,
|
|
405
|
+
7743,
|
|
406
|
+
414,
|
|
407
|
+
511,
|
|
408
|
+
421,
|
|
409
|
+
587,
|
|
410
|
+
345,
|
|
411
|
+
351,
|
|
412
|
+
359,
|
|
413
|
+
365,
|
|
414
|
+
7805,
|
|
415
|
+
7815,
|
|
416
|
+
7819,
|
|
417
|
+
7823,
|
|
418
|
+
7825
|
|
419
|
+
]
|
|
420
|
+
};
|
|
421
|
+
const FLIPPED_MAP = {
|
|
422
|
+
"caps": [
|
|
423
|
+
8704,
|
|
424
|
+
1296,
|
|
425
|
+
8579,
|
|
426
|
+
5601,
|
|
427
|
+
398,
|
|
428
|
+
8498,
|
|
429
|
+
8513,
|
|
430
|
+
72,
|
|
431
|
+
73,
|
|
432
|
+
383,
|
|
433
|
+
1276,
|
|
434
|
+
8514,
|
|
435
|
+
87,
|
|
436
|
+
78,
|
|
437
|
+
79,
|
|
438
|
+
1280,
|
|
439
|
+
210,
|
|
440
|
+
7450,
|
|
441
|
+
83,
|
|
442
|
+
8869,
|
|
443
|
+
8745,
|
|
444
|
+
581,
|
|
445
|
+
77,
|
|
446
|
+
88,
|
|
447
|
+
8516,
|
|
448
|
+
90
|
|
449
|
+
],
|
|
450
|
+
"small": [
|
|
451
|
+
592,
|
|
452
|
+
113,
|
|
453
|
+
596,
|
|
454
|
+
112,
|
|
455
|
+
477,
|
|
456
|
+
607,
|
|
457
|
+
387,
|
|
458
|
+
613,
|
|
459
|
+
305,
|
|
460
|
+
638,
|
|
461
|
+
670,
|
|
462
|
+
645,
|
|
463
|
+
623,
|
|
464
|
+
117,
|
|
465
|
+
111,
|
|
466
|
+
100,
|
|
467
|
+
98,
|
|
468
|
+
633,
|
|
469
|
+
115,
|
|
470
|
+
647,
|
|
471
|
+
110,
|
|
472
|
+
652,
|
|
473
|
+
653,
|
|
474
|
+
120,
|
|
475
|
+
654,
|
|
476
|
+
122
|
|
477
|
+
]
|
|
478
|
+
};
|
|
479
|
+
/**
|
|
480
|
+
* Based on: https://hg.mozilla.org/mozilla-central/file/a1f74e8c8fb72390d22054d6b00c28b1a32f6c43/intl/l10n/L10nRegistry.jsm#l425
|
|
481
|
+
*/
|
|
482
|
+
function transformString(map, elongate = false, msg) {
|
|
483
|
+
return msg.replace(/[a-z]/gi, (ch) => {
|
|
484
|
+
const cc = ch.charCodeAt(0);
|
|
485
|
+
if (cc >= 97 && cc <= 122) {
|
|
486
|
+
const newChar = String.fromCodePoint(map.small[cc - 97]);
|
|
487
|
+
if (elongate && (cc === 97 || cc === 101 || cc === 111 || cc === 117)) return newChar + newChar;
|
|
488
|
+
return newChar;
|
|
489
|
+
}
|
|
490
|
+
if (cc >= 65 && cc <= 90) return String.fromCodePoint(map.caps[cc - 65]);
|
|
491
|
+
return ch;
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* accented - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ
|
|
496
|
+
* --------------------------------
|
|
497
|
+
*
|
|
498
|
+
* This locale replaces all Latin characters with their accented equivalents, and duplicates some
|
|
499
|
+
* vowels to create roughly 30% longer strings. Strings are wrapped in markers (square brackets),
|
|
500
|
+
* which help with detecting truncation.
|
|
501
|
+
*/
|
|
502
|
+
function generateENXA(msg) {
|
|
503
|
+
const ast = typeof msg === "string" ? parse(msg) : msg;
|
|
504
|
+
forEachLiteralElement(ast, (el) => {
|
|
505
|
+
el.value = transformString(ACCENTED_MAP, true, el.value);
|
|
506
|
+
});
|
|
507
|
+
return [
|
|
508
|
+
{
|
|
509
|
+
type: TYPE.literal,
|
|
510
|
+
value: "["
|
|
511
|
+
},
|
|
512
|
+
...ast,
|
|
513
|
+
{
|
|
514
|
+
type: TYPE.literal,
|
|
515
|
+
value: "]"
|
|
516
|
+
}
|
|
517
|
+
];
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* bidi - ɥsıʅƃuƎ ıpıԐ
|
|
521
|
+
* -------------------
|
|
522
|
+
*
|
|
523
|
+
* This strategy replaces all Latin characters with their 180 degree rotated versions and enforces
|
|
524
|
+
* right to left text flow using Unicode UAX#9 Explicit Directional Embeddings. In this mode, the UI
|
|
525
|
+
* directionality will also be set to right-to-left.
|
|
526
|
+
*/
|
|
527
|
+
function generateENXB(msg) {
|
|
528
|
+
const ast = typeof msg === "string" ? parse(msg) : msg;
|
|
529
|
+
forEachLiteralElement(ast, (el) => {
|
|
530
|
+
el.value = transformString(FLIPPED_MAP, false, el.value);
|
|
531
|
+
});
|
|
532
|
+
return [
|
|
533
|
+
{
|
|
534
|
+
type: TYPE.literal,
|
|
535
|
+
value: ""
|
|
536
|
+
},
|
|
537
|
+
...ast,
|
|
538
|
+
{
|
|
539
|
+
type: TYPE.literal,
|
|
540
|
+
value: ""
|
|
541
|
+
}
|
|
542
|
+
];
|
|
543
|
+
}
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region packages/cli-lib/compile.ts
|
|
546
|
+
const stringify = stringifyNs.default || stringifyNs;
|
|
547
|
+
/**
|
|
548
|
+
* Aggregate `inputFiles` into a single JSON blob and compile.
|
|
549
|
+
* Also checks for conflicting IDs.
|
|
550
|
+
* Then returns the serialized result as a `string` since key order
|
|
551
|
+
* makes a difference in some vendor.
|
|
552
|
+
* @param inputFiles Input files
|
|
553
|
+
* @param opts Options
|
|
554
|
+
* @returns serialized result in string format
|
|
555
|
+
*/
|
|
556
|
+
async function compile(inputFiles, opts = {}) {
|
|
557
|
+
debug("Compiling files:", inputFiles);
|
|
558
|
+
const { ast, format, pseudoLocale, skipErrors, ignoreTag, signal } = opts;
|
|
559
|
+
signal?.throwIfAborted();
|
|
560
|
+
const formatter = await resolveBuiltinFormatter(format);
|
|
561
|
+
const messages = {};
|
|
562
|
+
const messageAsts = {};
|
|
563
|
+
const idsWithFileName = {};
|
|
564
|
+
const compiledFiles = await Promise.all(inputFiles.map((f) => readFile(f, {
|
|
565
|
+
encoding: "utf8",
|
|
566
|
+
signal
|
|
567
|
+
}).then((content) => JSON.parse(content)).then(formatter.compile)));
|
|
568
|
+
debug("Compiled files:", compiledFiles);
|
|
569
|
+
for (let i = 0; i < inputFiles.length; i++) {
|
|
570
|
+
const inputFile = inputFiles[i];
|
|
571
|
+
debug("Processing file:", inputFile);
|
|
572
|
+
const compiled = compiledFiles[i];
|
|
573
|
+
for (const id in compiled) {
|
|
574
|
+
if (messages[id] && messages[id] !== compiled[id]) throw new Error(`Conflicting ID "${id}" with different translation found in these 2 files:
|
|
575
|
+
ID: ${id}
|
|
576
|
+
Message from ${idsWithFileName[id]}: ${messages[id]}
|
|
577
|
+
Message from ${inputFile}: ${compiled[id]}
|
|
578
|
+
`);
|
|
579
|
+
try {
|
|
580
|
+
const msgAst = parse(compiled[id], { ignoreTag });
|
|
581
|
+
messages[id] = compiled[id];
|
|
582
|
+
switch (pseudoLocale) {
|
|
583
|
+
case "xx-LS":
|
|
584
|
+
messageAsts[id] = generateXXLS(msgAst);
|
|
585
|
+
break;
|
|
586
|
+
case "xx-AC":
|
|
587
|
+
messageAsts[id] = generateXXAC(msgAst);
|
|
588
|
+
break;
|
|
589
|
+
case "xx-HA":
|
|
590
|
+
messageAsts[id] = generateXXHA(msgAst);
|
|
591
|
+
break;
|
|
592
|
+
case "en-XA":
|
|
593
|
+
messageAsts[id] = generateENXA(msgAst);
|
|
594
|
+
break;
|
|
595
|
+
case "en-XB":
|
|
596
|
+
messageAsts[id] = generateENXB(msgAst);
|
|
597
|
+
break;
|
|
598
|
+
default:
|
|
599
|
+
messageAsts[id] = msgAst;
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
idsWithFileName[id] = inputFile;
|
|
603
|
+
} catch (e) {
|
|
604
|
+
warn("Error validating message \"%s\" with ID \"%s\" in file \"%s\"", compiled[id], id, inputFile);
|
|
605
|
+
if (!skipErrors) throw e;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return stringify(ast ? messageAsts : messages, {
|
|
610
|
+
space: 2,
|
|
611
|
+
cmp: formatter.compareMessages || void 0
|
|
612
|
+
}) ?? "";
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Aggregate `inputFiles` into a single JSON blob and compile.
|
|
616
|
+
* Also checks for conflicting IDs and write output to `outFile`.
|
|
617
|
+
* @param inputFiles Input files
|
|
618
|
+
* @param compileOpts options
|
|
619
|
+
* @returns A `Promise` that resolves if file was written successfully
|
|
620
|
+
*/
|
|
621
|
+
async function compileAndWrite(inputFiles, compileOpts = {}) {
|
|
622
|
+
const { outFile, ...opts } = compileOpts;
|
|
623
|
+
const serializedResult = await compile(inputFiles, opts) + "\n";
|
|
624
|
+
if (outFile) {
|
|
625
|
+
debug("Writing output file:", outFile);
|
|
626
|
+
return outputFile(outFile, serializedResult);
|
|
627
|
+
}
|
|
628
|
+
await writeStdout(serializedResult);
|
|
629
|
+
}
|
|
630
|
+
//#endregion
|
|
631
|
+
export { compile, compileAndWrite, extract, extractAndWrite };
|
|
632
|
+
|
|
633
|
+
//# sourceMappingURL=index.js.map
|