glotfile 0.5.4 → 0.6.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/dist/server/cli.js +976 -45
- package/dist/server/server.js +924 -43
- package/dist/ui/assets/{index-BGtqXjLQ.js → index-Vn-AfOXc.js} +13 -5
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +6 -1
- package/skill/references/cli-reference.md +6 -0
- package/skill/references/workflows.md +27 -1
package/dist/server/cli.js
CHANGED
|
@@ -122,6 +122,7 @@ function validate(raw) {
|
|
|
122
122
|
if (o.indent !== void 0 && typeof o.indent !== "number") fail("config.outputs[].indent must be a number");
|
|
123
123
|
if (o.finalNewline !== void 0 && typeof o.finalNewline !== "boolean") fail("config.outputs[].finalNewline must be a boolean");
|
|
124
124
|
if (o.includeLocale !== void 0 && typeof o.includeLocale !== "boolean") fail("config.outputs[].includeLocale must be a boolean");
|
|
125
|
+
if (o.skipSourceLocale !== void 0 && typeof o.skipSourceLocale !== "boolean") fail("config.outputs[].skipSourceLocale must be a boolean");
|
|
125
126
|
if (o.localeAliases !== void 0) {
|
|
126
127
|
if (!isObject(o.localeAliases)) fail("config.outputs[].localeAliases must be an object");
|
|
127
128
|
for (const [k, v] of Object.entries(o.localeAliases)) {
|
|
@@ -1574,27 +1575,41 @@ var init_vue_i18n_json = __esm({
|
|
|
1574
1575
|
function xmlEscape2(s) {
|
|
1575
1576
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1576
1577
|
}
|
|
1577
|
-
function
|
|
1578
|
+
function attrEscape(s) {
|
|
1579
|
+
return xmlEscape2(s).replace(/"/g, """);
|
|
1580
|
+
}
|
|
1581
|
+
function angularXMeta(placeholders, name) {
|
|
1582
|
+
return /^[A-Z][A-Z0-9_]*$/.test(name) ? placeholders?.[name] : void 0;
|
|
1583
|
+
}
|
|
1584
|
+
function renderInterpolations(text, ids, placeholders) {
|
|
1578
1585
|
let out = "";
|
|
1579
1586
|
let last = 0;
|
|
1580
1587
|
for (const m of text.matchAll(/\{(\w+)\}/g)) {
|
|
1581
1588
|
const name = m[1];
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1589
|
+
out += xmlEscape2(text.slice(last, m.index));
|
|
1590
|
+
const meta = angularXMeta(placeholders, name);
|
|
1591
|
+
if (meta) {
|
|
1592
|
+
const ctype = meta.type ? ` ctype="${attrEscape(meta.type)}"` : "";
|
|
1593
|
+
const equiv = meta.example !== void 0 ? ` equiv-text="${attrEscape(meta.example)}"` : "";
|
|
1594
|
+
out += `<x id="${attrEscape(name)}"${ctype}${equiv}/>`;
|
|
1595
|
+
} else {
|
|
1596
|
+
let id = ids.get(name);
|
|
1597
|
+
if (id === void 0) {
|
|
1598
|
+
id = ids.size === 0 ? "INTERPOLATION" : `INTERPOLATION_${ids.size}`;
|
|
1599
|
+
ids.set(name, id);
|
|
1600
|
+
}
|
|
1601
|
+
out += `<x id="${id}" equiv-text="{{${name}}}"/>`;
|
|
1586
1602
|
}
|
|
1587
|
-
out += xmlEscape2(text.slice(last, m.index)) + `<x id="${id}" equiv-text="{{${name}}}"/>`;
|
|
1588
1603
|
last = m.index + m[0].length;
|
|
1589
1604
|
}
|
|
1590
1605
|
return out + xmlEscape2(text.slice(last));
|
|
1591
1606
|
}
|
|
1592
|
-
function renderPluralIcu(forms, ids) {
|
|
1607
|
+
function renderPluralIcu(forms, ids, placeholders) {
|
|
1593
1608
|
const cats = [
|
|
1594
1609
|
...Object.keys(forms).filter((c) => c.startsWith("=")),
|
|
1595
1610
|
...PLURAL_CATEGORIES.filter((c) => forms[c] !== void 0)
|
|
1596
1611
|
];
|
|
1597
|
-
const branches = cats.map((cat) => `${cat} {${renderInterpolations(forms[cat] ?? "", ids)}}`);
|
|
1612
|
+
const branches = cats.map((cat) => `${cat} {${renderInterpolations(forms[cat] ?? "", ids, placeholders)}}`);
|
|
1598
1613
|
return `{VAR_PLURAL, plural, ${branches.join(" ")}}`;
|
|
1599
1614
|
}
|
|
1600
1615
|
function renderEmbeddedIcu(value) {
|
|
@@ -1604,8 +1619,8 @@ function renderEmbeddedIcu(value) {
|
|
|
1604
1619
|
);
|
|
1605
1620
|
return xmlEscape2(renamed);
|
|
1606
1621
|
}
|
|
1607
|
-
function renderScalar(value, ids) {
|
|
1608
|
-
return isIcuPluralOrSelect(value) ? renderEmbeddedIcu(value) : renderInterpolations(value, ids);
|
|
1622
|
+
function renderScalar(value, ids, placeholders) {
|
|
1623
|
+
return isIcuPluralOrSelect(value) ? renderEmbeddedIcu(value) : renderInterpolations(value, ids, placeholders);
|
|
1609
1624
|
}
|
|
1610
1625
|
var DEFAULT_LOCALE_CASE8, angularXliff;
|
|
1611
1626
|
var init_angular_xliff = __esm({
|
|
@@ -1636,6 +1651,7 @@ var init_angular_xliff = __esm({
|
|
|
1636
1651
|
const emptyAs = resolveEmptyAs(output, "source");
|
|
1637
1652
|
const keys = Object.keys(state.keys).sort();
|
|
1638
1653
|
for (const locale of state.config.locales) {
|
|
1654
|
+
if (output.skipSourceLocale && locale === sourceLocale) continue;
|
|
1639
1655
|
const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE8);
|
|
1640
1656
|
const units = [];
|
|
1641
1657
|
for (const key of keys) {
|
|
@@ -1646,17 +1662,18 @@ var init_angular_xliff = __esm({
|
|
|
1646
1662
|
if (entry.plural) {
|
|
1647
1663
|
const targetForms = resolveForms(entry, locale, sourceLocale, emptyAs);
|
|
1648
1664
|
if (targetForms === null) continue;
|
|
1649
|
-
source = renderPluralIcu(entry.values[sourceLocale]?.forms ?? {}, ids);
|
|
1650
|
-
target = renderPluralIcu(targetForms, ids);
|
|
1665
|
+
source = renderPluralIcu(entry.values[sourceLocale]?.forms ?? {}, ids, entry.placeholders);
|
|
1666
|
+
target = renderPluralIcu(targetForms, ids, entry.placeholders);
|
|
1651
1667
|
} else {
|
|
1652
1668
|
const targetValue = resolveScalar(entry, locale, sourceLocale, emptyAs);
|
|
1653
1669
|
if (targetValue === null) continue;
|
|
1654
|
-
source = renderScalar(entry.values[sourceLocale]?.value ?? "", ids);
|
|
1655
|
-
target = renderScalar(targetValue, ids);
|
|
1670
|
+
source = renderScalar(entry.values[sourceLocale]?.value ?? "", ids, entry.placeholders);
|
|
1671
|
+
target = renderScalar(targetValue, ids, entry.placeholders);
|
|
1656
1672
|
}
|
|
1673
|
+
const translated = locale === sourceLocale || (entry.plural ? entry.values[locale]?.forms !== void 0 : !!entry.values[locale]?.value);
|
|
1657
1674
|
units.push(` <trans-unit id="${xmlEscape2(key)}" datatype="html">`);
|
|
1658
1675
|
units.push(` <source>${source}</source>`);
|
|
1659
|
-
units.push(` <target>${target}</target>`);
|
|
1676
|
+
units.push(` <target${translated ? "" : ' state="new"'}>${target}</target>`);
|
|
1660
1677
|
if (entry.description) {
|
|
1661
1678
|
units.push(` <note priority="1" from="description">${xmlEscape2(entry.description)}</note>`);
|
|
1662
1679
|
}
|
|
@@ -4032,7 +4049,7 @@ var init_accept = __esm({
|
|
|
4032
4049
|
});
|
|
4033
4050
|
|
|
4034
4051
|
// src/server/import/detect.ts
|
|
4035
|
-
import { existsSync as existsSync10, readdirSync as readdirSync4, statSync as statSync3 } from "fs";
|
|
4052
|
+
import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
|
|
4036
4053
|
import { join as join4 } from "path";
|
|
4037
4054
|
function safeIsDir(p) {
|
|
4038
4055
|
try {
|
|
@@ -4119,6 +4136,110 @@ function detectApple(root) {
|
|
|
4119
4136
|
}
|
|
4120
4137
|
return best;
|
|
4121
4138
|
}
|
|
4139
|
+
function detectAngularXliff(root) {
|
|
4140
|
+
for (const rel of ANGULAR_DIR_CANDIDATES) {
|
|
4141
|
+
const localeRoot = rel === "." ? root : join4(root, rel);
|
|
4142
|
+
if (!safeIsDir(localeRoot)) continue;
|
|
4143
|
+
const files = readdirSync4(localeRoot).filter((f) => /^messages(\..+)?\.xlf$/.test(f)).sort();
|
|
4144
|
+
if (files.length === 0) continue;
|
|
4145
|
+
const locales = files.map((f) => f.match(/^messages\.(.+)\.xlf$/)?.[1]).filter((l) => !!l && LOCALE_RE.test(l));
|
|
4146
|
+
const attrFile = files.includes("messages.xlf") ? "messages.xlf" : files[0];
|
|
4147
|
+
let sourceLocale;
|
|
4148
|
+
try {
|
|
4149
|
+
sourceLocale = readFileSync11(join4(localeRoot, attrFile), "utf8").match(/source-language="([^"]+)"/)?.[1];
|
|
4150
|
+
} catch {
|
|
4151
|
+
}
|
|
4152
|
+
if (!sourceLocale && locales.length === 0) continue;
|
|
4153
|
+
sourceLocale ??= pickSource(locales, () => 0);
|
|
4154
|
+
if (!locales.includes(sourceLocale)) locales.unshift(sourceLocale);
|
|
4155
|
+
return { format: "angular-xliff", localeRoot, locales, sourceLocale };
|
|
4156
|
+
}
|
|
4157
|
+
return null;
|
|
4158
|
+
}
|
|
4159
|
+
function detectRails(root) {
|
|
4160
|
+
const localeRoot = join4(root, "config", "locales");
|
|
4161
|
+
if (!safeIsDir(localeRoot)) return null;
|
|
4162
|
+
const locales = [];
|
|
4163
|
+
for (const file of readdirSync4(localeRoot).sort()) {
|
|
4164
|
+
if (!/\.ya?ml$/.test(file)) continue;
|
|
4165
|
+
let text;
|
|
4166
|
+
try {
|
|
4167
|
+
text = readFileSync11(join4(localeRoot, file), "utf8");
|
|
4168
|
+
} catch {
|
|
4169
|
+
continue;
|
|
4170
|
+
}
|
|
4171
|
+
for (const m of text.matchAll(/^(["']?)([A-Za-z][\w-]*)\1:\s*(?:#.*)?$/gm)) {
|
|
4172
|
+
const token = m[2];
|
|
4173
|
+
if (LOCALE_RE.test(token) && !locales.includes(token)) locales.push(token);
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
if (locales.length === 0) return null;
|
|
4177
|
+
return { format: "rails-yaml", localeRoot, locales, sourceLocale: pickSource(locales, () => 0) };
|
|
4178
|
+
}
|
|
4179
|
+
function detectI18next(root) {
|
|
4180
|
+
for (const rel of I18NEXT_DIR_CANDIDATES) {
|
|
4181
|
+
const localeRoot = join4(root, rel);
|
|
4182
|
+
if (!safeIsDir(localeRoot)) continue;
|
|
4183
|
+
const locales = listDirs(localeRoot).filter(
|
|
4184
|
+
(d) => LOCALE_RE.test(d) && readdirSync4(join4(localeRoot, d)).some((f) => f.endsWith(".json"))
|
|
4185
|
+
);
|
|
4186
|
+
if (locales.length === 0) continue;
|
|
4187
|
+
const sourceLocale = pickSource(locales, (loc) => {
|
|
4188
|
+
try {
|
|
4189
|
+
return readdirSync4(join4(localeRoot, loc)).filter((f) => f.endsWith(".json")).reduce((sum, f) => sum + statSync3(join4(localeRoot, loc, f)).size, 0);
|
|
4190
|
+
} catch {
|
|
4191
|
+
return 0;
|
|
4192
|
+
}
|
|
4193
|
+
});
|
|
4194
|
+
return { format: "i18next-json", localeRoot, locales, sourceLocale };
|
|
4195
|
+
}
|
|
4196
|
+
return null;
|
|
4197
|
+
}
|
|
4198
|
+
function gettextLocales(dir) {
|
|
4199
|
+
const locales = [];
|
|
4200
|
+
for (const entry of readdirSync4(dir).sort()) {
|
|
4201
|
+
const flat = entry.match(/^(.+)\.po$/)?.[1];
|
|
4202
|
+
if (flat && LOCALE_RE.test(flat)) {
|
|
4203
|
+
if (!locales.includes(flat)) locales.push(flat);
|
|
4204
|
+
continue;
|
|
4205
|
+
}
|
|
4206
|
+
if (!LOCALE_RE.test(entry) || !safeIsDir(join4(dir, entry))) continue;
|
|
4207
|
+
const sub = join4(dir, entry);
|
|
4208
|
+
const hasPo = (d) => {
|
|
4209
|
+
try {
|
|
4210
|
+
return readdirSync4(d).some((f) => f.endsWith(".po"));
|
|
4211
|
+
} catch {
|
|
4212
|
+
return false;
|
|
4213
|
+
}
|
|
4214
|
+
};
|
|
4215
|
+
if (hasPo(join4(sub, "LC_MESSAGES")) || hasPo(sub)) {
|
|
4216
|
+
if (!locales.includes(entry)) locales.push(entry);
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
return locales;
|
|
4220
|
+
}
|
|
4221
|
+
function detectGettext(root) {
|
|
4222
|
+
for (const rel of GETTEXT_DIR_CANDIDATES) {
|
|
4223
|
+
const localeRoot = join4(root, rel);
|
|
4224
|
+
if (!safeIsDir(localeRoot)) continue;
|
|
4225
|
+
const locales = gettextLocales(localeRoot);
|
|
4226
|
+
if (locales.length === 0) continue;
|
|
4227
|
+
return { format: "gettext-po", localeRoot, locales, sourceLocale: pickSource(locales, () => 0) };
|
|
4228
|
+
}
|
|
4229
|
+
return null;
|
|
4230
|
+
}
|
|
4231
|
+
function detectAppleStringsdict(root) {
|
|
4232
|
+
const candidates = [root, ...listDirs(root).map((d) => join4(root, d))];
|
|
4233
|
+
let best = null;
|
|
4234
|
+
for (const dir of candidates) {
|
|
4235
|
+
const locales = listDirs(dir).map((d) => d.match(/^(.+)\.lproj$/)?.[1]).filter((l) => !!l && LOCALE_RE.test(l) && existsSync10(join4(dir, `${l}.lproj`, "Localizable.stringsdict")));
|
|
4236
|
+
if (locales.length === 0) continue;
|
|
4237
|
+
if (!best || locales.length > best.locales.length) {
|
|
4238
|
+
best = { format: "apple-stringsdict", localeRoot: dir, locales, sourceLocale: pickSource(locales, () => 0) };
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
return best;
|
|
4242
|
+
}
|
|
4122
4243
|
function detect(root, formatOverride) {
|
|
4123
4244
|
if (!existsSync10(root)) return null;
|
|
4124
4245
|
if (formatOverride) {
|
|
@@ -4132,18 +4253,36 @@ function detect(root, formatOverride) {
|
|
|
4132
4253
|
}
|
|
4133
4254
|
return null;
|
|
4134
4255
|
}
|
|
4135
|
-
var LOCALE_RE, VUE_DIR_CANDIDATES, DETECTORS, BY_FORMAT;
|
|
4256
|
+
var LOCALE_RE, VUE_DIR_CANDIDATES, ANGULAR_DIR_CANDIDATES, I18NEXT_DIR_CANDIDATES, GETTEXT_DIR_CANDIDATES, DETECTORS, BY_FORMAT;
|
|
4136
4257
|
var init_detect = __esm({
|
|
4137
4258
|
"src/server/import/detect.ts"() {
|
|
4138
4259
|
"use strict";
|
|
4139
4260
|
LOCALE_RE = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
4140
4261
|
VUE_DIR_CANDIDATES = ["src/locale", "src/locales", "src/i18n/locales", "locales", "lang"];
|
|
4141
|
-
|
|
4262
|
+
ANGULAR_DIR_CANDIDATES = [".", "src/locale", "src/locales", "src/i18n", "locale", "locales", "i18n", "translations"];
|
|
4263
|
+
I18NEXT_DIR_CANDIDATES = ["public/locales", "static/locales", "locales", "src/locales", "src/i18n/locales"];
|
|
4264
|
+
GETTEXT_DIR_CANDIDATES = ["locale", "locales", "po", "translations"];
|
|
4265
|
+
DETECTORS = [
|
|
4266
|
+
detectLaravel,
|
|
4267
|
+
detectVue,
|
|
4268
|
+
detectArb,
|
|
4269
|
+
detectApple,
|
|
4270
|
+
detectAngularXliff,
|
|
4271
|
+
detectRails,
|
|
4272
|
+
detectI18next,
|
|
4273
|
+
detectGettext,
|
|
4274
|
+
detectAppleStringsdict
|
|
4275
|
+
];
|
|
4142
4276
|
BY_FORMAT = {
|
|
4143
4277
|
"laravel-php": detectLaravel,
|
|
4144
4278
|
"vue-i18n-json": (root) => detectVue(root, true),
|
|
4145
4279
|
"flutter-arb": detectArb,
|
|
4146
|
-
"apple-strings": detectApple
|
|
4280
|
+
"apple-strings": detectApple,
|
|
4281
|
+
"angular-xliff": detectAngularXliff,
|
|
4282
|
+
"rails-yaml": detectRails,
|
|
4283
|
+
"i18next-json": detectI18next,
|
|
4284
|
+
"gettext-po": detectGettext,
|
|
4285
|
+
"apple-stringsdict": detectAppleStringsdict
|
|
4147
4286
|
};
|
|
4148
4287
|
}
|
|
4149
4288
|
});
|
|
@@ -4176,7 +4315,7 @@ var init_flatten = __esm({
|
|
|
4176
4315
|
});
|
|
4177
4316
|
|
|
4178
4317
|
// src/server/import/parsers/vue-i18n-json.ts
|
|
4179
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
4318
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
|
|
4180
4319
|
import { join as join5 } from "path";
|
|
4181
4320
|
var LOCALE_RE2, vueI18nJson2;
|
|
4182
4321
|
var init_vue_i18n_json2 = __esm({
|
|
@@ -4197,7 +4336,7 @@ var init_vue_i18n_json2 = __esm({
|
|
|
4197
4336
|
if (opts?.locales && !opts.locales.includes(locale)) continue;
|
|
4198
4337
|
let data;
|
|
4199
4338
|
try {
|
|
4200
|
-
data = JSON.parse(
|
|
4339
|
+
data = JSON.parse(readFileSync12(join5(localeRoot, file), "utf8"));
|
|
4201
4340
|
} catch (e) {
|
|
4202
4341
|
warnings.push(`vue-i18n-json: failed to parse ${file}: ${e.message}`);
|
|
4203
4342
|
continue;
|
|
@@ -4302,7 +4441,7 @@ var init_laravel_php2 = __esm({
|
|
|
4302
4441
|
});
|
|
4303
4442
|
|
|
4304
4443
|
// src/server/import/parsers/flutter-arb.ts
|
|
4305
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
4444
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync13 } from "fs";
|
|
4306
4445
|
import { join as join7 } from "path";
|
|
4307
4446
|
function localeFromArbName(file) {
|
|
4308
4447
|
const m = file.match(/^(.+)\.arb$/);
|
|
@@ -4343,7 +4482,7 @@ var init_flutter_arb2 = __esm({
|
|
|
4343
4482
|
if (opts?.locales && !opts.locales.includes(locale)) continue;
|
|
4344
4483
|
let data;
|
|
4345
4484
|
try {
|
|
4346
|
-
data = JSON.parse(
|
|
4485
|
+
data = JSON.parse(readFileSync13(join7(localeRoot, file), "utf8"));
|
|
4347
4486
|
} catch (e) {
|
|
4348
4487
|
warnings.push(`flutter-arb: failed to parse ${file}: ${e.message}`);
|
|
4349
4488
|
continue;
|
|
@@ -4370,7 +4509,7 @@ var init_flutter_arb2 = __esm({
|
|
|
4370
4509
|
});
|
|
4371
4510
|
|
|
4372
4511
|
// src/server/import/parsers/apple-strings.ts
|
|
4373
|
-
import { readdirSync as readdirSync8, readFileSync as
|
|
4512
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync14, statSync as statSync5 } from "fs";
|
|
4374
4513
|
import { join as join8 } from "path";
|
|
4375
4514
|
function localeFromLproj(dir) {
|
|
4376
4515
|
const m = dir.match(/^(.+)\.lproj$/);
|
|
@@ -4483,7 +4622,7 @@ var init_apple_strings2 = __esm({
|
|
|
4483
4622
|
let text;
|
|
4484
4623
|
try {
|
|
4485
4624
|
if (!statSync5(file).isFile()) continue;
|
|
4486
|
-
text =
|
|
4625
|
+
text = readFileSync14(file, "utf8");
|
|
4487
4626
|
} catch {
|
|
4488
4627
|
continue;
|
|
4489
4628
|
}
|
|
@@ -4502,6 +4641,773 @@ var init_apple_strings2 = __esm({
|
|
|
4502
4641
|
}
|
|
4503
4642
|
});
|
|
4504
4643
|
|
|
4644
|
+
// src/server/import/parsers/angular-xliff.ts
|
|
4645
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync15 } from "fs";
|
|
4646
|
+
import { join as join9 } from "path";
|
|
4647
|
+
function decodeEntities(s) {
|
|
4648
|
+
return s.replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16))).replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(Number(d))).replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
4649
|
+
}
|
|
4650
|
+
function parseAttrs(s) {
|
|
4651
|
+
const out = {};
|
|
4652
|
+
for (const m of s.matchAll(/([\w-]+)="([^"]*)"/g)) out[m[1]] = decodeEntities(m[2]);
|
|
4653
|
+
return out;
|
|
4654
|
+
}
|
|
4655
|
+
function decodeInline(raw, addMeta) {
|
|
4656
|
+
let out = "";
|
|
4657
|
+
let last = 0;
|
|
4658
|
+
for (const m of raw.matchAll(/<x\b([^>]*?)\/>/g)) {
|
|
4659
|
+
out += decodeEntities(raw.slice(last, m.index));
|
|
4660
|
+
const attrs = parseAttrs(m[1]);
|
|
4661
|
+
const id = attrs["id"] ?? "X";
|
|
4662
|
+
const equiv = attrs["equiv-text"];
|
|
4663
|
+
const simple = equiv?.match(/^\{\{\s*(\w+)\s*\}\}$/);
|
|
4664
|
+
if (simple) {
|
|
4665
|
+
out += `{${simple[1]}}`;
|
|
4666
|
+
} else {
|
|
4667
|
+
out += `{${id}}`;
|
|
4668
|
+
const meta = {};
|
|
4669
|
+
if (attrs["ctype"]) meta.type = attrs["ctype"];
|
|
4670
|
+
if (equiv !== void 0) meta.example = equiv;
|
|
4671
|
+
addMeta(id, meta);
|
|
4672
|
+
}
|
|
4673
|
+
last = m.index + m[0].length;
|
|
4674
|
+
}
|
|
4675
|
+
return out + decodeEntities(raw.slice(last));
|
|
4676
|
+
}
|
|
4677
|
+
var LOCALE_RE5, FILE_RE, angularXliff2;
|
|
4678
|
+
var init_angular_xliff2 = __esm({
|
|
4679
|
+
"src/server/import/parsers/angular-xliff.ts"() {
|
|
4680
|
+
"use strict";
|
|
4681
|
+
LOCALE_RE5 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
4682
|
+
FILE_RE = /^messages(?:\.(.+))?\.xlf$/;
|
|
4683
|
+
angularXliff2 = {
|
|
4684
|
+
name: "angular-xliff",
|
|
4685
|
+
parse(localeRoot, opts) {
|
|
4686
|
+
const warnings = [];
|
|
4687
|
+
const keys = {};
|
|
4688
|
+
const locales = [];
|
|
4689
|
+
const seen = (loc) => {
|
|
4690
|
+
if (!locales.includes(loc)) locales.push(loc);
|
|
4691
|
+
};
|
|
4692
|
+
const files = readdirSync9(localeRoot).filter((f) => FILE_RE.test(f)).sort((a, b) => (a === "messages.xlf" ? -1 : 0) - (b === "messages.xlf" ? -1 : 0) || a.localeCompare(b));
|
|
4693
|
+
for (const file of files) {
|
|
4694
|
+
const fnameLocale = file.match(FILE_RE)[1];
|
|
4695
|
+
if (fnameLocale !== void 0 && !LOCALE_RE5.test(fnameLocale)) continue;
|
|
4696
|
+
let xml;
|
|
4697
|
+
try {
|
|
4698
|
+
xml = readFileSync15(join9(localeRoot, file), "utf8");
|
|
4699
|
+
} catch (e) {
|
|
4700
|
+
warnings.push(`angular-xliff: failed to read ${file}: ${e.message}`);
|
|
4701
|
+
continue;
|
|
4702
|
+
}
|
|
4703
|
+
const sourceLocale = xml.match(/source-language="([^"]+)"/)?.[1];
|
|
4704
|
+
if (!sourceLocale) {
|
|
4705
|
+
warnings.push(`angular-xliff: ${file} has no source-language attribute; skipped`);
|
|
4706
|
+
continue;
|
|
4707
|
+
}
|
|
4708
|
+
const targetLocale = xml.match(/target-language="([^"]+)"/)?.[1] ?? fnameLocale;
|
|
4709
|
+
if (opts?.locales && !opts.locales.includes(targetLocale ?? sourceLocale)) continue;
|
|
4710
|
+
for (const unit of xml.matchAll(/<trans-unit\b([^>]*)>([\s\S]*?)<\/trans-unit>/g)) {
|
|
4711
|
+
const id = parseAttrs(unit[1])["id"];
|
|
4712
|
+
if (!id) {
|
|
4713
|
+
warnings.push(`angular-xliff: ${file} has a trans-unit without an id; skipped`);
|
|
4714
|
+
continue;
|
|
4715
|
+
}
|
|
4716
|
+
const body = unit[2];
|
|
4717
|
+
const src = body.match(/<source\b[^>]*>([\s\S]*?)<\/source>/);
|
|
4718
|
+
let tgt = body.match(/<target\b([^>]*)>([\s\S]*?)<\/target>/);
|
|
4719
|
+
if (tgt && /\bstate="new"/.test(tgt[1])) tgt = null;
|
|
4720
|
+
const entry = keys[id] ??= { values: {} };
|
|
4721
|
+
const addMeta = (name, meta) => {
|
|
4722
|
+
(entry.placeholders ??= {})[name] ??= meta;
|
|
4723
|
+
};
|
|
4724
|
+
if (src && entry.values[sourceLocale] === void 0) {
|
|
4725
|
+
entry.values[sourceLocale] = decodeInline(src[1], addMeta);
|
|
4726
|
+
seen(sourceLocale);
|
|
4727
|
+
}
|
|
4728
|
+
if (tgt && tgt[2] !== "" && targetLocale !== void 0) {
|
|
4729
|
+
entry.values[targetLocale] = decodeInline(tgt[2], addMeta);
|
|
4730
|
+
seen(targetLocale);
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
return { locales, keys, warnings };
|
|
4735
|
+
}
|
|
4736
|
+
};
|
|
4737
|
+
}
|
|
4738
|
+
});
|
|
4739
|
+
|
|
4740
|
+
// src/server/import/parsers/gettext-po.ts
|
|
4741
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync16 } from "fs";
|
|
4742
|
+
import { join as join10 } from "path";
|
|
4743
|
+
function unescapePo(s) {
|
|
4744
|
+
return s.replace(
|
|
4745
|
+
/\\([\\"ntr])/g,
|
|
4746
|
+
(_, c) => c === "n" ? "\n" : c === "t" ? " " : c === "r" ? "\r" : c
|
|
4747
|
+
);
|
|
4748
|
+
}
|
|
4749
|
+
function parseEntries(text) {
|
|
4750
|
+
const entries = [];
|
|
4751
|
+
let cur = null;
|
|
4752
|
+
let append = null;
|
|
4753
|
+
const flush = () => {
|
|
4754
|
+
if (cur && cur.msgid !== void 0) entries.push(cur);
|
|
4755
|
+
cur = null;
|
|
4756
|
+
append = null;
|
|
4757
|
+
};
|
|
4758
|
+
for (const line of text.split("\n")) {
|
|
4759
|
+
if (line.trim() === "") {
|
|
4760
|
+
flush();
|
|
4761
|
+
continue;
|
|
4762
|
+
}
|
|
4763
|
+
if (line.startsWith("#")) continue;
|
|
4764
|
+
const m = line.match(DIRECTIVE_RE);
|
|
4765
|
+
if (m) {
|
|
4766
|
+
const kw = m[1];
|
|
4767
|
+
const idx = m[2];
|
|
4768
|
+
const body = unescapePo(m[3]);
|
|
4769
|
+
if (cur && (kw === "msgctxt" || kw === "msgid" && cur.msgid !== void 0)) flush();
|
|
4770
|
+
cur ??= { plurals: /* @__PURE__ */ new Map() };
|
|
4771
|
+
const entry = cur;
|
|
4772
|
+
if (kw === "msgctxt") {
|
|
4773
|
+
entry.msgctxt = body;
|
|
4774
|
+
append = (c) => {
|
|
4775
|
+
entry.msgctxt = (entry.msgctxt ?? "") + c;
|
|
4776
|
+
};
|
|
4777
|
+
} else if (kw === "msgid") {
|
|
4778
|
+
entry.msgid = body;
|
|
4779
|
+
append = (c) => {
|
|
4780
|
+
entry.msgid = (entry.msgid ?? "") + c;
|
|
4781
|
+
};
|
|
4782
|
+
} else if (kw === "msgid_plural") {
|
|
4783
|
+
entry.msgidPlural = body;
|
|
4784
|
+
append = (c) => {
|
|
4785
|
+
entry.msgidPlural = (entry.msgidPlural ?? "") + c;
|
|
4786
|
+
};
|
|
4787
|
+
} else if (idx !== void 0) {
|
|
4788
|
+
const i = Number(idx);
|
|
4789
|
+
entry.plurals.set(i, body);
|
|
4790
|
+
append = (c) => {
|
|
4791
|
+
entry.plurals.set(i, (entry.plurals.get(i) ?? "") + c);
|
|
4792
|
+
};
|
|
4793
|
+
} else {
|
|
4794
|
+
entry.msgstr = body;
|
|
4795
|
+
append = (c) => {
|
|
4796
|
+
entry.msgstr = (entry.msgstr ?? "") + c;
|
|
4797
|
+
};
|
|
4798
|
+
}
|
|
4799
|
+
continue;
|
|
4800
|
+
}
|
|
4801
|
+
const cont = line.match(CONT_RE);
|
|
4802
|
+
if (cont && append) append(unescapePo(cont[1]));
|
|
4803
|
+
}
|
|
4804
|
+
flush();
|
|
4805
|
+
return entries;
|
|
4806
|
+
}
|
|
4807
|
+
function discoverPoFiles(root) {
|
|
4808
|
+
const found = [];
|
|
4809
|
+
const entries = readdirSync10(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
4810
|
+
for (const e of entries) {
|
|
4811
|
+
if (e.isFile() && e.name.endsWith(".po")) {
|
|
4812
|
+
const base = e.name.slice(0, -3);
|
|
4813
|
+
found.push({ path: join10(root, e.name), rel: e.name, locale: LOCALE_RE6.test(base) ? base : null });
|
|
4814
|
+
} else if (e.isDirectory() && LOCALE_RE6.test(e.name)) {
|
|
4815
|
+
for (const sub of [join10(e.name, "LC_MESSAGES"), e.name]) {
|
|
4816
|
+
let names;
|
|
4817
|
+
try {
|
|
4818
|
+
names = readdirSync10(join10(root, sub)).sort();
|
|
4819
|
+
} catch {
|
|
4820
|
+
continue;
|
|
4821
|
+
}
|
|
4822
|
+
for (const f of names) {
|
|
4823
|
+
if (f.endsWith(".po")) found.push({ path: join10(root, sub, f), rel: join10(sub, f), locale: e.name });
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
return found;
|
|
4829
|
+
}
|
|
4830
|
+
var LOCALE_RE6, DIRECTIVE_RE, CONT_RE, gettextPo2;
|
|
4831
|
+
var init_gettext_po2 = __esm({
|
|
4832
|
+
"src/server/import/parsers/gettext-po.ts"() {
|
|
4833
|
+
"use strict";
|
|
4834
|
+
init_plurals();
|
|
4835
|
+
LOCALE_RE6 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
4836
|
+
DIRECTIVE_RE = /^(msgctxt|msgid_plural|msgid|msgstr)(?:\[(\d+)\])?[ \t]+"(.*)"\s*$/;
|
|
4837
|
+
CONT_RE = /^[ \t]*"(.*)"\s*$/;
|
|
4838
|
+
gettextPo2 = {
|
|
4839
|
+
name: "gettext-po",
|
|
4840
|
+
parse(localeRoot, opts) {
|
|
4841
|
+
const warnings = [];
|
|
4842
|
+
const keys = {};
|
|
4843
|
+
const locales = [];
|
|
4844
|
+
for (const file of discoverPoFiles(localeRoot)) {
|
|
4845
|
+
let entries;
|
|
4846
|
+
try {
|
|
4847
|
+
entries = parseEntries(readFileSync16(file.path, "utf8"));
|
|
4848
|
+
} catch (e) {
|
|
4849
|
+
warnings.push(`gettext-po: failed to parse ${file.rel}: ${e.message}`);
|
|
4850
|
+
continue;
|
|
4851
|
+
}
|
|
4852
|
+
const header = entries.find((e) => e.msgid === "" && e.msgctxt === void 0);
|
|
4853
|
+
const headerLang = header?.msgstr?.match(/^Language:[ \t]*([A-Za-z0-9_-]+)/m)?.[1];
|
|
4854
|
+
const locale = file.locale ?? headerLang;
|
|
4855
|
+
if (!locale) {
|
|
4856
|
+
warnings.push(`gettext-po: cannot determine locale for ${file.rel}; skipped`);
|
|
4857
|
+
continue;
|
|
4858
|
+
}
|
|
4859
|
+
if (opts?.locales && !opts.locales.includes(locale)) continue;
|
|
4860
|
+
if (!locales.includes(locale)) locales.push(locale);
|
|
4861
|
+
const cats = categoriesFor(locale);
|
|
4862
|
+
for (const entry of entries) {
|
|
4863
|
+
if (entry === header) continue;
|
|
4864
|
+
const key = entry.msgctxt ?? entry.msgid;
|
|
4865
|
+
if (!key) continue;
|
|
4866
|
+
if (entry.msgidPlural !== void 0) {
|
|
4867
|
+
const forms = {};
|
|
4868
|
+
for (const [i, body] of [...entry.plurals].sort((a, b) => a[0] - b[0])) {
|
|
4869
|
+
if (body === "") continue;
|
|
4870
|
+
const cat = cats[i];
|
|
4871
|
+
if (!cat) {
|
|
4872
|
+
warnings.push(
|
|
4873
|
+
`gettext-po: ${file.rel} "${key}": msgstr[${i}] exceeds the ${cats.length} plural forms of "${locale}"; ignored`
|
|
4874
|
+
);
|
|
4875
|
+
continue;
|
|
4876
|
+
}
|
|
4877
|
+
forms[cat] = body.split("%d").join("{count}");
|
|
4878
|
+
}
|
|
4879
|
+
if (!forms.other) continue;
|
|
4880
|
+
(keys[key] ??= { values: {} }).values[locale] = formsToIcu("count", forms);
|
|
4881
|
+
} else {
|
|
4882
|
+
if (!entry.msgstr) continue;
|
|
4883
|
+
(keys[key] ??= { values: {} }).values[locale] = entry.msgstr;
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
return { locales, keys, warnings };
|
|
4888
|
+
}
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4891
|
+
});
|
|
4892
|
+
|
|
4893
|
+
// src/server/import/parsers/i18next-json.ts
|
|
4894
|
+
import { readdirSync as readdirSync11, readFileSync as readFileSync17, statSync as statSync6 } from "fs";
|
|
4895
|
+
import { join as join11 } from "path";
|
|
4896
|
+
function safeIsDir2(p) {
|
|
4897
|
+
try {
|
|
4898
|
+
return statSync6(p).isDirectory();
|
|
4899
|
+
} catch {
|
|
4900
|
+
return false;
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
function fromI18next(value) {
|
|
4904
|
+
if (isIcuPluralOrSelect(value)) return value;
|
|
4905
|
+
return value.replace(/\{\{(\w+)\}\}/g, "{$1}");
|
|
4906
|
+
}
|
|
4907
|
+
function ingestFile(path, label, prefix, locale, keys, warnings) {
|
|
4908
|
+
let data;
|
|
4909
|
+
try {
|
|
4910
|
+
data = JSON.parse(readFileSync17(path, "utf8"));
|
|
4911
|
+
} catch (e) {
|
|
4912
|
+
warnings.push(`i18next-json: failed to parse ${label}: ${e.message}`);
|
|
4913
|
+
return false;
|
|
4914
|
+
}
|
|
4915
|
+
const fileWarnings = [];
|
|
4916
|
+
const flat = flattenObject(data, "", fileWarnings);
|
|
4917
|
+
for (const w of fileWarnings) warnings.push(`i18next-json: ${label}: ${w}`);
|
|
4918
|
+
const families = /* @__PURE__ */ new Set();
|
|
4919
|
+
for (const [k, v] of Object.entries(flat)) {
|
|
4920
|
+
const m = PLURAL_SUFFIX_RE.exec(k);
|
|
4921
|
+
if (m && m[2] === "other" && v !== "") families.add(m[1]);
|
|
4922
|
+
}
|
|
4923
|
+
const pluralForms = {};
|
|
4924
|
+
for (const [k, raw] of Object.entries(flat)) {
|
|
4925
|
+
if (raw === "") continue;
|
|
4926
|
+
const value = fromI18next(raw);
|
|
4927
|
+
const m = PLURAL_SUFFIX_RE.exec(k);
|
|
4928
|
+
if (m && families.has(m[1])) {
|
|
4929
|
+
(pluralForms[m[1]] ??= {})[m[2]] = value;
|
|
4930
|
+
continue;
|
|
4931
|
+
}
|
|
4932
|
+
if (families.has(k)) {
|
|
4933
|
+
warnings.push(
|
|
4934
|
+
`i18next-json: ${label}: key "${k}" collides with its own plural suffix family; the plural wins`
|
|
4935
|
+
);
|
|
4936
|
+
continue;
|
|
4937
|
+
}
|
|
4938
|
+
(keys[prefix + k] ??= { values: {} }).values[locale] = value;
|
|
4939
|
+
}
|
|
4940
|
+
for (const [base, forms] of Object.entries(pluralForms)) {
|
|
4941
|
+
(keys[prefix + base] ??= { values: {} }).values[locale] = formsToIcu(PLURAL_ARG, forms);
|
|
4942
|
+
}
|
|
4943
|
+
return true;
|
|
4944
|
+
}
|
|
4945
|
+
var LOCALE_RE7, PLURAL_SUFFIX_RE, PLURAL_ARG, DEFAULT_NAMESPACE, i18nextJson2;
|
|
4946
|
+
var init_i18next_json2 = __esm({
|
|
4947
|
+
"src/server/import/parsers/i18next-json.ts"() {
|
|
4948
|
+
"use strict";
|
|
4949
|
+
init_flatten();
|
|
4950
|
+
init_plurals();
|
|
4951
|
+
init_placeholders();
|
|
4952
|
+
LOCALE_RE7 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
4953
|
+
PLURAL_SUFFIX_RE = /^(.+)_(zero|one|two|few|many|other)$/;
|
|
4954
|
+
PLURAL_ARG = "count";
|
|
4955
|
+
DEFAULT_NAMESPACE = "translation";
|
|
4956
|
+
i18nextJson2 = {
|
|
4957
|
+
name: "i18next-json",
|
|
4958
|
+
parse(localeRoot, opts) {
|
|
4959
|
+
const warnings = [];
|
|
4960
|
+
const keys = {};
|
|
4961
|
+
const locales = [];
|
|
4962
|
+
for (const entry of readdirSync11(localeRoot).sort()) {
|
|
4963
|
+
const full = join11(localeRoot, entry);
|
|
4964
|
+
if (safeIsDir2(full)) {
|
|
4965
|
+
if (!LOCALE_RE7.test(entry)) continue;
|
|
4966
|
+
if (opts?.locales && !opts.locales.includes(entry)) continue;
|
|
4967
|
+
let any = false;
|
|
4968
|
+
for (const file of readdirSync11(full).sort()) {
|
|
4969
|
+
if (!file.endsWith(".json")) continue;
|
|
4970
|
+
const ns = file.slice(0, -".json".length);
|
|
4971
|
+
const prefix = ns === DEFAULT_NAMESPACE ? "" : `${ns}.`;
|
|
4972
|
+
if (ingestFile(join11(full, file), `${entry}/${file}`, prefix, entry, keys, warnings)) any = true;
|
|
4973
|
+
}
|
|
4974
|
+
if (any && !locales.includes(entry)) locales.push(entry);
|
|
4975
|
+
} else if (entry.endsWith(".json")) {
|
|
4976
|
+
const locale = entry.slice(0, -".json".length);
|
|
4977
|
+
if (!LOCALE_RE7.test(locale)) continue;
|
|
4978
|
+
if (opts?.locales && !opts.locales.includes(locale)) continue;
|
|
4979
|
+
if (ingestFile(full, entry, "", locale, keys, warnings) && !locales.includes(locale)) {
|
|
4980
|
+
locales.push(locale);
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
return { locales, keys, warnings };
|
|
4985
|
+
}
|
|
4986
|
+
};
|
|
4987
|
+
}
|
|
4988
|
+
});
|
|
4989
|
+
|
|
4990
|
+
// src/server/import/parsers/rails-yaml.ts
|
|
4991
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync18 } from "fs";
|
|
4992
|
+
import { join as join12 } from "path";
|
|
4993
|
+
function fromRuby(value) {
|
|
4994
|
+
return value.replace(/%\{(\w+)\}/g, "{$1}");
|
|
4995
|
+
}
|
|
4996
|
+
function makeNode() {
|
|
4997
|
+
return /* @__PURE__ */ Object.create(null);
|
|
4998
|
+
}
|
|
4999
|
+
function decodeDouble(body) {
|
|
5000
|
+
let out = "";
|
|
5001
|
+
for (let i = 0; i < body.length; i++) {
|
|
5002
|
+
const c = body[i];
|
|
5003
|
+
if (c !== "\\") {
|
|
5004
|
+
out += c;
|
|
5005
|
+
continue;
|
|
5006
|
+
}
|
|
5007
|
+
const n = body[++i];
|
|
5008
|
+
if (n === void 0) break;
|
|
5009
|
+
out += n === "n" ? "\n" : n === "r" ? "\r" : n === "t" ? " " : n;
|
|
5010
|
+
}
|
|
5011
|
+
return out;
|
|
5012
|
+
}
|
|
5013
|
+
function scanQuoted(s, start) {
|
|
5014
|
+
const q = s[start];
|
|
5015
|
+
if (q === '"') {
|
|
5016
|
+
for (let i = start + 1; i < s.length; i++) {
|
|
5017
|
+
if (s[i] === "\\") i++;
|
|
5018
|
+
else if (s[i] === '"') return { text: decodeDouble(s.slice(start + 1, i)), end: i + 1 };
|
|
5019
|
+
}
|
|
5020
|
+
return null;
|
|
5021
|
+
}
|
|
5022
|
+
let out = "";
|
|
5023
|
+
for (let i = start + 1; i < s.length; i++) {
|
|
5024
|
+
if (s[i] === "'") {
|
|
5025
|
+
if (s[i + 1] === "'") {
|
|
5026
|
+
out += "'";
|
|
5027
|
+
i++;
|
|
5028
|
+
} else {
|
|
5029
|
+
return { text: out, end: i + 1 };
|
|
5030
|
+
}
|
|
5031
|
+
} else {
|
|
5032
|
+
out += s[i];
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
return null;
|
|
5036
|
+
}
|
|
5037
|
+
function stripPlainComment(s) {
|
|
5038
|
+
const m = /(^|\s)#/.exec(s);
|
|
5039
|
+
return (m && m.index >= 0 ? s.slice(0, m.index) : s).trim();
|
|
5040
|
+
}
|
|
5041
|
+
function onlyTrailing(s) {
|
|
5042
|
+
return /^\s*(#.*)?$/.test(s);
|
|
5043
|
+
}
|
|
5044
|
+
function parseYamlSubset(text, file, warnings) {
|
|
5045
|
+
const roots = {};
|
|
5046
|
+
const lines = text.split(/\r?\n/);
|
|
5047
|
+
let stack = [];
|
|
5048
|
+
let skipDeeperThan = null;
|
|
5049
|
+
let lastLeafIndent = null;
|
|
5050
|
+
for (let n = 0; n < lines.length; n++) {
|
|
5051
|
+
const raw = lines[n];
|
|
5052
|
+
const lineNo = n + 1;
|
|
5053
|
+
if (raw.trim() === "" || raw.trim().startsWith("#")) continue;
|
|
5054
|
+
if (raw.trim() === "---") continue;
|
|
5055
|
+
const indentMatch = /^[ \t]*/.exec(raw)[0];
|
|
5056
|
+
if (indentMatch.includes(" ")) {
|
|
5057
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: tab in indentation; line skipped`);
|
|
5058
|
+
continue;
|
|
5059
|
+
}
|
|
5060
|
+
const indent = indentMatch.length;
|
|
5061
|
+
if (skipDeeperThan !== null) {
|
|
5062
|
+
if (indent > skipDeeperThan) continue;
|
|
5063
|
+
skipDeeperThan = null;
|
|
5064
|
+
}
|
|
5065
|
+
const content = raw.slice(indent);
|
|
5066
|
+
if (content.startsWith("- ") || content === "-") {
|
|
5067
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: sequences are not supported; node skipped`);
|
|
5068
|
+
skipDeeperThan = indent;
|
|
5069
|
+
continue;
|
|
5070
|
+
}
|
|
5071
|
+
let key;
|
|
5072
|
+
let rest;
|
|
5073
|
+
if (content[0] === '"' || content[0] === "'") {
|
|
5074
|
+
const k = scanQuoted(content, 0);
|
|
5075
|
+
if (!k || content[k.end] !== ":") {
|
|
5076
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: unparseable quoted key; line skipped`);
|
|
5077
|
+
skipDeeperThan = indent;
|
|
5078
|
+
continue;
|
|
5079
|
+
}
|
|
5080
|
+
key = k.text;
|
|
5081
|
+
rest = content.slice(k.end + 1);
|
|
5082
|
+
} else {
|
|
5083
|
+
const m = /^(.*?):(?=\s|$)/.exec(content);
|
|
5084
|
+
if (!m) {
|
|
5085
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: not a "key: value" mapping line; line skipped`);
|
|
5086
|
+
skipDeeperThan = indent;
|
|
5087
|
+
continue;
|
|
5088
|
+
}
|
|
5089
|
+
key = m[1].trim();
|
|
5090
|
+
rest = content.slice(m[0].length);
|
|
5091
|
+
}
|
|
5092
|
+
if (lastLeafIndent !== null && indent > lastLeafIndent) {
|
|
5093
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: unexpected indentation under a scalar; line skipped`);
|
|
5094
|
+
skipDeeperThan = indent - 1;
|
|
5095
|
+
continue;
|
|
5096
|
+
}
|
|
5097
|
+
while (stack.length > 0 && stack[stack.length - 1].indent >= indent) stack.pop();
|
|
5098
|
+
const trimmed = rest.trim();
|
|
5099
|
+
let value;
|
|
5100
|
+
if (trimmed === "" || trimmed.startsWith("#")) {
|
|
5101
|
+
value = null;
|
|
5102
|
+
} else if (trimmed[0] === "&" || trimmed[0] === "*") {
|
|
5103
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: YAML anchors/aliases are not supported; node skipped`);
|
|
5104
|
+
skipDeeperThan = indent;
|
|
5105
|
+
continue;
|
|
5106
|
+
} else if (trimmed[0] === "|" || trimmed[0] === ">") {
|
|
5107
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: block scalars are not supported; node skipped`);
|
|
5108
|
+
skipDeeperThan = indent;
|
|
5109
|
+
continue;
|
|
5110
|
+
} else if (trimmed[0] === "[" || trimmed[0] === "{") {
|
|
5111
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: flow collections are not supported; node skipped`);
|
|
5112
|
+
skipDeeperThan = indent;
|
|
5113
|
+
continue;
|
|
5114
|
+
} else if (trimmed[0] === '"' || trimmed[0] === "'") {
|
|
5115
|
+
const v = scanQuoted(trimmed, 0);
|
|
5116
|
+
if (!v || !onlyTrailing(trimmed.slice(v.end))) {
|
|
5117
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: unterminated or trailing-garbage quoted value; line skipped`);
|
|
5118
|
+
continue;
|
|
5119
|
+
}
|
|
5120
|
+
value = v.text;
|
|
5121
|
+
} else {
|
|
5122
|
+
value = stripPlainComment(trimmed);
|
|
5123
|
+
}
|
|
5124
|
+
if (stack.length === 0) {
|
|
5125
|
+
if (value !== null) {
|
|
5126
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: top-level key "${key}" has a scalar value; skipped`);
|
|
5127
|
+
lastLeafIndent = indent;
|
|
5128
|
+
continue;
|
|
5129
|
+
}
|
|
5130
|
+
const root = roots[key] ??= makeNode();
|
|
5131
|
+
stack = [{ indent, node: root }];
|
|
5132
|
+
lastLeafIndent = null;
|
|
5133
|
+
continue;
|
|
5134
|
+
}
|
|
5135
|
+
const parent = stack[stack.length - 1].node;
|
|
5136
|
+
if (key in parent) {
|
|
5137
|
+
warnings.push(`rails-yaml: ${file}:${lineNo}: duplicate key "${key}"; later value wins`);
|
|
5138
|
+
}
|
|
5139
|
+
if (value === null) {
|
|
5140
|
+
const child = makeNode();
|
|
5141
|
+
parent[key] = child;
|
|
5142
|
+
stack.push({ indent, node: child });
|
|
5143
|
+
lastLeafIndent = null;
|
|
5144
|
+
} else {
|
|
5145
|
+
parent[key] = value;
|
|
5146
|
+
lastLeafIndent = indent;
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
return { roots };
|
|
5150
|
+
}
|
|
5151
|
+
function asPluralForms(node) {
|
|
5152
|
+
const entries = Object.entries(node);
|
|
5153
|
+
if (entries.length === 0) return null;
|
|
5154
|
+
const forms = {};
|
|
5155
|
+
for (const [k, v] of entries) {
|
|
5156
|
+
if (!CATEGORY_SET.has(k) || typeof v !== "string") return null;
|
|
5157
|
+
if (v !== "") forms[k] = v;
|
|
5158
|
+
}
|
|
5159
|
+
if (!("other" in forms)) return null;
|
|
5160
|
+
return forms;
|
|
5161
|
+
}
|
|
5162
|
+
function synthesizeIcu(forms, file, key, warnings) {
|
|
5163
|
+
const parts = [];
|
|
5164
|
+
for (const cat of PLURAL_CATEGORIES) {
|
|
5165
|
+
const body = forms[cat];
|
|
5166
|
+
if (body === void 0) continue;
|
|
5167
|
+
if (body.includes("#")) {
|
|
5168
|
+
warnings.push(
|
|
5169
|
+
`rails-yaml: ${file}: plural "${key}" form "${cat}" contains "#", which ICU reads as the count placeholder`
|
|
5170
|
+
);
|
|
5171
|
+
}
|
|
5172
|
+
parts.push(`${cat} {${fromRuby(body)}}`);
|
|
5173
|
+
}
|
|
5174
|
+
return `{count, plural, ${parts.join(" ")}}`;
|
|
5175
|
+
}
|
|
5176
|
+
var LOCALE_RE8, CATEGORY_SET, railsYaml2;
|
|
5177
|
+
var init_rails_yaml2 = __esm({
|
|
5178
|
+
"src/server/import/parsers/rails-yaml.ts"() {
|
|
5179
|
+
"use strict";
|
|
5180
|
+
init_schema();
|
|
5181
|
+
LOCALE_RE8 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/i;
|
|
5182
|
+
CATEGORY_SET = new Set(PLURAL_CATEGORIES);
|
|
5183
|
+
railsYaml2 = {
|
|
5184
|
+
name: "rails-yaml",
|
|
5185
|
+
parse(localeRoot, opts) {
|
|
5186
|
+
const warnings = [];
|
|
5187
|
+
const keys = {};
|
|
5188
|
+
const locales = [];
|
|
5189
|
+
const wanted = opts?.locales?.map((l) => l.toLowerCase());
|
|
5190
|
+
const addValue = (key, locale, value) => {
|
|
5191
|
+
(keys[key] ??= { values: {} }).values[locale] = value;
|
|
5192
|
+
};
|
|
5193
|
+
const flatten = (node, prefix, locale, file) => {
|
|
5194
|
+
for (const [k, v] of Object.entries(node)) {
|
|
5195
|
+
const key = prefix ? `${prefix}.${k}` : k;
|
|
5196
|
+
if (typeof v === "string") {
|
|
5197
|
+
if (v !== "") addValue(key, locale, fromRuby(v));
|
|
5198
|
+
continue;
|
|
5199
|
+
}
|
|
5200
|
+
const forms = asPluralForms(v);
|
|
5201
|
+
if (forms) addValue(key, locale, synthesizeIcu(forms, file, key, warnings));
|
|
5202
|
+
else flatten(v, key, locale, file);
|
|
5203
|
+
}
|
|
5204
|
+
};
|
|
5205
|
+
for (const file of readdirSync12(localeRoot).sort()) {
|
|
5206
|
+
if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
|
|
5207
|
+
let text;
|
|
5208
|
+
try {
|
|
5209
|
+
text = readFileSync18(join12(localeRoot, file), "utf8");
|
|
5210
|
+
} catch (e) {
|
|
5211
|
+
warnings.push(`rails-yaml: failed to read ${file}: ${e.message}`);
|
|
5212
|
+
continue;
|
|
5213
|
+
}
|
|
5214
|
+
const { roots } = parseYamlSubset(text, file, warnings);
|
|
5215
|
+
for (const token of Object.keys(roots).sort()) {
|
|
5216
|
+
if (!LOCALE_RE8.test(token)) {
|
|
5217
|
+
warnings.push(`rails-yaml: ${file}: top-level key "${token}" is not a locale; subtree skipped`);
|
|
5218
|
+
continue;
|
|
5219
|
+
}
|
|
5220
|
+
if (wanted && !wanted.includes(token.toLowerCase())) continue;
|
|
5221
|
+
if (!locales.includes(token)) locales.push(token);
|
|
5222
|
+
flatten(roots[token], "", token, file);
|
|
5223
|
+
}
|
|
5224
|
+
}
|
|
5225
|
+
return { locales, keys, warnings };
|
|
5226
|
+
}
|
|
5227
|
+
};
|
|
5228
|
+
}
|
|
5229
|
+
});
|
|
5230
|
+
|
|
5231
|
+
// src/server/import/parsers/apple-stringsdict.ts
|
|
5232
|
+
import { readdirSync as readdirSync13, readFileSync as readFileSync19, statSync as statSync7 } from "fs";
|
|
5233
|
+
import { join as join13 } from "path";
|
|
5234
|
+
function localeFromLproj2(dir) {
|
|
5235
|
+
const m = dir.match(/^(.+)\.lproj$/);
|
|
5236
|
+
if (!m) return null;
|
|
5237
|
+
return LOCALE_RE9.test(m[1]) ? m[1] : null;
|
|
5238
|
+
}
|
|
5239
|
+
function decodeEntities2(s) {
|
|
5240
|
+
return s.replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16))).replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(Number(d))).replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
5241
|
+
}
|
|
5242
|
+
function parsePlistDict(xml) {
|
|
5243
|
+
let i = 0;
|
|
5244
|
+
const n = xml.length;
|
|
5245
|
+
const skipTrivia = () => {
|
|
5246
|
+
for (; ; ) {
|
|
5247
|
+
while (i < n && /\s/.test(xml[i])) i++;
|
|
5248
|
+
if (xml.startsWith("<!--", i)) {
|
|
5249
|
+
const end = xml.indexOf("-->", i + 4);
|
|
5250
|
+
if (end === -1) throw new Error("unterminated comment");
|
|
5251
|
+
i = end + 3;
|
|
5252
|
+
continue;
|
|
5253
|
+
}
|
|
5254
|
+
if (xml.startsWith("<?", i) || xml.startsWith("<!", i) && !xml.startsWith("<!--", i)) {
|
|
5255
|
+
const end = xml.indexOf(">", i);
|
|
5256
|
+
if (end === -1) throw new Error("unterminated declaration");
|
|
5257
|
+
i = end + 1;
|
|
5258
|
+
continue;
|
|
5259
|
+
}
|
|
5260
|
+
break;
|
|
5261
|
+
}
|
|
5262
|
+
};
|
|
5263
|
+
const readTag = () => {
|
|
5264
|
+
if (xml[i] !== "<") throw new Error(`expected a tag at offset ${i}`);
|
|
5265
|
+
const end = xml.indexOf(">", i);
|
|
5266
|
+
if (end === -1) throw new Error("unterminated tag");
|
|
5267
|
+
let body = xml.slice(i + 1, end).trim();
|
|
5268
|
+
i = end + 1;
|
|
5269
|
+
const closing = body.startsWith("/");
|
|
5270
|
+
if (closing) body = body.slice(1).trim();
|
|
5271
|
+
const selfClosing = body.endsWith("/");
|
|
5272
|
+
if (selfClosing) body = body.slice(0, -1).trim();
|
|
5273
|
+
const name = body.split(/\s/)[0];
|
|
5274
|
+
if (!name) throw new Error(`empty tag at offset ${end}`);
|
|
5275
|
+
return { name, closing, selfClosing };
|
|
5276
|
+
};
|
|
5277
|
+
const readElementText = (name) => {
|
|
5278
|
+
const re = new RegExp(`</${name}\\s*>`, "g");
|
|
5279
|
+
re.lastIndex = i;
|
|
5280
|
+
const m = re.exec(xml);
|
|
5281
|
+
if (!m) throw new Error(`unterminated <${name}>`);
|
|
5282
|
+
const text = xml.slice(i, m.index);
|
|
5283
|
+
i = m.index + m[0].length;
|
|
5284
|
+
return decodeEntities2(text);
|
|
5285
|
+
};
|
|
5286
|
+
const readValue = (tag2) => {
|
|
5287
|
+
if (tag2.name === "dict") return tag2.selfClosing ? {} : readDict();
|
|
5288
|
+
if (tag2.name === "true" || tag2.name === "false") {
|
|
5289
|
+
if (!tag2.selfClosing) readElementText(tag2.name);
|
|
5290
|
+
return tag2.name;
|
|
5291
|
+
}
|
|
5292
|
+
if (["string", "integer", "real", "date", "data"].includes(tag2.name)) {
|
|
5293
|
+
return tag2.selfClosing ? "" : readElementText(tag2.name);
|
|
5294
|
+
}
|
|
5295
|
+
throw new Error(`unsupported plist element <${tag2.name}>`);
|
|
5296
|
+
};
|
|
5297
|
+
const readDict = () => {
|
|
5298
|
+
const out = {};
|
|
5299
|
+
for (; ; ) {
|
|
5300
|
+
skipTrivia();
|
|
5301
|
+
const tag2 = readTag();
|
|
5302
|
+
if (tag2.closing) {
|
|
5303
|
+
if (tag2.name !== "dict") throw new Error(`unexpected </${tag2.name}> inside <dict>`);
|
|
5304
|
+
return out;
|
|
5305
|
+
}
|
|
5306
|
+
if (tag2.name !== "key") throw new Error(`expected <key> inside <dict>, got <${tag2.name}>`);
|
|
5307
|
+
const key = readElementText("key");
|
|
5308
|
+
skipTrivia();
|
|
5309
|
+
const vt = readTag();
|
|
5310
|
+
if (vt.closing) throw new Error(`<key>${key}</key> has no value`);
|
|
5311
|
+
out[key] = readValue(vt);
|
|
5312
|
+
}
|
|
5313
|
+
};
|
|
5314
|
+
skipTrivia();
|
|
5315
|
+
let tag = readTag();
|
|
5316
|
+
if (tag.name === "plist" && !tag.closing && !tag.selfClosing) {
|
|
5317
|
+
skipTrivia();
|
|
5318
|
+
tag = readTag();
|
|
5319
|
+
}
|
|
5320
|
+
if (tag.name !== "dict" || tag.closing) throw new Error("expected a root <dict>");
|
|
5321
|
+
return tag.selfClosing ? {} : readDict();
|
|
5322
|
+
}
|
|
5323
|
+
function entryToIcu(key, entry, file, warnings) {
|
|
5324
|
+
const warn = (msg) => {
|
|
5325
|
+
warnings.push(`apple-stringsdict: ${file}: key "${key}": ${msg}`);
|
|
5326
|
+
return null;
|
|
5327
|
+
};
|
|
5328
|
+
if (typeof entry !== "object") return warn("value is not a dict; skipped");
|
|
5329
|
+
const fmt = entry["NSStringLocalizedFormatKey"];
|
|
5330
|
+
if (typeof fmt !== "string") return warn("missing NSStringLocalizedFormatKey; skipped");
|
|
5331
|
+
const vars = [...fmt.matchAll(VAR_RE)];
|
|
5332
|
+
if (vars.length !== 1) {
|
|
5333
|
+
return warn(`format key has ${vars.length} %#@\u2026@ variables; only exactly one is supported; skipped`);
|
|
5334
|
+
}
|
|
5335
|
+
const arg = vars[0][1];
|
|
5336
|
+
if (!/^\w+$/.test(arg)) return warn(`variable name "${arg}" is not a valid ICU argument; skipped`);
|
|
5337
|
+
const prefix = fmt.slice(0, vars[0].index);
|
|
5338
|
+
const suffix = fmt.slice(vars[0].index + vars[0][0].length);
|
|
5339
|
+
const varDict = entry[arg];
|
|
5340
|
+
if (typeof varDict !== "object") return warn(`variable "${arg}" has no dict; skipped`);
|
|
5341
|
+
const specType = varDict["NSStringFormatSpecTypeKey"];
|
|
5342
|
+
if (specType !== void 0 && specType !== "NSStringPluralRuleType") {
|
|
5343
|
+
return warn(`variable "${arg}" is not a plural rule (${String(specType)}); skipped`);
|
|
5344
|
+
}
|
|
5345
|
+
const valueType = varDict["NSStringFormatValueTypeKey"];
|
|
5346
|
+
const token = `%${typeof valueType === "string" && valueType ? valueType : "d"}`;
|
|
5347
|
+
const forms = {};
|
|
5348
|
+
for (const cat of PLURAL_CATEGORIES) {
|
|
5349
|
+
const body = varDict[cat];
|
|
5350
|
+
if (typeof body !== "string") continue;
|
|
5351
|
+
forms[cat] = prefix + body.split(token).join(`{${arg}}`) + suffix;
|
|
5352
|
+
}
|
|
5353
|
+
if (forms.other === void 0) return warn(`variable "${arg}" has no "other" form; skipped`);
|
|
5354
|
+
return formsToIcu(arg, forms);
|
|
5355
|
+
}
|
|
5356
|
+
var LOCALE_RE9, TABLE2, VAR_RE, appleStringsdict2;
|
|
5357
|
+
var init_apple_stringsdict2 = __esm({
|
|
5358
|
+
"src/server/import/parsers/apple-stringsdict.ts"() {
|
|
5359
|
+
"use strict";
|
|
5360
|
+
init_schema();
|
|
5361
|
+
init_plurals();
|
|
5362
|
+
LOCALE_RE9 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
|
|
5363
|
+
TABLE2 = "Localizable.stringsdict";
|
|
5364
|
+
VAR_RE = /%#@([^@]*)@/g;
|
|
5365
|
+
appleStringsdict2 = {
|
|
5366
|
+
name: "apple-stringsdict",
|
|
5367
|
+
parse(localeRoot, opts) {
|
|
5368
|
+
const warnings = [];
|
|
5369
|
+
const keys = {};
|
|
5370
|
+
const locales = [];
|
|
5371
|
+
for (const dir of readdirSync13(localeRoot).sort()) {
|
|
5372
|
+
const locale = localeFromLproj2(dir);
|
|
5373
|
+
if (!locale) continue;
|
|
5374
|
+
if (opts?.locales && !opts.locales.includes(locale)) continue;
|
|
5375
|
+
const file = join13(localeRoot, dir, TABLE2);
|
|
5376
|
+
let text;
|
|
5377
|
+
try {
|
|
5378
|
+
if (!statSync7(file).isFile()) continue;
|
|
5379
|
+
text = readFileSync19(file, "utf8");
|
|
5380
|
+
} catch {
|
|
5381
|
+
continue;
|
|
5382
|
+
}
|
|
5383
|
+
locales.push(locale);
|
|
5384
|
+
const others = readdirSync13(join13(localeRoot, dir)).filter(
|
|
5385
|
+
(f) => f.endsWith(".stringsdict") && f !== TABLE2
|
|
5386
|
+
);
|
|
5387
|
+
if (others.length) {
|
|
5388
|
+
warnings.push(
|
|
5389
|
+
`apple-stringsdict: ${dir} has other .stringsdict tables (${others.join(", ")}); only ${TABLE2} is imported`
|
|
5390
|
+
);
|
|
5391
|
+
}
|
|
5392
|
+
let root;
|
|
5393
|
+
try {
|
|
5394
|
+
root = parsePlistDict(text);
|
|
5395
|
+
} catch (e) {
|
|
5396
|
+
warnings.push(`apple-stringsdict: failed to parse ${file}: ${e.message}`);
|
|
5397
|
+
continue;
|
|
5398
|
+
}
|
|
5399
|
+
for (const key of Object.keys(root).sort()) {
|
|
5400
|
+
const icu = entryToIcu(key, root[key], file, warnings);
|
|
5401
|
+
if (icu === null) continue;
|
|
5402
|
+
(keys[key] ??= { values: {} }).values[locale] = icu;
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
return { locales, keys, warnings };
|
|
5406
|
+
}
|
|
5407
|
+
};
|
|
5408
|
+
}
|
|
5409
|
+
});
|
|
5410
|
+
|
|
4505
5411
|
// src/server/import/parsers/index.ts
|
|
4506
5412
|
function getParser(name) {
|
|
4507
5413
|
const p = REGISTRY[name];
|
|
@@ -4516,11 +5422,21 @@ var init_parsers = __esm({
|
|
|
4516
5422
|
init_laravel_php2();
|
|
4517
5423
|
init_flutter_arb2();
|
|
4518
5424
|
init_apple_strings2();
|
|
5425
|
+
init_angular_xliff2();
|
|
5426
|
+
init_gettext_po2();
|
|
5427
|
+
init_i18next_json2();
|
|
5428
|
+
init_rails_yaml2();
|
|
5429
|
+
init_apple_stringsdict2();
|
|
4519
5430
|
REGISTRY = {
|
|
4520
5431
|
[vueI18nJson2.name]: vueI18nJson2,
|
|
4521
5432
|
[laravelPhp2.name]: laravelPhp2,
|
|
4522
5433
|
[flutterArb2.name]: flutterArb2,
|
|
4523
|
-
[appleStrings2.name]: appleStrings2
|
|
5434
|
+
[appleStrings2.name]: appleStrings2,
|
|
5435
|
+
[angularXliff2.name]: angularXliff2,
|
|
5436
|
+
[gettextPo2.name]: gettextPo2,
|
|
5437
|
+
[i18nextJson2.name]: i18nextJson2,
|
|
5438
|
+
[railsYaml2.name]: railsYaml2,
|
|
5439
|
+
[appleStringsdict2.name]: appleStringsdict2
|
|
4524
5440
|
};
|
|
4525
5441
|
}
|
|
4526
5442
|
});
|
|
@@ -4606,7 +5522,14 @@ var init_assemble = __esm({
|
|
|
4606
5522
|
"laravel-php": { adapter: "laravel-php", path: "lang/{locale}/{namespace}.php" },
|
|
4607
5523
|
"vue-i18n-json": { adapter: "vue-i18n-json", path: "src/locale/{locale}.json" },
|
|
4608
5524
|
"flutter-arb": { adapter: "flutter-arb", path: "lib/l10n/app_{locale}.arb" },
|
|
4609
|
-
"apple-strings": { adapter: "apple-strings", path: "{locale}.lproj/Localizable.strings", rootRelative: true }
|
|
5525
|
+
"apple-strings": { adapter: "apple-strings", path: "{locale}.lproj/Localizable.strings", rootRelative: true },
|
|
5526
|
+
// skipSourceLocale: ng extract-i18n owns messages.xlf (the source file); glotfile
|
|
5527
|
+
// only writes the translation files back next to it.
|
|
5528
|
+
"angular-xliff": { adapter: "angular-xliff", path: "messages.{locale}.xlf", rootRelative: true, skipSourceLocale: true },
|
|
5529
|
+
"gettext-po": { adapter: "gettext-po", path: "{locale}.po", rootRelative: true },
|
|
5530
|
+
"i18next-json": { adapter: "i18next-json", path: "{locale}/translation.json", rootRelative: true },
|
|
5531
|
+
"rails-yaml": { adapter: "rails-yaml", path: "config/locales/{locale}.yml" },
|
|
5532
|
+
"apple-stringsdict": { adapter: "apple-stringsdict", path: "{locale}.lproj/Localizable.stringsdict", rootRelative: true }
|
|
4610
5533
|
};
|
|
4611
5534
|
}
|
|
4612
5535
|
});
|
|
@@ -4917,12 +5840,12 @@ var init_checks = __esm({
|
|
|
4917
5840
|
});
|
|
4918
5841
|
|
|
4919
5842
|
// src/server/ui-prefs.ts
|
|
4920
|
-
import { readFileSync as
|
|
5843
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
4921
5844
|
import { homedir } from "os";
|
|
4922
|
-
import { join as
|
|
5845
|
+
import { join as join14 } from "path";
|
|
4923
5846
|
function readJson2(path) {
|
|
4924
5847
|
try {
|
|
4925
|
-
const parsed = JSON.parse(
|
|
5848
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
4926
5849
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
4927
5850
|
} catch {
|
|
4928
5851
|
return {};
|
|
@@ -4947,7 +5870,7 @@ var init_ui_prefs = __esm({
|
|
|
4947
5870
|
THEMES = ["system", "light", "dark"];
|
|
4948
5871
|
isThemeMode = (v) => THEMES.includes(v);
|
|
4949
5872
|
isPanelWidth = (v) => typeof v === "number" && Number.isFinite(v) && v >= 120 && v <= 1200;
|
|
4950
|
-
defaultUiPrefsPath = () =>
|
|
5873
|
+
defaultUiPrefsPath = () => join14(homedir(), ".glotfile", "ui.json");
|
|
4951
5874
|
DEFAULTS = { theme: "system" };
|
|
4952
5875
|
}
|
|
4953
5876
|
});
|
|
@@ -4955,13 +5878,13 @@ var init_ui_prefs = __esm({
|
|
|
4955
5878
|
// src/server/api.ts
|
|
4956
5879
|
import { Hono } from "hono";
|
|
4957
5880
|
import { streamSSE } from "hono/streaming";
|
|
4958
|
-
import { readFileSync as
|
|
5881
|
+
import { readFileSync as readFileSync21, existsSync as existsSync11, readdirSync as readdirSync14, statSync as statSync8, rmSync as rmSync4 } from "fs";
|
|
4959
5882
|
import { dirname as dirname3, resolve as resolve9, basename, relative as relative4, sep as sep2 } from "path";
|
|
4960
5883
|
function projectName(root) {
|
|
4961
5884
|
const nameFile = resolve9(root, ".idea", ".name");
|
|
4962
5885
|
if (existsSync11(nameFile)) {
|
|
4963
5886
|
try {
|
|
4964
|
-
const name =
|
|
5887
|
+
const name = readFileSync21(nameFile, "utf8").trim();
|
|
4965
5888
|
if (name) return name;
|
|
4966
5889
|
} catch {
|
|
4967
5890
|
}
|
|
@@ -5086,7 +6009,7 @@ function createApi(deps) {
|
|
|
5086
6009
|
if (depth > 4) return;
|
|
5087
6010
|
let entries = [];
|
|
5088
6011
|
try {
|
|
5089
|
-
entries =
|
|
6012
|
+
entries = readdirSync14(dir);
|
|
5090
6013
|
} catch {
|
|
5091
6014
|
return;
|
|
5092
6015
|
}
|
|
@@ -5100,7 +6023,7 @@ function createApi(deps) {
|
|
|
5100
6023
|
filePath = abs;
|
|
5101
6024
|
} else {
|
|
5102
6025
|
try {
|
|
5103
|
-
if (
|
|
6026
|
+
if (statSync8(abs).isDirectory()) walk(abs, depth + 1);
|
|
5104
6027
|
} catch {
|
|
5105
6028
|
}
|
|
5106
6029
|
continue;
|
|
@@ -5918,7 +6841,7 @@ __export(server_exports, {
|
|
|
5918
6841
|
import { Hono as Hono2 } from "hono";
|
|
5919
6842
|
import { serve } from "@hono/node-server";
|
|
5920
6843
|
import { fileURLToPath } from "url";
|
|
5921
|
-
import { dirname as dirname4, join as
|
|
6844
|
+
import { dirname as dirname4, join as join15, resolve as resolve10, extname as extname3, sep as sep3 } from "path";
|
|
5922
6845
|
import { readFile, stat } from "fs/promises";
|
|
5923
6846
|
import { createServer } from "net";
|
|
5924
6847
|
import open from "open";
|
|
@@ -5961,7 +6884,7 @@ function buildApp(opts) {
|
|
|
5961
6884
|
const file = await readFileResponse(target);
|
|
5962
6885
|
if (file) return file;
|
|
5963
6886
|
}
|
|
5964
|
-
const index = await readFileResponse(
|
|
6887
|
+
const index = await readFileResponse(join15(root, "index.html"));
|
|
5965
6888
|
if (index) return index;
|
|
5966
6889
|
return c.notFound();
|
|
5967
6890
|
});
|
|
@@ -6019,7 +6942,7 @@ var init_server = __esm({
|
|
|
6019
6942
|
init_scan();
|
|
6020
6943
|
init_scanner();
|
|
6021
6944
|
here = dirname4(fileURLToPath(import.meta.url));
|
|
6022
|
-
DEFAULT_UI_DIR =
|
|
6945
|
+
DEFAULT_UI_DIR = join15(here, "..", "ui");
|
|
6023
6946
|
MIME = {
|
|
6024
6947
|
".html": "text/html; charset=utf-8",
|
|
6025
6948
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -6063,8 +6986,8 @@ init_scanner();
|
|
|
6063
6986
|
init_context();
|
|
6064
6987
|
init_run2();
|
|
6065
6988
|
init_outputs();
|
|
6066
|
-
import { resolve as resolve11, dirname as dirname5, join as
|
|
6067
|
-
import { readFileSync as
|
|
6989
|
+
import { resolve as resolve11, dirname as dirname5, join as join16 } from "path";
|
|
6990
|
+
import { readFileSync as readFileSync22, existsSync as existsSync12, mkdirSync as mkdirSync4, cpSync } from "fs";
|
|
6068
6991
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6069
6992
|
|
|
6070
6993
|
// src/server/lint/locate.ts
|
|
@@ -6139,6 +7062,9 @@ function parseArgs(argv) {
|
|
|
6139
7062
|
if (first === "help" || first === "--help" || first === "-h") {
|
|
6140
7063
|
return isCommand(argv[1]) ? { command: argv[1], statePath, help: true } : { command: "help", statePath };
|
|
6141
7064
|
}
|
|
7065
|
+
if (first === "version" || first === "--version" || first === "-v") {
|
|
7066
|
+
return { command: "version", statePath };
|
|
7067
|
+
}
|
|
6142
7068
|
if (first !== void 0 && !first.startsWith("-") && !isCommand(first)) {
|
|
6143
7069
|
return { command: "serve", statePath, unknownCommand: first };
|
|
6144
7070
|
}
|
|
@@ -6372,7 +7298,7 @@ async function runLintCmd(args) {
|
|
|
6372
7298
|
}
|
|
6373
7299
|
return;
|
|
6374
7300
|
}
|
|
6375
|
-
const rawText = existsSync12(args.statePath) ?
|
|
7301
|
+
const rawText = existsSync12(args.statePath) ? readFileSync22(args.statePath, "utf8") : "";
|
|
6376
7302
|
const report = await runLint(state, {
|
|
6377
7303
|
locales: args.locales,
|
|
6378
7304
|
ruleIds: args.ruleIds,
|
|
@@ -6396,7 +7322,7 @@ async function runCheck(args) {
|
|
|
6396
7322
|
process.exitCode = 1;
|
|
6397
7323
|
return;
|
|
6398
7324
|
}
|
|
6399
|
-
const rawText = existsSync12(args.statePath) ?
|
|
7325
|
+
const rawText = existsSync12(args.statePath) ? readFileSync22(args.statePath, "utf8") : "";
|
|
6400
7326
|
const root = dirname5(resolve11(args.statePath));
|
|
6401
7327
|
const lint = await runLint(state, {});
|
|
6402
7328
|
const findings = sortFindings([...lint.findings, ...checkOutputs(state, root)]);
|
|
@@ -6557,10 +7483,10 @@ function runSplit(args) {
|
|
|
6557
7483
|
`Split catalog into ${splitDirFor(args.statePath)}/ (config.json, keys.json, locales/ \u2014 up to ${state.config.locales.length} locale files). Removed ${args.statePath}.`
|
|
6558
7484
|
);
|
|
6559
7485
|
}
|
|
6560
|
-
var SKILL_SRC =
|
|
7486
|
+
var SKILL_SRC = join16(dirname5(fileURLToPath2(import.meta.url)), "..", "..", "skill");
|
|
6561
7487
|
function runSkill(args) {
|
|
6562
7488
|
if (args.print) {
|
|
6563
|
-
console.log(
|
|
7489
|
+
console.log(readFileSync22(join16(SKILL_SRC, "SKILL.md"), "utf8").trimEnd());
|
|
6564
7490
|
return;
|
|
6565
7491
|
}
|
|
6566
7492
|
const dest = resolve11(process.cwd(), ".claude", "skills", "glotfile");
|
|
@@ -6691,12 +7617,16 @@ ${formatOpts([...options, ...GLOBAL_OPTS])}`);
|
|
|
6691
7617
|
formatOpts(commands),
|
|
6692
7618
|
"",
|
|
6693
7619
|
"Global options:",
|
|
6694
|
-
formatOpts(GLOBAL_OPTS),
|
|
7620
|
+
formatOpts([...GLOBAL_OPTS, ["-v, --version", "Print the glotfile version"]]),
|
|
6695
7621
|
"",
|
|
6696
7622
|
"Run `glotfile <command> --help` for a command's options."
|
|
6697
7623
|
].join("\n")
|
|
6698
7624
|
);
|
|
6699
7625
|
}
|
|
7626
|
+
function printVersion() {
|
|
7627
|
+
const pkgPath = join16(dirname5(fileURLToPath2(import.meta.url)), "..", "..", "package.json");
|
|
7628
|
+
console.log(JSON.parse(readFileSync22(pkgPath, "utf8")).version);
|
|
7629
|
+
}
|
|
6700
7630
|
async function main(argv) {
|
|
6701
7631
|
const args = parseArgs(argv);
|
|
6702
7632
|
if (args.unknownCommand) {
|
|
@@ -6705,6 +7635,7 @@ async function main(argv) {
|
|
|
6705
7635
|
return;
|
|
6706
7636
|
}
|
|
6707
7637
|
if (args.command === "help") return printHelp();
|
|
7638
|
+
if (args.command === "version") return printVersion();
|
|
6708
7639
|
if (args.help) return printHelp(args.command);
|
|
6709
7640
|
loadDotEnv();
|
|
6710
7641
|
if (args.command === "export") return runExport(args);
|