@verbaly/compiler 0.11.0 → 0.12.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/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/AronSoto/verbaly/develop/assets/logo-light.png" alt="Verbaly" width="300" />
2
+ <img src="https://raw.githubusercontent.com/AronSoto/verbaly/develop/assets/logo.png" alt="Verbaly" width="300" />
3
3
  </p>
4
4
 
5
5
  <p align="center"><em>Message extraction, type-safe codegen and CLI for Verbaly.</em></p>
@@ -18,6 +18,7 @@ The compiler behind [Verbaly](https://github.com/AronSoto/verbaly): AST extracti
18
18
  ## CLI
19
19
 
20
20
  ```bash
21
+ npx verbaly init # scaffold config + locale catalogs (detects your bundler)
21
22
  npx verbaly extract # sync catalogs + types
22
23
  npx verbaly check # exit 1 if anything is missing (CI)
23
24
  npx verbaly extract --prune # drop orphaned keys
@@ -0,0 +1,53 @@
1
+ //#region src/providers/claude.ts
2
+ const DEFAULT_MODEL = "claude-sonnet-5";
3
+ const SYSTEM = `You translate UI strings for the Verbaly i18n library.
4
+ Rules:
5
+ - Translate only the human-readable text, naturally for the target locale.
6
+ - 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 {{ }} || ##.
7
+ - Keys are opaque identifiers: return exactly the same keys, never translate or rename them.`;
8
+ function claudeProvider(options = {}) {
9
+ return async (request) => {
10
+ const text = (await new (await (loadSdk()))(options.apiKey ? { apiKey: options.apiKey } : {}).messages.create({
11
+ model: options.model ?? DEFAULT_MODEL,
12
+ max_tokens: options.maxTokens ?? 16e3,
13
+ thinking: { type: "disabled" },
14
+ system: SYSTEM,
15
+ messages: [{
16
+ role: "user",
17
+ content: buildPrompt(request)
18
+ }],
19
+ output_config: { format: batchFormat(request) }
20
+ })).content.find((block) => block.type === "text")?.text ?? "{}";
21
+ return JSON.parse(text);
22
+ };
23
+ }
24
+ function buildPrompt(request) {
25
+ 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);
26
+ }
27
+ function batchFormat(request) {
28
+ const keys = Object.keys(request.messages);
29
+ return {
30
+ type: "json_schema",
31
+ schema: {
32
+ type: "object",
33
+ properties: Object.fromEntries(keys.map((key) => [key, { type: "string" }])),
34
+ required: keys,
35
+ additionalProperties: false
36
+ }
37
+ };
38
+ }
39
+ async function loadSdk() {
40
+ try {
41
+ return (await import("@anthropic-ai/sdk")).default;
42
+ } catch (error) {
43
+ if (isModuleNotFound(error, "@anthropic-ai/sdk")) throw new Error("[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", { cause: error });
44
+ throw error;
45
+ }
46
+ }
47
+ function isModuleNotFound(error, name) {
48
+ return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
49
+ }
50
+ //#endregion
51
+ export { claudeProvider };
52
+
53
+ //# sourceMappingURL=claude-BhP-eK5E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-BhP-eK5E.js","names":[],"sources":["../src/providers/claude.ts"],"sourcesContent":["import 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\nasync function loadSdk(): Promise<typeof import('@anthropic-ai/sdk').default> {\n try {\n const mod = await import('@anthropic-ai/sdk');\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\nfunction isModuleNotFound(error: unknown, name: string): boolean {\n return (\n error instanceof Error &&\n (error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' &&\n error.message.includes(name)\n );\n}\n"],"mappings":";AASA,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;AAEA,eAAe,UAA+D;CAC5E,IAAI;EAEF,QAAO,MADW,OAAO,qBAAA,CACd;CACb,SAAS,OAAO;EACd,IAAI,iBAAiB,OAAO,mBAAmB,GAC7C,MAAM,IAAI,MACR,oMACA,EAAE,OAAO,MAAM,CACjB;EAEF,MAAM;CACR;AACF;AAEA,SAAS,iBAAiB,OAAgB,MAAuB;CAC/D,OACE,iBAAiB,SAChB,MAA4B,SAAS,0BACtC,MAAM,QAAQ,SAAS,IAAI;AAE/B"}