rosetta-i18n 0.2.0 → 0.2.1

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/esm/cli.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ import { realpathSync } from 'node:fs';
2
3
  import { writeFile, readFile } from 'node:fs/promises';
3
4
  import { resolve, dirname, join } from 'node:path';
4
- import { pathToFileURL } from 'node:url';
5
+ import { fileURLToPath } from 'node:url';
5
6
 
6
7
  function buildSystemPrompt(options) {
7
8
  const { brandVoice, glossary, source, target } = options;
@@ -365,7 +366,14 @@ async function main() {
365
366
  const client = await buildClient(parsed);
366
367
  await runCatalog(parsed, client);
367
368
  }
368
- const isMain = process.argv[1] !== void 0 && import.meta.url === pathToFileURL(process.argv[1]).href;
369
+ let isMain = false;
370
+ if (process.argv[1]) {
371
+ try {
372
+ isMain = realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
373
+ } catch {
374
+ isMain = false;
375
+ }
376
+ }
369
377
  if (isMain) {
370
378
  main().catch((error) => {
371
379
  const message = error instanceof Error ? error.message : String(error);
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sources":["../../src/prompt.ts","../../src/index.ts","../../src/cli.ts"],"sourcesContent":["import type { BrandVoice, Glossary, TranslateDataOptions } from \"./config\";\n\n/**\n * Builds the system prompt for a translation request: brand voice, glossary\n * enforcement, locale conventions, and output-shape rules.\n */\nexport function buildSystemPrompt(options: {\n\tbrandVoice: BrandVoice;\n\tglossary?: Glossary;\n\tsource: string;\n\ttarget: string;\n}): string {\n\tconst { brandVoice, glossary, source, target } = options;\n\n\tconst voice =\n\t\tbrandVoice.variations[target] ?? brandVoice.variations[\"*\"] ?? \"\";\n\n\tconst parts = [voice];\n\n\tif (glossary) {\n\t\tconst terms = glossary[target];\n\t\tif (terms && Object.keys(terms).length > 0) {\n\t\t\tparts.push(\n\t\t\t\t`Glossary (use these exact renderings):\\n${Object.entries(terms)\n\t\t\t\t\t.map(([src, tgt]) => `- \"${src}\" -> ${tgt}`)\n\t\t\t\t\t.join(\"\\n\")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tparts.push(\n\t\t`Translate from ${source} to ${target}. Return ONLY the translated text — no explanations, no quotes around it.`,\n\t);\n\n\treturn parts.filter(Boolean).join(\"\\n\\n\");\n}\n\n/**\n * Builds the user prompt for a key-value payload translation.\n */\nexport function buildDataPrompt(\n\tdata: Record<string, unknown>,\n\toptions: TranslateDataOptions & { hints?: Record<string, string[]> },\n): string {\n\tconst lines = [\n\t\t`Translate the values of this JSON object from ${options.source} to ${options.target}. Keep every key exactly as provided — return a JSON object with identical keys and translated values. Preserve {placeholders} and ICU plural syntax (=1 {...} other {...}) intact, translating only the inner text. Output ONLY the JSON.`,\n\t];\n\n\tif (options.context) {\n\t\tlines.push(`Context: ${options.context}`);\n\t}\n\n\tif (options.hints) {\n\t\tconst hints = Object.entries(options.hints)\n\t\t\t.map(([key, breadcrumb]) => `- ${key}: ${breadcrumb.join(\" > \")}`)\n\t\t\t.join(\"\\n\");\n\t\tif (hints) {\n\t\t\tlines.push(`Key disambiguation:\\n${hints}`);\n\t\t}\n\t}\n\n\tlines.push(JSON.stringify(data, null, 1));\n\n\treturn lines.join(\"\\n\\n\");\n}\n","import type {\n\tRosettaConfig,\n\tTranslateDataOptions,\n\tTranslateTextOptions,\n} from \"./config\";\nimport { buildDataPrompt, buildSystemPrompt } from \"./prompt\";\n\nexport type {\n\tRosettaConfig,\n\tTranslateDataOptions,\n\tTranslateTextOptions,\n} from \"./config\";\n\ninterface ChatMessage {\n\trole: \"system\" | \"user\" | \"assistant\";\n\tcontent: string;\n}\n\n/**\n * Rosetta — self-hosted AI translation engine.\n *\n * Translates key-value content across locales through any OpenAI-compatible\n * LLM, with brand voice, glossary, and per-locale rules applied on every\n * call. Works with OpenRouter, direct Anthropic/OpenAI, or any\n * OpenAI-compatible endpoint.\n *\n * Non-throwing at the batch level: failed batches are logged and skipped, so\n * partial translations degrade gracefully to the source locale.\n */\nexport class Rosetta {\n\tprivate config: RosettaConfig;\n\n\tconstructor(config: RosettaConfig) {\n\t\tif (!config.apiKey) throw new Error(\"Rosetta: apiKey is required\");\n\t\tif (!config.model) throw new Error(\"Rosetta: model is required\");\n\t\tthis.config = config;\n\t}\n\n\t/** Translate a single text string. */\n\tasync translateText(\n\t\ttext: string,\n\t\toptions: TranslateTextOptions,\n\t): Promise<string> {\n\t\tconst system = buildSystemPrompt({\n\t\t\tbrandVoice: this.config.brandVoice,\n\t\t\tglossary: this.config.glossary,\n\t\t\tsource: options.source,\n\t\t\ttarget: options.target,\n\t\t});\n\n\t\tconst res = await this.chat([\n\t\t\t{ role: \"system\", content: system },\n\t\t\t{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: options.context\n\t\t\t\t\t? `${text}\\n\\nContext: ${options.context}`\n\t\t\t\t\t: text,\n\t\t\t},\n\t\t]);\n\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Rosetta: ${res.status} ${await res.text()}`);\n\t\t}\n\t\tconst json = (await res.json()) as {\n\t\t\tchoices: Array<{ message: { content: string } }>;\n\t\t};\n\t\treturn json.choices[0].message.content.trim();\n\t}\n\n\t/**\n\t * Translate a key-value payload, batched with concurrency. Failed batches\n\t * are logged and skipped — the returned object carries only successfully\n\t * translated keys, letting the caller fall back to the source locale for\n\t * the rest.\n\t */\n\tasync translate(\n\t\tdata: Record<string, unknown>,\n\t\toptions: TranslateDataOptions,\n\t): Promise<Record<string, unknown>> {\n\t\tconst batchSize = this.config.batchSize ?? 25;\n\t\tconst concurrency = this.config.concurrency ?? 4;\n\t\tconst keys = Object.keys(data);\n\t\tconst output: Record<string, unknown> = {};\n\n\t\tfor (let i = 0; i < keys.length; i += batchSize * concurrency) {\n\t\t\tconst chunk = keys.slice(i, i + batchSize * concurrency);\n\t\t\tconst batches = Array.from(\n\t\t\t\t{ length: Math.ceil(chunk.length / batchSize) },\n\t\t\t\t(_, b) => {\n\t\t\t\t\tconst batchKeys = chunk.slice(b * batchSize, (b + 1) * batchSize);\n\t\t\t\t\tconst batch = Object.fromEntries(batchKeys.map((k) => [k, data[k]]));\n\t\t\t\t\treturn { batch, batchKeys };\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tconst results = await Promise.allSettled(\n\t\t\t\tbatches.map(({ batch, batchKeys }) =>\n\t\t\t\t\tthis.translateBatch(batch, options, batchKeys),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tfor (let b = 0; b < results.length; b++) {\n\t\t\t\tconst result = results[b];\n\t\t\t\tif (result.status !== \"fulfilled\") {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`[rosetta] batch failed (${batches[b].batchKeys.length} keys):`,\n\t\t\t\t\t\tresult.reason?.message?.slice(0, 120),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tObject.assign(output, result.value);\n\t\t\t}\n\t\t}\n\n\t\treturn output;\n\t}\n\n\tprivate async translateBatch(\n\t\tbatch: Record<string, unknown>,\n\t\toptions: TranslateDataOptions,\n\t\tbatchKeys: string[],\n\t): Promise<Record<string, unknown>> {\n\t\tconst retries = this.config.retries ?? 2;\n\t\tconst system = buildSystemPrompt({\n\t\t\tbrandVoice: this.config.brandVoice,\n\t\t\tglossary: this.config.glossary,\n\t\t\tsource: options.source,\n\t\t\ttarget: options.target,\n\t\t});\n\n\t\tconst user = buildDataPrompt(batch, {\n\t\t\t...options,\n\t\t\thints: options.hints\n\t\t\t\t? Object.fromEntries(\n\t\t\t\t\t\tbatchKeys.map((key) => [key, options.hints?.[key] ?? []]),\n\t\t\t\t\t)\n\t\t\t\t: undefined,\n\t\t});\n\n\t\tlet lastError: Error | undefined;\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst res = await this.chat([\n\t\t\t\t\t{ role: \"system\", content: system },\n\t\t\t\t\t{ role: \"user\", content: user },\n\t\t\t\t]);\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tthrow new Error(`Rosetta: ${res.status} ${await res.text()}`);\n\t\t\t\t}\n\t\t\t\tconst json = (await res.json()) as {\n\t\t\t\t\tchoices: Array<{ message: { content: string } }>;\n\t\t\t\t};\n\t\t\t\treturn JSON.parse(json.choices[0].message.content);\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tif (attempt < retries) {\n\t\t\t\t\tawait new Promise((ok) => setTimeout(ok, 2000 * (attempt + 1)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow lastError ?? new Error(\"Rosetta: batch failed\");\n\t}\n\n\tprivate async chat(messages: ChatMessage[]): Promise<Response> {\n\t\tconst baseURL = this.config.baseURL ?? \"https://openrouter.ai/api/v1\";\n\t\treturn fetch(`${baseURL}/chat/completions`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${this.config.apiKey}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tmodel: this.config.model,\n\t\t\t\ttemperature: this.config.temperature ?? 0.3,\n\t\t\t\tmessages,\n\t\t\t}),\n\t\t});\n\t}\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { Glossary } from \"./config\";\nimport { Rosetta } from \"./index\";\n\nexport const HELP = Symbol(\"help\");\n\nexport interface CatalogOptions {\n\t/** Path to the source catalog JSON. */\n\tinput: string;\n\t/** Target locales to write. */\n\ttargets: string[];\n\t/** Source locale. */\n\tsource: string;\n\t/** Directory to write `<target>.json` into. Defaults to the input's dir. */\n\toutDir: string;\n\tcontext?: string;\n\tbrandVoice?: string;\n\tglossaryPath?: string;\n\tmodel?: string;\n\tbaseURL?: string;\n\tconcurrency?: number;\n\tbatchSize?: number;\n\tretries?: number;\n\t/** Print the plan without calling the model or writing files. */\n\tdryRun?: boolean;\n\t/** Only translate keys missing from an existing target file. */\n\tmerge?: boolean;\n}\n\n/** Minimal surface the catalog runner needs — the real `Rosetta` or a fake. */\nexport interface TranslateClient {\n\ttranslate(\n\t\tdata: Record<string, unknown>,\n\t\toptions: {\n\t\t\tsource: string;\n\t\t\ttarget: string;\n\t\t\tcontext?: string;\n\t\t},\n\t): Promise<Record<string, unknown>>;\n}\n\nconst COMMAND = \"translate-catalog\";\n\nfunction takeValue(args: string[], index: number, flag: string): string {\n\tconst value = args[index];\n\tif (value === undefined) throw new Error(`Missing value for ${flag}`);\n\treturn value;\n}\n\nfunction toInt(value: string, flag: string): number {\n\tconst parsed = Number.parseInt(value, 10);\n\tif (!Number.isFinite(parsed) || parsed < 0) {\n\t\tthrow new Error(`${flag} expects a non-negative integer, got \"${value}\"`);\n\t}\n\treturn parsed;\n}\n\n/**\n * Parse CLI arguments for `translate-catalog`.\n * Returns `HELP` for `--help`, throws a `UsageError` message otherwise.\n */\nexport function parseArgs(argv: string[]): CatalogOptions | typeof HELP {\n\tconst args = [...argv];\n\tif (args.length === 0 || args.includes(\"--help\") || args.includes(\"-h\")) {\n\t\treturn HELP;\n\t}\n\n\tconst command = args.shift();\n\tif (command !== COMMAND) {\n\t\tthrow new Error(\n\t\t\t`Unknown command \"${command}\". Try \\`rosetta-i18n --help\\`.`,\n\t\t);\n\t}\n\n\tlet input: string | undefined;\n\tconst targets: string[] = [];\n\tconst options: Partial<CatalogOptions> = { source: \"en\" };\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\t\tswitch (arg) {\n\t\t\tcase \"--target\":\n\t\t\t\ttargets.push(takeValue(args, ++i, arg));\n\t\t\t\tbreak;\n\t\t\tcase \"--source\":\n\t\t\t\toptions.source = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--out\":\n\t\t\t\toptions.outDir = resolve(takeValue(args, ++i, arg));\n\t\t\t\tbreak;\n\t\t\tcase \"--context\":\n\t\t\t\toptions.context = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--brand-voice\":\n\t\t\t\toptions.brandVoice = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--glossary\":\n\t\t\t\toptions.glossaryPath = resolve(takeValue(args, ++i, arg));\n\t\t\t\tbreak;\n\t\t\tcase \"--model\":\n\t\t\t\toptions.model = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--base-url\":\n\t\t\t\toptions.baseURL = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--concurrency\":\n\t\t\t\toptions.concurrency = toInt(takeValue(args, ++i, arg), arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--batch-size\":\n\t\t\t\toptions.batchSize = toInt(takeValue(args, ++i, arg), arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--retries\":\n\t\t\t\toptions.retries = toInt(takeValue(args, ++i, arg), arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--dry-run\":\n\t\t\t\toptions.dryRun = true;\n\t\t\t\tbreak;\n\t\t\tcase \"--merge\":\n\t\t\t\toptions.merge = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (arg.startsWith(\"-\")) throw new Error(`Unknown flag \"${arg}\".`);\n\t\t\t\tif (input) throw new Error(`Unexpected argument \"${arg}\".`);\n\t\t\t\tinput = resolve(arg);\n\t\t}\n\t}\n\n\tif (!input) throw new Error(\"Missing input catalog path.\");\n\tif (targets.length === 0) {\n\t\tthrow new Error(\"At least one --target <locale> is required.\");\n\t}\n\n\toptions.input = input;\n\toptions.targets = targets;\n\toptions.outDir = options.outDir ?? dirname(input);\n\treturn options as CatalogOptions;\n}\n\nasync function readJson(path: string): Promise<Record<string, unknown>> {\n\treturn JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n}\n\nasync function readJsonIfExists(\n\tpath: string,\n): Promise<Record<string, unknown>> {\n\ttry {\n\t\treturn await readJson(path);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n\t\tthrow error;\n\t}\n}\n\n/** Keys present in `input` but missing from `existing` (top-level). */\nexport function missingKeys(\n\tinput: Record<string, unknown>,\n\texisting: Record<string, unknown>,\n): Record<string, unknown> {\n\treturn Object.fromEntries(\n\t\tObject.entries(input).filter(([key]) => !(key in existing)),\n\t);\n}\n\n/**\n * Translate a catalog into each target locale and write `<outDir>/<target>.json`.\n * The `client` is only required when not running `--dry-run`.\n */\nexport async function runCatalog(\n\toptions: CatalogOptions,\n\tclient?: TranslateClient,\n): Promise<void> {\n\tconst input = await readJson(options.input);\n\tconst keys = Object.keys(input).length;\n\n\tif (options.dryRun) {\n\t\tconsole.log(\n\t\t\t`[rosetta] dry run — ${keys} keys ${options.source} → ${options.targets.join(\", \")}`,\n\t\t);\n\t\tconsole.log(`[rosetta] would write to ${options.outDir}/`);\n\t\treturn;\n\t}\n\n\tif (!client) throw new Error(\"A translate client is required.\");\n\n\tfor (const target of options.targets) {\n\t\tconst outputPath = join(options.outDir, `${target}.json`);\n\t\tconst existing = options.merge ? await readJsonIfExists(outputPath) : {};\n\n\t\tconst payload = options.merge ? missingKeys(input, existing) : input;\n\t\tconst payloadKeys = Object.keys(payload);\n\n\t\tif (payloadKeys.length === 0) {\n\t\t\tconsole.log(`[rosetta] ${target} — up to date, skipping`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst translated = await client.translate(payload, {\n\t\t\tsource: options.source,\n\t\t\ttarget,\n\t\t\tcontext: options.context,\n\t\t});\n\n\t\tconst result = options.merge ? { ...existing, ...translated } : translated;\n\t\tconst written = Object.keys(translated).length;\n\t\tawait writeFile(outputPath, `${JSON.stringify(result, null, 2)}\\n`);\n\n\t\tconsole.log(\n\t\t\t`[rosetta] ${target} → ${outputPath} (${written}/${payloadKeys.length} keys)`,\n\t\t);\n\t}\n}\n\n/** Build a real Rosetta client from CLI options + env. */\nexport async function buildClient(options: CatalogOptions): Promise<Rosetta> {\n\tconst apiKey = process.env.OPENROUTER_API_KEY;\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"OPENROUTER_API_KEY is not set (pass it in the environment, or use --dry-run).\",\n\t\t);\n\t}\n\n\tlet glossary: Glossary | undefined;\n\tif (options.glossaryPath) {\n\t\tglossary = JSON.parse(\n\t\t\tawait readFile(options.glossaryPath, \"utf8\"),\n\t\t) as Glossary;\n\t}\n\n\treturn new Rosetta({\n\t\tapiKey,\n\t\tmodel:\n\t\t\toptions.model ??\n\t\t\tprocess.env.ROSETTA_MODEL ??\n\t\t\t\"anthropic/claude-sonnet-4.5\",\n\t\tbaseURL: options.baseURL ?? process.env.ROSETTA_BASE_URL,\n\t\tbrandVoice: {\n\t\t\tvariations: {\n\t\t\t\t\"*\": options.brandVoice ?? process.env.ROSETTA_BRAND_VOICE ?? \"\",\n\t\t\t},\n\t\t},\n\t\tglossary,\n\t\tbatchSize: options.batchSize,\n\t\tconcurrency: options.concurrency,\n\t\tretries: options.retries,\n\t});\n}\n\nconst HELP_TEXT = `rosetta-i18n — translate a JSON catalog with an LLM\n\nUsage:\n rosetta-i18n translate-catalog <input.json> --target <locale> [--target <locale>...] [options]\n\nOptions:\n --target <locale> Target locale (repeatable, required)\n --source <locale> Source locale (default: en)\n --out <dir> Output directory (default: the input file's directory)\n --merge Only translate keys missing from existing <target>.json\n --context <text> Broad context passed to the model\n --brand-voice <text> Brand voice briefing (default: ROSETTA_BRAND_VOICE)\n --glossary <file> JSON file of per-locale exact term mappings\n --model <id> Model id (default: ROSETTA_MODEL or claude-sonnet-4.5)\n --base-url <url> OpenAI-compatible endpoint (default: ROSETTA_BASE_URL)\n --concurrency <n> Parallel batches in flight\n --batch-size <n> Keys per request\n --retries <n> Retries per batch\n --dry-run Print the plan without calling the model\n -h, --help Show this help\n\nExample:\n OPENROUTER_API_KEY=sk-... rosetta-i18n translate-catalog messages/en.json \\\\\n --target es --target pt-BR --merge --context \"Next.js UI catalog\"\n`;\n\nexport function printHelp(): void {\n\tconsole.log(HELP_TEXT);\n}\n\nasync function main(): Promise<void> {\n\tconst parsed = parseArgs(process.argv.slice(2));\n\tif (parsed === HELP) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.dryRun) {\n\t\tawait runCatalog(parsed);\n\t\treturn;\n\t}\n\n\tconst client = await buildClient(parsed);\n\tawait runCatalog(parsed, client);\n}\n\nconst isMain =\n\tprocess.argv[1] !== undefined &&\n\timport.meta.url === pathToFileURL(process.argv[1]).href;\n\nif (isMain) {\n\tmain().catch((error: unknown) => {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(`[rosetta] ${message}`);\n\t\tprocess.exit(1);\n\t});\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sources":["../../src/prompt.ts","../../src/index.ts","../../src/cli.ts"],"sourcesContent":["import type { BrandVoice, Glossary, TranslateDataOptions } from \"./config\";\n\n/**\n * Builds the system prompt for a translation request: brand voice, glossary\n * enforcement, locale conventions, and output-shape rules.\n */\nexport function buildSystemPrompt(options: {\n\tbrandVoice: BrandVoice;\n\tglossary?: Glossary;\n\tsource: string;\n\ttarget: string;\n}): string {\n\tconst { brandVoice, glossary, source, target } = options;\n\n\tconst voice =\n\t\tbrandVoice.variations[target] ?? brandVoice.variations[\"*\"] ?? \"\";\n\n\tconst parts = [voice];\n\n\tif (glossary) {\n\t\tconst terms = glossary[target];\n\t\tif (terms && Object.keys(terms).length > 0) {\n\t\t\tparts.push(\n\t\t\t\t`Glossary (use these exact renderings):\\n${Object.entries(terms)\n\t\t\t\t\t.map(([src, tgt]) => `- \"${src}\" -> ${tgt}`)\n\t\t\t\t\t.join(\"\\n\")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tparts.push(\n\t\t`Translate from ${source} to ${target}. Return ONLY the translated text — no explanations, no quotes around it.`,\n\t);\n\n\treturn parts.filter(Boolean).join(\"\\n\\n\");\n}\n\n/**\n * Builds the user prompt for a key-value payload translation.\n */\nexport function buildDataPrompt(\n\tdata: Record<string, unknown>,\n\toptions: TranslateDataOptions & { hints?: Record<string, string[]> },\n): string {\n\tconst lines = [\n\t\t`Translate the values of this JSON object from ${options.source} to ${options.target}. Keep every key exactly as provided — return a JSON object with identical keys and translated values. Preserve {placeholders} and ICU plural syntax (=1 {...} other {...}) intact, translating only the inner text. Output ONLY the JSON.`,\n\t];\n\n\tif (options.context) {\n\t\tlines.push(`Context: ${options.context}`);\n\t}\n\n\tif (options.hints) {\n\t\tconst hints = Object.entries(options.hints)\n\t\t\t.map(([key, breadcrumb]) => `- ${key}: ${breadcrumb.join(\" > \")}`)\n\t\t\t.join(\"\\n\");\n\t\tif (hints) {\n\t\t\tlines.push(`Key disambiguation:\\n${hints}`);\n\t\t}\n\t}\n\n\tlines.push(JSON.stringify(data, null, 1));\n\n\treturn lines.join(\"\\n\\n\");\n}\n","import type {\n\tRosettaConfig,\n\tTranslateDataOptions,\n\tTranslateTextOptions,\n} from \"./config\";\nimport { buildDataPrompt, buildSystemPrompt } from \"./prompt\";\n\nexport type {\n\tRosettaConfig,\n\tTranslateDataOptions,\n\tTranslateTextOptions,\n} from \"./config\";\n\ninterface ChatMessage {\n\trole: \"system\" | \"user\" | \"assistant\";\n\tcontent: string;\n}\n\n/**\n * Rosetta — self-hosted AI translation engine.\n *\n * Translates key-value content across locales through any OpenAI-compatible\n * LLM, with brand voice, glossary, and per-locale rules applied on every\n * call. Works with OpenRouter, direct Anthropic/OpenAI, or any\n * OpenAI-compatible endpoint.\n *\n * Non-throwing at the batch level: failed batches are logged and skipped, so\n * partial translations degrade gracefully to the source locale.\n */\nexport class Rosetta {\n\tprivate config: RosettaConfig;\n\n\tconstructor(config: RosettaConfig) {\n\t\tif (!config.apiKey) throw new Error(\"Rosetta: apiKey is required\");\n\t\tif (!config.model) throw new Error(\"Rosetta: model is required\");\n\t\tthis.config = config;\n\t}\n\n\t/** Translate a single text string. */\n\tasync translateText(\n\t\ttext: string,\n\t\toptions: TranslateTextOptions,\n\t): Promise<string> {\n\t\tconst system = buildSystemPrompt({\n\t\t\tbrandVoice: this.config.brandVoice,\n\t\t\tglossary: this.config.glossary,\n\t\t\tsource: options.source,\n\t\t\ttarget: options.target,\n\t\t});\n\n\t\tconst res = await this.chat([\n\t\t\t{ role: \"system\", content: system },\n\t\t\t{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: options.context\n\t\t\t\t\t? `${text}\\n\\nContext: ${options.context}`\n\t\t\t\t\t: text,\n\t\t\t},\n\t\t]);\n\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Rosetta: ${res.status} ${await res.text()}`);\n\t\t}\n\t\tconst json = (await res.json()) as {\n\t\t\tchoices: Array<{ message: { content: string } }>;\n\t\t};\n\t\treturn json.choices[0].message.content.trim();\n\t}\n\n\t/**\n\t * Translate a key-value payload, batched with concurrency. Failed batches\n\t * are logged and skipped — the returned object carries only successfully\n\t * translated keys, letting the caller fall back to the source locale for\n\t * the rest.\n\t */\n\tasync translate(\n\t\tdata: Record<string, unknown>,\n\t\toptions: TranslateDataOptions,\n\t): Promise<Record<string, unknown>> {\n\t\tconst batchSize = this.config.batchSize ?? 25;\n\t\tconst concurrency = this.config.concurrency ?? 4;\n\t\tconst keys = Object.keys(data);\n\t\tconst output: Record<string, unknown> = {};\n\n\t\tfor (let i = 0; i < keys.length; i += batchSize * concurrency) {\n\t\t\tconst chunk = keys.slice(i, i + batchSize * concurrency);\n\t\t\tconst batches = Array.from(\n\t\t\t\t{ length: Math.ceil(chunk.length / batchSize) },\n\t\t\t\t(_, b) => {\n\t\t\t\t\tconst batchKeys = chunk.slice(b * batchSize, (b + 1) * batchSize);\n\t\t\t\t\tconst batch = Object.fromEntries(batchKeys.map((k) => [k, data[k]]));\n\t\t\t\t\treturn { batch, batchKeys };\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tconst results = await Promise.allSettled(\n\t\t\t\tbatches.map(({ batch, batchKeys }) =>\n\t\t\t\t\tthis.translateBatch(batch, options, batchKeys),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tfor (let b = 0; b < results.length; b++) {\n\t\t\t\tconst result = results[b];\n\t\t\t\tif (result.status !== \"fulfilled\") {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`[rosetta] batch failed (${batches[b].batchKeys.length} keys):`,\n\t\t\t\t\t\tresult.reason?.message?.slice(0, 120),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tObject.assign(output, result.value);\n\t\t\t}\n\t\t}\n\n\t\treturn output;\n\t}\n\n\tprivate async translateBatch(\n\t\tbatch: Record<string, unknown>,\n\t\toptions: TranslateDataOptions,\n\t\tbatchKeys: string[],\n\t): Promise<Record<string, unknown>> {\n\t\tconst retries = this.config.retries ?? 2;\n\t\tconst system = buildSystemPrompt({\n\t\t\tbrandVoice: this.config.brandVoice,\n\t\t\tglossary: this.config.glossary,\n\t\t\tsource: options.source,\n\t\t\ttarget: options.target,\n\t\t});\n\n\t\tconst user = buildDataPrompt(batch, {\n\t\t\t...options,\n\t\t\thints: options.hints\n\t\t\t\t? Object.fromEntries(\n\t\t\t\t\t\tbatchKeys.map((key) => [key, options.hints?.[key] ?? []]),\n\t\t\t\t\t)\n\t\t\t\t: undefined,\n\t\t});\n\n\t\tlet lastError: Error | undefined;\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst res = await this.chat([\n\t\t\t\t\t{ role: \"system\", content: system },\n\t\t\t\t\t{ role: \"user\", content: user },\n\t\t\t\t]);\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tthrow new Error(`Rosetta: ${res.status} ${await res.text()}`);\n\t\t\t\t}\n\t\t\t\tconst json = (await res.json()) as {\n\t\t\t\t\tchoices: Array<{ message: { content: string } }>;\n\t\t\t\t};\n\t\t\t\treturn JSON.parse(json.choices[0].message.content);\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tif (attempt < retries) {\n\t\t\t\t\tawait new Promise((ok) => setTimeout(ok, 2000 * (attempt + 1)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow lastError ?? new Error(\"Rosetta: batch failed\");\n\t}\n\n\tprivate async chat(messages: ChatMessage[]): Promise<Response> {\n\t\tconst baseURL = this.config.baseURL ?? \"https://openrouter.ai/api/v1\";\n\t\treturn fetch(`${baseURL}/chat/completions`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${this.config.apiKey}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tmodel: this.config.model,\n\t\t\t\ttemperature: this.config.temperature ?? 0.3,\n\t\t\t\tmessages,\n\t\t\t}),\n\t\t});\n\t}\n}\n","import { realpathSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Glossary } from \"./config\";\nimport { Rosetta } from \"./index\";\n\nexport const HELP = Symbol(\"help\");\n\nexport interface CatalogOptions {\n\t/** Path to the source catalog JSON. */\n\tinput: string;\n\t/** Target locales to write. */\n\ttargets: string[];\n\t/** Source locale. */\n\tsource: string;\n\t/** Directory to write `<target>.json` into. Defaults to the input's dir. */\n\toutDir: string;\n\tcontext?: string;\n\tbrandVoice?: string;\n\tglossaryPath?: string;\n\tmodel?: string;\n\tbaseURL?: string;\n\tconcurrency?: number;\n\tbatchSize?: number;\n\tretries?: number;\n\t/** Print the plan without calling the model or writing files. */\n\tdryRun?: boolean;\n\t/** Only translate keys missing from an existing target file. */\n\tmerge?: boolean;\n}\n\n/** Minimal surface the catalog runner needs — the real `Rosetta` or a fake. */\nexport interface TranslateClient {\n\ttranslate(\n\t\tdata: Record<string, unknown>,\n\t\toptions: {\n\t\t\tsource: string;\n\t\t\ttarget: string;\n\t\t\tcontext?: string;\n\t\t},\n\t): Promise<Record<string, unknown>>;\n}\n\nconst COMMAND = \"translate-catalog\";\n\nfunction takeValue(args: string[], index: number, flag: string): string {\n\tconst value = args[index];\n\tif (value === undefined) throw new Error(`Missing value for ${flag}`);\n\treturn value;\n}\n\nfunction toInt(value: string, flag: string): number {\n\tconst parsed = Number.parseInt(value, 10);\n\tif (!Number.isFinite(parsed) || parsed < 0) {\n\t\tthrow new Error(`${flag} expects a non-negative integer, got \"${value}\"`);\n\t}\n\treturn parsed;\n}\n\n/**\n * Parse CLI arguments for `translate-catalog`.\n * Returns `HELP` for `--help`, throws a `UsageError` message otherwise.\n */\nexport function parseArgs(argv: string[]): CatalogOptions | typeof HELP {\n\tconst args = [...argv];\n\tif (args.length === 0 || args.includes(\"--help\") || args.includes(\"-h\")) {\n\t\treturn HELP;\n\t}\n\n\tconst command = args.shift();\n\tif (command !== COMMAND) {\n\t\tthrow new Error(\n\t\t\t`Unknown command \"${command}\". Try \\`rosetta-i18n --help\\`.`,\n\t\t);\n\t}\n\n\tlet input: string | undefined;\n\tconst targets: string[] = [];\n\tconst options: Partial<CatalogOptions> = { source: \"en\" };\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\t\tswitch (arg) {\n\t\t\tcase \"--target\":\n\t\t\t\ttargets.push(takeValue(args, ++i, arg));\n\t\t\t\tbreak;\n\t\t\tcase \"--source\":\n\t\t\t\toptions.source = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--out\":\n\t\t\t\toptions.outDir = resolve(takeValue(args, ++i, arg));\n\t\t\t\tbreak;\n\t\t\tcase \"--context\":\n\t\t\t\toptions.context = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--brand-voice\":\n\t\t\t\toptions.brandVoice = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--glossary\":\n\t\t\t\toptions.glossaryPath = resolve(takeValue(args, ++i, arg));\n\t\t\t\tbreak;\n\t\t\tcase \"--model\":\n\t\t\t\toptions.model = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--base-url\":\n\t\t\t\toptions.baseURL = takeValue(args, ++i, arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--concurrency\":\n\t\t\t\toptions.concurrency = toInt(takeValue(args, ++i, arg), arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--batch-size\":\n\t\t\t\toptions.batchSize = toInt(takeValue(args, ++i, arg), arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--retries\":\n\t\t\t\toptions.retries = toInt(takeValue(args, ++i, arg), arg);\n\t\t\t\tbreak;\n\t\t\tcase \"--dry-run\":\n\t\t\t\toptions.dryRun = true;\n\t\t\t\tbreak;\n\t\t\tcase \"--merge\":\n\t\t\t\toptions.merge = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (arg.startsWith(\"-\")) throw new Error(`Unknown flag \"${arg}\".`);\n\t\t\t\tif (input) throw new Error(`Unexpected argument \"${arg}\".`);\n\t\t\t\tinput = resolve(arg);\n\t\t}\n\t}\n\n\tif (!input) throw new Error(\"Missing input catalog path.\");\n\tif (targets.length === 0) {\n\t\tthrow new Error(\"At least one --target <locale> is required.\");\n\t}\n\n\toptions.input = input;\n\toptions.targets = targets;\n\toptions.outDir = options.outDir ?? dirname(input);\n\treturn options as CatalogOptions;\n}\n\nasync function readJson(path: string): Promise<Record<string, unknown>> {\n\treturn JSON.parse(await readFile(path, \"utf8\")) as Record<string, unknown>;\n}\n\nasync function readJsonIfExists(\n\tpath: string,\n): Promise<Record<string, unknown>> {\n\ttry {\n\t\treturn await readJson(path);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n\t\tthrow error;\n\t}\n}\n\n/** Keys present in `input` but missing from `existing` (top-level). */\nexport function missingKeys(\n\tinput: Record<string, unknown>,\n\texisting: Record<string, unknown>,\n): Record<string, unknown> {\n\treturn Object.fromEntries(\n\t\tObject.entries(input).filter(([key]) => !(key in existing)),\n\t);\n}\n\n/**\n * Translate a catalog into each target locale and write `<outDir>/<target>.json`.\n * The `client` is only required when not running `--dry-run`.\n */\nexport async function runCatalog(\n\toptions: CatalogOptions,\n\tclient?: TranslateClient,\n): Promise<void> {\n\tconst input = await readJson(options.input);\n\tconst keys = Object.keys(input).length;\n\n\tif (options.dryRun) {\n\t\tconsole.log(\n\t\t\t`[rosetta] dry run — ${keys} keys ${options.source} → ${options.targets.join(\", \")}`,\n\t\t);\n\t\tconsole.log(`[rosetta] would write to ${options.outDir}/`);\n\t\treturn;\n\t}\n\n\tif (!client) throw new Error(\"A translate client is required.\");\n\n\tfor (const target of options.targets) {\n\t\tconst outputPath = join(options.outDir, `${target}.json`);\n\t\tconst existing = options.merge ? await readJsonIfExists(outputPath) : {};\n\n\t\tconst payload = options.merge ? missingKeys(input, existing) : input;\n\t\tconst payloadKeys = Object.keys(payload);\n\n\t\tif (payloadKeys.length === 0) {\n\t\t\tconsole.log(`[rosetta] ${target} — up to date, skipping`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst translated = await client.translate(payload, {\n\t\t\tsource: options.source,\n\t\t\ttarget,\n\t\t\tcontext: options.context,\n\t\t});\n\n\t\tconst result = options.merge ? { ...existing, ...translated } : translated;\n\t\tconst written = Object.keys(translated).length;\n\t\tawait writeFile(outputPath, `${JSON.stringify(result, null, 2)}\\n`);\n\n\t\tconsole.log(\n\t\t\t`[rosetta] ${target} → ${outputPath} (${written}/${payloadKeys.length} keys)`,\n\t\t);\n\t}\n}\n\n/** Build a real Rosetta client from CLI options + env. */\nexport async function buildClient(options: CatalogOptions): Promise<Rosetta> {\n\tconst apiKey = process.env.OPENROUTER_API_KEY;\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"OPENROUTER_API_KEY is not set (pass it in the environment, or use --dry-run).\",\n\t\t);\n\t}\n\n\tlet glossary: Glossary | undefined;\n\tif (options.glossaryPath) {\n\t\tglossary = JSON.parse(\n\t\t\tawait readFile(options.glossaryPath, \"utf8\"),\n\t\t) as Glossary;\n\t}\n\n\treturn new Rosetta({\n\t\tapiKey,\n\t\tmodel:\n\t\t\toptions.model ??\n\t\t\tprocess.env.ROSETTA_MODEL ??\n\t\t\t\"anthropic/claude-sonnet-4.5\",\n\t\tbaseURL: options.baseURL ?? process.env.ROSETTA_BASE_URL,\n\t\tbrandVoice: {\n\t\t\tvariations: {\n\t\t\t\t\"*\": options.brandVoice ?? process.env.ROSETTA_BRAND_VOICE ?? \"\",\n\t\t\t},\n\t\t},\n\t\tglossary,\n\t\tbatchSize: options.batchSize,\n\t\tconcurrency: options.concurrency,\n\t\tretries: options.retries,\n\t});\n}\n\nconst HELP_TEXT = `rosetta-i18n — translate a JSON catalog with an LLM\n\nUsage:\n rosetta-i18n translate-catalog <input.json> --target <locale> [--target <locale>...] [options]\n\nOptions:\n --target <locale> Target locale (repeatable, required)\n --source <locale> Source locale (default: en)\n --out <dir> Output directory (default: the input file's directory)\n --merge Only translate keys missing from existing <target>.json\n --context <text> Broad context passed to the model\n --brand-voice <text> Brand voice briefing (default: ROSETTA_BRAND_VOICE)\n --glossary <file> JSON file of per-locale exact term mappings\n --model <id> Model id (default: ROSETTA_MODEL or claude-sonnet-4.5)\n --base-url <url> OpenAI-compatible endpoint (default: ROSETTA_BASE_URL)\n --concurrency <n> Parallel batches in flight\n --batch-size <n> Keys per request\n --retries <n> Retries per batch\n --dry-run Print the plan without calling the model\n -h, --help Show this help\n\nExample:\n OPENROUTER_API_KEY=sk-... rosetta-i18n translate-catalog messages/en.json \\\\\n --target es --target pt-BR --merge --context \"Next.js UI catalog\"\n`;\n\nexport function printHelp(): void {\n\tconsole.log(HELP_TEXT);\n}\n\nasync function main(): Promise<void> {\n\tconst parsed = parseArgs(process.argv.slice(2));\n\tif (parsed === HELP) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.dryRun) {\n\t\tawait runCatalog(parsed);\n\t\treturn;\n\t}\n\n\tconst client = await buildClient(parsed);\n\tawait runCatalog(parsed, client);\n}\n\n// Resolve symlinks — a package bin is a symlink in node_modules/.bin, so\n// `process.argv[1]` is the link path while `import.meta.url` is the real file.\nlet isMain = false;\nif (process.argv[1]) {\n\ttry {\n\t\tisMain = realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);\n\t} catch {\n\t\tisMain = false;\n\t}\n}\n\nif (isMain) {\n\tmain().catch((error: unknown) => {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(`[rosetta] ${message}`);\n\t\tprocess.exit(1);\n\t});\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rosetta-i18n",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "author": "Ian Hunter <ian@01.studio>",
5
5
  "license": "MIT",
6
6
  "description": "Self-hosted AI translation engine — translate key-value content across locales through any OpenAI-compatible LLM, with brand voice and glossary enforcement.",
@@ -32,7 +32,7 @@
32
32
  "main": "./dist/esm/index.js",
33
33
  "types": "./dist/types/index.d.ts",
34
34
  "bin": {
35
- "rosetta-i18n": "./dist/esm/cli.js"
35
+ "rosetta-i18n": "dist/esm/cli.js"
36
36
  },
37
37
  "exports": {
38
38
  ".": {