vite-plugin-ai-auto-i18n 0.5.8 → 0.5.10

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
@@ -11,7 +11,7 @@ Implicit build-time localization for JSX: author English directly, let the compi
11
11
  - Uses tag/attribute/semantic-attribute/handler context to form fingerprints; component names and file paths are excluded so ordinary moves and renames do not automatically invalidate IDs.
12
12
  - Persists opaque message IDs in `.autoi18n/registry.json`.
13
13
  - Marks disappeared messages as orphaned and deletes them after a retention period (default 30 days, see `orphanRetentionMinutes`).
14
- - Supports a pluggable translation provider; includes an OpenAI-compatible implementation and an Anthropic (Claude) implementation.
14
+ - Supports a pluggable translation provider. Ships with Anthropic and Gemini implementations and one OpenAI-compatible implementation that covers OpenAI, Mistral, Groq, DeepSeek, OpenRouter, xAI, Cerebras and Ollama (see [Choosing a provider](#choosing-a-provider)).
15
15
  - Writes locale JSON catalogues and keeps source English as fallback.
16
16
  - Two delivery modes: `build` (one compiled bundle per locale) and `runtime` (one bundle, locales loaded on demand through `virtual:autoi18n`).
17
17
  - Re-translates a message when its source text changes, tracked in the `sources` map of `.autoi18n/locales/<locale>.json`.
@@ -267,7 +267,7 @@ Put the key for your provider in `.env.local` and keep that file out of git. For
267
267
  OPENAI_API_KEY=your-key-here
268
268
  ```
269
269
 
270
- For Claude, use `ANTHROPIC_API_KEY` instead (see [AnthropicProvider options](#anthropicprovider-options)). For Gemini, use `GEMINI_API_KEY` (see [GeminiProvider options](#geminiprovider-options)).
270
+ For Claude, use `ANTHROPIC_API_KEY` instead (see [AnthropicProvider options](#anthropicprovider-options)). For Gemini, use `GEMINI_API_KEY` (see [GeminiProvider options](#geminiprovider-options)). For the other services, see [Choosing a provider](#choosing-a-provider) for the key name and endpoint of each one.
271
271
 
272
272
  ### 3️⃣ Configure Vite
273
273
 
@@ -399,7 +399,7 @@ Create the provider with `new OpenAICompatibleProvider(options)`.
399
399
 
400
400
  | Option | Type | Default | Description |
401
401
  |---|---|---|---|
402
- | `apiKey` | `string` | _required_ | Bearer token for the endpoint. Read it from an environment variable and do not commit it. |
402
+ | `apiKey` | `string` | _optional_ | Bearer token for the endpoint. Omit it for servers that need no key, such as a local Ollama. Read it from an environment variable and do not commit it. |
403
403
  | `model` | `string` | _required_ | Model name sent in each request, for example `"gpt-4.1-mini"`. |
404
404
  | `baseURL` | `string` | `"https://api.openai.com/v1"` | Base URL of any OpenAI-compatible API. Requests go to `${baseURL}/chat/completions`. |
405
405
  | `batchSize` | `number` | `40` | Messages per request. Smaller batches keep responses within output limits. |
@@ -440,9 +440,39 @@ Create the provider with `new GeminiProvider(options)`. It calls the [Gemini API
440
440
  | `maxRetries` | `number` | `2` | Extra attempts for network errors, timeouts, 408/409/429/5xx responses and malformed output. |
441
441
  | `retryDelayMs` | `number` | `500` | Delay before the first retry. It doubles on each attempt. |
442
442
 
443
+ ### Choosing a provider
444
+
445
+ Every service below is either native (`AnthropicProvider`, `GeminiProvider`) or speaks the OpenAI Chat Completions protocol, so it is used through `OpenAICompatibleProvider` with a different `baseURL`. The library does not read any environment variables itself; the key names below are what the example config uses.
446
+
447
+ | Service | Class | Key variable | Base URL | Example default model |
448
+ |---|---|---|---|---|
449
+ | Anthropic (Claude) | `AnthropicProvider` | `ANTHROPIC_API_KEY` | `https://api.anthropic.com/v1` | `claude-haiku-5-5` |
450
+ | Gemini | `GeminiProvider` | `GEMINI_API_KEY` | `https://generativelanguage.googleapis.com/v1beta` | `gemini-3.5-flash-lite` |
451
+ | OpenAI | `OpenAICompatibleProvider` | `OPENAI_API_KEY` | `https://api.openai.com/v1` (default) | `gpt-4.1-mini` |
452
+ | Mistral | `OpenAICompatibleProvider` | `MISTRAL_API_KEY` | `https://api.mistral.ai/v1` | `mistral-small-latest` |
453
+ | Groq | `OpenAICompatibleProvider` | `GROQ_API_KEY` | `https://api.groq.com/openai/v1` | `llama-3.3-70b-versatile` |
454
+ | DeepSeek | `OpenAICompatibleProvider` | `DEEPSEEK_API_KEY` | `https://api.deepseek.com/v1` | `deepseek-chat` |
455
+ | OpenRouter | `OpenAICompatibleProvider` | `OPENROUTER_API_KEY` | `https://openrouter.ai/api/v1` | `openai/gpt-4.1-mini` |
456
+ | xAI | `OpenAICompatibleProvider` | `XAI_API_KEY` | `https://api.x.ai/v1` | `grok-3-mini` |
457
+ | Cerebras | `OpenAICompatibleProvider` | `CEREBRAS_API_KEY` | `https://api.cerebras.ai/v1` | `llama-3.3-70b` |
458
+ | Ollama (local, no key) | `OpenAICompatibleProvider` | none | `http://localhost:11434/v1` | none, you must set one |
459
+
460
+ Notes:
461
+
462
+ - Model names change often. The defaults above are examples; set `model` to any name the service currently offers.
463
+ - The example config enables a provider only when its key variable is set. Ollama has no key, so it is enabled by `OLLAMA_MODEL` (the model you have pulled), with `OLLAMA_BASE_URL` to override the server address. `<NAME>_MODEL` (for example `MISTRAL_MODEL`) overrides the default model of any other service.
464
+ - For a keyless server, omit `apiKey`. The `Authorization` header is then not sent.
465
+ - Use a model that reliably returns JSON. Small local models often do not, and the malformed replies are retried and then left untranslated.
466
+ - The Mistral, Groq, DeepSeek, OpenRouter, xAI, Cerebras and Ollama endpoints were checked with stubbed HTTP responses only, not against the live services.
467
+
468
+ ```ts
469
+ new OpenAICompatibleProvider({ apiKey: env.GROQ_API_KEY!, baseURL: "https://api.groq.com/openai/v1", model: "llama-3.3-70b-versatile" });
470
+ new OpenAICompatibleProvider({ baseURL: "http://localhost:11434/v1", model: "qwen2.5:7b" }); // Ollama, no key
471
+ ```
472
+
443
473
  ### Multiple providers and fallback
444
474
 
445
- Pass `provider` as an object with one entry per provider. `primary` names the one tried first. Entries set to `undefined` are skipped, so you can keep every provider in the config and enable each one by its key:
475
+ Pass `provider` as an object with one entry per provider. `primary` names the one tried first. It is never inferred: set it yourself, or leave it out to try the providers in declaration order. Entries set to `undefined` are skipped, so you can keep every provider in the config and enable each one by its key:
446
476
 
447
477
  ```ts
448
478
  provider: {
package/dist/index.d.ts CHANGED
@@ -154,14 +154,20 @@ declare abstract class BatchedProvider<O extends BatchedProviderOptions> impleme
154
154
  private translateBatch;
155
155
  /** Sends one batch to the provider and returns the translations it produced for the requested IDs. */
156
156
  protected abstract request(input: TranslateInput): Promise<Catalog>;
157
- /** Parses the model's text reply, which must be a JSON object mapping IDs to strings. */
157
+ /** POSTs a JSON body with the given headers and returns the parsed JSON reply. Non-2xx replies throw a classified error. */
158
+ protected post(url: string, headers: Record<string, string>, body: unknown): Promise<unknown>;
159
+ /**
160
+ * Parses the model's text reply, which must be a JSON object mapping IDs to strings. A surrounding Markdown code fence
161
+ * is stripped first, because models sometimes add one even when told not to.
162
+ */
158
163
  protected toCatalog(content: string | undefined, messages: TranslationRequest[]): Catalog;
159
164
  }
160
165
  /** Options for an OpenAI or OpenAI-compatible chat completions endpoint. */
161
166
  interface OpenAICompatibleOptions extends BatchedProviderOptions {
162
- apiKey: string;
167
+ /** Sent as a bearer token. Omit it for servers that need no key, such as a local Ollama. */
168
+ apiKey?: string;
163
169
  model: string;
164
- /** Overrides the API root, for example to target a proxy or a self-hosted compatible server. */
170
+ /** API root. Defaults to OpenAI. Point it at any compatible service, for example `http://localhost:11434/v1` for Ollama. */
165
171
  baseURL?: string;
166
172
  }
167
173
  /**
@@ -184,7 +190,6 @@ interface AnthropicOptions extends BatchedProviderOptions {
184
190
  * Translates batches through the Anthropic Messages API. The reply's text blocks are joined before parsing.
185
191
  */
186
192
  export declare class AnthropicProvider extends BatchedProvider<AnthropicOptions> {
187
- /** Sends one batch and parses the model's text reply; a surrounding Markdown code fence is stripped first. */
188
193
  protected request(input: TranslateInput): Promise<Catalog>;
189
194
  }
190
195
  /** Options for the Gemini `generateContent` API. */
@@ -201,7 +206,6 @@ interface GeminiOptions extends BatchedProviderOptions {
201
206
  * `responseMimeType` requests JSON output.
202
207
  */
203
208
  export declare class GeminiProvider extends BatchedProvider<GeminiOptions> {
204
- /** Sends one batch and parses the first candidate's text; a surrounding Markdown code fence is stripped first. */
205
209
  protected request(input: TranslateInput): Promise<Catalog>;
206
210
  }
207
211
  export interface AutoI18nOptions {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{A as e,C as t,D as n,E as r,F as i,I as a,L as o,M as s,N as c,O as l,P as u,R as d,S as f,T as p,_ as m,b as h,c as g,g as _,i as v,j as y,k as b,l as x,m as ee,n as S,o as C,r as w,t as T,u as E,w as D,x as O,y as k}from"./registry-4CzY0FGn.js";import{n as te,t as ne}from"./resolve-ELDMKdoA.js";import*as A from"valibot";import{glob as re,readFile as ie,rm as ae,stat as oe}from"node:fs/promises";import j from"node:path";import{createHash as se}from"node:crypto";import{parseSync as ce}from"oxc-parser";import le from"magic-string";async function ue(e,t,n){let r=Array(e.length),i=0;return await Promise.all(Array.from({length:Math.min(Math.max(1,t),e.length)},async()=>{for(;i<e.length;){let t=i++;try{r[t]={status:`fulfilled`,value:await n(e[t])}}catch(e){r[t]={status:`rejected`,reason:e}}}})),r}var de=class{available;waiters=[];constructor(e){this.available=Math.max(1,Math.floor(e))}async run(e){await this.acquire();try{return await e()}finally{this.release()}}acquire(){return this.available>0?(this.available--,Promise.resolve()):new Promise(e=>this.waiters.push(e))}release(){let e=this.waiters.shift();e?e():this.available++}};function fe(e){return[...e.matchAll(k)].map(e=>e[1])}function pe(e,t){let n=new Set(e),r=new Set(t);return n.size===r.size&&[...n].every(e=>r.has(e))}function M(e,t){let n=fe(e);return e.trim()!==``&&n.length===t.length&&pe(n,t)}var N=class extends Error{partial;failed;constructor(e,t,n){super(e),this.partial=t,this.failed=n,this.name=`PartialTranslationError`}};async function me(e){let{provider:t,sourceLocale:n,locale:r,requests:i,previous:a,debug:o}=e,s=new Set(i.map(e=>e.id)),c=b(a.messages,s).catalog,l=b(a.sources,s).catalog,u=i.filter(e=>c[e.id]===void 0||l[e.id]!==e.source),f={},p=0,m;if(u.length&&t){if(o)for(let e of u)d(`sent ${n} -> ${r} ${e.id}: ${JSON.stringify(e.source)} (context: ${e.context})`);try{f=await t.translate({sourceLocale:n,targetLocale:r,messages:u})}catch(e){if(!(e instanceof N))throw e;f=e.partial,p=e.failed,m=e.message}if(o)for(let e of u){let t=f[e.id];d(`received ${n} -> ${r} ${e.id}: ${typeof t==`string`?JSON.stringify(t):`(no translation)`}`)}}let h={...c},g={...l},_=0;for(let e of u){delete h[e.id],delete g[e.id];let t=f[e.id];typeof t==`string`&&t.trim()&&(e.kind===`mixed`&&!M(t,e.placeholders??[])?_++:(h[e.id]=t,g[e.id]=e.source))}let v=i.filter(e=>h[e.id]===void 0).length;return{file:{messages:h,sources:g},untranslated:v,rejected:_,requested:u.length,failed:p,failure:m}}async function he(e){let t=structuredClone(e.registry),{ambiguous:r,messages:i}=w(t,e.files.flatMap(e=>e.messages)),a=S(t,e.orphanRetentionMinutes),o={};for(let e of i)o[e.id]=e.source;let s=i.map(e=>({id:e.id,source:e.source,context:`${e.component} > ${e.context}`,kind:e.kind,placeholders:e.placeholders})),c=await ue(e.locales,e.localeConcurrency,t=>me({provider:e.provider,sourceLocale:e.sourceLocale,locale:t,requests:s,previous:e.previous.get(t)??n(),debug:e.debug})),l=/* @__PURE__ */ new Map,u=/* @__PURE__ */ new Map;return c.forEach((t,n)=>{let r=e.locales[n];t.status===`fulfilled`?l.set(r,t.value):u.set(r,t.reason)}),{registry:t,ambiguous:r,pruned:a,messages:i,sourceFile:{messages:o,sources:{}},locales:l,failures:u}}function P(e){return e.replace(/\s+/g,` `).trim()}function F(e){let t=[P(e.source),e.kind,e.tag.toLowerCase(),e.attribute??``,e.context].join(``);return se(`sha256`).update(t).digest(`hex`).slice(0,24)}function I(e,t){let n=ce(t,e,{lang:`tsx`,sourceType:`unambiguous`});if(n.errors.length)throw SyntaxError(`${t}: ${n.errors[0].message}`);return{program:n.program,commentsByEnd:new Map(n.comments.map(e=>[e.end,e]))}}function L(e,t){let n=[],r=e=>{if(t(e,n)!==!1){n.push(e);for(let t of Object.keys(e)){if(t===`parent`)continue;let n=e[t];Array.isArray(n)?n.forEach(e=>R(e)&&r(e)):R(n)&&r(n)}n.pop()}};r(e)}function R(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}function z(e){return R(e)&&e.type===`Literal`&&typeof e.value==`string`}function ge(e,t,n,r){let i=n;for(;;){let n=i;for(;n>0&&/\s/.test(e[n-1]);)n--;let a=t.get(n);if(!a)return!1;if(a.type===`Block`&&a.value.trim()===r)return!0;i=a.start}}function B(e){return e.includes(`&`)?e.replace(O,(e,t,n)=>{if(n!==void 0)return m.get(n)??e;let r=t.startsWith(`x`)?parseInt(t.slice(1),16):parseInt(t,10);return r<=1114111?String.fromCodePoint(r):e}):e}function V(e){return z(e)?B(e.value):void 0}function H(e){return B(e.value)}function _e(e){return e.attributes.some(e=>e.type===`JSXAttribute`&&e.name.type===`JSXIdentifier`&&e.name.name===`translate`&&V(e.value)===`no`)}function U(e){return e.type===`JSXIdentifier`&&!g.test(e.name)&&!ee.has(e.name)}function W(e){if(e.type===`Identifier`)return e.name;if(e.type===`MemberExpression`&&!e.computed&&e.property.type===`Identifier`){let t=W(e.object);return t?`${t}.${e.property.name}`:void 0}}const ve=(e,t)=>e.type===`FunctionExpression`?!(t?.type===`MethodDefinition`||t?.type===`Property`&&(t.method||t.kind!==`init`)):e.type===`FunctionDeclaration`||e.type===`ArrowFunctionExpression`;function ye(e,t){for(let n=e.length-1;n>=0;n--)if(t(e[n]))return n;return-1}function G(e){return e.some(e=>{let t=e.type===`JSXOpeningElement`?e:e.type===`JSXElement`?e.openingElement:void 0;return t!==void 0&&_e(t)})}function K(e,t,n){let r=e.attributes.filter(e=>e.type===`JSXAttribute`&&e.name.type===`JSXIdentifier`&&[`aria-label`,`title`,`name`,`type`,`role`].includes(e.name.name)).map(e=>`${e.name.name}=${V(e.value)??`{expr}`}`).sort().join(`;`),i=e.attributes.filter(e=>e.type===`JSXAttribute`&&e.name.type===`JSXIdentifier`&&/^on[A-Z]/.test(e.name.name)).map(e=>e.name.name).sort().join(`,`);return[t,n??``,r,i].filter(Boolean).join(` | `)}function be(e,t,n={}){let{program:r,commentsByEnd:i}=I(e,t),a=[],o=e=>Object.prototype.hasOwnProperty.call(n.componentProps??{},e)?n.componentProps[e]:[],s=e=>{if(e.type!==`JSXIdentifier`)return!1;let t=e.name;return U(e)||g.test(t)&&o(t).includes(`children`)};function c(e){let t=e.length-1;for(;t>=0&&!ve(e[t],e[t-1]);)t--;if(t<0)return`<module>`;let n=e[t],r=e[t-1];return r?.type===`VariableDeclarator`&&r.id.type===`Identifier`?r.id.name:n.type===`FunctionDeclaration`&&n.id?n.id.name:`<anonymous>`}function l(e,n){if(G([...n,e]))return;let r=e.children,i=r.filter(e=>e.type===`JSXExpressionContainer`);if(!i.length||r.some(e=>e.type!==`JSXText`&&e.type!==`JSXExpressionContainer`)||!i.every(e=>W(e.expression)!==void 0)||!r.some(e=>e.type===`JSXText`&&/[A-Za-z]/.test(H(e))))return;let o=e.openingElement.name;if(!s(o))return;let l=[];for(let e of i){let t=W(e.expression),n=t;for(let e=2;l.includes(n);e++)n=`${t}_${e}`;l.push(n)}let u=``,d=0;for(let e of r)u+=e.type===`JSXText`?H(e):`{${l[d++]}}`;let f=P(u);if(!f)return;let p=o.name,m={source:f,prefix:u.match(/^\s*/)?.[0]??``,suffix:u.match(/\s*$/)?.[0]??``,kind:`mixed`,placeholders:l,file:t,start:e.start,end:e.end,tag:p,component:c(n),context:K(e.openingElement,p),fingerprint:``};m.fingerprint=F(m),a.push(m)}function u(e,n){if(G([...n,e]))return;let r=H(e),i=P(r);if(!i)return;let o=ye(n,e=>e.type===`JSXElement`);if(o<0)return;let l=n[o];if(l.children.some(e=>e.type===`JSXExpressionContainer`))return;let u=l.openingElement.name;if(!s(u))return;let d=u.name,f={source:i,prefix:r.match(/^\s*/)?.[0]??``,suffix:r.match(/\s*$/)?.[0]??``,kind:`text`,file:t,start:e.start,end:e.end,tag:d,component:c(n.slice(0,o)),context:K(l.openingElement,d),fingerprint:``};f.fingerprint=F(f),a.push(f)}function d(e,n){if(e.name.type!==`JSXIdentifier`)return;let r=e.name.name,i=n[n.length-1];if(i?.type!==`JSXOpeningElement`)return;let s=i.name.type===`JSXIdentifier`?i.name.name:``;if(!(U(i.name)?p.has(r):o(s).includes(r))||G([...n,e])||!z(e.value))return;let l=P(B(e.value.value));if(!l||!/[A-Za-z]/.test(l))return;let u=ye(n,e=>e.type===`JSXElement`);if(u<0)return;let d=n[u],f={source:l,kind:`attribute`,file:t,start:e.value.start,end:e.value.end,tag:s,component:c(n.slice(0,u)),attribute:r,context:K(d.openingElement,s,r),fingerprint:``};f.fingerprint=F(f),a.push(f)}function f(r,o){if(!z(r)||o[o.length-1]?.type===`JSXAttribute`||o.some(e=>e.start===r.start)||!ge(e,i,r.start,n.stringMarker??`i18n`))return;let s=P(r.value);if(!s||!/[A-Za-z]/.test(s))return;let l={source:s,kind:`string`,file:t,start:r.start,end:r.end,tag:``,component:c(o),context:``,fingerprint:``};l.fingerprint=F(l),a.push(l)}return L(r,(e,t)=>{switch(e.type){case`JSXElement`:return l(e,t);case`JSXText`:return u(e,t);case`JSXAttribute`:return d(e,t);case`Literal`:return f(e,t)}}),a.sort((e,t)=>e.start-t.start)}function xe(e,n,r,i,a,o=!1,s=`en`){let{program:c}=I(e,n),l={mixed:/* @__PURE__ */ new Map,text:/* @__PURE__ */ new Map,attribute:/* @__PURE__ */ new Map,string:/* @__PURE__ */ new Map};for(let e of r)l[e.kind]?.set(e.start,e);let u=r.map(e=>e.start).sort((e,t)=>e-t),d=(e,t)=>{let n=0,r=u.length;for(;n<r;){let t=n+r>>1;u[t]<e?n=t+1:r=t}return n<u.length&&u[n]<t},p=[];L(c,e=>{if(!d(e.start,e.end))return!1;if(e.type===`JSXElement`){let t=l.mixed.get(e.start);t&&p.push({kind:`mixed`,node:e,message:t})}else if(e.type===`JSXText`){let t=l.text.get(e.start);t&&p.push({kind:`text`,node:e,message:t})}else if(e.type===`JSXAttribute`&&e.value?.type===`Literal`){let t=l.attribute.get(e.value.start);t&&p.push({kind:`attribute`,node:e.value,message:t})}else if(e.type===`Literal`&&typeof e.value==`string`){let t=l.string.get(e.start);t&&p.push({kind:`string`,node:e,message:t})}});let m=new le(e),h=!1,g=!1,_=e=>i===s?e.source:a[e.id]??e.source,v=e=>o?(h=!0,`${t}(${JSON.stringify(e.id)}, ${JSON.stringify(e.source)})`):JSON.stringify(_(e)),y=e=>{let t=e.prefix??``,n=e.suffix??``;return o?[t&&JSON.stringify(t),v(e),n&&JSON.stringify(n)].filter(Boolean).join(` + `):JSON.stringify(`${t}${_(e)}${n}`)};for(let e of p.reverse()){let{message:t}=e;if(e.kind===`mixed`){let n=e.node;if(!n.closingElement)continue;let r=t.placeholders??[],i=n.children.flatMap(e=>e.type===`JSXExpressionContainer`?[e.expression]:[]),a=e=>m.slice(i[e].start,i[e].end),s=n.openingElement.end,c=n.closingElement.start;if(o){g=!0;let e=r.map((e,t)=>`${JSON.stringify(e)}: ${a(t)}`).join(`, `);m.overwrite(s,c,`{${f}(${y(t)}, {${e}})}`);continue}let l=_(t),u=M(l,r)?l:t.source,d=[],p=0,h=e=>{e&&d.push(`{${JSON.stringify(e)}}`)};h(t.prefix??``);for(let e of u.matchAll(k))h(u.slice(p,e.index)),d.push(`{${a(r.indexOf(e[1]))}}`),p=e.index+e[0].length;h(u.slice(p)+(t.suffix??``)),m.overwrite(s,c,d.join(``))}else e.kind===`text`?m.overwrite(e.node.start,e.node.end,`{${y(t)}}`):e.kind===`attribute`?m.overwrite(e.node.start,e.node.end,o?`{${v(t)}}`:v(t)):m.overwrite(e.node.start,e.node.end,v(t))}let b=[...h?[`t as ${t}`]:[],...g?[`format as ${f}`]:[]];return b.length&&m.prepend(`import { ${b.join(`, `)} } from "${D}";\n`),{code:m.toString(),map:m.generateMap({source:n,hires:!0,includeContent:!0})}}function Se(e,t,n){let r=[],i=0;for(let a of e.matchAll(n)){if(!Object.hasOwn(t,a[1]))continue;let n=a.index??0;n>i&&r.push(e.slice(i,n)),r.push(t[a[1]]),i=n+a[0].length}return i<e.length&&r.push(e.slice(i)),r}function Ce(e,t){return[`const loaders = {\n${Object.entries(e).map(([e,t])=>` ${JSON.stringify(e)}: () => import(${JSON.stringify(t)}).then(m => (m.default ?? m).messages)`).join(`,
2
- `)}\n};`,`const sourceLocale = ${JSON.stringify(t)};`,`const resolveLocale = ${ne.toString()};`,`const createLocaleLoader = ${te.toString()};`,`const formatPlaceholders = ${Se.toString()};`,`const PLACEHOLDER = new RegExp(${JSON.stringify(h)}, "g");`,`const loadLocale = createLocaleLoader(loaders, sourceLocale);`,`const listeners = new Set();`,`let currentLocale = sourceLocale;`,`let messages = {};`,`let version = 0;`,`let latestRequest = 0;`,`export const locales = Object.keys(loaders);`,`export function getLocale() { return currentLocale; }`,`export function getVersion() { return version; }`,`export function resolveSupported(preferred) { return resolveLocale(preferred, locales, sourceLocale); }`,`export async function setLocale(locale) {`,` const request = ++latestRequest;`,` let selected = loaders[locale] ? locale : sourceLocale;`,` let catalog;`,` try {`,` catalog = await loadLocale(selected);`,` } catch (error) {`,` console.warn('autoi18n: could not load "' + selected + '", falling back to "' + sourceLocale + '".', error);`,` selected = sourceLocale;`,` try {`,` catalog = await loadLocale(selected);`,` } catch (fallbackError) {`,` // Keep the current locale and messages so the UI stays consistent. Texts without a catalogue show their source.`,` console.error('autoi18n: could not load the source catalogue "' + sourceLocale + '".', fallbackError);`,` return;`,` }`,` }`,` // A slower earlier request must not overwrite the locale the user picked last.`,` if (request !== latestRequest) return;`,` messages = catalog;`,` currentLocale = selected;`,` notify();`,`}`,`function notify() {`,` version++;`,` listeners.forEach(fn => fn(currentLocale));`,`}`,`export function subscribe(fn) { listeners.add(fn); return () => { listeners.delete(fn); }; }`,`export function t(id, source) { return messages[id] ?? source; }`,`export function format(text, values) { return formatPlaceholders(text, values, PLACEHOLDER); }`,`// Dev server pushes the catalogues that changed after a source change; no page reload is needed.`,`if (import.meta.hot) {`,` import.meta.hot.on("autoi18n:update", data => {`,` for (const [locale, catalog] of Object.entries(data.catalogs)) loadLocale.prime(locale, catalog);`,` if (!data.catalogs[currentLocale]) return;`,` messages = data.catalogs[currentLocale];`,` notify();`,` });`,`}`].join(`
1
+ import{A as e,C as t,D as n,E as r,F as i,I as a,L as o,M as s,N as c,O as l,P as u,R as d,S as f,T as p,_ as m,b as h,c as g,g as _,i as v,j as y,k as b,l as x,m as S,n as C,o as w,r as T,t as E,u as D,v as O,w as k,x as A,y as j}from"./registry-4CzY0FGn.js";import{n as ee,t as te}from"./resolve-ELDMKdoA.js";import*as M from"valibot";import{glob as ne,readFile as re,rm as ie,stat as ae}from"node:fs/promises";import N from"node:path";import{createHash as oe}from"node:crypto";import{parseSync as se}from"rolldown/utils";import ce from"magic-string";async function le(e,t,n){let r=Array(e.length),i=0;return await Promise.all(Array.from({length:Math.min(Math.max(1,t),e.length)},async()=>{for(;i<e.length;){let t=i++;try{r[t]={status:`fulfilled`,value:await n(e[t])}}catch(e){r[t]={status:`rejected`,reason:e}}}})),r}var ue=class{available;waiters=[];constructor(e){this.available=Math.max(1,Math.floor(e))}async run(e){await this.acquire();try{return await e()}finally{this.release()}}acquire(){return this.available>0?(this.available--,Promise.resolve()):new Promise(e=>this.waiters.push(e))}release(){let e=this.waiters.shift();e?e():this.available++}};function de(e){return[...e.matchAll(j)].map(e=>e[1])}function fe(e,t){let n=new Set(e),r=new Set(t);return n.size===r.size&&[...n].every(e=>r.has(e))}function P(e,t){let n=de(e);return e.trim()!==``&&n.length===t.length&&fe(n,t)}var F=class extends Error{partial;failed;constructor(e,t,n){super(e),this.partial=t,this.failed=n,this.name=`PartialTranslationError`}};async function pe(e){let{provider:t,sourceLocale:n,locale:r,requests:i,previous:a,debug:o}=e,s=new Set(i.map(e=>e.id)),c=b(a.messages,s).catalog,l=b(a.sources,s).catalog,u=i.filter(e=>c[e.id]===void 0||l[e.id]!==e.source),f={},p=0,m;if(u.length&&t){if(o)for(let e of u)d(`sent ${n} -> ${r} ${e.id}: ${JSON.stringify(e.source)} (context: ${e.context})`);try{f=await t.translate({sourceLocale:n,targetLocale:r,messages:u})}catch(e){if(!(e instanceof F))throw e;f=e.partial,p=e.failed,m=e.message}if(o)for(let e of u){let t=f[e.id];d(`received ${n} -> ${r} ${e.id}: ${typeof t==`string`?JSON.stringify(t):`(no translation)`}`)}}let h={...c},g={...l},_=0;for(let e of u){delete h[e.id],delete g[e.id];let t=f[e.id];typeof t==`string`&&t.trim()&&(e.kind===`mixed`&&!P(t,e.placeholders??[])?_++:(h[e.id]=t,g[e.id]=e.source))}let v=i.filter(e=>h[e.id]===void 0).length;return{file:{messages:h,sources:g},untranslated:v,rejected:_,requested:u.length,failed:p,failure:m}}async function me(e){let t=structuredClone(e.registry),{ambiguous:r,messages:i}=T(t,e.files.flatMap(e=>e.messages)),a=C(t,e.orphanRetentionMinutes),o={};for(let e of i)o[e.id]=e.source;let s=i.map(e=>({id:e.id,source:e.source,context:`${e.component} > ${e.context}`,kind:e.kind,placeholders:e.placeholders})),c=await le(e.locales,e.localeConcurrency,t=>pe({provider:e.provider,sourceLocale:e.sourceLocale,locale:t,requests:s,previous:e.previous.get(t)??n(),debug:e.debug})),l=/* @__PURE__ */ new Map,u=/* @__PURE__ */ new Map;return c.forEach((t,n)=>{let r=e.locales[n];t.status===`fulfilled`?l.set(r,t.value):u.set(r,t.reason)}),{registry:t,ambiguous:r,pruned:a,messages:i,sourceFile:{messages:o,sources:{}},locales:l,failures:u}}function I(e){return e.replace(/\s+/g,` `).trim()}function L(e){let t=[I(e.source),e.kind,e.tag.toLowerCase(),e.attribute??``,e.context].join(``);return oe(`sha256`).update(t).digest(`hex`).slice(0,24)}function R(e,t){let n=se(t,e,{lang:`tsx`,sourceType:`unambiguous`});if(n.errors.length)throw SyntaxError(`${t}: ${n.errors[0].message}`);return{program:n.program,commentsByEnd:new Map(n.comments.map(e=>[e.end,e]))}}function z(e,t){let n=[],r=e=>{if(t(e,n)!==!1){n.push(e);for(let t of Object.keys(e)){if(t===`parent`)continue;let n=e[t];Array.isArray(n)?n.forEach(e=>B(e)&&r(e)):B(n)&&r(n)}n.pop()}};r(e)}function B(e){return typeof e==`object`&&!!e&&typeof e.type==`string`}function V(e){return B(e)&&e.type===`Literal`&&typeof e.value==`string`}function he(e,t,n,r){let i=n;for(;;){let n=i;for(;n>0&&/\s/.test(e[n-1]);)n--;let a=t.get(n);if(!a)return!1;if(a.type===`Block`&&a.value.trim()===r)return!0;i=a.start}}function H(e){return e.includes(`&`)?e.replace(A,(e,t,n)=>{if(n!==void 0)return m.get(n)??e;let r=t.startsWith(`x`)?parseInt(t.slice(1),16):parseInt(t,10);return r<=1114111?String.fromCodePoint(r):e}):e}function U(e){return V(e)?H(e.value):void 0}function W(e){return H(e.value)}function ge(e){return e.attributes.some(e=>e.type===`JSXAttribute`&&e.name.type===`JSXIdentifier`&&e.name.name===`translate`&&U(e.value)===`no`)}function G(e){return e.type===`JSXIdentifier`&&!g.test(e.name)&&!S.has(e.name)}function K(e){if(e.type===`Identifier`)return e.name;if(e.type===`MemberExpression`&&!e.computed&&e.property.type===`Identifier`){let t=K(e.object);return t?`${t}.${e.property.name}`:void 0}}const _e=(e,t)=>e.type===`FunctionExpression`?!(t?.type===`MethodDefinition`||t?.type===`Property`&&(t.method||t.kind!==`init`)):e.type===`FunctionDeclaration`||e.type===`ArrowFunctionExpression`;function ve(e,t){for(let n=e.length-1;n>=0;n--)if(t(e[n]))return n;return-1}function q(e){return e.some(e=>{let t=e.type===`JSXOpeningElement`?e:e.type===`JSXElement`?e.openingElement:void 0;return t!==void 0&&ge(t)})}function J(e,t,n){let r=e.attributes.filter(e=>e.type===`JSXAttribute`&&e.name.type===`JSXIdentifier`&&[`aria-label`,`title`,`name`,`type`,`role`].includes(e.name.name)).map(e=>`${e.name.name}=${U(e.value)??`{expr}`}`).sort().join(`;`),i=e.attributes.filter(e=>e.type===`JSXAttribute`&&e.name.type===`JSXIdentifier`&&/^on[A-Z]/.test(e.name.name)).map(e=>e.name.name).sort().join(`,`);return[t,n??``,r,i].filter(Boolean).join(` | `)}function ye(e,t,n={}){let{program:r,commentsByEnd:i}=R(e,t),a=[],o=e=>Object.prototype.hasOwnProperty.call(n.componentProps??{},e)?n.componentProps[e]:[],s=e=>{if(e.type!==`JSXIdentifier`)return!1;let t=e.name;return G(e)||g.test(t)&&o(t).includes(`children`)};function c(e){let t=e.length-1;for(;t>=0&&!_e(e[t],e[t-1]);)t--;if(t<0)return`<module>`;let n=e[t],r=e[t-1];return r?.type===`VariableDeclarator`&&r.id.type===`Identifier`?r.id.name:n.type===`FunctionDeclaration`&&n.id?n.id.name:`<anonymous>`}function l(e,n){if(q([...n,e]))return;let r=e.children,i=r.filter(e=>e.type===`JSXExpressionContainer`);if(!i.length||r.some(e=>e.type!==`JSXText`&&e.type!==`JSXExpressionContainer`)||!i.every(e=>K(e.expression)!==void 0)||!r.some(e=>e.type===`JSXText`&&/[A-Za-z]/.test(W(e))))return;let o=e.openingElement.name;if(!s(o))return;let l=[];for(let e of i){let t=K(e.expression),n=t;for(let e=2;l.includes(n);e++)n=`${t}_${e}`;l.push(n)}let u=``,d=0;for(let e of r)u+=e.type===`JSXText`?W(e):`{${l[d++]}}`;let f=I(u);if(!f)return;let p=o.name,m={source:f,prefix:u.match(/^\s*/)?.[0]??``,suffix:u.match(/\s*$/)?.[0]??``,kind:`mixed`,placeholders:l,file:t,start:e.start,end:e.end,tag:p,component:c(n),context:J(e.openingElement,p),fingerprint:``};m.fingerprint=L(m),a.push(m)}function u(e,n){if(q([...n,e]))return;let r=W(e),i=I(r);if(!i)return;let o=ve(n,e=>e.type===`JSXElement`);if(o<0)return;let l=n[o];if(l.children.some(e=>e.type===`JSXExpressionContainer`))return;let u=l.openingElement.name;if(!s(u))return;let d=u.name,f={source:i,prefix:r.match(/^\s*/)?.[0]??``,suffix:r.match(/\s*$/)?.[0]??``,kind:`text`,file:t,start:e.start,end:e.end,tag:d,component:c(n.slice(0,o)),context:J(l.openingElement,d),fingerprint:``};f.fingerprint=L(f),a.push(f)}function d(e,n){if(e.name.type!==`JSXIdentifier`)return;let r=e.name.name,i=n[n.length-1];if(i?.type!==`JSXOpeningElement`)return;let s=i.name.type===`JSXIdentifier`?i.name.name:``;if(!(G(i.name)?p.has(r):o(s).includes(r))||q([...n,e])||!V(e.value))return;let l=I(H(e.value.value));if(!l||!/[A-Za-z]/.test(l))return;let u=ve(n,e=>e.type===`JSXElement`);if(u<0)return;let d=n[u],f={source:l,kind:`attribute`,file:t,start:e.value.start,end:e.value.end,tag:s,component:c(n.slice(0,u)),attribute:r,context:J(d.openingElement,s,r),fingerprint:``};f.fingerprint=L(f),a.push(f)}function f(r,o){if(!V(r)||o[o.length-1]?.type===`JSXAttribute`||o.some(e=>e.start===r.start)||!he(e,i,r.start,n.stringMarker??`i18n`))return;let s=I(r.value);if(!s||!/[A-Za-z]/.test(s))return;let l={source:s,kind:`string`,file:t,start:r.start,end:r.end,tag:``,component:c(o),context:``,fingerprint:``};l.fingerprint=L(l),a.push(l)}return z(r,(e,t)=>{switch(e.type){case`JSXElement`:return l(e,t);case`JSXText`:return u(e,t);case`JSXAttribute`:return d(e,t);case`Literal`:return f(e,t)}}),a.sort((e,t)=>e.start-t.start)}function be(e,n,r,i,a,o=!1,s=`en`){let{program:c}=R(e,n),l={mixed:/* @__PURE__ */ new Map,text:/* @__PURE__ */ new Map,attribute:/* @__PURE__ */ new Map,string:/* @__PURE__ */ new Map};for(let e of r)l[e.kind]?.set(e.start,e);let u=r.map(e=>e.start).sort((e,t)=>e-t),d=(e,t)=>{let n=0,r=u.length;for(;n<r;){let t=n+r>>1;u[t]<e?n=t+1:r=t}return n<u.length&&u[n]<t},p=[];z(c,e=>{if(!d(e.start,e.end))return!1;if(e.type===`JSXElement`){let t=l.mixed.get(e.start);t&&p.push({kind:`mixed`,node:e,message:t})}else if(e.type===`JSXText`){let t=l.text.get(e.start);t&&p.push({kind:`text`,node:e,message:t})}else if(e.type===`JSXAttribute`&&e.value?.type===`Literal`){let t=l.attribute.get(e.value.start);t&&p.push({kind:`attribute`,node:e.value,message:t})}else if(e.type===`Literal`&&typeof e.value==`string`){let t=l.string.get(e.start);t&&p.push({kind:`string`,node:e,message:t})}});let m=new ce(e),h=!1,g=!1,_=e=>i===s?e.source:a[e.id]??e.source,v=e=>o?(h=!0,`${t}(${JSON.stringify(e.id)}, ${JSON.stringify(e.source)})`):JSON.stringify(_(e)),y=e=>{let t=e.prefix??``,n=e.suffix??``;return o?[t&&JSON.stringify(t),v(e),n&&JSON.stringify(n)].filter(Boolean).join(` + `):JSON.stringify(`${t}${_(e)}${n}`)};for(let e of p.reverse()){let{message:t}=e;if(e.kind===`mixed`){let n=e.node;if(!n.closingElement)continue;let r=t.placeholders??[],i=n.children.flatMap(e=>e.type===`JSXExpressionContainer`?[e.expression]:[]),a=e=>m.slice(i[e].start,i[e].end),s=n.openingElement.end,c=n.closingElement.start;if(o){g=!0;let e=r.map((e,t)=>`${JSON.stringify(e)}: ${a(t)}`).join(`, `);m.overwrite(s,c,`{${f}(${y(t)}, {${e}})}`);continue}let l=_(t),u=P(l,r)?l:t.source,d=[],p=0,h=e=>{e&&d.push(`{${JSON.stringify(e)}}`)};h(t.prefix??``);for(let e of u.matchAll(j))h(u.slice(p,e.index)),d.push(`{${a(r.indexOf(e[1]))}}`),p=e.index+e[0].length;h(u.slice(p)+(t.suffix??``)),m.overwrite(s,c,d.join(``))}else e.kind===`text`?m.overwrite(e.node.start,e.node.end,`{${y(t)}}`):e.kind===`attribute`?m.overwrite(e.node.start,e.node.end,o?`{${v(t)}}`:v(t)):m.overwrite(e.node.start,e.node.end,v(t))}let b=[...h?[`t as ${t}`]:[],...g?[`format as ${f}`]:[]];return b.length&&m.prepend(`import { ${b.join(`, `)} } from "${k}";\n`),{code:m.toString(),map:m.generateMap({source:n,hires:!0,includeContent:!0})}}function xe(e,t,n){let r=[],i=0;for(let a of e.matchAll(n)){if(!Object.hasOwn(t,a[1]))continue;let n=a.index??0;n>i&&r.push(e.slice(i,n)),r.push(t[a[1]]),i=n+a[0].length}return i<e.length&&r.push(e.slice(i)),r}function Se(e,t){return[`const loaders = {\n${Object.entries(e).map(([e,t])=>` ${JSON.stringify(e)}: () => import(${JSON.stringify(t)}).then(m => (m.default ?? m).messages)`).join(`,
2
+ `)}\n};`,`const sourceLocale = ${JSON.stringify(t)};`,`const resolveLocale = ${te.toString()};`,`const createLocaleLoader = ${ee.toString()};`,`const formatPlaceholders = ${xe.toString()};`,`const PLACEHOLDER = new RegExp(${JSON.stringify(h)}, "g");`,`const loadLocale = createLocaleLoader(loaders, sourceLocale);`,`const listeners = new Set();`,`let currentLocale = sourceLocale;`,`let messages = {};`,`let version = 0;`,`let latestRequest = 0;`,`export const locales = Object.keys(loaders);`,`export function getLocale() { return currentLocale; }`,`export function getVersion() { return version; }`,`export function resolveSupported(preferred) { return resolveLocale(preferred, locales, sourceLocale); }`,`export async function setLocale(locale) {`,` const request = ++latestRequest;`,` let selected = loaders[locale] ? locale : sourceLocale;`,` let catalog;`,` try {`,` catalog = await loadLocale(selected);`,` } catch (error) {`,` console.warn('autoi18n: could not load "' + selected + '", falling back to "' + sourceLocale + '".', error);`,` selected = sourceLocale;`,` try {`,` catalog = await loadLocale(selected);`,` } catch (fallbackError) {`,` // Keep the current locale and messages so the UI stays consistent. Texts without a catalogue show their source.`,` console.error('autoi18n: could not load the source catalogue "' + sourceLocale + '".', fallbackError);`,` return;`,` }`,` }`,` // A slower earlier request must not overwrite the locale the user picked last.`,` if (request !== latestRequest) return;`,` messages = catalog;`,` currentLocale = selected;`,` notify();`,`}`,`function notify() {`,` version++;`,` listeners.forEach(fn => fn(currentLocale));`,`}`,`export function subscribe(fn) { listeners.add(fn); return () => { listeners.delete(fn); }; }`,`export function t(id, source) { return messages[id] ?? source; }`,`export function format(text, values) { return formatPlaceholders(text, values, PLACEHOLDER); }`,`// Dev server pushes the catalogues that changed after a source change; no page reload is needed.`,`if (import.meta.hot) {`,` import.meta.hot.on("autoi18n:update", data => {`,` for (const [locale, catalog] of Object.entries(data.catalogs)) loadLocale.prime(locale, catalog);`,` if (!data.catalogs[currentLocale]) return;`,` messages = data.catalogs[currentLocale];`,` notify();`,` });`,`}`].join(`
3
3
  `)+`
4
- `}async function q(e){try{let t=await oe(e);return`${t.mtimeMs}:${t.size}`}catch{return}}function we(e,t){if(!e)return!1;let n=Object.keys(t);return n.length===Object.keys(e).length&&n.every(n=>e[n]===t[n])}var Te=class{extractions=/* @__PURE__ */ new Map;fileMessages=/* @__PURE__ */ new Map;catalogs=/* @__PURE__ */ new Map;catalogFiles={};warnedFiles=/* @__PURE__ */ new Set;legacySidecarsRemoved=!1;localeFiles=/* @__PURE__ */ new Map;registryCache;hashOf(e){return this.extractions.get(e)?.hash}async readLocale(t,n){let r=await q(t),i=this.localeFiles.get(t);if(r!==void 0&&i?.stamp===r)return i.value;let a=await e(t,n);return r!==void 0&&this.localeFiles.set(t,{stamp:r,value:a}),a}async writeLocale(e,t){await y(e,t);let n=await q(e);n!==void 0&&this.localeFiles.set(e,{stamp:n,value:t})}async readRegistry(e,t){let n=await q(e);if(n!==void 0&&this.registryCache?.stamp===n)return this.registryCache.value;let r=await T(e,t);return n!==void 0&&(this.registryCache={stamp:n,value:r}),r}async writeRegistry(e,t){await v(e,t);let n=await q(e);n!==void 0&&(this.registryCache={stamp:n,value:t})}},Ee=class{providers;onFallback;constructor(e,t=()=>{}){this.providers=e,this.onFallback=t}async translate(e){let t={},n=e.messages,r,i;for(let[a,{name:o,provider:s}]of this.providers.entries()){try{Object.assign(t,await s.translate({...e,messages:n}))}catch(e){r??=e,i=e,e instanceof N&&Object.assign(t,e.partial);let s=this.providers[a+1];s&&n.some(e=>t[e.id]===void 0)&&this.onFallback(`${o} failed (${De(e)}); sending the rest to ${s.name}.`)}if(n=n.filter(e=>t[e.id]===void 0),!n.length)return t}throw Object.keys(t).length===0?r:new N(`${n.length} messages failed to translate: ${De(i)}`,t,n.length)}};const De=e=>e instanceof Error?e.message:String(e),Oe=e=>typeof e==`object`&&!!e&&typeof e.translate==`function`;function ke(e,t){if(e===void 0)return;if(Oe(e))return e;let{primary:n,...r}=e,i=[];for(let[e,t]of Object.entries(r))if(t!==void 0){if(!Oe(t))throw Error(`autoi18n: provider "${e}" must be an object with a translate(input) method.`);i.push({name:e,provider:t})}if(n!==void 0){let e=i.findIndex(e=>e.name===n);if(e<0)throw Error(`autoi18n: primary provider "${n}" is not configured.`);i.unshift(...i.splice(e,1))}if(i.length!==0)return i.length===1?i[0].provider:new Ee(i,t)}var J=class extends Error{retryable;retryAfterMs;constructor(e,t,n){super(e),this.retryable=t,this.retryAfterMs=n}};function Ae(e,t=Date.now()){if(!e)return;let n=Number(e);if(Number.isFinite(n))return Math.min(Math.max(n,0)*1e3,_);let r=Date.parse(e);if(!Number.isNaN(r))return Math.min(Math.max(r-t,0),_)}const je=e=>new Promise(t=>setTimeout(t,e));function Y(e){return`You are a professional software localization engine. Translate UI messages from ${e.sourceLocale} to ${e.targetLocale}. Preserve placeholders exactly. Respect context and UI conventions. Return ONLY a JSON object mapping message IDs to translated strings. Do not translate product names or code.`}function X(e){return JSON.stringify(e.map(e=>({id:e.id,text:e.source,context:e.context,kind:e.kind})))}async function Z(e){let t=e.status===408||e.status===409||e.status===429||e.status>=500;return await e.body?.cancel().catch(()=>void 0),new J(`Translation API failed (${e.status})`,t,Ae(e.headers.get(`retry-after`)))}var Q=class{options;slots;constructor(e){this.options=e,this.slots=new de(e.concurrency??4)}async translate(e){let t=Math.max(1,this.options.batchSize??40),n=[];for(let r=0;r<e.messages.length;r+=t)n.push(e.messages.slice(r,r+t));let r=await Promise.allSettled(n.map(t=>this.slots.run(()=>this.translateBatch({...e,messages:t})))),i={},a=[],o=0,s=0;if(r.forEach((e,t)=>{e.status===`fulfilled`?(s++,Object.assign(i,e.value)):(a.push(e.reason),o+=n[t].length)}),!a.length)return i;throw s?new N(`${o} messages failed to translate: ${a[0].message}`,i,o):a[0]}async translateBatch(e){let t=this.options.maxRetries??2,n=this.options.retryDelayMs??500;for(let r=0;;r++)try{return await this.request(e)}catch(e){if(e instanceof J&&!e.retryable||r>=t)throw e;let i=e instanceof J?e.retryAfterMs:void 0;await je(i??n*2**r*(.5+Math.random()/2))}}toCatalog(e,t){if(!e)throw new J(`Translation provider returned no content`,!0);let n;try{n=JSON.parse(e)}catch{throw new J(`Translation provider returned invalid JSON`,!0)}let r=A.safeParse(a,n);if(!r.success)throw new J(`Translation provider returned a non-object`,!0);let i=new Set(t.map(e=>e.id)),o={};for(let[e,t]of Object.entries(r.output))i.has(e)&&typeof t==`string`&&t.trim()&&(o[e]=t);return o}},Me=class extends Q{async request(e){let t=await fetch(`${this.options.baseURL??`https://api.openai.com/v1`}/chat/completions`,{method:`POST`,signal:AbortSignal.timeout(this.options.timeoutMs??6e4),headers:{Authorization:`Bearer ${this.options.apiKey}`,"Content-Type":`application/json`},body:JSON.stringify({model:this.options.model,temperature:0,response_format:{type:`json_object`},messages:[{role:`system`,content:Y(e)},{role:`user`,content:X(e.messages)}]})});if(!t.ok)throw await Z(t);let n=A.safeParse(c,await t.json());return this.toCatalog(n.success?n.output.choices[0].message.content??void 0:void 0,e.messages)}},Ne=class extends Q{async request(e){let t=await fetch(`${this.options.baseURL??`https://api.anthropic.com/v1`}/messages`,{method:`POST`,signal:AbortSignal.timeout(this.options.timeoutMs??6e4),headers:{"x-api-key":this.options.apiKey,"anthropic-version":C,"content-type":`application/json`},body:JSON.stringify({model:this.options.model,max_tokens:this.options.maxTokens??8192,system:Y(e),messages:[{role:`user`,content:X(e.messages)}]})});if(!t.ok)throw await Z(t);let n=A.safeParse(s,await t.json()),r=n.success?n.output.content.filter(e=>e.type===`text`).map(e=>e.text??``).join(``):``;return this.toCatalog(r.trim().replace(/^```(?:json)?\s*|\s*```$/g,``)||void 0,e.messages)}},Pe=class extends Q{async request(e){let t=this.options.baseURL??`https://generativelanguage.googleapis.com/v1beta`,n=await fetch(`${t}/models/${encodeURIComponent(this.options.model)}:generateContent`,{method:`POST`,signal:AbortSignal.timeout(this.options.timeoutMs??6e4),headers:{"x-goog-api-key":this.options.apiKey,"content-type":`application/json`},body:JSON.stringify({systemInstruction:{parts:[{text:Y(e)}]},contents:[{role:`user`,parts:[{text:X(e.messages)}]}],generationConfig:{responseMimeType:`application/json`,maxOutputTokens:this.options.maxOutputTokens??8192}})});if(!n.ok)throw await Z(n);let r=A.safeParse(u,await n.json()),i=r.success?(r.output.candidates[0]?.content?.parts??[]).map(e=>e.text??``).join(``):``;return this.toCatalog(i.trim().replace(/^```(?:json)?\s*|\s*```$/g,``)||void 0,e.messages)}};function Fe(e){let t=A.safeParse(i,e);if(t.success)return;let n=t.issues.map(e=>`${e.path?.map(e=>e.key).join(`.`)||`options`}: ${e.message}`).join(`; `);throw Error(`autoi18n: invalid options (${n}).`)}const $=e=>({info:t=>e.info(t),warn:t=>e.warn(t),error:t=>e.error(t)});async function Ie(e,t){await Promise.all(t.map(t=>ae(j.join(e,`${t}.sources.json`),{force:!0})))}function Le(e){Fe(e);let t=e.sourceLocale??`en`,n=e.directory??`.autoi18n`,i=e.delivery??`build`,a=e.debug??!1,s={componentProps:e.componentProps,stringMarker:e.stringMarker},c=e.orphanRetentionMinutes??43200,u,f=ke(e.provider,e=>u?.warn(e)),p,m=``,h=new Te,g,_=Promise.resolve(),v,y=()=>e.locale??process.env.AUTOI18N_LOCALE??t;function b(){let n=e.locales.filter(e=>e!==t),r=e.locale??process.env.AUTOI18N_LOCALE;return i===`build`&&r&&p.command!==`serve`?n.filter(e=>e===r):n}async function ee(){let t=[];for await(let n of re(e.include??E,{cwd:p.root,exclude:e.exclude??x}))t.push(j.resolve(p.root,n));return t}async function S(r,i){u=r;let g=i!==void 0&&h.extractions.has(i),_=g?[...h.extractions.keys()]:await ee(),v=/* @__PURE__ */ new Map,y=[];for(let e of _){let t=h.extractions.get(e),n;if(t&&g&&e!==i)n=t;else{let r=await ie(e,`utf8`),i=o(r);n=t?.hash===i?t:{hash:i,messages:be(r,e,s)}}v.set(e,n),y.push({file:e,messages:n.messages})}let x=j.resolve(p.root,n,`locales`),S=b(),C=/* @__PURE__ */ new Map;for(let e of S)C.set(e,await h.readLocale(l(x,e),r.warn));let w=await he({files:y,registry:await h.readRegistry(m,r.warn),orphanRetentionMinutes:c,sourceLocale:t,locales:S,localeConcurrency:Math.max(1,Math.floor(e.localeConcurrency??2)),previous:C,provider:f,debug:a});if(a)for(let e of w.pruned)d(`removed ${e.id} ${JSON.stringify(e.source)} (last seen ${e.lastSeen})`);if(w.pruned.length&&r.info(`autoi18n: removed ${w.pruned.length} registry entries no longer in the source.`),await h.writeRegistry(m,w.registry),w.ambiguous.length){let t=`autoi18n: ${w.ambiguous.length} duplicated texts changed occurrence count, so their IDs may have moved (e.g. "${w.ambiguous[0]}").`;e.onAmbiguous===`error`?r.error(t):r.warn(t)}await h.writeLocale(l(x,t),w.sourceFile),h.legacySidecarsRemoved||=(await Ie(x,[t,...e.locales]),!0);let T=/* @__PURE__ */ new Map([[t,w.sourceFile.messages]]);for(let[e,t]of w.locales)await h.writeLocale(l(x,e),t.file),T.set(e,t.file.messages);for(let e of w.failures.keys())T.set(e,C.get(e)?.messages??{});let E=[...T].filter(([e,t])=>!we(h.catalogs.get(e),t)).map(([e])=>e),D=0;h.fileMessages=/* @__PURE__ */ new Map;for(let{file:e,messages:t}of y)h.fileMessages.set(e,w.messages.slice(D,D+t.length)),D+=t.length;h.extractions=v,h.catalogs=T,h.catalogFiles=Object.fromEntries(e.locales.map(e=>[e,l(x,e)]));for(let[t,n]of w.locales)n.requested&&!f&&e.failOnMissingProvider&&r.error(`Missing ${n.requested} ${t} translations and no provider is configured.`),n.rejected&&r.info(`autoi18n: rejected ${n.rejected} ${t} translations with changed placeholders.`),n.untranslated&&(f?r.warn:r.info)(`autoi18n: ${n.untranslated} ${t} messages have no translation and fall back to the source text.`),n.failed&&r.warn(`autoi18n: ${n.failed} ${t} translation batches failed (${n.failure}); they will be requested again on the next build.`);w.messages.length&&r.info(`autoi18n: indexed ${w.messages.length} messages across ${_.length} files.`);let O=[...w.failures];for(let[e,t]of O.slice(1))r.warn(`autoi18n: ${e} translation failed: ${t.message}`);if(O.length)throw O[0][1];return E}function C(e,t){let n=_.then(()=>S(e,t));return _=n.then(()=>void 0,()=>void 0),n}function w(e){e.length&&g?.ws.send({type:`custom`,event:r,data:{catalogs:Object.fromEntries(e.map(e=>[e,h.catalogs.get(e)]))}})}async function T(e){try{w(await C($(p.logger),e))}catch(e){p.logger.error(`autoi18n: re-translation failed: ${e.message}`)}}function O(){clearTimeout(v),v=setTimeout(()=>void T(),150)}function k(t){if(!/\.[jt]sx?$/.test(t))return!1;let n=j.relative(p.root,t).split(j.sep).join(`/`);return(e.include??E).some(e=>j.matchesGlob(n,e))&&!(e.exclude??x).some(e=>j.matchesGlob(n,e))}return{name:`vite-plugin-autoi18n`,enforce:`pre`,configResolved(r){p=r,m=j.resolve(p.root,n,`registry.json`);let i=y();if(i!==t&&!e.locales.includes(i))throw Error(`autoi18n: locale "${i}" is not one of the configured locales (${e.locales.join(`, `)}).`)},resolveId(e){if(e===`virtual:autoi18n`)return`\0${D}`},load(e){if(e===`\0virtual:autoi18n`)return Ce(h.catalogFiles,t)},configureServer(e){g=e,e.watcher.on(`add`,e=>{k(e)&&O()}),e.watcher.on(`unlink`,e=>{h.fileMessages.has(e)&&O()})},closeBundle(){clearTimeout(v)},async buildStart(){await S({info:e=>this.info(e),warn:e=>this.warn(e),error:e=>this.error(e)})},async transform(e,n){let r=n.split(`?`)[0];if(!h.fileMessages.has(r))return null;let a=o(e);a!==h.hashOf(r)&&await C($(p.logger),r);let s=h.fileMessages.get(r);if(!s||a!==h.hashOf(r))return h.warnedFiles.has(r)||(h.warnedFiles.add(r),this.warn(`autoi18n: ${r} changed while it was being indexed; its messages are left untranslated.`)),null;let c=p.command===`serve`?t:y(),l=xe(e,r,s,c,h.catalogs.get(c)??{},i===`runtime`,t);return{code:l.code,map:l.map}},async handleHotUpdate({file:e}){if(e.startsWith(j.resolve(p.root,n,`locales`)+j.sep))return[];h.fileMessages.has(e)&&await T(e)}}}export{Ne as AnthropicProvider,Ee as FallbackProvider,Pe as GeminiProvider,Me as OpenAICompatibleProvider,N as PartialTranslationError,Le as default,ke as resolveProvider};
4
+ `}async function Y(e){try{let t=await ae(e);return`${t.mtimeMs}:${t.size}`}catch{return}}function Ce(e,t){if(!e)return!1;let n=Object.keys(t);return n.length===Object.keys(e).length&&n.every(n=>e[n]===t[n])}var we=class{extractions=/* @__PURE__ */ new Map;fileMessages=/* @__PURE__ */ new Map;catalogs=/* @__PURE__ */ new Map;catalogFiles={};warnedFiles=/* @__PURE__ */ new Set;legacySidecarsRemoved=!1;localeFiles=/* @__PURE__ */ new Map;registryCache;hashOf(e){return this.extractions.get(e)?.hash}async readLocale(t,n){let r=await Y(t),i=this.localeFiles.get(t);if(r!==void 0&&i?.stamp===r)return i.value;let a=await e(t,n);return r!==void 0&&this.localeFiles.set(t,{stamp:r,value:a}),a}async writeLocale(e,t){await y(e,t);let n=await Y(e);n!==void 0&&this.localeFiles.set(e,{stamp:n,value:t})}async readRegistry(e,t){let n=await Y(e);if(n!==void 0&&this.registryCache?.stamp===n)return this.registryCache.value;let r=await E(e,t);return n!==void 0&&(this.registryCache={stamp:n,value:r}),r}async writeRegistry(e,t){await v(e,t);let n=await Y(e);n!==void 0&&(this.registryCache={stamp:n,value:t})}},Te=class{providers;onFallback;constructor(e,t=()=>{}){this.providers=e,this.onFallback=t}async translate(e){let t={},n=e.messages,r,i;for(let[a,{name:o,provider:s}]of this.providers.entries()){try{Object.assign(t,await s.translate({...e,messages:n}))}catch(e){r??=e,i=e,e instanceof F&&Object.assign(t,e.partial);let s=this.providers[a+1];s&&n.some(e=>t[e.id]===void 0)&&this.onFallback(`${o} failed (${Ee(e)}); sending the rest to ${s.name}.`)}if(n=n.filter(e=>t[e.id]===void 0),!n.length)return t}throw Object.keys(t).length===0?r:new F(`${n.length} messages failed to translate: ${Ee(i)}`,t,n.length)}};const Ee=e=>e instanceof Error?e.message:String(e),De=e=>typeof e==`object`&&!!e&&typeof e.translate==`function`;function Oe(e,t){if(e===void 0)return;if(De(e))return e;let{primary:n,...r}=e,i=[];for(let[e,t]of Object.entries(r))if(t!==void 0){if(!De(t))throw Error(`autoi18n: provider "${e}" must be an object with a translate(input) method.`);i.push({name:e,provider:t})}if(n!==void 0){let e=i.findIndex(e=>e.name===n);if(e<0)throw Error(`autoi18n: primary provider "${n}" is not configured.`);i.unshift(...i.splice(e,1))}if(i.length!==0)return i.length===1?i[0].provider:new Te(i,t)}var X=class extends Error{retryable;retryAfterMs;constructor(e,t,n){super(e),this.retryable=t,this.retryAfterMs=n}};function ke(e,t=Date.now()){if(!e)return;let n=Number(e);if(Number.isFinite(n))return Math.min(Math.max(n,0)*1e3,_);let r=Date.parse(e);if(!Number.isNaN(r))return Math.min(Math.max(r-t,0),_)}const Ae=e=>new Promise(t=>setTimeout(t,e));function Z(e){return`You are a professional software localization engine. Translate UI messages from ${e.sourceLocale} to ${e.targetLocale}. Preserve placeholders exactly. Respect context and UI conventions. Return ONLY a JSON object mapping message IDs to translated strings. Do not translate product names or code.`}function Q(e){return JSON.stringify(e.map(e=>({id:e.id,text:e.source,context:e.context,kind:e.kind})))}async function je(e){let t=e.status===408||e.status===409||e.status===429||e.status>=500;return await e.body?.cancel().catch(()=>void 0),new X(`Translation API failed (${e.status})`,t,ke(e.headers.get(`retry-after`)))}var $=class{options;slots;constructor(e){this.options=e,this.slots=new ue(e.concurrency??4)}async translate(e){let t=Math.max(1,this.options.batchSize??40),n=[];for(let r=0;r<e.messages.length;r+=t)n.push(e.messages.slice(r,r+t));let r=await Promise.allSettled(n.map(t=>this.slots.run(()=>this.translateBatch({...e,messages:t})))),i={},a=[],o=0,s=0;if(r.forEach((e,t)=>{e.status===`fulfilled`?(s++,Object.assign(i,e.value)):(a.push(e.reason),o+=n[t].length)}),!a.length)return i;throw s?new F(`${o} messages failed to translate: ${a[0].message}`,i,o):a[0]}async translateBatch(e){let t=this.options.maxRetries??2,n=this.options.retryDelayMs??500;for(let r=0;;r++)try{return await this.request(e)}catch(e){if(e instanceof X&&!e.retryable||r>=t)throw e;let i=e instanceof X?e.retryAfterMs:void 0;await Ae(i??n*2**r*(.5+Math.random()/2))}}async post(e,t,n){let r=await fetch(e,{method:`POST`,signal:AbortSignal.timeout(this.options.timeoutMs??6e4),headers:{...t,"content-type":`application/json`},body:JSON.stringify(n)});if(!r.ok)throw await je(r);return r.json()}toCatalog(e,t){let n=e?.trim().replace(/^```(?:json)?\s*|\s*```$/g,``);if(!n)throw new X(`Translation provider returned no content`,!0);let r;try{r=JSON.parse(n)}catch{throw new X(`Translation provider returned invalid JSON`,!0)}let i=M.safeParse(a,r);if(!i.success)throw new X(`Translation provider returned a non-object`,!0);let o=new Set(t.map(e=>e.id)),s={};for(let[e,t]of Object.entries(i.output))o.has(e)&&typeof t==`string`&&t.trim()&&(s[e]=t);return s}},Me=class extends ${async request(e){let{apiKey:t,model:n,baseURL:r=O}=this.options,i=M.safeParse(c,await this.post(`${r}/chat/completions`,t?{Authorization:`Bearer ${t}`}:{},{model:n,temperature:0,response_format:{type:`json_object`},messages:[{role:`system`,content:Z(e)},{role:`user`,content:Q(e.messages)}]}));return this.toCatalog(i.success?i.output.choices[0].message.content??void 0:void 0,e.messages)}},Ne=class extends ${async request(e){let t=M.safeParse(s,await this.post(`${this.options.baseURL??`https://api.anthropic.com/v1`}/messages`,{"x-api-key":this.options.apiKey,"anthropic-version":w},{model:this.options.model,max_tokens:this.options.maxTokens??8192,system:Z(e),messages:[{role:`user`,content:Q(e.messages)}]}));return this.toCatalog(t.success?t.output.content.filter(e=>e.type===`text`).map(e=>e.text??``).join(``):void 0,e.messages)}},Pe=class extends ${async request(e){let t=M.safeParse(u,await this.post(`${this.options.baseURL??`https://generativelanguage.googleapis.com/v1beta`}/models/${encodeURIComponent(this.options.model)}:generateContent`,{"x-goog-api-key":this.options.apiKey},{systemInstruction:{parts:[{text:Z(e)}]},contents:[{role:`user`,parts:[{text:Q(e.messages)}]}],generationConfig:{responseMimeType:`application/json`,maxOutputTokens:this.options.maxOutputTokens??8192}}));return this.toCatalog(t.success?(t.output.candidates[0]?.content?.parts??[]).map(e=>e.text??``).join(``):void 0,e.messages)}};function Fe(e){let t=M.safeParse(i,e);if(t.success)return;let n=t.issues.map(e=>`${e.path?.map(e=>e.key).join(`.`)||`options`}: ${e.message}`).join(`; `);throw Error(`autoi18n: invalid options (${n}).`)}const Ie=e=>({info:t=>e.info(t),warn:t=>e.warn(t),error:t=>e.error(t)});async function Le(e,t){await Promise.all(t.map(t=>ie(N.join(e,`${t}.sources.json`),{force:!0})))}function Re(e){Fe(e);let t=e.sourceLocale??`en`,n=e.directory??`.autoi18n`,i=e.delivery??`build`,a=e.debug??!1,s={componentProps:e.componentProps,stringMarker:e.stringMarker},c=e.orphanRetentionMinutes??43200,u,f=Oe(e.provider,e=>u?.warn(e)),p,m=``,h=new we,g,_=Promise.resolve(),v,y=()=>e.locale??process.env.AUTOI18N_LOCALE??t;function b(){let n=e.locales.filter(e=>e!==t),r=e.locale??process.env.AUTOI18N_LOCALE;return i===`build`&&r&&p.command!==`serve`?n.filter(e=>e===r):n}async function S(){let t=[];for await(let n of ne(e.include??D,{cwd:p.root,exclude:e.exclude??x}))t.push(N.resolve(p.root,n));return t}async function C(r,i){u=r;let g=i!==void 0&&h.extractions.has(i),_=g?[...h.extractions.keys()]:await S(),v=/* @__PURE__ */ new Map,y=[];for(let e of _){let t=h.extractions.get(e),n;if(t&&g&&e!==i)n=t;else{let r=await re(e,`utf8`),i=o(r);n=t?.hash===i?t:{hash:i,messages:ye(r,e,s)}}v.set(e,n),y.push({file:e,messages:n.messages})}let x=N.resolve(p.root,n,`locales`),C=b(),w=/* @__PURE__ */ new Map;for(let e of C)w.set(e,await h.readLocale(l(x,e),r.warn));let T=await me({files:y,registry:await h.readRegistry(m,r.warn),orphanRetentionMinutes:c,sourceLocale:t,locales:C,localeConcurrency:Math.max(1,Math.floor(e.localeConcurrency??2)),previous:w,provider:f,debug:a});if(a)for(let e of T.pruned)d(`removed ${e.id} ${JSON.stringify(e.source)} (last seen ${e.lastSeen})`);if(T.pruned.length&&r.info(`autoi18n: removed ${T.pruned.length} registry entries no longer in the source.`),await h.writeRegistry(m,T.registry),T.ambiguous.length){let t=`autoi18n: ${T.ambiguous.length} duplicated texts changed occurrence count, so their IDs may have moved (e.g. "${T.ambiguous[0]}").`;e.onAmbiguous===`error`?r.error(t):r.warn(t)}await h.writeLocale(l(x,t),T.sourceFile),h.legacySidecarsRemoved||=(await Le(x,[t,...e.locales]),!0);let E=/* @__PURE__ */ new Map([[t,T.sourceFile.messages]]);for(let[e,t]of T.locales)await h.writeLocale(l(x,e),t.file),E.set(e,t.file.messages);for(let e of T.failures.keys())E.set(e,w.get(e)?.messages??{});let D=[...E].filter(([e,t])=>!Ce(h.catalogs.get(e),t)).map(([e])=>e),O=0;h.fileMessages=/* @__PURE__ */ new Map;for(let{file:e,messages:t}of y)h.fileMessages.set(e,T.messages.slice(O,O+t.length)),O+=t.length;h.extractions=v,h.catalogs=E,h.catalogFiles=Object.fromEntries(e.locales.map(e=>[e,l(x,e)]));for(let[t,n]of T.locales)n.requested&&!f&&e.failOnMissingProvider&&r.error(`Missing ${n.requested} ${t} translations and no provider is configured.`),n.rejected&&r.info(`autoi18n: rejected ${n.rejected} ${t} translations with changed placeholders.`),n.untranslated&&(f?r.warn:r.info)(`autoi18n: ${n.untranslated} ${t} messages have no translation and fall back to the source text.`),n.failed&&r.warn(`autoi18n: ${n.failed} ${t} translation batches failed (${n.failure}); they will be requested again on the next build.`);T.messages.length&&r.info(`autoi18n: indexed ${T.messages.length} messages across ${_.length} files.`);let k=[...T.failures];for(let[e,t]of k.slice(1))r.warn(`autoi18n: ${e} translation failed: ${t.message}`);if(k.length)throw k[0][1];return D}function w(e,t){let n=_.then(()=>C(e,t));return _=n.then(()=>void 0,()=>void 0),n}function T(e){e.length&&g?.ws.send({type:`custom`,event:r,data:{catalogs:Object.fromEntries(e.map(e=>[e,h.catalogs.get(e)]))}})}async function E(e){try{T(await w(Ie(p.logger),e))}catch(e){p.logger.error(`autoi18n: re-translation failed: ${e.message}`)}}function O(){clearTimeout(v),v=setTimeout(()=>void E(),150)}function A(t){if(!/\.[jt]sx?$/.test(t))return!1;let n=N.relative(p.root,t).split(N.sep).join(`/`);return(e.include??D).some(e=>N.matchesGlob(n,e))&&!(e.exclude??x).some(e=>N.matchesGlob(n,e))}return{name:`vite-plugin-autoi18n`,enforce:`pre`,configResolved(r){p=r,m=N.resolve(p.root,n,`registry.json`);let i=y();if(i!==t&&!e.locales.includes(i))throw Error(`autoi18n: locale "${i}" is not one of the configured locales (${e.locales.join(`, `)}).`)},resolveId(e){if(e===`virtual:autoi18n`)return`\0${k}`},load(e){if(e===`\0virtual:autoi18n`)return Se(h.catalogFiles,t)},configureServer(e){g=e,e.watcher.on(`add`,e=>{A(e)&&O()}),e.watcher.on(`unlink`,e=>{h.fileMessages.has(e)&&O()})},closeBundle(){clearTimeout(v)},async buildStart(){await C({info:e=>this.info(e),warn:e=>this.warn(e),error:e=>this.error(e)})},async transform(e,n){let r=n.split(`?`)[0];if(!h.fileMessages.has(r))return null;let a=o(e);a!==h.hashOf(r)&&await w(Ie(p.logger),r);let s=h.fileMessages.get(r);if(!s||a!==h.hashOf(r))return h.warnedFiles.has(r)||(h.warnedFiles.add(r),this.warn(`autoi18n: ${r} changed while it was being indexed; its messages are left untranslated.`)),null;let c=p.command===`serve`?t:y(),l=be(e,r,s,c,h.catalogs.get(c)??{},i===`runtime`,t);return{code:l.code,map:l.map}},async handleHotUpdate({file:e}){if(e.startsWith(N.resolve(p.root,n,`locales`)+N.sep))return[];h.fileMessages.has(e)&&await E(e)}}}export{Ne as AnthropicProvider,Te as FallbackProvider,Pe as GeminiProvider,Me as OpenAICompatibleProvider,F as PartialTranslationError,Re as default,Oe as resolveProvider};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-ai-auto-i18n",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
4
4
  "type": "module",
5
5
  "description": "Implicit, build-time AI localization for JSX applications",
6
6
  "license": "ISC",
@@ -52,11 +52,11 @@
52
52
  "typecheck": "tsc --noEmit"
53
53
  },
54
54
  "peerDependencies": {
55
+ "rolldown": "^1.2.18",
55
56
  "vite": "^8.0.0"
56
57
  },
57
58
  "dependencies": {
58
59
  "magic-string": "^1.4.5",
59
- "oxc-parser": "^0.158.0",
60
60
  "valibot": "^1.5.0"
61
61
  },
62
62
  "devDependencies": {
@@ -65,6 +65,7 @@
65
65
  "@vitest/coverage-v8": "^5.1.0",
66
66
  "react": "^19.3.1",
67
67
  "react-dom": "^19.3.1",
68
+ "rolldown": "^1.3.2",
68
69
  "rollup-plugin-visualizer": "^7.1.3",
69
70
  "tsdown": "^0.23.7",
70
71
  "typescript": "^7.1.0-beta",