@verbaly/compiler 0.16.0 → 0.18.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/{claude-Bi_Ex2hm.js → claude-mpjdqqnY.js} +2 -4
- package/dist/claude-mpjdqqnY.js.map +1 -0
- package/dist/cli.js +199 -134
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +168 -129
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/claude-Bi_Ex2hm.js.map +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as isModuleNotFound } from "./cli.js";
|
|
1
2
|
//#region src/providers/claude.ts
|
|
2
3
|
const DEFAULT_MODEL = "claude-sonnet-5";
|
|
3
4
|
const SYSTEM = `You translate UI strings for the Verbaly i18n library.
|
|
@@ -44,10 +45,7 @@ async function loadSdk(load = () => import("@anthropic-ai/sdk")) {
|
|
|
44
45
|
throw error;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
function isModuleNotFound(error, name) {
|
|
48
|
-
return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
|
|
49
|
-
}
|
|
50
48
|
//#endregion
|
|
51
49
|
export { claudeProvider };
|
|
52
50
|
|
|
53
|
-
//# sourceMappingURL=claude-
|
|
51
|
+
//# sourceMappingURL=claude-mpjdqqnY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-mpjdqqnY.js","names":[],"sources":["../src/providers/claude.ts"],"sourcesContent":["import { isModuleNotFound } from '../config';\nimport type { TranslateProvider, TranslateRequest } from '../translate';\n\nexport interface ClaudeProviderOptions {\n model?: string;\n apiKey?: string;\n maxTokens?: number;\n}\n\n// balanced default for translation\nconst DEFAULT_MODEL = 'claude-sonnet-5';\n\nconst SYSTEM = `You translate UI strings for the Verbaly i18n library.\nRules:\n- Translate only the human-readable text, naturally for the target locale.\n- Preserve verbatim: placeholders like {name}, format specs like {price:currency/EUR}, variant blocks like {count | one: ... | other: # ...} (translate only the text inside each variant, keep keys and # as-is), ICU syntax, named tags like <em>...</em> and escapes {{ }} || ##.\n- Keys are opaque identifiers: return exactly the same keys, never translate or rename them.`;\n\nexport function claudeProvider(options: ClaudeProviderOptions = {}): TranslateProvider {\n return async (request: TranslateRequest) => {\n const Anthropic = await loadSdk();\n const client = new Anthropic(options.apiKey ? { apiKey: options.apiKey } : {});\n const response = await client.messages.create({\n model: options.model ?? DEFAULT_MODEL,\n max_tokens: options.maxTokens ?? 16000,\n thinking: { type: 'disabled' },\n system: SYSTEM,\n messages: [{ role: 'user', content: buildPrompt(request) }],\n output_config: { format: batchFormat(request) },\n });\n const text = response.content.find((block) => block.type === 'text')?.text ?? '{}';\n return JSON.parse(text) as Record<string, string>;\n };\n}\n\nexport function buildPrompt(request: TranslateRequest): string {\n return (\n `Translate each value from \"${request.sourceLocale}\" to \"${request.targetLocale}\". ` +\n `Return a JSON object with the same keys and translated values.\\n\\n` +\n JSON.stringify(request.messages, null, 2)\n );\n}\n\nexport function batchFormat(request: TranslateRequest) {\n const keys = Object.keys(request.messages);\n return {\n type: 'json_schema' as const,\n schema: {\n type: 'object',\n properties: Object.fromEntries(keys.map((key) => [key, { type: 'string' }])),\n required: keys,\n additionalProperties: false,\n },\n };\n}\n\ntype SdkModule = { default: typeof import('@anthropic-ai/sdk').default };\n\n// injectable for tests\nexport async function loadSdk(\n load: () => Promise<SdkModule> = () => import('@anthropic-ai/sdk'),\n): Promise<SdkModule['default']> {\n try {\n const mod = await load();\n return mod.default;\n } catch (error) {\n if (isModuleNotFound(error, '@anthropic-ai/sdk')) {\n throw new Error(\n '[verbaly] the claude translate provider needs @anthropic-ai/sdk — install it as a dev dependency (e.g. `pnpm add -D @anthropic-ai/sdk` / `npm i -D @anthropic-ai/sdk`) and set ANTHROPIC_API_KEY',\n { cause: error },\n );\n }\n throw error;\n }\n}\n\n"],"mappings":";;AAUA,MAAM,gBAAgB;AAEtB,MAAM,SAAS;;;;;AAMf,SAAgB,eAAe,UAAiC,CAAC,GAAsB;CACrF,OAAO,OAAO,YAA8B;EAW1C,MAAM,QAAO,MARU,KADJ,OADK,QAAQ,IACH,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC,CAChD,CAAC,CAAC,SAAS,OAAO;GAC5C,OAAO,QAAQ,SAAS;GACxB,YAAY,QAAQ,aAAa;GACjC,UAAU,EAAE,MAAM,WAAW;GAC7B,QAAQ;GACR,UAAU,CAAC;IAAE,MAAM;IAAQ,SAAS,YAAY,OAAO;GAAE,CAAC;GAC1D,eAAe,EAAE,QAAQ,YAAY,OAAO,EAAE;EAChD,CAAC,EAAA,CACqB,QAAQ,MAAM,UAAU,MAAM,SAAS,MAAM,CAAC,EAAE,QAAQ;EAC9E,OAAO,KAAK,MAAM,IAAI;CACxB;AACF;AAEA,SAAgB,YAAY,SAAmC;CAC7D,OACE,8BAA8B,QAAQ,aAAa,QAAQ,QAAQ,aAAa,yEAEhF,KAAK,UAAU,QAAQ,UAAU,MAAM,CAAC;AAE5C;AAEA,SAAgB,YAAY,SAA2B;CACrD,MAAM,OAAO,OAAO,KAAK,QAAQ,QAAQ;CACzC,OAAO;EACL,MAAM;EACN,QAAQ;GACN,MAAM;GACN,YAAY,OAAO,YAAY,KAAK,KAAK,QAAQ,CAAC,KAAK,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC;GAC3E,UAAU;GACV,sBAAsB;EACxB;CACF;AACF;AAKA,eAAsB,QACpB,aAAuC,OAAO,sBACf;CAC/B,IAAI;EAEF,QAAO,MADW,KAAK,EAAA,CACZ;CACb,SAAS,OAAO;EACd,IAAI,iBAAiB,OAAO,mBAAmB,GAC7C,MAAM,IAAI,MACR,oMACA,EAAE,OAAO,MAAM,CACjB;EAEF,MAAM;CACR;AACF"}
|
package/dist/cli.js
CHANGED
|
@@ -159,6 +159,9 @@ ${lines.join("\n")}
|
|
|
159
159
|
export function createInstance(
|
|
160
160
|
options?: import('verbaly').VerbalyOptions<VerbalyKey>,
|
|
161
161
|
): import('verbaly').Verbaly<VerbalyKey>;
|
|
162
|
+
export function createRequestInstance(
|
|
163
|
+
locale: string,
|
|
164
|
+
): Promise<import('verbaly').Verbaly<VerbalyKey>>;
|
|
162
165
|
|
|
163
166
|
export function t<K extends VerbalyKey>(
|
|
164
167
|
key: K,
|
|
@@ -186,6 +189,125 @@ function writeDts(cfg, entries) {
|
|
|
186
189
|
writeFileSync(join(cfg.root, "verbaly.d.ts"), generateDts(entries));
|
|
187
190
|
}
|
|
188
191
|
//#endregion
|
|
192
|
+
//#region src/pseudo.ts
|
|
193
|
+
const PSEUDO_LOCALE = "en-XA";
|
|
194
|
+
const ACCENTS = {
|
|
195
|
+
a: "á",
|
|
196
|
+
b: "ƀ",
|
|
197
|
+
c: "ç",
|
|
198
|
+
d: "đ",
|
|
199
|
+
e: "é",
|
|
200
|
+
f: "ƒ",
|
|
201
|
+
g: "ğ",
|
|
202
|
+
h: "ĥ",
|
|
203
|
+
i: "í",
|
|
204
|
+
j: "ĵ",
|
|
205
|
+
k: "ķ",
|
|
206
|
+
l: "ĺ",
|
|
207
|
+
m: "ɱ",
|
|
208
|
+
n: "ñ",
|
|
209
|
+
o: "ó",
|
|
210
|
+
p: "þ",
|
|
211
|
+
q: "ǫ",
|
|
212
|
+
r: "ŕ",
|
|
213
|
+
s: "š",
|
|
214
|
+
t: "ţ",
|
|
215
|
+
u: "ú",
|
|
216
|
+
v: "ṽ",
|
|
217
|
+
w: "ŵ",
|
|
218
|
+
x: "ẋ",
|
|
219
|
+
y: "ý",
|
|
220
|
+
z: "ž",
|
|
221
|
+
A: "Á",
|
|
222
|
+
B: "Ɓ",
|
|
223
|
+
C: "Ç",
|
|
224
|
+
D: "Đ",
|
|
225
|
+
E: "É",
|
|
226
|
+
F: "Ƒ",
|
|
227
|
+
G: "Ğ",
|
|
228
|
+
H: "Ĥ",
|
|
229
|
+
I: "Í",
|
|
230
|
+
J: "Ĵ",
|
|
231
|
+
K: "Ķ",
|
|
232
|
+
L: "Ĺ",
|
|
233
|
+
M: "Ḿ",
|
|
234
|
+
N: "Ñ",
|
|
235
|
+
O: "Ó",
|
|
236
|
+
P: "Þ",
|
|
237
|
+
Q: "Ǫ",
|
|
238
|
+
R: "Ŕ",
|
|
239
|
+
S: "Š",
|
|
240
|
+
T: "Ţ",
|
|
241
|
+
U: "Ú",
|
|
242
|
+
V: "Ṽ",
|
|
243
|
+
W: "Ŵ",
|
|
244
|
+
X: "Ẍ",
|
|
245
|
+
Y: "Ý",
|
|
246
|
+
Z: "Ž"
|
|
247
|
+
};
|
|
248
|
+
const TAG_AT = /<\/?[a-zA-Z][\w-]*\/?>/y;
|
|
249
|
+
function pseudoLocalize(message) {
|
|
250
|
+
let out = "";
|
|
251
|
+
let letters = 0;
|
|
252
|
+
let i = 0;
|
|
253
|
+
while (i < message.length) {
|
|
254
|
+
const two = message.slice(i, i + 2);
|
|
255
|
+
if (two === "{{" || two === "}}" || two === "||" || two === "##") {
|
|
256
|
+
out += two;
|
|
257
|
+
i += 2;
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const ch = message[i];
|
|
261
|
+
if (ch === "{") {
|
|
262
|
+
const end = matchBrace(message, i);
|
|
263
|
+
out += message.slice(i, end);
|
|
264
|
+
i = end;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (ch === "<") {
|
|
268
|
+
TAG_AT.lastIndex = i;
|
|
269
|
+
const m = TAG_AT.exec(message);
|
|
270
|
+
if (m) {
|
|
271
|
+
out += m[0];
|
|
272
|
+
i += m[0].length;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const mapped = ACCENTS[ch];
|
|
277
|
+
if (mapped) {
|
|
278
|
+
out += mapped;
|
|
279
|
+
letters += 1;
|
|
280
|
+
} else out += ch;
|
|
281
|
+
i += 1;
|
|
282
|
+
}
|
|
283
|
+
const pad = "~".repeat(Math.ceil(letters / 3));
|
|
284
|
+
return `⟦${out}${pad ? " " + pad : ""}⟧`;
|
|
285
|
+
}
|
|
286
|
+
function matchBrace(message, start) {
|
|
287
|
+
let depth = 0;
|
|
288
|
+
for (let i = start; i < message.length; i++) {
|
|
289
|
+
const two = message.slice(i, i + 2);
|
|
290
|
+
if (two === "{{" || two === "}}") {
|
|
291
|
+
i += 1;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const ch = message[i];
|
|
295
|
+
if (ch === "{") depth += 1;
|
|
296
|
+
else if (ch === "}") {
|
|
297
|
+
depth -= 1;
|
|
298
|
+
if (depth === 0) return i + 1;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return message.length;
|
|
302
|
+
}
|
|
303
|
+
function pseudoCatalogs(cfg, catalogs, locale = PSEUDO_LOCALE) {
|
|
304
|
+
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
305
|
+
const target = {};
|
|
306
|
+
for (const [key, msg] of Object.entries(source)) target[key] = msg ? pseudoLocalize(msg) : "";
|
|
307
|
+
catalogs[locale] = target;
|
|
308
|
+
return Object.keys(target).filter((key) => target[key]);
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
189
311
|
//#region src/config.ts
|
|
190
312
|
function resolveConfig(config = {}) {
|
|
191
313
|
const root = resolve(config.root ?? process.cwd());
|
|
@@ -193,7 +315,7 @@ function resolveConfig(config = {}) {
|
|
|
193
315
|
const sourceLocale = config.sourceLocale ?? "en";
|
|
194
316
|
const locales = /* @__PURE__ */ new Set([sourceLocale, ...config.locales ?? []]);
|
|
195
317
|
if (existsSync(dir)) {
|
|
196
|
-
for (const file of readdirSync(dir)) if (file.endsWith(".json")) locales.add(file.slice(0, -5));
|
|
318
|
+
for (const file of readdirSync(dir)) if (file.endsWith(".json") && file !== `en-XA.json`) locales.add(file.slice(0, -5));
|
|
197
319
|
}
|
|
198
320
|
return {
|
|
199
321
|
root,
|
|
@@ -242,6 +364,9 @@ async function loadTsConfig(path) {
|
|
|
242
364
|
function isModuleNotFound(error, name) {
|
|
243
365
|
return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
|
|
244
366
|
}
|
|
367
|
+
function targetLocales(cfg, override) {
|
|
368
|
+
return (override ?? cfg.locales).filter((locale) => locale !== cfg.sourceLocale && cfg.locales.includes(locale));
|
|
369
|
+
}
|
|
245
370
|
async function loadConfig(root, overrides = {}) {
|
|
246
371
|
return resolveConfig({
|
|
247
372
|
...await loadConfigFile(root),
|
|
@@ -395,7 +520,7 @@ function buildTransMessage(code, children) {
|
|
|
395
520
|
text += escapeText(expr.value);
|
|
396
521
|
continue;
|
|
397
522
|
}
|
|
398
|
-
if (expr
|
|
523
|
+
if (containsTaggedT(expr)) return void 0;
|
|
399
524
|
const source = code.slice(expr.start, expr.end);
|
|
400
525
|
const name = uniqueName(deriveName(expr, params.length), source, takenParams);
|
|
401
526
|
params.push({
|
|
@@ -448,6 +573,15 @@ function selfClosedSource(code, opening) {
|
|
|
448
573
|
const src = code.slice(opening.start, opening.end);
|
|
449
574
|
return src.endsWith("/>") ? src : `${src.slice(0, -1).trimEnd()} />`;
|
|
450
575
|
}
|
|
576
|
+
function containsTaggedT(node) {
|
|
577
|
+
let found = false;
|
|
578
|
+
walk(node, (n) => {
|
|
579
|
+
if (n.type !== "TaggedTemplateExpression") return;
|
|
580
|
+
const tag = n.tag;
|
|
581
|
+
if (isTReference(tag) || explicitId(tag)) found = true;
|
|
582
|
+
});
|
|
583
|
+
return found;
|
|
584
|
+
}
|
|
451
585
|
function isTReference(node) {
|
|
452
586
|
if (node.type === "Identifier") return node.name === "t";
|
|
453
587
|
if (node.type === "MemberExpression" && !node.computed) {
|
|
@@ -465,6 +599,7 @@ function buildMessage(code, quasi) {
|
|
|
465
599
|
for (let i = 0; i < expressions.length; i++) {
|
|
466
600
|
const expr = expressions[i];
|
|
467
601
|
if (!expr) return void 0;
|
|
602
|
+
if (containsTaggedT(expr)) return void 0;
|
|
468
603
|
const source = code.slice(expr.start, expr.end);
|
|
469
604
|
const name = uniqueName(deriveName(expr, i), source, taken);
|
|
470
605
|
params.push({
|
|
@@ -768,7 +903,7 @@ function readDeps(root) {
|
|
|
768
903
|
//#region src/translate.ts
|
|
769
904
|
async function translateCatalogs(cfg, catalogs, provider, options = {}) {
|
|
770
905
|
const batchSize = options.batchSize ?? 20;
|
|
771
|
-
const targets = (
|
|
906
|
+
const targets = targetLocales(cfg, options.locales);
|
|
772
907
|
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
773
908
|
const result = {
|
|
774
909
|
translated: {},
|
|
@@ -827,7 +962,7 @@ function exportCatalogs(cfg, catalogs, options = {}) {
|
|
|
827
962
|
const format = options.format ?? "xliff";
|
|
828
963
|
const dir = resolve(cfg.root, options.out ?? "verbaly-export");
|
|
829
964
|
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
830
|
-
const targets = (
|
|
965
|
+
const targets = targetLocales(cfg, options.locales);
|
|
831
966
|
const files = [];
|
|
832
967
|
mkdirSync(dir, { recursive: true });
|
|
833
968
|
for (const locale of targets) {
|
|
@@ -1006,125 +1141,6 @@ function csvRows(content) {
|
|
|
1006
1141
|
return rows;
|
|
1007
1142
|
}
|
|
1008
1143
|
//#endregion
|
|
1009
|
-
//#region src/pseudo.ts
|
|
1010
|
-
const PSEUDO_LOCALE = "en-XA";
|
|
1011
|
-
const ACCENTS = {
|
|
1012
|
-
a: "á",
|
|
1013
|
-
b: "ƀ",
|
|
1014
|
-
c: "ç",
|
|
1015
|
-
d: "đ",
|
|
1016
|
-
e: "é",
|
|
1017
|
-
f: "ƒ",
|
|
1018
|
-
g: "ğ",
|
|
1019
|
-
h: "ĥ",
|
|
1020
|
-
i: "í",
|
|
1021
|
-
j: "ĵ",
|
|
1022
|
-
k: "ķ",
|
|
1023
|
-
l: "ĺ",
|
|
1024
|
-
m: "ɱ",
|
|
1025
|
-
n: "ñ",
|
|
1026
|
-
o: "ó",
|
|
1027
|
-
p: "þ",
|
|
1028
|
-
q: "ǫ",
|
|
1029
|
-
r: "ŕ",
|
|
1030
|
-
s: "š",
|
|
1031
|
-
t: "ţ",
|
|
1032
|
-
u: "ú",
|
|
1033
|
-
v: "ṽ",
|
|
1034
|
-
w: "ŵ",
|
|
1035
|
-
x: "ẋ",
|
|
1036
|
-
y: "ý",
|
|
1037
|
-
z: "ž",
|
|
1038
|
-
A: "Á",
|
|
1039
|
-
B: "Ɓ",
|
|
1040
|
-
C: "Ç",
|
|
1041
|
-
D: "Đ",
|
|
1042
|
-
E: "É",
|
|
1043
|
-
F: "Ƒ",
|
|
1044
|
-
G: "Ğ",
|
|
1045
|
-
H: "Ĥ",
|
|
1046
|
-
I: "Í",
|
|
1047
|
-
J: "Ĵ",
|
|
1048
|
-
K: "Ķ",
|
|
1049
|
-
L: "Ĺ",
|
|
1050
|
-
M: "Ḿ",
|
|
1051
|
-
N: "Ñ",
|
|
1052
|
-
O: "Ó",
|
|
1053
|
-
P: "Þ",
|
|
1054
|
-
Q: "Ǫ",
|
|
1055
|
-
R: "Ŕ",
|
|
1056
|
-
S: "Š",
|
|
1057
|
-
T: "Ţ",
|
|
1058
|
-
U: "Ú",
|
|
1059
|
-
V: "Ṽ",
|
|
1060
|
-
W: "Ŵ",
|
|
1061
|
-
X: "Ẍ",
|
|
1062
|
-
Y: "Ý",
|
|
1063
|
-
Z: "Ž"
|
|
1064
|
-
};
|
|
1065
|
-
const TAG_AT = /<\/?[a-zA-Z][\w-]*\/?>/y;
|
|
1066
|
-
function pseudoLocalize(message) {
|
|
1067
|
-
let out = "";
|
|
1068
|
-
let letters = 0;
|
|
1069
|
-
let i = 0;
|
|
1070
|
-
while (i < message.length) {
|
|
1071
|
-
const two = message.slice(i, i + 2);
|
|
1072
|
-
if (two === "{{" || two === "}}" || two === "||" || two === "##") {
|
|
1073
|
-
out += two;
|
|
1074
|
-
i += 2;
|
|
1075
|
-
continue;
|
|
1076
|
-
}
|
|
1077
|
-
const ch = message[i];
|
|
1078
|
-
if (ch === "{") {
|
|
1079
|
-
const end = matchBrace(message, i);
|
|
1080
|
-
out += message.slice(i, end);
|
|
1081
|
-
i = end;
|
|
1082
|
-
continue;
|
|
1083
|
-
}
|
|
1084
|
-
if (ch === "<") {
|
|
1085
|
-
TAG_AT.lastIndex = i;
|
|
1086
|
-
const m = TAG_AT.exec(message);
|
|
1087
|
-
if (m) {
|
|
1088
|
-
out += m[0];
|
|
1089
|
-
i += m[0].length;
|
|
1090
|
-
continue;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
const mapped = ACCENTS[ch];
|
|
1094
|
-
if (mapped) {
|
|
1095
|
-
out += mapped;
|
|
1096
|
-
letters += 1;
|
|
1097
|
-
} else out += ch;
|
|
1098
|
-
i += 1;
|
|
1099
|
-
}
|
|
1100
|
-
const pad = "~".repeat(Math.ceil(letters / 3));
|
|
1101
|
-
return `⟦${out}${pad ? " " + pad : ""}⟧`;
|
|
1102
|
-
}
|
|
1103
|
-
function matchBrace(message, start) {
|
|
1104
|
-
let depth = 0;
|
|
1105
|
-
for (let i = start; i < message.length; i++) {
|
|
1106
|
-
const two = message.slice(i, i + 2);
|
|
1107
|
-
if (two === "{{" || two === "}}") {
|
|
1108
|
-
i += 1;
|
|
1109
|
-
continue;
|
|
1110
|
-
}
|
|
1111
|
-
const ch = message[i];
|
|
1112
|
-
if (ch === "{") depth += 1;
|
|
1113
|
-
else if (ch === "}") {
|
|
1114
|
-
depth -= 1;
|
|
1115
|
-
if (depth === 0) return i + 1;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
return message.length;
|
|
1119
|
-
}
|
|
1120
|
-
function pseudoCatalogs(cfg, catalogs, locale = PSEUDO_LOCALE) {
|
|
1121
|
-
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
1122
|
-
const target = {};
|
|
1123
|
-
for (const [key, msg] of Object.entries(source)) target[key] = msg ? pseudoLocalize(msg) : "";
|
|
1124
|
-
catalogs[locale] = target;
|
|
1125
|
-
return Object.keys(target).filter((key) => target[key]);
|
|
1126
|
-
}
|
|
1127
|
-
//#endregion
|
|
1128
1144
|
//#region src/render.ts
|
|
1129
1145
|
const HREFLANG_OPEN = "<!--verbaly:hreflang-->";
|
|
1130
1146
|
const HREFLANG_CLOSE = "<!--/verbaly:hreflang-->";
|
|
@@ -1395,7 +1411,7 @@ function decodeEntities(text) {
|
|
|
1395
1411
|
return text.replace(/"/g, "\"").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
1396
1412
|
}
|
|
1397
1413
|
//#endregion
|
|
1398
|
-
//#region src/
|
|
1414
|
+
//#region src/run.ts
|
|
1399
1415
|
const HELP = `verbaly — i18n compiler
|
|
1400
1416
|
|
|
1401
1417
|
Usage:
|
|
@@ -1416,7 +1432,7 @@ Options:
|
|
|
1416
1432
|
--locales <csv> extra locales; for translate: target locales to fill
|
|
1417
1433
|
--prune drop keys no longer referenced (extract)
|
|
1418
1434
|
--model <id> model override for the claude provider (translate)
|
|
1419
|
-
--dry-run list what would happen, write nothing (translate, import)
|
|
1435
|
+
--dry-run list what would happen, write nothing (translate, import, extract)
|
|
1420
1436
|
--format <f> export format: xliff (default) or csv (export)
|
|
1421
1437
|
--out <path> export directory (export, default: verbaly-export)
|
|
1422
1438
|
--missing export only untranslated entries (export)
|
|
@@ -1431,8 +1447,9 @@ Options:
|
|
|
1431
1447
|
Config file: verbaly.config.{js,mjs,ts,mts,json} at root (flags win).
|
|
1432
1448
|
The claude provider needs @anthropic-ai/sdk installed and ANTHROPIC_API_KEY set.
|
|
1433
1449
|
`;
|
|
1434
|
-
async function
|
|
1450
|
+
async function runCli(args = process.argv.slice(2)) {
|
|
1435
1451
|
const { positionals, values } = parseArgs({
|
|
1452
|
+
args,
|
|
1436
1453
|
allowPositionals: true,
|
|
1437
1454
|
options: {
|
|
1438
1455
|
root: { type: "string" },
|
|
@@ -1464,6 +1481,7 @@ async function main() {
|
|
|
1464
1481
|
process.exitCode = command ? 0 : 1;
|
|
1465
1482
|
return;
|
|
1466
1483
|
}
|
|
1484
|
+
if (rejectStrayFlags(command, values)) return;
|
|
1467
1485
|
if (command === "init") {
|
|
1468
1486
|
const result = init({
|
|
1469
1487
|
root: values.root,
|
|
@@ -1505,18 +1523,21 @@ async function main() {
|
|
|
1505
1523
|
return;
|
|
1506
1524
|
}
|
|
1507
1525
|
if (command === "extract") {
|
|
1526
|
+
const dryRun = values["dry-run"];
|
|
1508
1527
|
const registry = await extractProject(cfg);
|
|
1509
1528
|
const catalogs = loadCatalogs(cfg);
|
|
1510
1529
|
if (values.prune) {
|
|
1511
1530
|
const removed = pruneCatalogs(cfg, catalogs, registry);
|
|
1512
|
-
for (const [locale, keys] of Object.entries(removed)) console.log(` ${locale}: -${keys.length} pruned`);
|
|
1531
|
+
for (const [locale, keys] of Object.entries(removed)) console.log(dryRun ? ` ${locale}: would prune ${keys.length} — ${keys.join(", ")}` : ` ${locale}: -${keys.length} pruned`);
|
|
1513
1532
|
}
|
|
1514
1533
|
const { added } = syncCatalogs(cfg, catalogs, registry);
|
|
1515
|
-
|
|
1516
|
-
|
|
1534
|
+
if (!dryRun) {
|
|
1535
|
+
for (const locale of cfg.locales) writeCatalog(cfg, locale, catalogs[locale] ?? {});
|
|
1536
|
+
writeDts(cfg, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));
|
|
1537
|
+
}
|
|
1517
1538
|
const total = registry.messages().size;
|
|
1518
|
-
console.log(`[verbaly] ${total} messages · locales: ${cfg.locales.join(", ")}`);
|
|
1519
|
-
for (const [locale, keys] of Object.entries(added)) console.log(` ${locale}:
|
|
1539
|
+
console.log(`[verbaly] ${total} messages · locales: ${cfg.locales.join(", ")}${dryRun ? " (dry run, nothing written)" : ""}`);
|
|
1540
|
+
for (const [locale, keys] of Object.entries(added)) console.log(` ${locale}: ${dryRun ? `would add ${keys.length}` : `+${keys.length}`}`);
|
|
1520
1541
|
return;
|
|
1521
1542
|
}
|
|
1522
1543
|
if (command === "check") {
|
|
@@ -1623,17 +1644,61 @@ async function main() {
|
|
|
1623
1644
|
console.error(`[verbaly] unknown command "${command}"\n${HELP}`);
|
|
1624
1645
|
process.exitCode = 1;
|
|
1625
1646
|
}
|
|
1647
|
+
const COMMON_FLAGS = /* @__PURE__ */ new Set([
|
|
1648
|
+
"root",
|
|
1649
|
+
"dir",
|
|
1650
|
+
"source",
|
|
1651
|
+
"locales",
|
|
1652
|
+
"help"
|
|
1653
|
+
]);
|
|
1654
|
+
const COMMAND_FLAGS = {
|
|
1655
|
+
init: [],
|
|
1656
|
+
doctor: [],
|
|
1657
|
+
extract: ["prune", "dry-run"],
|
|
1658
|
+
check: [],
|
|
1659
|
+
translate: ["model", "dry-run"],
|
|
1660
|
+
export: [
|
|
1661
|
+
"format",
|
|
1662
|
+
"out",
|
|
1663
|
+
"missing"
|
|
1664
|
+
],
|
|
1665
|
+
import: [
|
|
1666
|
+
"locale",
|
|
1667
|
+
"overwrite",
|
|
1668
|
+
"dry-run"
|
|
1669
|
+
],
|
|
1670
|
+
pseudo: ["locale"],
|
|
1671
|
+
render: [
|
|
1672
|
+
"site",
|
|
1673
|
+
"attribute",
|
|
1674
|
+
"base-url",
|
|
1675
|
+
"sitemap",
|
|
1676
|
+
"clean"
|
|
1677
|
+
]
|
|
1678
|
+
};
|
|
1679
|
+
function rejectStrayFlags(command, values) {
|
|
1680
|
+
const own = COMMAND_FLAGS[command];
|
|
1681
|
+
if (!own) return false;
|
|
1682
|
+
const allowed = /* @__PURE__ */ new Set([...COMMON_FLAGS, ...own]);
|
|
1683
|
+
const stray = Object.keys(values).filter((k) => values[k] !== void 0 && !allowed.has(k));
|
|
1684
|
+
if (stray.length === 0) return false;
|
|
1685
|
+
for (const flag of stray) console.error(`[verbaly] --${flag} is not a "${command}" flag${flag === "locale" ? " — did you mean --locales?" : ""}`);
|
|
1686
|
+
process.exitCode = 1;
|
|
1687
|
+
return true;
|
|
1688
|
+
}
|
|
1626
1689
|
async function resolveProvider(cfg, model) {
|
|
1627
1690
|
const configured = cfg.translate.provider;
|
|
1628
1691
|
if (typeof configured === "function") return configured;
|
|
1629
|
-
const { claudeProvider } = await import("./claude-
|
|
1692
|
+
const { claudeProvider } = await import("./claude-mpjdqqnY.js");
|
|
1630
1693
|
return claudeProvider({ model: model ?? cfg.translate.model });
|
|
1631
1694
|
}
|
|
1632
|
-
|
|
1695
|
+
//#endregion
|
|
1696
|
+
//#region src/cli.ts
|
|
1697
|
+
runCli().catch((error) => {
|
|
1633
1698
|
console.error("[verbaly]", error instanceof Error ? error.message : error);
|
|
1634
1699
|
process.exitCode = 1;
|
|
1635
1700
|
});
|
|
1636
1701
|
//#endregion
|
|
1637
|
-
export {};
|
|
1702
|
+
export { isModuleNotFound as t };
|
|
1638
1703
|
|
|
1639
1704
|
//# sourceMappingURL=cli.js.map
|