glotfile 0.8.6 → 0.8.7
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/README.md +7 -7
- package/dist/server/cli.js +166 -42
- package/dist/server/server.js +160 -40
- package/dist/ui/assets/{index-amdKG3Do.js → index-CVA535xu.js} +88 -15
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/cli-reference.md +25 -8
package/dist/server/server.js
CHANGED
|
@@ -1515,17 +1515,57 @@ function extractPlaceholders(value) {
|
|
|
1515
1515
|
function isIcuPluralOrSelect(value) {
|
|
1516
1516
|
return ICU_PLURAL_SELECT.test(value);
|
|
1517
1517
|
}
|
|
1518
|
+
function withLiterals(value, convertGap, emitLiteral) {
|
|
1519
|
+
let out = "";
|
|
1520
|
+
let gap = "";
|
|
1521
|
+
const flushGap = () => {
|
|
1522
|
+
if (gap) {
|
|
1523
|
+
out += convertGap(gap);
|
|
1524
|
+
gap = "";
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
for (let i = 0; i < value.length; ) {
|
|
1528
|
+
if (value[i] === "'") {
|
|
1529
|
+
const next = value[i + 1];
|
|
1530
|
+
if (next === "'") {
|
|
1531
|
+
gap += "'";
|
|
1532
|
+
i += 2;
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
if (next === "{" || next === "}" || next === "#" || next === "|") {
|
|
1536
|
+
flushGap();
|
|
1537
|
+
let j = i + 1;
|
|
1538
|
+
while (j < value.length && value[j] !== "'") j++;
|
|
1539
|
+
out += emitLiteral(value.slice(i + 1, j));
|
|
1540
|
+
i = j < value.length ? j + 1 : j;
|
|
1541
|
+
continue;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
gap += value[i];
|
|
1545
|
+
i++;
|
|
1546
|
+
}
|
|
1547
|
+
flushGap();
|
|
1548
|
+
return out;
|
|
1549
|
+
}
|
|
1550
|
+
function extractLiterals2(value) {
|
|
1551
|
+
const out = [];
|
|
1552
|
+
withLiterals(value, () => "", (lit) => {
|
|
1553
|
+
out.push(lit);
|
|
1554
|
+
return "";
|
|
1555
|
+
});
|
|
1556
|
+
return out;
|
|
1557
|
+
}
|
|
1518
1558
|
function toLaravel(value) {
|
|
1519
1559
|
if (isIcuPluralOrSelect(value)) return value;
|
|
1520
|
-
return value.replace(/\{(\w+)\}/g, ":$1");
|
|
1560
|
+
return withLiterals(value, (gap) => gap.replace(/\{(\w+)\}/g, ":$1"), (lit) => lit);
|
|
1521
1561
|
}
|
|
1522
1562
|
function toI18next(value) {
|
|
1523
1563
|
if (isIcuPluralOrSelect(value)) return value;
|
|
1524
|
-
return value.replace(/(?<!\{)\{(\w+)\}(?!\})/g, "{{$1}}");
|
|
1564
|
+
return withLiterals(value, (gap) => gap.replace(/(?<!\{)\{(\w+)\}(?!\})/g, "{{$1}}"), (lit) => lit);
|
|
1525
1565
|
}
|
|
1526
1566
|
function toRuby(value) {
|
|
1527
1567
|
if (isIcuPluralOrSelect(value)) return value;
|
|
1528
|
-
return value.replace(/(?<!%)\{(\w+)\}/g, "%{$1}");
|
|
1568
|
+
return withLiterals(value, (gap) => gap.replace(/(?<!%)\{(\w+)\}/g, "%{$1}"), (lit) => lit);
|
|
1529
1569
|
}
|
|
1530
1570
|
function placeholdersMatch(source, translation) {
|
|
1531
1571
|
const a = extractPlaceholders(source).sort();
|
|
@@ -2266,6 +2306,20 @@ var laravelPhp = {
|
|
|
2266
2306
|
message: "laravel-php cannot represent ICU plural/select; written unconverted"
|
|
2267
2307
|
});
|
|
2268
2308
|
}
|
|
2309
|
+
if (raw) {
|
|
2310
|
+
const names = new Set(extractPlaceholders(raw));
|
|
2311
|
+
for (const m of raw.matchAll(/:([a-zA-Z][a-zA-Z0-9_]*)/g)) {
|
|
2312
|
+
if (names.has(m[1])) {
|
|
2313
|
+
warnings.push({
|
|
2314
|
+
code: "lossy-literal",
|
|
2315
|
+
key,
|
|
2316
|
+
locale,
|
|
2317
|
+
message: `literal ":${m[1]}" collides with the :${m[1]} placeholder; Laravel will interpolate both`
|
|
2318
|
+
});
|
|
2319
|
+
break;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2269
2323
|
(tree[locale][namespace] ??= {})[inner] = toLaravel(raw);
|
|
2270
2324
|
}
|
|
2271
2325
|
}
|
|
@@ -2346,6 +2400,9 @@ var i18nextJson = {
|
|
|
2346
2400
|
if (raw && isIcuPluralOrSelect(raw)) {
|
|
2347
2401
|
warnings.push({ code: "lossy-plural", key, locale, message: "i18next-json does not yet convert ICU plural/select; written unconverted" });
|
|
2348
2402
|
}
|
|
2403
|
+
if (raw && extractLiterals2(raw).some((lit) => /\{\{\w+\}\}/.test(lit))) {
|
|
2404
|
+
warnings.push({ code: "lossy-literal", key, locale, message: "i18next will interpolate a literal containing {{\u2026}}; i18next has no escape for it" });
|
|
2405
|
+
}
|
|
2349
2406
|
if (setNested(obj, segments, toI18next(raw))) collided.add(key);
|
|
2350
2407
|
}
|
|
2351
2408
|
files.push({ path: resolvePath(output.path, resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE3)), contents: serializeJson(obj, fmt) });
|
|
@@ -2362,7 +2419,9 @@ function poString(s) {
|
|
|
2362
2419
|
return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r") + '"';
|
|
2363
2420
|
}
|
|
2364
2421
|
function toGettext(body, arg) {
|
|
2365
|
-
|
|
2422
|
+
const gap = (text) => text.replace(/%/g, "%%").split(`{${arg}}`).join("%d");
|
|
2423
|
+
if (isIcuPluralOrSelect(body)) return gap(body);
|
|
2424
|
+
return withLiterals(body, gap, (lit) => lit.replace(/%/g, "%%"));
|
|
2366
2425
|
}
|
|
2367
2426
|
var DEFAULT_LOCALE_CASE4 = "lower-hyphen";
|
|
2368
2427
|
var gettextPo = {
|
|
@@ -2422,8 +2481,10 @@ var gettextPo = {
|
|
|
2422
2481
|
blocks.push(
|
|
2423
2482
|
[
|
|
2424
2483
|
`msgctxt ${poString(key)}`,
|
|
2425
|
-
|
|
2426
|
-
|
|
2484
|
+
// Scalar keys carry no count arg; toGettext still strips literal
|
|
2485
|
+
// spans and escapes a literal % to %%.
|
|
2486
|
+
`msgid ${poString(toGettext(src?.value ?? "", ""))}`,
|
|
2487
|
+
`msgstr ${poString(toGettext(lv?.value ?? "", ""))}`
|
|
2427
2488
|
].join("\n")
|
|
2428
2489
|
);
|
|
2429
2490
|
}
|
|
@@ -2447,7 +2508,9 @@ function xmlEscape(s) {
|
|
|
2447
2508
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2448
2509
|
}
|
|
2449
2510
|
function toApple(body, arg) {
|
|
2450
|
-
|
|
2511
|
+
const gap = (text) => text.replace(/%/g, "%%").split(`{${arg}}`).join("%d");
|
|
2512
|
+
if (isIcuPluralOrSelect(body)) return gap(body);
|
|
2513
|
+
return withLiterals(body, gap, (lit) => lit.replace(/%/g, "%%"));
|
|
2451
2514
|
}
|
|
2452
2515
|
var DEFAULT_LOCALE_CASE5 = "lower-hyphen";
|
|
2453
2516
|
var appleStringsdict = {
|
|
@@ -2511,6 +2574,11 @@ var DEFAULT_LOCALE_CASE6 = "bcp47-hyphen";
|
|
|
2511
2574
|
function escape(s) {
|
|
2512
2575
|
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r");
|
|
2513
2576
|
}
|
|
2577
|
+
function toApple2(value) {
|
|
2578
|
+
const gap = (text) => text.replace(/%/g, "%%");
|
|
2579
|
+
if (isIcuPluralOrSelect(value)) return gap(value);
|
|
2580
|
+
return withLiterals(value, gap, (lit) => lit.replace(/%/g, "%%"));
|
|
2581
|
+
}
|
|
2514
2582
|
var appleStrings = {
|
|
2515
2583
|
name: "apple-strings",
|
|
2516
2584
|
capabilities: {
|
|
@@ -2536,7 +2604,7 @@ var appleStrings = {
|
|
|
2536
2604
|
if (entry.plural) continue;
|
|
2537
2605
|
const value = resolveScalar(entry, locale, state.config.sourceLocale, emptyAs);
|
|
2538
2606
|
if (value === null) continue;
|
|
2539
|
-
lines.push(`"${escape(key)}" = "${escape(value)}";`);
|
|
2607
|
+
lines.push(`"${escape(key)}" = "${escape(toApple2(value))}";`);
|
|
2540
2608
|
}
|
|
2541
2609
|
const contents = lines.length ? lines.join("\n") + "\n" : "";
|
|
2542
2610
|
files.push({
|
|
@@ -2550,6 +2618,10 @@ var appleStrings = {
|
|
|
2550
2618
|
|
|
2551
2619
|
// src/server/adapters/vue-i18n-json.ts
|
|
2552
2620
|
var DEFAULT_LOCALE_CASE7 = "lower-hyphen";
|
|
2621
|
+
function toVueI18n(value) {
|
|
2622
|
+
if (isIcuPluralOrSelect(value)) return value;
|
|
2623
|
+
return withLiterals(value, (gap) => gap, (content) => `{'${content}'}`);
|
|
2624
|
+
}
|
|
2553
2625
|
var vueI18nJson = {
|
|
2554
2626
|
name: "vue-i18n-json",
|
|
2555
2627
|
capabilities: {
|
|
@@ -2575,7 +2647,7 @@ var vueI18nJson = {
|
|
|
2575
2647
|
if (entry.plural) {
|
|
2576
2648
|
const forms = resolveForms(entry, locale, state.config.sourceLocale, emptyAs);
|
|
2577
2649
|
if (!forms) continue;
|
|
2578
|
-
const parts = PLURAL_CATEGORIES.map((c) => forms[c]).filter((v) => v !== void 0);
|
|
2650
|
+
const parts = PLURAL_CATEGORIES.map((c) => forms[c]).filter((v) => v !== void 0).map(toVueI18n);
|
|
2579
2651
|
flat[key] = parts.join(" | ");
|
|
2580
2652
|
} else {
|
|
2581
2653
|
const raw = resolveScalar(entry, locale, state.config.sourceLocale, emptyAs);
|
|
@@ -2588,7 +2660,7 @@ var vueI18nJson = {
|
|
|
2588
2660
|
message: "vue-i18n-json does not yet convert ICU plural/select; written unconverted"
|
|
2589
2661
|
});
|
|
2590
2662
|
}
|
|
2591
|
-
flat[key] = raw;
|
|
2663
|
+
flat[key] = toVueI18n(raw);
|
|
2592
2664
|
}
|
|
2593
2665
|
}
|
|
2594
2666
|
let payload = flat;
|
|
@@ -2623,27 +2695,30 @@ function angularXMeta(placeholders, name) {
|
|
|
2623
2695
|
return /^[A-Z][A-Z0-9_]*$/.test(name) || meta?.origin === "x" ? meta : void 0;
|
|
2624
2696
|
}
|
|
2625
2697
|
function renderInterpolations(text, ids, placeholders) {
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
const
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
id
|
|
2640
|
-
|
|
2698
|
+
const convertGap = (gap) => {
|
|
2699
|
+
let out = "";
|
|
2700
|
+
let last = 0;
|
|
2701
|
+
for (const m of gap.matchAll(/\{(\w+)\}/g)) {
|
|
2702
|
+
const name = m[1];
|
|
2703
|
+
out += xmlEscape2(gap.slice(last, m.index));
|
|
2704
|
+
const meta = angularXMeta(placeholders, name);
|
|
2705
|
+
if (meta) {
|
|
2706
|
+
const ctype = meta.type ? ` ctype="${attrEscape(meta.type)}"` : "";
|
|
2707
|
+
const equiv = meta.example !== void 0 ? ` equiv-text="${attrEscape(meta.example)}"` : "";
|
|
2708
|
+
out += `<x id="${attrEscape(name)}"${ctype}${equiv}/>`;
|
|
2709
|
+
} else {
|
|
2710
|
+
let id = ids.get(name);
|
|
2711
|
+
if (id === void 0) {
|
|
2712
|
+
id = ids.size === 0 ? "INTERPOLATION" : `INTERPOLATION_${ids.size}`;
|
|
2713
|
+
ids.set(name, id);
|
|
2714
|
+
}
|
|
2715
|
+
out += `<x id="${id}" equiv-text="{{${name}}}"/>`;
|
|
2641
2716
|
}
|
|
2642
|
-
|
|
2717
|
+
last = m.index + m[0].length;
|
|
2643
2718
|
}
|
|
2644
|
-
|
|
2645
|
-
}
|
|
2646
|
-
return
|
|
2719
|
+
return out + xmlEscape2(gap.slice(last));
|
|
2720
|
+
};
|
|
2721
|
+
return withLiterals(text, convertGap, (lit) => xmlEscape2(`'${lit}'`));
|
|
2647
2722
|
}
|
|
2648
2723
|
function renderPluralIcu(forms, ids, placeholders) {
|
|
2649
2724
|
const cats = [
|
|
@@ -4579,6 +4654,9 @@ function flattenObject(value, prefix, warnings) {
|
|
|
4579
4654
|
|
|
4580
4655
|
// src/server/import/parsers/vue-i18n-json.ts
|
|
4581
4656
|
var LOCALE_RE2 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
4657
|
+
function fromVueI18n(value) {
|
|
4658
|
+
return value.replace(/\{'([^']*)'\}/g, "'$1'");
|
|
4659
|
+
}
|
|
4582
4660
|
var vueI18nJson2 = {
|
|
4583
4661
|
name: "vue-i18n-json",
|
|
4584
4662
|
parse(localeRoot, opts) {
|
|
@@ -4599,7 +4677,7 @@ var vueI18nJson2 = {
|
|
|
4599
4677
|
}
|
|
4600
4678
|
if (!locales.includes(locale)) locales.push(locale);
|
|
4601
4679
|
for (const [key, value] of Object.entries(flattenObject(data, "", warnings))) {
|
|
4602
|
-
(keys[key] ??= { values: {} }).values[locale] = value;
|
|
4680
|
+
(keys[key] ??= { values: {} }).values[locale] = fromVueI18n(value);
|
|
4603
4681
|
}
|
|
4604
4682
|
}
|
|
4605
4683
|
return { locales, keys, warnings };
|
|
@@ -4612,8 +4690,14 @@ import { join as join8, relative as relative2 } from "path";
|
|
|
4612
4690
|
import { execFileSync } from "child_process";
|
|
4613
4691
|
|
|
4614
4692
|
// src/server/import/placeholders.ts
|
|
4693
|
+
function markBareBracesLiteral(value) {
|
|
4694
|
+
return value.replace(/(?<!%)\{(\w+)\}/g, "'{$1}'");
|
|
4695
|
+
}
|
|
4615
4696
|
function laravelToCanonical(value) {
|
|
4616
|
-
return value.replace(/:([a-zA-Z][a-zA-Z0-9_]*)/g, "{$1}");
|
|
4697
|
+
return markBareBracesLiteral(value).replace(/:([a-zA-Z][a-zA-Z0-9_]*)/g, "{$1}");
|
|
4698
|
+
}
|
|
4699
|
+
function railsToCanonical(value) {
|
|
4700
|
+
return markBareBracesLiteral(value).replace(/%\{(\w+)\}/g, "{$1}");
|
|
4617
4701
|
}
|
|
4618
4702
|
|
|
4619
4703
|
// src/server/import/parsers/laravel-php.ts
|
|
@@ -4755,6 +4839,9 @@ function localeFromLproj(dir) {
|
|
|
4755
4839
|
if (!m) return null;
|
|
4756
4840
|
return LOCALE_RE4.test(m[1]) ? m[1] : null;
|
|
4757
4841
|
}
|
|
4842
|
+
function printfToCanonical(s) {
|
|
4843
|
+
return s.replace(/%%/g, "%");
|
|
4844
|
+
}
|
|
4758
4845
|
function unescape(body) {
|
|
4759
4846
|
return body.replace(/\\(U[0-9a-fA-F]{4}|u[0-9a-fA-F]{4}|.)/g, (_m, esc) => {
|
|
4760
4847
|
const c = esc[0];
|
|
@@ -4874,7 +4961,7 @@ var appleStrings2 = {
|
|
|
4874
4961
|
warnings.push(`apple-strings: ${dir} has other .strings tables (${others.join(", ")}); only ${TABLE} is imported`);
|
|
4875
4962
|
}
|
|
4876
4963
|
for (const { key, value } of parseStrings(text, file, warnings)) {
|
|
4877
|
-
(keys[key] ??= { values: {} }).values[locale] = value;
|
|
4964
|
+
(keys[key] ??= { values: {} }).values[locale] = printfToCanonical(value);
|
|
4878
4965
|
}
|
|
4879
4966
|
}
|
|
4880
4967
|
return { locales, keys, warnings };
|
|
@@ -5006,6 +5093,24 @@ function unescapePo(s) {
|
|
|
5006
5093
|
(_, c) => c === "n" ? "\n" : c === "t" ? " " : c === "r" ? "\r" : c
|
|
5007
5094
|
);
|
|
5008
5095
|
}
|
|
5096
|
+
function printfToCanonical2(s, arg) {
|
|
5097
|
+
let out = "";
|
|
5098
|
+
for (let i = 0; i < s.length; ) {
|
|
5099
|
+
if (s[i] === "%" && s[i + 1] === "%") {
|
|
5100
|
+
out += "%";
|
|
5101
|
+
i += 2;
|
|
5102
|
+
continue;
|
|
5103
|
+
}
|
|
5104
|
+
if (arg && s[i] === "%" && s[i + 1] === "d") {
|
|
5105
|
+
out += `{${arg}}`;
|
|
5106
|
+
i += 2;
|
|
5107
|
+
continue;
|
|
5108
|
+
}
|
|
5109
|
+
out += s[i];
|
|
5110
|
+
i++;
|
|
5111
|
+
}
|
|
5112
|
+
return out;
|
|
5113
|
+
}
|
|
5009
5114
|
function parseEntries(text) {
|
|
5010
5115
|
const entries = [];
|
|
5011
5116
|
let cur = null;
|
|
@@ -5126,13 +5231,13 @@ var gettextPo2 = {
|
|
|
5126
5231
|
);
|
|
5127
5232
|
continue;
|
|
5128
5233
|
}
|
|
5129
|
-
forms[cat] = body
|
|
5234
|
+
forms[cat] = printfToCanonical2(body, "count");
|
|
5130
5235
|
}
|
|
5131
5236
|
if (!forms.other) continue;
|
|
5132
5237
|
(keys[key] ??= { values: {} }).values[locale] = formsToIcu("count", forms);
|
|
5133
5238
|
} else {
|
|
5134
5239
|
if (!entry.msgstr) continue;
|
|
5135
|
-
(keys[key] ??= { values: {} }).values[locale] = entry.msgstr;
|
|
5240
|
+
(keys[key] ??= { values: {} }).values[locale] = printfToCanonical2(entry.msgstr, "");
|
|
5136
5241
|
}
|
|
5137
5242
|
}
|
|
5138
5243
|
}
|
|
@@ -5233,9 +5338,6 @@ import { readdirSync as readdirSync11, readFileSync as readFileSync18 } from "fs
|
|
|
5233
5338
|
import { join as join14 } from "path";
|
|
5234
5339
|
var LOCALE_RE8 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/i;
|
|
5235
5340
|
var CATEGORY_SET = new Set(PLURAL_CATEGORIES);
|
|
5236
|
-
function fromRuby(value) {
|
|
5237
|
-
return value.replace(/%\{(\w+)\}/g, "{$1}");
|
|
5238
|
-
}
|
|
5239
5341
|
function makeNode() {
|
|
5240
5342
|
return /* @__PURE__ */ Object.create(null);
|
|
5241
5343
|
}
|
|
@@ -5421,7 +5523,7 @@ function synthesizeIcu(forms, file, key, warnings) {
|
|
|
5421
5523
|
`rails-yaml: ${file}: plural "${key}" form "${cat}" contains "#", which ICU reads as the count placeholder`
|
|
5422
5524
|
);
|
|
5423
5525
|
}
|
|
5424
|
-
parts.push(`${cat} {${
|
|
5526
|
+
parts.push(`${cat} {${railsToCanonical(body)}}`);
|
|
5425
5527
|
}
|
|
5426
5528
|
return `{count, plural, ${parts.join(" ")}}`;
|
|
5427
5529
|
}
|
|
@@ -5439,7 +5541,7 @@ var railsYaml2 = {
|
|
|
5439
5541
|
for (const [k, v] of Object.entries(node)) {
|
|
5440
5542
|
const key = prefix ? `${prefix}.${k}` : k;
|
|
5441
5543
|
if (typeof v === "string") {
|
|
5442
|
-
if (v !== "") addValue(key, locale,
|
|
5544
|
+
if (v !== "") addValue(key, locale, railsToCanonical(v));
|
|
5443
5545
|
continue;
|
|
5444
5546
|
}
|
|
5445
5547
|
const forms = asPluralForms(v);
|
|
@@ -5566,6 +5668,24 @@ function parsePlistDict(xml) {
|
|
|
5566
5668
|
return tag.selfClosing ? {} : readDict();
|
|
5567
5669
|
}
|
|
5568
5670
|
var VAR_RE = /%#@([^@]*)@/g;
|
|
5671
|
+
function printfToCanonical3(body, token, arg) {
|
|
5672
|
+
let out = "";
|
|
5673
|
+
for (let i = 0; i < body.length; ) {
|
|
5674
|
+
if (body[i] === "%" && body[i + 1] === "%") {
|
|
5675
|
+
out += "%";
|
|
5676
|
+
i += 2;
|
|
5677
|
+
continue;
|
|
5678
|
+
}
|
|
5679
|
+
if (body.startsWith(token, i)) {
|
|
5680
|
+
out += `{${arg}}`;
|
|
5681
|
+
i += token.length;
|
|
5682
|
+
continue;
|
|
5683
|
+
}
|
|
5684
|
+
out += body[i];
|
|
5685
|
+
i++;
|
|
5686
|
+
}
|
|
5687
|
+
return out;
|
|
5688
|
+
}
|
|
5569
5689
|
function entryToIcu(key, entry, file, warnings) {
|
|
5570
5690
|
const warn = (msg) => {
|
|
5571
5691
|
warnings.push(`apple-stringsdict: ${file}: key "${key}": ${msg}`);
|
|
@@ -5594,7 +5714,7 @@ function entryToIcu(key, entry, file, warnings) {
|
|
|
5594
5714
|
for (const cat of PLURAL_CATEGORIES) {
|
|
5595
5715
|
const body = varDict[cat];
|
|
5596
5716
|
if (typeof body !== "string") continue;
|
|
5597
|
-
forms[cat] = prefix + body
|
|
5717
|
+
forms[cat] = prefix + printfToCanonical3(body, token, arg) + suffix;
|
|
5598
5718
|
}
|
|
5599
5719
|
if (forms.other === void 0) return warn(`variable "${arg}" has no "other" form; skipped`);
|
|
5600
5720
|
return formsToIcu(arg, forms);
|