@verbaly/compiler 0.26.0 → 0.27.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-C-ETlqsW.js → claude-h9WA0ppg.js} +4 -2
- package/dist/claude-h9WA0ppg.js.map +1 -0
- package/dist/cli.js +131 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.js +75 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/claude-C-ETlqsW.js.map +0 -1
|
@@ -23,7 +23,9 @@ function claudeProvider(options = {}) {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
function buildPrompt(request) {
|
|
26
|
-
|
|
26
|
+
const origins = request.origins;
|
|
27
|
+
const context = origins && Object.keys(origins).length ? `\n\nWhere each string appears (context for tone and length, do not translate these paths):\n` + Object.entries(origins).map(([key, files]) => ` ${key}: ${files.join(", ")}`).join("\n") : "";
|
|
28
|
+
return `Translate each value from "${request.sourceLocale}" to "${request.targetLocale}". Return a JSON object with the same keys and translated values.\n\n` + JSON.stringify(request.messages, null, 2) + context;
|
|
27
29
|
}
|
|
28
30
|
function batchFormat(request) {
|
|
29
31
|
const keys = Object.keys(request.messages);
|
|
@@ -48,4 +50,4 @@ async function loadSdk(load = () => import("@anthropic-ai/sdk")) {
|
|
|
48
50
|
//#endregion
|
|
49
51
|
export { claudeProvider };
|
|
50
52
|
|
|
51
|
-
//# sourceMappingURL=claude-
|
|
53
|
+
//# sourceMappingURL=claude-h9WA0ppg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-h9WA0ppg.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 const origins = request.origins;\n const context =\n origins && Object.keys(origins).length\n ? `\\n\\nWhere each string appears (context for tone and length, do not translate these paths):\\n` +\n Object.entries(origins)\n .map(([key, files]) => ` ${key}: ${files.join(', ')}`)\n .join('\\n')\n : '';\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 context\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"],"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,MAAM,UAAU,QAAQ;CACxB,MAAM,UACJ,WAAW,OAAO,KAAK,OAAO,CAAC,CAAC,SAC5B,iGACA,OAAO,QAAQ,OAAO,CAAC,CACpB,KAAK,CAAC,KAAK,WAAW,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,GAAG,CAAC,CACtD,KAAK,IAAI,IACZ;CACN,OACE,8BAA8B,QAAQ,aAAa,QAAQ,QAAQ,aAAa,yEAEhF,KAAK,UAAU,QAAQ,UAAU,MAAM,CAAC,IACxC;AAEJ;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,mMACA,EAAE,OAAO,MAAM,CACjB;EAEF,MAAM;CACR;AACF"}
|
package/dist/cli.js
CHANGED
|
@@ -387,7 +387,7 @@ function resolveConfig(config = {}) {
|
|
|
387
387
|
const sourceLocale = config.sourceLocale ?? "en";
|
|
388
388
|
const locales = /* @__PURE__ */ new Set([sourceLocale, ...config.locales ?? []]);
|
|
389
389
|
if (existsSync(dir)) {
|
|
390
|
-
for (const file of readdirSync(dir)) if (file.endsWith(".json") && file !== `en-XA.json`) locales.add(file.slice(0, -5));
|
|
390
|
+
for (const file of readdirSync(dir)) if (file.endsWith(".json") && !file.startsWith(".") && file !== `en-XA.json`) locales.add(file.slice(0, -5));
|
|
391
391
|
}
|
|
392
392
|
return {
|
|
393
393
|
root,
|
|
@@ -1083,6 +1083,66 @@ function readDeps(root) {
|
|
|
1083
1083
|
}
|
|
1084
1084
|
}
|
|
1085
1085
|
//#endregion
|
|
1086
|
+
//#region src/drafts.ts
|
|
1087
|
+
const DRAFTS_FILE = ".verbaly-drafts.json";
|
|
1088
|
+
function draftsPath(cfg) {
|
|
1089
|
+
return join(cfg.dir, DRAFTS_FILE);
|
|
1090
|
+
}
|
|
1091
|
+
function loadDrafts(cfg) {
|
|
1092
|
+
let content;
|
|
1093
|
+
try {
|
|
1094
|
+
content = readFileSync(draftsPath(cfg), "utf8");
|
|
1095
|
+
} catch {
|
|
1096
|
+
return {};
|
|
1097
|
+
}
|
|
1098
|
+
try {
|
|
1099
|
+
return JSON.parse(content.replace(/^\uFEFF/, ""));
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
throw new Error(`[verbaly] ${draftsPath(cfg)} is not valid JSON, fix or delete the file`, { cause: error });
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
function serializeDrafts(drafts) {
|
|
1105
|
+
const sorted = {};
|
|
1106
|
+
for (const locale of Object.keys(drafts).sort()) {
|
|
1107
|
+
const keys = [...new Set(drafts[locale])].sort();
|
|
1108
|
+
if (keys.length) sorted[locale] = keys;
|
|
1109
|
+
}
|
|
1110
|
+
return JSON.stringify(sorted, null, 2) + "\n";
|
|
1111
|
+
}
|
|
1112
|
+
function saveDrafts(cfg, drafts) {
|
|
1113
|
+
const serialized = serializeDrafts(drafts);
|
|
1114
|
+
const path = draftsPath(cfg);
|
|
1115
|
+
try {
|
|
1116
|
+
if (readFileSync(path, "utf8") === serialized) return;
|
|
1117
|
+
} catch {
|
|
1118
|
+
mkdirSync(cfg.dir, { recursive: true });
|
|
1119
|
+
}
|
|
1120
|
+
writeFileSync(path, serialized);
|
|
1121
|
+
}
|
|
1122
|
+
function markDrafts(drafts, locale, keys) {
|
|
1123
|
+
if (!keys.length) return;
|
|
1124
|
+
drafts[locale] = [.../* @__PURE__ */ new Set([...drafts[locale] ?? [], ...keys])];
|
|
1125
|
+
}
|
|
1126
|
+
function clearDrafts(drafts, locale, keys) {
|
|
1127
|
+
if (!drafts[locale]) return;
|
|
1128
|
+
if (!keys) {
|
|
1129
|
+
delete drafts[locale];
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
const drop = new Set(keys);
|
|
1133
|
+
drafts[locale] = drafts[locale].filter((key) => !drop.has(key));
|
|
1134
|
+
if (!drafts[locale].length) delete drafts[locale];
|
|
1135
|
+
}
|
|
1136
|
+
function effectiveDrafts(drafts, catalogs) {
|
|
1137
|
+
const out = {};
|
|
1138
|
+
for (const [locale, keys] of Object.entries(drafts)) {
|
|
1139
|
+
const catalog = catalogs[locale] ?? {};
|
|
1140
|
+
const live = keys.filter((key) => catalog[key]);
|
|
1141
|
+
if (live.length) out[locale] = live;
|
|
1142
|
+
}
|
|
1143
|
+
return out;
|
|
1144
|
+
}
|
|
1145
|
+
//#endregion
|
|
1086
1146
|
//#region src/mobile.ts
|
|
1087
1147
|
function androidValuesDir(locale, sourceLocale) {
|
|
1088
1148
|
if (locale === sourceLocale) return "values";
|
|
@@ -1143,10 +1203,12 @@ async function translateCatalogs(cfg, catalogs, provider, options = {}) {
|
|
|
1143
1203
|
for (let i = 0; i < missing.length; i += batchSize) {
|
|
1144
1204
|
const keys = missing.slice(i, i + batchSize);
|
|
1145
1205
|
const messages = Object.fromEntries(keys.map((key) => [key, source[key]]));
|
|
1206
|
+
const origins = options.origins ? Object.fromEntries(keys.filter((key) => options.origins[key]).map((key) => [key, options.origins[key]])) : void 0;
|
|
1146
1207
|
const out = await provider({
|
|
1147
1208
|
sourceLocale: cfg.sourceLocale,
|
|
1148
1209
|
targetLocale: locale,
|
|
1149
|
-
messages
|
|
1210
|
+
messages,
|
|
1211
|
+
origins
|
|
1150
1212
|
});
|
|
1151
1213
|
for (const key of keys) {
|
|
1152
1214
|
const text = out[key];
|
|
@@ -1670,9 +1732,10 @@ function decodeEntities(text) {
|
|
|
1670
1732
|
}
|
|
1671
1733
|
//#endregion
|
|
1672
1734
|
//#region src/status.ts
|
|
1673
|
-
function status(cfg, catalogs, registry) {
|
|
1735
|
+
function status(cfg, catalogs, registry, drafts = {}) {
|
|
1674
1736
|
const source = catalogs[cfg.sourceLocale] ?? {};
|
|
1675
1737
|
const needed = /* @__PURE__ */ new Set([...registry.messages().keys(), ...Object.keys(source)]);
|
|
1738
|
+
const live = effectiveDrafts(drafts, catalogs);
|
|
1676
1739
|
const locales = [];
|
|
1677
1740
|
for (const locale of cfg.locales) {
|
|
1678
1741
|
if (locale === cfg.sourceLocale) continue;
|
|
@@ -1681,7 +1744,8 @@ function status(cfg, catalogs, registry) {
|
|
|
1681
1744
|
locales.push({
|
|
1682
1745
|
locale,
|
|
1683
1746
|
translated,
|
|
1684
|
-
total: needed.size
|
|
1747
|
+
total: needed.size,
|
|
1748
|
+
drafts: live[locale]?.length ?? 0
|
|
1685
1749
|
});
|
|
1686
1750
|
}
|
|
1687
1751
|
return {
|
|
@@ -1696,10 +1760,11 @@ function formatStatusResult(result) {
|
|
|
1696
1760
|
lines.push(" no target locales (add locales to your config)");
|
|
1697
1761
|
return lines.join("\n");
|
|
1698
1762
|
}
|
|
1699
|
-
for (const { locale, translated, total } of result.locales) {
|
|
1763
|
+
for (const { locale, translated, total, drafts } of result.locales) {
|
|
1700
1764
|
const pct = total === 0 ? 100 : Math.floor(translated / total * 100);
|
|
1701
1765
|
const mark = translated === total ? " ✓" : "";
|
|
1702
|
-
|
|
1766
|
+
const draftNote = drafts > 0 ? `, ${drafts} unreviewed` : "";
|
|
1767
|
+
lines.push(` ${locale}: ${translated}/${total} translated (${pct}%${draftNote})${mark}`);
|
|
1703
1768
|
}
|
|
1704
1769
|
return lines.join("\n");
|
|
1705
1770
|
}
|
|
@@ -1969,6 +2034,7 @@ Usage:
|
|
|
1969
2034
|
verbaly status translation coverage per locale, at a glance
|
|
1970
2035
|
verbaly check verify translations are complete (CI)
|
|
1971
2036
|
verbaly translate fill missing translations via a provider (default: claude)
|
|
2037
|
+
verbaly review list machine translations awaiting review (--approve marks them reviewed)
|
|
1972
2038
|
verbaly export write translator files (XLIFF 2.0, CSV) or mobile resources (Android, iOS)
|
|
1973
2039
|
verbaly import <files…> fill catalogs back from translated XLIFF/CSV files
|
|
1974
2040
|
verbaly pseudo generate a pseudo-locale catalog for i18n QA (default: en-XA)
|
|
@@ -1983,6 +2049,8 @@ Options:
|
|
|
1983
2049
|
--watch keep extracting as source files change (extract)
|
|
1984
2050
|
--write apply the rewrites instead of only reporting (wrap)
|
|
1985
2051
|
--json machine-readable output (status)
|
|
2052
|
+
--drafts also fail on unreviewed machine translations (check)
|
|
2053
|
+
--approve mark listed drafts as reviewed (review)
|
|
1986
2054
|
--reporter <name> failure format: text (default) or github annotations (check)
|
|
1987
2055
|
--model <id> model override for the claude provider (translate)
|
|
1988
2056
|
--dry-run list what would happen, write nothing (translate, import, extract)
|
|
@@ -2013,6 +2081,8 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
2013
2081
|
watch: { type: "boolean" },
|
|
2014
2082
|
write: { type: "boolean" },
|
|
2015
2083
|
json: { type: "boolean" },
|
|
2084
|
+
drafts: { type: "boolean" },
|
|
2085
|
+
approve: { type: "boolean" },
|
|
2016
2086
|
reporter: { type: "string" },
|
|
2017
2087
|
model: { type: "string" },
|
|
2018
2088
|
locale: { type: "string" },
|
|
@@ -2131,7 +2201,7 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
2131
2201
|
}
|
|
2132
2202
|
if (command === "status") {
|
|
2133
2203
|
const registry = await extractProject(cfg);
|
|
2134
|
-
const result = status(cfg, loadCatalogs(cfg), registry);
|
|
2204
|
+
const result = status(cfg, loadCatalogs(cfg), registry, loadDrafts(cfg));
|
|
2135
2205
|
console.log(values.json ? JSON.stringify(result) : formatStatusResult(result));
|
|
2136
2206
|
return;
|
|
2137
2207
|
}
|
|
@@ -2143,11 +2213,20 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
2143
2213
|
return;
|
|
2144
2214
|
}
|
|
2145
2215
|
const registry = await extractProject(cfg);
|
|
2146
|
-
const
|
|
2147
|
-
|
|
2216
|
+
const catalogs = loadCatalogs(cfg);
|
|
2217
|
+
const result = check(cfg, catalogs, registry);
|
|
2218
|
+
const unreviewed = values.drafts ? effectiveDrafts(loadDrafts(cfg), catalogs) : {};
|
|
2219
|
+
const draftKeys = Object.entries(unreviewed);
|
|
2220
|
+
if (result.ok && draftKeys.length === 0) {
|
|
2148
2221
|
console.log("[verbaly] all translations complete ✓");
|
|
2149
2222
|
return;
|
|
2150
2223
|
}
|
|
2224
|
+
if (result.ok && draftKeys.length > 0) {
|
|
2225
|
+
for (const [locale, keys] of draftKeys) console.error(` [${locale}] ${keys.length} unreviewed: ${keys.join(", ")}`);
|
|
2226
|
+
console.error("[verbaly] check failed: machine translations awaiting review (run verbaly review --approve)");
|
|
2227
|
+
process.exitCode = 1;
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
2151
2230
|
if (reporter === "github") {
|
|
2152
2231
|
for (const line of githubCheckAnnotations(result, registry, cfg.root)) console.error(line);
|
|
2153
2232
|
console.error(`[verbaly] check failed: ${result.missing.length} missing, ${result.unknown.length} unknown`);
|
|
@@ -2160,7 +2239,8 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
2160
2239
|
const result = await translateCatalogs(cfg, catalogs, await resolveProvider(cfg, values.model), {
|
|
2161
2240
|
locales: values.locales?.split(","),
|
|
2162
2241
|
batchSize: cfg.translate.batchSize,
|
|
2163
|
-
dryRun: values["dry-run"]
|
|
2242
|
+
dryRun: values["dry-run"],
|
|
2243
|
+
origins: values["dry-run"] ? void 0 : await collectOrigins(cfg)
|
|
2164
2244
|
});
|
|
2165
2245
|
if (values["dry-run"]) {
|
|
2166
2246
|
const entries = Object.entries(result.pending);
|
|
@@ -2171,14 +2251,43 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
2171
2251
|
for (const [locale, keys] of entries) console.log(` ${locale}: ${keys.length} missing: ${keys.join(", ")}`);
|
|
2172
2252
|
return;
|
|
2173
2253
|
}
|
|
2254
|
+
const drafts = loadDrafts(cfg);
|
|
2174
2255
|
for (const locale of Object.keys(result.translated)) {
|
|
2175
2256
|
writeCatalog(cfg, locale, catalogs[locale] ?? {});
|
|
2176
|
-
|
|
2257
|
+
markDrafts(drafts, locale, result.translated[locale]);
|
|
2258
|
+
console.log(` ${locale}: +${result.translated[locale].length} translated (draft)`);
|
|
2177
2259
|
}
|
|
2260
|
+
if (Object.keys(result.translated).length > 0) saveDrafts(cfg, drafts);
|
|
2178
2261
|
for (const [locale, keys] of Object.entries(result.invalid)) console.warn(` ${locale}: ${keys.length} rejected (params/tags not preserved): ${keys.join(", ")}`);
|
|
2179
2262
|
if (Object.keys(result.translated).length === 0 && Object.keys(result.invalid).length === 0) console.log("[verbaly] nothing to translate ✓");
|
|
2180
2263
|
return;
|
|
2181
2264
|
}
|
|
2265
|
+
if (command === "review") {
|
|
2266
|
+
const catalogs = loadCatalogs(cfg);
|
|
2267
|
+
const drafts = loadDrafts(cfg);
|
|
2268
|
+
const live = effectiveDrafts(drafts, catalogs);
|
|
2269
|
+
const targets = values.locale ? { [values.locale]: live[values.locale] ?? [] } : live;
|
|
2270
|
+
const entries = Object.entries(targets).filter(([, keys]) => keys.length);
|
|
2271
|
+
if (entries.length === 0) {
|
|
2272
|
+
console.log("[verbaly] no machine translations awaiting review ✓");
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
if (values.approve) {
|
|
2276
|
+
let count = 0;
|
|
2277
|
+
for (const [locale, keys] of entries) {
|
|
2278
|
+
clearDrafts(drafts, locale, keys);
|
|
2279
|
+
count += keys.length;
|
|
2280
|
+
console.log(` ${locale}: ${keys.length} approved`);
|
|
2281
|
+
}
|
|
2282
|
+
saveDrafts(cfg, drafts);
|
|
2283
|
+
console.log(`[verbaly] ${count} translations marked reviewed ✓`);
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
const total = entries.reduce((sum, [, keys]) => sum + keys.length, 0);
|
|
2287
|
+
console.log(`[verbaly] ${total} machine translations awaiting review (--approve to accept)`);
|
|
2288
|
+
for (const [locale, keys] of entries) console.log(` ${locale}: ${keys.join(", ")}`);
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2182
2291
|
if (command === "export") {
|
|
2183
2292
|
const format = values.format ?? "xliff";
|
|
2184
2293
|
if (![
|
|
@@ -2225,11 +2334,18 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
2225
2334
|
overwrite: values.overwrite,
|
|
2226
2335
|
dryRun: values["dry-run"]
|
|
2227
2336
|
});
|
|
2337
|
+
const drafts = loadDrafts(cfg);
|
|
2338
|
+
let draftsChanged = false;
|
|
2228
2339
|
for (const [locale, keys] of Object.entries(result.imported)) {
|
|
2229
|
-
if (!values["dry-run"])
|
|
2340
|
+
if (!values["dry-run"]) {
|
|
2341
|
+
writeCatalog(cfg, locale, catalogs[locale] ?? {});
|
|
2342
|
+
clearDrafts(drafts, locale, keys);
|
|
2343
|
+
draftsChanged = true;
|
|
2344
|
+
}
|
|
2230
2345
|
const verb = values["dry-run"] ? "would import" : "imported";
|
|
2231
2346
|
console.log(` ${locale}: +${keys.length} ${verb}`);
|
|
2232
2347
|
}
|
|
2348
|
+
if (draftsChanged) saveDrafts(cfg, drafts);
|
|
2233
2349
|
for (const [locale, keys] of Object.entries(result.skipped)) console.log(` ${locale}: ${keys.length} already translated, kept (use --overwrite to replace)`);
|
|
2234
2350
|
for (const [locale, keys] of Object.entries(result.rejected)) console.warn(` ${locale}: ${keys.length} rejected (params/tags not preserved): ${keys.join(", ")}`);
|
|
2235
2351
|
for (const [locale, keys] of Object.entries(result.unknown)) console.warn(` ${locale}: ${keys.length} unknown keys ignored (not in the source catalog): ${keys.join(", ")}`);
|
|
@@ -2277,8 +2393,9 @@ const COMMAND_FLAGS = {
|
|
|
2277
2393
|
],
|
|
2278
2394
|
wrap: ["write"],
|
|
2279
2395
|
status: ["json"],
|
|
2280
|
-
check: ["reporter"],
|
|
2396
|
+
check: ["reporter", "drafts"],
|
|
2281
2397
|
translate: ["model", "dry-run"],
|
|
2398
|
+
review: ["approve", "locale"],
|
|
2282
2399
|
export: [
|
|
2283
2400
|
"format",
|
|
2284
2401
|
"out",
|
|
@@ -2317,7 +2434,7 @@ async function collectOrigins(cfg) {
|
|
|
2317
2434
|
async function resolveProvider(cfg, model) {
|
|
2318
2435
|
const configured = cfg.translate.provider;
|
|
2319
2436
|
if (typeof configured === "function") return configured;
|
|
2320
|
-
const { claudeProvider } = await import("./claude-
|
|
2437
|
+
const { claudeProvider } = await import("./claude-h9WA0ppg.js");
|
|
2321
2438
|
return claudeProvider({ model: model ?? cfg.translate.model });
|
|
2322
2439
|
}
|
|
2323
2440
|
//#endregion
|