vite-plugin-ai-auto-i18n 0.7.0 → 0.8.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 +4 -4
- package/dist/index.d.ts +49 -56
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -398,13 +398,13 @@ In dev, saving a source file re-indexes the project, translates only new or chan
|
|
|
398
398
|
|
|
399
399
|
### Locale groups
|
|
400
400
|
|
|
401
|
-
The package exports ready-made locale lists for the `locales` option. Each is a plain array of BCP 47 codes,
|
|
401
|
+
The package exports a `locales` object with ready-made locale lists for the `locales` option. Each group is a plain array of BCP 47 codes, listed below by its key on `locales`. Spread a group to keep the shared object unchanged:
|
|
402
402
|
|
|
403
403
|
```ts
|
|
404
|
-
import autoI18n, {
|
|
404
|
+
import autoI18n, { locales } from "vite-plugin-ai-auto-i18n";
|
|
405
405
|
|
|
406
|
-
autoI18n({ locales: [...EUROPE] });
|
|
407
|
-
autoI18n({ locales: ["en", ...ASIA] });
|
|
406
|
+
autoI18n({ locales: [...locales.EUROPE] });
|
|
407
|
+
autoI18n({ locales: ["en", ...locales.ASIA] });
|
|
408
408
|
```
|
|
409
409
|
|
|
410
410
|
| Group | Languages |
|
package/dist/index.d.ts
CHANGED
|
@@ -68,13 +68,29 @@ interface TranslationProvider {
|
|
|
68
68
|
messages: TranslationRequest[];
|
|
69
69
|
}): Promise<Catalog>;
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Named providers. The services listed in the README have typed keys so editors can suggest them; any other name is
|
|
73
|
+
* accepted through the index signature.
|
|
74
|
+
*/
|
|
75
|
+
interface NamedProviders {
|
|
76
|
+
primary?: string;
|
|
77
|
+
anthropic?: TranslationProvider;
|
|
78
|
+
gemini?: TranslationProvider;
|
|
79
|
+
openai?: TranslationProvider;
|
|
80
|
+
mistral?: TranslationProvider;
|
|
81
|
+
groq?: TranslationProvider;
|
|
82
|
+
deepseek?: TranslationProvider;
|
|
83
|
+
openrouter?: TranslationProvider;
|
|
84
|
+
xai?: TranslationProvider;
|
|
85
|
+
cerebras?: TranslationProvider;
|
|
86
|
+
ollama?: TranslationProvider;
|
|
87
|
+
[name: string]: TranslationProvider | string | undefined;
|
|
88
|
+
}
|
|
71
89
|
/**
|
|
72
90
|
* A single provider, or named providers. In the named form, `primary` picks the provider tried first; the rest are
|
|
73
91
|
* fallbacks in declaration order. Entries set to `undefined` are skipped, so unconfigured providers can stay in the map.
|
|
74
92
|
*/
|
|
75
|
-
type ProviderConfig = TranslationProvider |
|
|
76
|
-
primary?: string;
|
|
77
|
-
} & Record<string, TranslationProvider | string | undefined>);
|
|
93
|
+
type ProviderConfig = TranslationProvider | NamedProviders;
|
|
78
94
|
/**
|
|
79
95
|
* Some batches of a translation request failed while others succeeded. `partial` holds the translations
|
|
80
96
|
* that did come back, so the caller can keep them and report the rest.
|
|
@@ -214,58 +230,35 @@ interface GeminiOptions extends BatchedProviderOptions {
|
|
|
214
230
|
export declare class GeminiProvider extends BatchedProvider<GeminiOptions> {
|
|
215
231
|
protected request(input: TranslateInput): Promise<Catalog>;
|
|
216
232
|
}
|
|
217
|
-
/**
|
|
218
|
-
export declare const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
export declare const NORTH_AMERICA: string[];
|
|
247
|
-
/** Central America and the Caribbean: Spanish, English, French, Haitian Creole, Dutch. */
|
|
248
|
-
export declare const CENTRAL_AMERICA_CARIBBEAN: string[];
|
|
249
|
-
/** South America: Spanish, Portuguese, Quechua, Guarani, Aymara. */
|
|
250
|
-
export declare const SOUTH_AMERICA: string[];
|
|
251
|
-
/** Americas: North, Central, Caribbean and South America. */
|
|
252
|
-
export declare const AMERICAS: string[];
|
|
253
|
-
/** Oceania: English, Maori, Samoan, Tongan. */
|
|
254
|
-
export declare const OCEANIA: string[];
|
|
255
|
-
/** ASEAN: Indonesian, Malay, Burmese, Khmer, Lao, Filipino, Thai, Vietnamese, plus English as working language. */
|
|
256
|
-
export declare const ASEAN: string[];
|
|
257
|
-
/** Mercosur: Spanish, Portuguese, Guarani. */
|
|
258
|
-
export declare const MERCOSUR: string[];
|
|
259
|
-
/** Gulf Cooperation Council: Arabic and English, the working language of most member states. */
|
|
260
|
-
export declare const GCC: string[];
|
|
261
|
-
/** Commonwealth of Independent States: Russian, Belarusian, Armenian, Azerbaijani, Kazakh, Kyrgyz, Tajik, Uzbek. */
|
|
262
|
-
export declare const CIS: string[];
|
|
263
|
-
/** G7 official languages: English, French, German, Italian, Japanese. */
|
|
264
|
-
export declare const G7: string[];
|
|
265
|
-
/** Francophonie: French and major French-speaking African and Caribbean languages. */
|
|
266
|
-
export declare const FRANCOPHONE: string[];
|
|
267
|
-
/** Most widely spoken languages worldwide by total speakers. */
|
|
268
|
-
export declare const WORLDWIDE_POPULAR: string[];
|
|
233
|
+
/** Ready-made locale lists for the `locales` option, grouped by region or organization: `locales: [...locales.EUROPE]`. */
|
|
234
|
+
export declare const locales: {
|
|
235
|
+
EU: string[];
|
|
236
|
+
EUROPE: string[];
|
|
237
|
+
NORDIC: string[];
|
|
238
|
+
BALTIC: string[];
|
|
239
|
+
BALKANS: string[];
|
|
240
|
+
BENELUX: string[];
|
|
241
|
+
EAST_ASIA: string[];
|
|
242
|
+
SOUTH_ASIA: string[];
|
|
243
|
+
SOUTHEAST_ASIA: string[];
|
|
244
|
+
CENTRAL_ASIA: string[];
|
|
245
|
+
CAUCASUS: string[];
|
|
246
|
+
ASIA: string[];
|
|
247
|
+
MIDDLE_EAST: string[];
|
|
248
|
+
AFRICA: string[];
|
|
249
|
+
NORTH_AMERICA: string[];
|
|
250
|
+
CENTRAL_AMERICA_CARIBBEAN: string[];
|
|
251
|
+
SOUTH_AMERICA: string[];
|
|
252
|
+
AMERICAS: string[];
|
|
253
|
+
OCEANIA: string[];
|
|
254
|
+
ASEAN: string[];
|
|
255
|
+
MERCOSUR: string[];
|
|
256
|
+
GCC: string[];
|
|
257
|
+
CIS: string[];
|
|
258
|
+
G7: string[];
|
|
259
|
+
FRANCOPHONE: string[];
|
|
260
|
+
WORLDWIDE_POPULAR: string[];
|
|
261
|
+
};
|
|
269
262
|
export interface AutoI18nOptions {
|
|
270
263
|
locales: string[];
|
|
271
264
|
sourceLocale?: string;
|
|
@@ -317,4 +310,4 @@ export interface AutoI18nOptions {
|
|
|
317
310
|
* ```
|
|
318
311
|
*/
|
|
319
312
|
export default function autoI18n(options: AutoI18nOptions): Plugin;
|
|
320
|
-
export type { AnthropicOptions, Catalog, ExtractedMessage, GeminiOptions, MessageKind, OpenAICompatibleOptions, ProviderConfig, Registry, RegistryEntry, ResolvedMessage, TranslationProvider, TranslationRequest };
|
|
313
|
+
export type { AnthropicOptions, Catalog, ExtractedMessage, GeminiOptions, MessageKind, NamedProviders, OpenAICompatibleOptions, ProviderConfig, Registry, RegistryEntry, ResolvedMessage, TranslationProvider, TranslationRequest };
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,4 @@ import{t as e}from"./debug-DVrC55pP.js";import{A as t,C as n,D as r,E as i,F as
|
|
|
3
3
|
`}function K(e){return JSON.stringify(e).replace(/[<>&\u2028\u2029]/g,e=>`\\u${e.charCodeAt(0).toString(16).padStart(4,`0`)}`)}function Fe(e,t){return[`const loaders = {\n${Object.entries(e).map(([e,t])=>` ${K(e)}: () => import(${K(t)}).then(m => (m.default ?? m).messages)`).join(`,
|
|
4
4
|
`)}\n};`,`const sourceLocale = ${K(t)};`,`const resolveLocale = ${j.toString()};`,`const createLocaleLoader = ${A.toString()};`,`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; }`,`// 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(`
|
|
5
5
|
`)+`
|
|
6
|
-
`}async function q(e){try{let t=await ae(e);return`${t.mtimeMs}:${t.size}`}catch{return}}var Ie=class{entries=/* @__PURE__ */ new Map;async read(e,t){let n=await q(e),r=this.entries.get(e);if(n!==void 0&&r?.stamp===n)return r.value;let i=await t();return n!==void 0&&this.entries.set(e,{stamp:n,value:i,written:!1}),i}async holds(e,t,n){let r=this.entries.get(e);return!r?.written||!n(r.value,t)?!1:await q(e)===r.stamp}async write(e,t,n){await n();let r=await q(e);r!==void 0&&this.entries.set(e,{stamp:r,value:t,written:!0})}};function J(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])}function Le(e,t){return J(e.messages,t.messages)&&J(e.sources,t.sources)}var Re=class{extractions=/* @__PURE__ */ new Map;parsedSources=/* @__PURE__ */ new Map;fileMessages=/* @__PURE__ */ new Map;catalogs=/* @__PURE__ */ new Map;catalogFiles={};warnedFiles=/* @__PURE__ */ new Set;legacySidecarsRemoved=!1;locales=new Ie;registries=new Ie;hashOf(e){return this.extractions.get(e)?.hash}readLocale(e,t){return this.locales.read(e,()=>y(e,t))}async writeLocale(e,t){if(!await this.locales.holds(e,t,Le))return this.locales.write(e,t,()=>c(e,t))}readRegistry(e,t){return this.registries.read(e,()=>ee(e,t))}writeRegistry(e,t){return this.registries.write(e,t,()=>v(e,t))}},ze=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 (${Be(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: ${Be(i)}`,t,n.length)}};const Be=e=>e instanceof Error?e.message:String(e),Ve=e=>typeof e==`object`&&!!e&&typeof e.translate==`function`;function He(e,t){if(e===void 0)return;if(Ve(e))return e;let{primary:n,...r}=e,i=[];for(let[e,t]of Object.entries(r))if(t!==void 0){if(!Ve(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 ze(i,t)}var Y=class extends Error{retryable;retryAfterMs;constructor(e,t,n){super(e),this.retryable=t,this.retryAfterMs=n}};function Ue(e,t=Date.now()){if(!e)return;let n=Number(e);if(Number.isFinite(n))return Math.min(Math.max(n,0)*1e3,D);let r=Date.parse(e);if(!Number.isNaN(r))return Math.min(Math.max(r-t,0),D)}const We=e=>new Promise(t=>setTimeout(t,e));function X(e){return`You are a professional software localization engine. Translate UI messages from ${e.sourceLocale} to ${e.targetLocale}. Preserve placeholders exactly. Messages of kind "mixed" are ICU MessageFormat: keep each {name} argument unchanged, keep apostrophes and braces valid ICU (a literal apostrophe is written as two apostrophes, and literal braces are quoted), and use plural or select only when the target language needs them. Respect context and UI conventions. Return ONLY a JSON object mapping message IDs to translated strings. Do not translate product names or code.`}function Z(e){return JSON.stringify(e.map(e=>({id:e.id,text:e.source,context:e.context,kind:e.kind})))}async function Ge(e){let t=e.status===408||e.status===409||e.status===429||e.status>=500;return await e.body?.cancel().catch(()=>void 0),new Y(`Translation API failed (${e.status})`,t,Ue(e.headers.get(`retry-after`)))}var Q=class{options;slots;constructor(e){this.options=e,this.slots=new fe(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 Y&&!e.retryable||r>=t)throw e;let i=e instanceof Y?e.retryAfterMs:void 0;await We(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 Ge(r);return r.json()}async complete(e,t,n,r,i,a){let o=r(await this.post(e,t,n));return this.toCatalog(o instanceof M?void 0:i(o),a.messages)}toCatalog(e,t){let n=e?.trim().replace(/^```(?:json)?\s*|\s*```$/g,``);if(!n)throw new Y(`Translation provider returned no content`,!0);let r;try{r=JSON.parse(n)}catch{throw new Y(`Translation provider returned invalid JSON`,!0)}let i=s(r);if(i instanceof M)throw new Y(`Translation provider returned a non-object`,!0);let a=new Set(t.map(e=>e.id)),o={};for(let[e,t]of Object.entries(i))a.has(e)&&typeof t==`string`&&t.trim()&&(o[e]=t);return o}},Ke=class extends Q{async request(e){let{apiKey:t,model:n,baseURL:r=h}=this.options;return this.complete(`${r}/chat/completions`,t?{Authorization:`Bearer ${t}`}:{},{model:n,temperature:0,response_format:{type:`json_object`},messages:[{role:`system`,content:X(e)},{role:`user`,content:Z(e.messages)}]},d,e=>e.choices[0].message.content??void 0,e)}},qe=class extends Q{async request(e){return this.complete(`${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:X(e),messages:[{role:`user`,content:Z(e.messages)}]},l,e=>e.content.filter(e=>e.type===`text`).map(e=>e.text??``).join(``),e)}},Je=class extends Q{async request(e){return this.complete(`${this.options.baseURL??`https://generativelanguage.googleapis.com/v1beta`}/models/${encodeURIComponent(this.options.model)}:generateContent`,{"x-goog-api-key":this.options.apiKey},{systemInstruction:{parts:[{text:X(e)}]},contents:[{role:`user`,parts:[{text:Z(e.messages)}]}],generationConfig:{responseMimeType:`application/json`,maxOutputTokens:this.options.maxOutputTokens??8192}},a,e=>(e.candidates[0]?.content?.parts??[]).map(e=>e.text??``).join(``),e)}};const $=[`bg`,`cs`,`da`,`de`,`el`,`es`,`et`,`fi`,`fr`,`ga`,`hr`,`hu`,`it`,`lt`,`lv`,`mt`,`nl`,`pl`,`pt`,`ro`,`sk`,`sl`,`sv`],Ye=[`da`,`fi`,`is`,`no`,`sv`],Xe=[`et`,`lv`,`lt`],Ze=[`bg`,`bs`,`el`,`hr`,`mk`,`ro`,`sq`,`sr`,`sl`],Qe=[`nl`,`fr`,`lb`],$e=[.../* @__PURE__ */ new Set([...$,`no`,`is`,`sq`,`bs`,`mk`,`sr`,`uk`,`ru`,`be`,`ca`])],et=[`zh`,`ja`,`ko`,`mn`],tt=[`hi`,`bn`,`ur`,`pa`,`ta`,`te`,`mr`,`gu`,`kn`,`ml`,`ne`,`si`],nt=[`id`,`ms`,`my`,`km`,`lo`,`fil`,`th`,`vi`],rt=[`kk`,`ky`,`tg`,`tk`,`uz`],it=[`hy`,`az`,`ka`],at=[.../* @__PURE__ */ new Set([...et,...tt,...nt,...rt,...it])],ot=[`ar`,`fa`,`he`,`tr`,`ku`],st=[`ar`,`fr`,`pt`,`sw`,`am`,`ha`,`yo`,`ig`,`zu`,`xh`,`af`,`so`,`rw`,`wo`],ct=[`en`,`fr`,`es`],lt=[`es`,`en`,`fr`,`ht`,`nl`],ut=[`es`,`pt`,`qu`,`gn`,`ay`],dt=[.../* @__PURE__ */ new Set([...ct,...lt,...ut])],ft=[`en`,`mi`,`sm`,`to`],pt=[`id`,`ms`,`my`,`km`,`lo`,`fil`,`th`,`vi`,`en`],mt=[`es`,`pt`,`gn`],ht=[`ar`,`en`],gt=[`ru`,`be`,`hy`,`az`,`kk`,`ky`,`tg`,`uz`],_t=[`en`,`fr`,`de`,`it`,`ja`],vt=[`fr`,`ht`,`wo`,`ln`,`rw`],yt=[`en`,`zh`,`hi`,`es`,`ar`,`bn`,`pt`,`ru`,`ja`,`de`,`fr`,`ko`,`it`,`tr`,`vi`,`id`,`ur`];function bt(e){let t=o(e);if(!(t instanceof M))return;let n=t.map(e=>`${e.path.join(`.`)||`options`}: ${e.message}`).join(`; `);throw Error(`autoi18n: invalid options (${n}).`)}const xt=e=>({info:t=>e.info(t),warn:t=>e.warn(t),error:t=>e.error(t)});async function St(e,t){await Promise.all(t.map(t=>N(P.join(e,`${t}.sources.json`),{force:!0})))}function Ct(t){bt(t);let n=t.sourceLocale??`en`,i=t.directory??`.autoi18n`,a=t.delivery??`build`,o=t.debug??!1,s={componentProps:t.componentProps,stringMarker:t.stringMarker},c=t.orphanRetentionMinutes??43200,l,u=He(t.provider,e=>l?.warn(e)),d,p=``,h=new Re,g,_=Promise.resolve(),v,y=()=>t.locale??process.env.AUTOI18N_LOCALE,C=()=>y()??n,w=()=>P.resolve(d.root,i,`locales`),T=t.include??te,E=t.exclude??x;function ee(){let e=t.locales.filter(e=>e!==n),r=y();return a===`build`&&r&&d.command!==`serve`?e.filter(e=>e===r):e}async function D(){let e=[];for await(let t of re(T,{cwd:d.root,exclude:E}))e.push(P.resolve(d.root,t));return e}async function O(e,t,n){let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map,a=[];for(let o of e){let e=h.extractions.get(o),c;if(e&&t&&o!==n)c=e;else{let t=await ie(o,`utf8`),n=f(t);if(e?.hash===n)c=e;else{let e=L(t,o);c={hash:n,messages:je(t,o,s,e)},i.set(o,e)}}r.set(o,c),a.push({file:o,messages:c.messages})}return{nextExtractions:r,nextParsed:i,indexed:a}}function ne(e,n){for(let[r,i]of n.locales)i.requested&&!u&&t.failOnMissingProvider&&e.error(`Missing ${i.requested} ${r} translations and no provider is configured.`),i.rejected&&e.info(`autoi18n: rejected ${i.rejected} ${r} translations with changed placeholders.`),i.untranslated&&(u?e.warn:e.info)(`autoi18n: ${i.untranslated} ${r} messages have no translation and fall back to the source text.`),i.failed&&e.warn(`autoi18n: ${i.failed} ${r} translation batches failed (${i.failure}); they will be requested again on the next build.`)}async function k(r,i){l=r;let a=i!==void 0&&h.extractions.has(i),s=a?[...h.extractions.keys()]:await D(),{nextExtractions:d,nextParsed:f,indexed:m}=await O(s,a,i),g=w(),_=ee(),v=/* @__PURE__ */ new Map;for(let e of _)v.set(e,await h.readLocale(b(g,e),r.warn));let y=await be({files:m,registry:await h.readRegistry(p,r.warn),orphanRetentionMinutes:c,sourceLocale:n,locales:_,localeConcurrency:Math.max(1,Math.floor(t.localeConcurrency??2)),previous:v,provider:u,debug:o});if(o)for(let t of y.pruned)e(`removed ${t.id} ${JSON.stringify(t.source)} (last seen ${t.lastSeen})`);if(y.pruned.length&&r.info(`autoi18n: removed ${y.pruned.length} registry entries no longer in the source.`),await h.writeRegistry(p,y.registry),y.ambiguous.length){let e=`autoi18n: ${y.ambiguous.length} duplicated texts changed occurrence count, so their IDs may have moved (e.g. "${y.ambiguous[0]}").`;t.onAmbiguous===`error`?r.error(e):r.warn(e)}await h.writeLocale(b(g,n),y.sourceFile),h.legacySidecarsRemoved||=(await St(g,[n,...t.locales]),!0);let x=/* @__PURE__ */ new Map([[n,y.sourceFile.messages]]);for(let[e,t]of y.locales)await h.writeLocale(b(g,e),t.file),x.set(e,t.file.messages);for(let e of y.failures.keys())x.set(e,v.get(e)?.messages??{});let S=[...x].filter(([e,t])=>!J(h.catalogs.get(e),t)).map(([e])=>e),C=0;h.fileMessages=/* @__PURE__ */ new Map;for(let{file:e,messages:t}of m)h.fileMessages.set(e,y.messages.slice(C,C+t.length)),C+=t.length;h.extractions=d,h.parsedSources=f,h.catalogs=x,h.catalogFiles=Object.fromEntries(t.locales.map(e=>[e,b(g,e)])),ne(r,y),y.messages.length&&r.info(`autoi18n: indexed ${y.messages.length} messages across ${s.length} files.`);let T=[...y.failures];for(let[e,t]of T.slice(1))r.warn(`autoi18n: ${e} translation failed: ${t.message}`);if(T.length)throw T[0][1];return S}function A(e,t){let n=_.then(()=>k(e,t));return _=n.then(()=>void 0,()=>void 0),n}function j(e){e.length&&g?.ws.send({type:`custom`,event:r,data:{catalogs:Object.fromEntries(e.map(e=>[e,h.catalogs.get(e)]))}})}async function M(e){try{j(await A(xt(d.logger),e))}catch(e){d.logger.error(`autoi18n: re-translation failed: ${e.message}`)}}function N(){clearTimeout(v),v=setTimeout(()=>void M(),150)}function ae(e){if(!/\.[jt]sx?$/.test(e))return!1;let t=P.relative(d.root,e).split(P.sep).join(`/`);return T.some(e=>P.matchesGlob(t,e))&&!E.some(e=>P.matchesGlob(t,e))}return{name:`vite-plugin-autoi18n`,enforce:`pre`,configResolved(e){d=e,p=P.resolve(d.root,i,`registry.json`);let r=C();if(r!==n&&!t.locales.includes(r))throw Error(`autoi18n: locale "${r}" is not one of the configured locales (${t.locales.join(`, `)}).`)},resolveId(e,t){if(e===`virtual:autoi18n`)return`\0${m}`;if(e===`virtual:autoi18n-format`)return`\0${S}`;if(t===`\0virtual:autoi18n-format`&&e===`intl-messageformat`)return this.resolve(e,oe(import.meta.url),{skipSelf:!0})},load(e){if(e===`\0virtual:autoi18n`)return Fe(h.catalogFiles,n);if(e===`\0virtual:autoi18n-format`)return Pe()},configureServer(e){g=e,e.watcher.on(`add`,e=>{ae(e)&&N()}),e.watcher.on(`unlink`,e=>{h.fileMessages.has(e)&&N()})},closeBundle(){clearTimeout(v)},async buildStart(){await k({info:e=>this.info(e),warn:e=>this.warn(e),error:e=>this.error(e)})},async transform(e,t){let r=t.split(`?`)[0];if(!h.fileMessages.has(r))return null;let i=f(e);i!==h.hashOf(r)&&await A(xt(d.logger),r);let o=h.fileMessages.get(r);if(!o||i!==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 s=d.command===`serve`?n:C(),c=h.parsedSources.get(r);h.parsedSources.delete(r);let l=Me(e,r,o,s,h.catalogs.get(s)??{},a===`runtime`,n,c);return{code:l.code,map:l.map}},async handleHotUpdate({file:e}){if(e.startsWith(w()+P.sep))return[];h.fileMessages.has(e)&&await M(e)}}}export{st as AFRICA,dt as AMERICAS,pt as ASEAN,at as ASIA,qe as AnthropicProvider,Ze as BALKANS,Xe as BALTIC,Qe as BENELUX,it as CAUCASUS,lt as CENTRAL_AMERICA_CARIBBEAN,rt as CENTRAL_ASIA,gt as CIS,et as EAST_ASIA,$ as EU,$e as EUROPE,vt as FRANCOPHONE,ze as FallbackProvider,_t as G7,ht as GCC,Je as GeminiProvider,mt as MERCOSUR,ot as MIDDLE_EAST,Ye as NORDIC,ct as NORTH_AMERICA,ft as OCEANIA,Ke as OpenAICompatibleProvider,F as PartialTranslationError,nt as SOUTHEAST_ASIA,ut as SOUTH_AMERICA,tt as SOUTH_ASIA,yt as WORLDWIDE_POPULAR,Ct as default,He as resolveProvider};
|
|
6
|
+
`}async function q(e){try{let t=await ae(e);return`${t.mtimeMs}:${t.size}`}catch{return}}var Ie=class{entries=/* @__PURE__ */ new Map;async read(e,t){let n=await q(e),r=this.entries.get(e);if(n!==void 0&&r?.stamp===n)return r.value;let i=await t();return n!==void 0&&this.entries.set(e,{stamp:n,value:i,written:!1}),i}async holds(e,t,n){let r=this.entries.get(e);return!r?.written||!n(r.value,t)?!1:await q(e)===r.stamp}async write(e,t,n){await n();let r=await q(e);r!==void 0&&this.entries.set(e,{stamp:r,value:t,written:!0})}};function J(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])}function Le(e,t){return J(e.messages,t.messages)&&J(e.sources,t.sources)}var Re=class{extractions=/* @__PURE__ */ new Map;parsedSources=/* @__PURE__ */ new Map;fileMessages=/* @__PURE__ */ new Map;catalogs=/* @__PURE__ */ new Map;catalogFiles={};warnedFiles=/* @__PURE__ */ new Set;legacySidecarsRemoved=!1;locales=new Ie;registries=new Ie;hashOf(e){return this.extractions.get(e)?.hash}readLocale(e,t){return this.locales.read(e,()=>y(e,t))}async writeLocale(e,t){if(!await this.locales.holds(e,t,Le))return this.locales.write(e,t,()=>c(e,t))}readRegistry(e,t){return this.registries.read(e,()=>ee(e,t))}writeRegistry(e,t){return this.registries.write(e,t,()=>v(e,t))}},ze=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 (${Be(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: ${Be(i)}`,t,n.length)}};const Be=e=>e instanceof Error?e.message:String(e),Ve=e=>typeof e==`object`&&!!e&&typeof e.translate==`function`;function He(e,t){if(e===void 0)return;if(Ve(e))return e;let{primary:n,...r}=e,i=[];for(let[e,t]of Object.entries(r))if(t!==void 0){if(!Ve(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 ze(i,t)}var Y=class extends Error{retryable;retryAfterMs;constructor(e,t,n){super(e),this.retryable=t,this.retryAfterMs=n}};function Ue(e,t=Date.now()){if(!e)return;let n=Number(e);if(Number.isFinite(n))return Math.min(Math.max(n,0)*1e3,D);let r=Date.parse(e);if(!Number.isNaN(r))return Math.min(Math.max(r-t,0),D)}const We=e=>new Promise(t=>setTimeout(t,e));function X(e){return`You are a professional software localization engine. Translate UI messages from ${e.sourceLocale} to ${e.targetLocale}. Preserve placeholders exactly. Messages of kind "mixed" are ICU MessageFormat: keep each {name} argument unchanged, keep apostrophes and braces valid ICU (a literal apostrophe is written as two apostrophes, and literal braces are quoted), and use plural or select only when the target language needs them. Respect context and UI conventions. Return ONLY a JSON object mapping message IDs to translated strings. Do not translate product names or code.`}function Z(e){return JSON.stringify(e.map(e=>({id:e.id,text:e.source,context:e.context,kind:e.kind})))}async function Ge(e){let t=e.status===408||e.status===409||e.status===429||e.status>=500;return await e.body?.cancel().catch(()=>void 0),new Y(`Translation API failed (${e.status})`,t,Ue(e.headers.get(`retry-after`)))}var Q=class{options;slots;constructor(e){this.options=e,this.slots=new fe(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 Y&&!e.retryable||r>=t)throw e;let i=e instanceof Y?e.retryAfterMs:void 0;await We(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 Ge(r);return r.json()}async complete(e,t,n,r,i,a){let o=r(await this.post(e,t,n));return this.toCatalog(o instanceof M?void 0:i(o),a.messages)}toCatalog(e,t){let n=e?.trim().replace(/^```(?:json)?\s*|\s*```$/g,``);if(!n)throw new Y(`Translation provider returned no content`,!0);let r;try{r=JSON.parse(n)}catch{throw new Y(`Translation provider returned invalid JSON`,!0)}let i=s(r);if(i instanceof M)throw new Y(`Translation provider returned a non-object`,!0);let a=new Set(t.map(e=>e.id)),o={};for(let[e,t]of Object.entries(i))a.has(e)&&typeof t==`string`&&t.trim()&&(o[e]=t);return o}},Ke=class extends Q{async request(e){let{apiKey:t,model:n,baseURL:r=h}=this.options;return this.complete(`${r}/chat/completions`,t?{Authorization:`Bearer ${t}`}:{},{model:n,temperature:0,response_format:{type:`json_object`},messages:[{role:`system`,content:X(e)},{role:`user`,content:Z(e.messages)}]},d,e=>e.choices[0].message.content??void 0,e)}},qe=class extends Q{async request(e){return this.complete(`${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:X(e),messages:[{role:`user`,content:Z(e.messages)}]},l,e=>e.content.filter(e=>e.type===`text`).map(e=>e.text??``).join(``),e)}},Je=class extends Q{async request(e){return this.complete(`${this.options.baseURL??`https://generativelanguage.googleapis.com/v1beta`}/models/${encodeURIComponent(this.options.model)}:generateContent`,{"x-goog-api-key":this.options.apiKey},{systemInstruction:{parts:[{text:X(e)}]},contents:[{role:`user`,parts:[{text:Z(e.messages)}]}],generationConfig:{responseMimeType:`application/json`,maxOutputTokens:this.options.maxOutputTokens??8192}},a,e=>(e.candidates[0]?.content?.parts??[]).map(e=>e.text??``).join(``),e)}};const Ye=[`bg`,`cs`,`da`,`de`,`el`,`es`,`et`,`fi`,`fr`,`ga`,`hr`,`hu`,`it`,`lt`,`lv`,`mt`,`nl`,`pl`,`pt`,`ro`,`sk`,`sl`,`sv`],Xe=[`da`,`fi`,`is`,`no`,`sv`],Ze=[`et`,`lv`,`lt`],Qe=[`bg`,`bs`,`el`,`hr`,`mk`,`ro`,`sq`,`sr`,`sl`],$e=[`nl`,`fr`,`lb`],et=[.../* @__PURE__ */ new Set([...Ye,`no`,`is`,`sq`,`bs`,`mk`,`sr`,`uk`,`ru`,`be`,`ca`])],tt=[`zh`,`ja`,`ko`,`mn`],$=[`hi`,`bn`,`ur`,`pa`,`ta`,`te`,`mr`,`gu`,`kn`,`ml`,`ne`,`si`],nt=[`id`,`ms`,`my`,`km`,`lo`,`fil`,`th`,`vi`],rt=[`kk`,`ky`,`tg`,`tk`,`uz`],it=[`hy`,`az`,`ka`],at=[.../* @__PURE__ */ new Set([...tt,...$,...nt,...rt,...it])],ot=[`ar`,`fa`,`he`,`tr`,`ku`],st=[`ar`,`fr`,`pt`,`sw`,`am`,`ha`,`yo`,`ig`,`zu`,`xh`,`af`,`so`,`rw`,`wo`],ct=[`en`,`fr`,`es`],lt=[`es`,`en`,`fr`,`ht`,`nl`],ut=[`es`,`pt`,`qu`,`gn`,`ay`],dt={EU:Ye,EUROPE:et,NORDIC:Xe,BALTIC:Ze,BALKANS:Qe,BENELUX:$e,EAST_ASIA:tt,SOUTH_ASIA:$,SOUTHEAST_ASIA:nt,CENTRAL_ASIA:rt,CAUCASUS:it,ASIA:at,MIDDLE_EAST:ot,AFRICA:st,NORTH_AMERICA:ct,CENTRAL_AMERICA_CARIBBEAN:lt,SOUTH_AMERICA:ut,AMERICAS:[.../* @__PURE__ */ new Set([...ct,...lt,...ut])],OCEANIA:[`en`,`mi`,`sm`,`to`],ASEAN:[`id`,`ms`,`my`,`km`,`lo`,`fil`,`th`,`vi`,`en`],MERCOSUR:[`es`,`pt`,`gn`],GCC:[`ar`,`en`],CIS:[`ru`,`be`,`hy`,`az`,`kk`,`ky`,`tg`,`uz`],G7:[`en`,`fr`,`de`,`it`,`ja`],FRANCOPHONE:[`fr`,`ht`,`wo`,`ln`,`rw`],WORLDWIDE_POPULAR:[`en`,`zh`,`hi`,`es`,`ar`,`bn`,`pt`,`ru`,`ja`,`de`,`fr`,`ko`,`it`,`tr`,`vi`,`id`,`ur`]};function ft(e){let t=o(e);if(!(t instanceof M))return;let n=t.map(e=>`${e.path.join(`.`)||`options`}: ${e.message}`).join(`; `);throw Error(`autoi18n: invalid options (${n}).`)}const pt=e=>({info:t=>e.info(t),warn:t=>e.warn(t),error:t=>e.error(t)});async function mt(e,t){await Promise.all(t.map(t=>N(P.join(e,`${t}.sources.json`),{force:!0})))}function ht(t){ft(t);let n=t.sourceLocale??`en`,i=t.directory??`.autoi18n`,a=t.delivery??`build`,o=t.debug??!1,s={componentProps:t.componentProps,stringMarker:t.stringMarker},c=t.orphanRetentionMinutes??43200,l,u=He(t.provider,e=>l?.warn(e)),d,p=``,h=new Re,g,_=Promise.resolve(),v,y=()=>t.locale??process.env.AUTOI18N_LOCALE,C=()=>y()??n,w=()=>P.resolve(d.root,i,`locales`),T=t.include??te,E=t.exclude??x;function ee(){let e=t.locales.filter(e=>e!==n),r=y();return a===`build`&&r&&d.command!==`serve`?e.filter(e=>e===r):e}async function D(){let e=[];for await(let t of re(T,{cwd:d.root,exclude:E}))e.push(P.resolve(d.root,t));return e}async function O(e,t,n){let r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map,a=[];for(let o of e){let e=h.extractions.get(o),c;if(e&&t&&o!==n)c=e;else{let t=await ie(o,`utf8`),n=f(t);if(e?.hash===n)c=e;else{let e=L(t,o);c={hash:n,messages:je(t,o,s,e)},i.set(o,e)}}r.set(o,c),a.push({file:o,messages:c.messages})}return{nextExtractions:r,nextParsed:i,indexed:a}}function ne(e,n){for(let[r,i]of n.locales)i.requested&&!u&&t.failOnMissingProvider&&e.error(`Missing ${i.requested} ${r} translations and no provider is configured.`),i.rejected&&e.info(`autoi18n: rejected ${i.rejected} ${r} translations with changed placeholders.`),i.untranslated&&(u?e.warn:e.info)(`autoi18n: ${i.untranslated} ${r} messages have no translation and fall back to the source text.`),i.failed&&e.warn(`autoi18n: ${i.failed} ${r} translation batches failed (${i.failure}); they will be requested again on the next build.`)}async function k(r,i){l=r;let a=i!==void 0&&h.extractions.has(i),s=a?[...h.extractions.keys()]:await D(),{nextExtractions:d,nextParsed:f,indexed:m}=await O(s,a,i),g=w(),_=ee(),v=/* @__PURE__ */ new Map;for(let e of _)v.set(e,await h.readLocale(b(g,e),r.warn));let y=await be({files:m,registry:await h.readRegistry(p,r.warn),orphanRetentionMinutes:c,sourceLocale:n,locales:_,localeConcurrency:Math.max(1,Math.floor(t.localeConcurrency??2)),previous:v,provider:u,debug:o});if(o)for(let t of y.pruned)e(`removed ${t.id} ${JSON.stringify(t.source)} (last seen ${t.lastSeen})`);if(y.pruned.length&&r.info(`autoi18n: removed ${y.pruned.length} registry entries no longer in the source.`),await h.writeRegistry(p,y.registry),y.ambiguous.length){let e=`autoi18n: ${y.ambiguous.length} duplicated texts changed occurrence count, so their IDs may have moved (e.g. "${y.ambiguous[0]}").`;t.onAmbiguous===`error`?r.error(e):r.warn(e)}await h.writeLocale(b(g,n),y.sourceFile),h.legacySidecarsRemoved||=(await mt(g,[n,...t.locales]),!0);let x=/* @__PURE__ */ new Map([[n,y.sourceFile.messages]]);for(let[e,t]of y.locales)await h.writeLocale(b(g,e),t.file),x.set(e,t.file.messages);for(let e of y.failures.keys())x.set(e,v.get(e)?.messages??{});let S=[...x].filter(([e,t])=>!J(h.catalogs.get(e),t)).map(([e])=>e),C=0;h.fileMessages=/* @__PURE__ */ new Map;for(let{file:e,messages:t}of m)h.fileMessages.set(e,y.messages.slice(C,C+t.length)),C+=t.length;h.extractions=d,h.parsedSources=f,h.catalogs=x,h.catalogFiles=Object.fromEntries(t.locales.map(e=>[e,b(g,e)])),ne(r,y),y.messages.length&&r.info(`autoi18n: indexed ${y.messages.length} messages across ${s.length} files.`);let T=[...y.failures];for(let[e,t]of T.slice(1))r.warn(`autoi18n: ${e} translation failed: ${t.message}`);if(T.length)throw T[0][1];return S}function A(e,t){let n=_.then(()=>k(e,t));return _=n.then(()=>void 0,()=>void 0),n}function j(e){e.length&&g?.ws.send({type:`custom`,event:r,data:{catalogs:Object.fromEntries(e.map(e=>[e,h.catalogs.get(e)]))}})}async function M(e){try{j(await A(pt(d.logger),e))}catch(e){d.logger.error(`autoi18n: re-translation failed: ${e.message}`)}}function N(){clearTimeout(v),v=setTimeout(()=>void M(),150)}function ae(e){if(!/\.[jt]sx?$/.test(e))return!1;let t=P.relative(d.root,e).split(P.sep).join(`/`);return T.some(e=>P.matchesGlob(t,e))&&!E.some(e=>P.matchesGlob(t,e))}return{name:`vite-plugin-autoi18n`,enforce:`pre`,configResolved(e){d=e,p=P.resolve(d.root,i,`registry.json`);let r=C();if(r!==n&&!t.locales.includes(r))throw Error(`autoi18n: locale "${r}" is not one of the configured locales (${t.locales.join(`, `)}).`)},resolveId(e,t){if(e===`virtual:autoi18n`)return`\0${m}`;if(e===`virtual:autoi18n-format`)return`\0${S}`;if(t===`\0virtual:autoi18n-format`&&e===`intl-messageformat`)return this.resolve(e,oe(import.meta.url),{skipSelf:!0})},load(e){if(e===`\0virtual:autoi18n`)return Fe(h.catalogFiles,n);if(e===`\0virtual:autoi18n-format`)return Pe()},configureServer(e){g=e,e.watcher.on(`add`,e=>{ae(e)&&N()}),e.watcher.on(`unlink`,e=>{h.fileMessages.has(e)&&N()})},closeBundle(){clearTimeout(v)},async buildStart(){await k({info:e=>this.info(e),warn:e=>this.warn(e),error:e=>this.error(e)})},async transform(e,t){let r=t.split(`?`)[0];if(!h.fileMessages.has(r))return null;let i=f(e);i!==h.hashOf(r)&&await A(pt(d.logger),r);let o=h.fileMessages.get(r);if(!o||i!==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 s=d.command===`serve`?n:C(),c=h.parsedSources.get(r);h.parsedSources.delete(r);let l=Me(e,r,o,s,h.catalogs.get(s)??{},a===`runtime`,n,c);return{code:l.code,map:l.map}},async handleHotUpdate({file:e}){if(e.startsWith(w()+P.sep))return[];h.fileMessages.has(e)&&await M(e)}}}export{qe as AnthropicProvider,ze as FallbackProvider,Je as GeminiProvider,Ke as OpenAICompatibleProvider,F as PartialTranslationError,ht as default,dt as locales,He as resolveProvider};
|