@verbaly/compiler 0.15.0 → 0.17.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 +205 -134
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +191 -137
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- 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
|
@@ -154,6 +154,15 @@ ${lines.join("\n")}
|
|
|
154
154
|
|
|
155
155
|
export const verbaly: import('verbaly').Verbaly<VerbalyKey>;
|
|
156
156
|
|
|
157
|
+
export const sourceLocale: string;
|
|
158
|
+
export const locales: string[];
|
|
159
|
+
export function createInstance(
|
|
160
|
+
options?: import('verbaly').VerbalyOptions<VerbalyKey>,
|
|
161
|
+
): import('verbaly').Verbaly<VerbalyKey>;
|
|
162
|
+
export function createRequestInstance(
|
|
163
|
+
locale: string,
|
|
164
|
+
): Promise<import('verbaly').Verbaly<VerbalyKey>>;
|
|
165
|
+
|
|
157
166
|
export function t<K extends VerbalyKey>(
|
|
158
167
|
key: K,
|
|
159
168
|
...args: [VerbalyMessages[K]] extends [never] ? [] : [VerbalyMessages[K]]
|
|
@@ -180,6 +189,125 @@ function writeDts(cfg, entries) {
|
|
|
180
189
|
writeFileSync(join(cfg.root, "verbaly.d.ts"), generateDts(entries));
|
|
181
190
|
}
|
|
182
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
|
|
183
311
|
//#region src/config.ts
|
|
184
312
|
function resolveConfig(config = {}) {
|
|
185
313
|
const root = resolve(config.root ?? process.cwd());
|
|
@@ -187,7 +315,7 @@ function resolveConfig(config = {}) {
|
|
|
187
315
|
const sourceLocale = config.sourceLocale ?? "en";
|
|
188
316
|
const locales = /* @__PURE__ */ new Set([sourceLocale, ...config.locales ?? []]);
|
|
189
317
|
if (existsSync(dir)) {
|
|
190
|
-
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));
|
|
191
319
|
}
|
|
192
320
|
return {
|
|
193
321
|
root,
|
|
@@ -236,6 +364,9 @@ async function loadTsConfig(path) {
|
|
|
236
364
|
function isModuleNotFound(error, name) {
|
|
237
365
|
return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
|
|
238
366
|
}
|
|
367
|
+
function targetLocales(cfg, override) {
|
|
368
|
+
return (override ?? cfg.locales).filter((locale) => locale !== cfg.sourceLocale && cfg.locales.includes(locale));
|
|
369
|
+
}
|
|
239
370
|
async function loadConfig(root, overrides = {}) {
|
|
240
371
|
return resolveConfig({
|
|
241
372
|
...await loadConfigFile(root),
|
|
@@ -389,7 +520,7 @@ function buildTransMessage(code, children) {
|
|
|
389
520
|
text += escapeText(expr.value);
|
|
390
521
|
continue;
|
|
391
522
|
}
|
|
392
|
-
if (expr
|
|
523
|
+
if (containsTaggedT(expr)) return void 0;
|
|
393
524
|
const source = code.slice(expr.start, expr.end);
|
|
394
525
|
const name = uniqueName(deriveName(expr, params.length), source, takenParams);
|
|
395
526
|
params.push({
|
|
@@ -442,6 +573,15 @@ function selfClosedSource(code, opening) {
|
|
|
442
573
|
const src = code.slice(opening.start, opening.end);
|
|
443
574
|
return src.endsWith("/>") ? src : `${src.slice(0, -1).trimEnd()} />`;
|
|
444
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
|
+
}
|
|
445
585
|
function isTReference(node) {
|
|
446
586
|
if (node.type === "Identifier") return node.name === "t";
|
|
447
587
|
if (node.type === "MemberExpression" && !node.computed) {
|
|
@@ -459,6 +599,7 @@ function buildMessage(code, quasi) {
|
|
|
459
599
|
for (let i = 0; i < expressions.length; i++) {
|
|
460
600
|
const expr = expressions[i];
|
|
461
601
|
if (!expr) return void 0;
|
|
602
|
+
if (containsTaggedT(expr)) return void 0;
|
|
462
603
|
const source = code.slice(expr.start, expr.end);
|
|
463
604
|
const name = uniqueName(deriveName(expr, i), source, taken);
|
|
464
605
|
params.push({
|
|
@@ -762,7 +903,7 @@ function readDeps(root) {
|
|
|
762
903
|
//#region src/translate.ts
|
|
763
904
|
async function translateCatalogs(cfg, catalogs, provider, options = {}) {
|
|
764
905
|
const batchSize = options.batchSize ?? 20;
|
|
765
|
-
const targets = (
|
|
906
|
+
const targets = targetLocales(cfg, options.locales);
|
|
766
907
|
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
767
908
|
const result = {
|
|
768
909
|
translated: {},
|
|
@@ -821,7 +962,7 @@ function exportCatalogs(cfg, catalogs, options = {}) {
|
|
|
821
962
|
const format = options.format ?? "xliff";
|
|
822
963
|
const dir = resolve(cfg.root, options.out ?? "verbaly-export");
|
|
823
964
|
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
824
|
-
const targets = (
|
|
965
|
+
const targets = targetLocales(cfg, options.locales);
|
|
825
966
|
const files = [];
|
|
826
967
|
mkdirSync(dir, { recursive: true });
|
|
827
968
|
for (const locale of targets) {
|
|
@@ -1000,125 +1141,6 @@ function csvRows(content) {
|
|
|
1000
1141
|
return rows;
|
|
1001
1142
|
}
|
|
1002
1143
|
//#endregion
|
|
1003
|
-
//#region src/pseudo.ts
|
|
1004
|
-
const PSEUDO_LOCALE = "en-XA";
|
|
1005
|
-
const ACCENTS = {
|
|
1006
|
-
a: "á",
|
|
1007
|
-
b: "ƀ",
|
|
1008
|
-
c: "ç",
|
|
1009
|
-
d: "đ",
|
|
1010
|
-
e: "é",
|
|
1011
|
-
f: "ƒ",
|
|
1012
|
-
g: "ğ",
|
|
1013
|
-
h: "ĥ",
|
|
1014
|
-
i: "í",
|
|
1015
|
-
j: "ĵ",
|
|
1016
|
-
k: "ķ",
|
|
1017
|
-
l: "ĺ",
|
|
1018
|
-
m: "ɱ",
|
|
1019
|
-
n: "ñ",
|
|
1020
|
-
o: "ó",
|
|
1021
|
-
p: "þ",
|
|
1022
|
-
q: "ǫ",
|
|
1023
|
-
r: "ŕ",
|
|
1024
|
-
s: "š",
|
|
1025
|
-
t: "ţ",
|
|
1026
|
-
u: "ú",
|
|
1027
|
-
v: "ṽ",
|
|
1028
|
-
w: "ŵ",
|
|
1029
|
-
x: "ẋ",
|
|
1030
|
-
y: "ý",
|
|
1031
|
-
z: "ž",
|
|
1032
|
-
A: "Á",
|
|
1033
|
-
B: "Ɓ",
|
|
1034
|
-
C: "Ç",
|
|
1035
|
-
D: "Đ",
|
|
1036
|
-
E: "É",
|
|
1037
|
-
F: "Ƒ",
|
|
1038
|
-
G: "Ğ",
|
|
1039
|
-
H: "Ĥ",
|
|
1040
|
-
I: "Í",
|
|
1041
|
-
J: "Ĵ",
|
|
1042
|
-
K: "Ķ",
|
|
1043
|
-
L: "Ĺ",
|
|
1044
|
-
M: "Ḿ",
|
|
1045
|
-
N: "Ñ",
|
|
1046
|
-
O: "Ó",
|
|
1047
|
-
P: "Þ",
|
|
1048
|
-
Q: "Ǫ",
|
|
1049
|
-
R: "Ŕ",
|
|
1050
|
-
S: "Š",
|
|
1051
|
-
T: "Ţ",
|
|
1052
|
-
U: "Ú",
|
|
1053
|
-
V: "Ṽ",
|
|
1054
|
-
W: "Ŵ",
|
|
1055
|
-
X: "Ẍ",
|
|
1056
|
-
Y: "Ý",
|
|
1057
|
-
Z: "Ž"
|
|
1058
|
-
};
|
|
1059
|
-
const TAG_AT = /<\/?[a-zA-Z][\w-]*\/?>/y;
|
|
1060
|
-
function pseudoLocalize(message) {
|
|
1061
|
-
let out = "";
|
|
1062
|
-
let letters = 0;
|
|
1063
|
-
let i = 0;
|
|
1064
|
-
while (i < message.length) {
|
|
1065
|
-
const two = message.slice(i, i + 2);
|
|
1066
|
-
if (two === "{{" || two === "}}" || two === "||" || two === "##") {
|
|
1067
|
-
out += two;
|
|
1068
|
-
i += 2;
|
|
1069
|
-
continue;
|
|
1070
|
-
}
|
|
1071
|
-
const ch = message[i];
|
|
1072
|
-
if (ch === "{") {
|
|
1073
|
-
const end = matchBrace(message, i);
|
|
1074
|
-
out += message.slice(i, end);
|
|
1075
|
-
i = end;
|
|
1076
|
-
continue;
|
|
1077
|
-
}
|
|
1078
|
-
if (ch === "<") {
|
|
1079
|
-
TAG_AT.lastIndex = i;
|
|
1080
|
-
const m = TAG_AT.exec(message);
|
|
1081
|
-
if (m) {
|
|
1082
|
-
out += m[0];
|
|
1083
|
-
i += m[0].length;
|
|
1084
|
-
continue;
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
const mapped = ACCENTS[ch];
|
|
1088
|
-
if (mapped) {
|
|
1089
|
-
out += mapped;
|
|
1090
|
-
letters += 1;
|
|
1091
|
-
} else out += ch;
|
|
1092
|
-
i += 1;
|
|
1093
|
-
}
|
|
1094
|
-
const pad = "~".repeat(Math.ceil(letters / 3));
|
|
1095
|
-
return `⟦${out}${pad ? " " + pad : ""}⟧`;
|
|
1096
|
-
}
|
|
1097
|
-
function matchBrace(message, start) {
|
|
1098
|
-
let depth = 0;
|
|
1099
|
-
for (let i = start; i < message.length; i++) {
|
|
1100
|
-
const two = message.slice(i, i + 2);
|
|
1101
|
-
if (two === "{{" || two === "}}") {
|
|
1102
|
-
i += 1;
|
|
1103
|
-
continue;
|
|
1104
|
-
}
|
|
1105
|
-
const ch = message[i];
|
|
1106
|
-
if (ch === "{") depth += 1;
|
|
1107
|
-
else if (ch === "}") {
|
|
1108
|
-
depth -= 1;
|
|
1109
|
-
if (depth === 0) return i + 1;
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
return message.length;
|
|
1113
|
-
}
|
|
1114
|
-
function pseudoCatalogs(cfg, catalogs, locale = PSEUDO_LOCALE) {
|
|
1115
|
-
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
1116
|
-
const target = {};
|
|
1117
|
-
for (const [key, msg] of Object.entries(source)) target[key] = msg ? pseudoLocalize(msg) : "";
|
|
1118
|
-
catalogs[locale] = target;
|
|
1119
|
-
return Object.keys(target).filter((key) => target[key]);
|
|
1120
|
-
}
|
|
1121
|
-
//#endregion
|
|
1122
1144
|
//#region src/render.ts
|
|
1123
1145
|
const HREFLANG_OPEN = "<!--verbaly:hreflang-->";
|
|
1124
1146
|
const HREFLANG_CLOSE = "<!--/verbaly:hreflang-->";
|
|
@@ -1389,7 +1411,7 @@ function decodeEntities(text) {
|
|
|
1389
1411
|
return text.replace(/"/g, "\"").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
1390
1412
|
}
|
|
1391
1413
|
//#endregion
|
|
1392
|
-
//#region src/
|
|
1414
|
+
//#region src/run.ts
|
|
1393
1415
|
const HELP = `verbaly — i18n compiler
|
|
1394
1416
|
|
|
1395
1417
|
Usage:
|
|
@@ -1410,7 +1432,7 @@ Options:
|
|
|
1410
1432
|
--locales <csv> extra locales; for translate: target locales to fill
|
|
1411
1433
|
--prune drop keys no longer referenced (extract)
|
|
1412
1434
|
--model <id> model override for the claude provider (translate)
|
|
1413
|
-
--dry-run list what would happen, write nothing (translate, import)
|
|
1435
|
+
--dry-run list what would happen, write nothing (translate, import, extract)
|
|
1414
1436
|
--format <f> export format: xliff (default) or csv (export)
|
|
1415
1437
|
--out <path> export directory (export, default: verbaly-export)
|
|
1416
1438
|
--missing export only untranslated entries (export)
|
|
@@ -1425,8 +1447,9 @@ Options:
|
|
|
1425
1447
|
Config file: verbaly.config.{js,mjs,ts,mts,json} at root (flags win).
|
|
1426
1448
|
The claude provider needs @anthropic-ai/sdk installed and ANTHROPIC_API_KEY set.
|
|
1427
1449
|
`;
|
|
1428
|
-
async function
|
|
1450
|
+
async function runCli(args = process.argv.slice(2)) {
|
|
1429
1451
|
const { positionals, values } = parseArgs({
|
|
1452
|
+
args,
|
|
1430
1453
|
allowPositionals: true,
|
|
1431
1454
|
options: {
|
|
1432
1455
|
root: { type: "string" },
|
|
@@ -1458,6 +1481,7 @@ async function main() {
|
|
|
1458
1481
|
process.exitCode = command ? 0 : 1;
|
|
1459
1482
|
return;
|
|
1460
1483
|
}
|
|
1484
|
+
if (rejectStrayFlags(command, values)) return;
|
|
1461
1485
|
if (command === "init") {
|
|
1462
1486
|
const result = init({
|
|
1463
1487
|
root: values.root,
|
|
@@ -1499,18 +1523,21 @@ async function main() {
|
|
|
1499
1523
|
return;
|
|
1500
1524
|
}
|
|
1501
1525
|
if (command === "extract") {
|
|
1526
|
+
const dryRun = values["dry-run"];
|
|
1502
1527
|
const registry = await extractProject(cfg);
|
|
1503
1528
|
const catalogs = loadCatalogs(cfg);
|
|
1504
1529
|
if (values.prune) {
|
|
1505
1530
|
const removed = pruneCatalogs(cfg, catalogs, registry);
|
|
1506
|
-
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`);
|
|
1507
1532
|
}
|
|
1508
1533
|
const { added } = syncCatalogs(cfg, catalogs, registry);
|
|
1509
|
-
|
|
1510
|
-
|
|
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
|
+
}
|
|
1511
1538
|
const total = registry.messages().size;
|
|
1512
|
-
console.log(`[verbaly] ${total} messages · locales: ${cfg.locales.join(", ")}`);
|
|
1513
|
-
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}`}`);
|
|
1514
1541
|
return;
|
|
1515
1542
|
}
|
|
1516
1543
|
if (command === "check") {
|
|
@@ -1617,17 +1644,61 @@ async function main() {
|
|
|
1617
1644
|
console.error(`[verbaly] unknown command "${command}"\n${HELP}`);
|
|
1618
1645
|
process.exitCode = 1;
|
|
1619
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
|
+
}
|
|
1620
1689
|
async function resolveProvider(cfg, model) {
|
|
1621
1690
|
const configured = cfg.translate.provider;
|
|
1622
1691
|
if (typeof configured === "function") return configured;
|
|
1623
|
-
const { claudeProvider } = await import("./claude-
|
|
1692
|
+
const { claudeProvider } = await import("./claude-mpjdqqnY.js");
|
|
1624
1693
|
return claudeProvider({ model: model ?? cfg.translate.model });
|
|
1625
1694
|
}
|
|
1626
|
-
|
|
1695
|
+
//#endregion
|
|
1696
|
+
//#region src/cli.ts
|
|
1697
|
+
runCli().catch((error) => {
|
|
1627
1698
|
console.error("[verbaly]", error instanceof Error ? error.message : error);
|
|
1628
1699
|
process.exitCode = 1;
|
|
1629
1700
|
});
|
|
1630
1701
|
//#endregion
|
|
1631
|
-
export {};
|
|
1702
|
+
export { isModuleNotFound as t };
|
|
1632
1703
|
|
|
1633
1704
|
//# sourceMappingURL=cli.js.map
|