vite-plugin-ai-auto-i18n 0.5.11 โ 0.6.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 +50 -8
- package/dist/cli/prune.js +1 -1
- package/dist/cli/stats.d.ts +1 -0
- package/dist/cli/stats.js +2 -0
- package/dist/debug-DVrC55pP.js +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +5 -3
- package/dist/registry-W7usElv-.js +2 -0
- package/package.json +3 -1
- package/dist/registry-aikqFLy2.js +0 -2
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ The compiler parses each file in `include` and records every translatable string
|
|
|
47
47
|
]
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Mixed sentences
|
|
50
|
+
Mixed sentences are ICU MessageFormat messages. Simple expressions become named arguments (`{count}`), and the translator can use ICU syntax such as plurals and selects. Literal `'`, `{` and `}` in the text are escaped (`Don''t`).
|
|
51
51
|
|
|
52
52
|
### Phase 2: identify
|
|
53
53
|
|
|
@@ -112,7 +112,7 @@ The source is rewritten according to `delivery`.
|
|
|
112
112
|
- <button title="Save the current document">Save</button>
|
|
113
113
|
- <p>You have {count} documents</p>
|
|
114
114
|
+ <button title="Dokument speichern">{"Speichern"}</button>
|
|
115
|
-
+ <p>{"Du hast
|
|
115
|
+
+ <p>{__autoI18nFormat("Du hast {count} Dokumente", {"count": count}, "de")}</p>
|
|
116
116
|
</section>
|
|
117
117
|
);
|
|
118
118
|
}
|
|
@@ -121,14 +121,15 @@ The source is rewritten according to `delivery`.
|
|
|
121
121
|
**`delivery: "runtime"`** (one bundle, catalogues loaded in the browser) replaces each string with a lookup. The source text stays in the code as the fallback:
|
|
122
122
|
|
|
123
123
|
```diff
|
|
124
|
-
+import { t as __autoI18nT,
|
|
124
|
+
+import { t as __autoI18nT, getLocale as __autoI18nLocale } from "virtual:autoi18n";
|
|
125
|
+
+import { format as __autoI18nFormat } from "virtual:autoi18n-format";
|
|
125
126
|
export function Doc({ count }: { count: number }) {
|
|
126
127
|
return (
|
|
127
128
|
<section>
|
|
128
129
|
- <button title="Save the current document">Save</button>
|
|
129
130
|
- <p>You have {count} documents</p>
|
|
130
131
|
+ <button title={__autoI18nT("msg_a1", "Save the current document")}>{__autoI18nT("msg_a2", "Save")}</button>
|
|
131
|
-
+ <p>{__autoI18nFormat(__autoI18nT("msg_a3", "You have {count} documents"), {"count": count})}</p>
|
|
132
|
+
+ <p>{__autoI18nFormat(__autoI18nT("msg_a3", "You have {count} documents"), {"count": count}, __autoI18nLocale())}</p>
|
|
132
133
|
</section>
|
|
133
134
|
);
|
|
134
135
|
}
|
|
@@ -237,7 +238,7 @@ const deepl: TranslationProvider = {
|
|
|
237
238
|
async translate({ sourceLocale, targetLocale, messages }) {
|
|
238
239
|
const out: Record<string, string> = {};
|
|
239
240
|
for (const m of messages) {
|
|
240
|
-
// Keep {
|
|
241
|
+
// Keep ICU arguments such as {count} intact; the plugin rejects translations that change them or are not valid ICU.
|
|
241
242
|
out[m.id] = await callYourService(m.source, targetLocale);
|
|
242
243
|
}
|
|
243
244
|
return out;
|
|
@@ -346,6 +347,8 @@ Messages removed from the source are kept as orphans in `registry.json` so their
|
|
|
346
347
|
|
|
347
348
|
๐งน To prune without running a build, use the CLI. In this repository, `npm run prune` builds and then runs it on the example app's `example/.autoi18n/` with `--retention-minutes 0`, so every orphan is removed. Pass options after `--` to override, for example `npm run prune -- --directory .autoi18n --retention-minutes 1440 --debug`. Options: `--directory` (default `.autoi18n`, relative to the current directory), `--retention-minutes` (default `43200`, i.e. 30 days, the plugin default) and `--debug` (prints each removed entry with its source text and last-seen time). The CLI also removes any key from the `messages` and `sources` maps of `locales/*.json` that is no longer an active registry entry, so the catalogues match the registry. If no registry exists at that path, the CLI says so and changes nothing. Installed packages can run `node node_modules/vite-plugin-ai-auto-i18n/dist/cli/prune.js` with the same options.
|
|
348
349
|
|
|
350
|
+
๐ To see translation statistics without running a build, use the stats CLI. In this repository, `npm run stats` builds and then reports on the example app's `example/.autoi18n/`. Pass options after `--`, for example `npm run stats -- --directory .autoi18n --source-locale en --verbose`. The report shows registry counts (active, orphaned, by kind and component), and for each locale file the translated count, coverage percentage, missing messages (no translation), outdated messages (translated from a source that has since changed), and orphaned keys (what `prune` would remove). It also lists orphaned registry entries. Options: `--directory` (default `.autoi18n`), `--source-locale` (default `en`, excluded from the figures), `--verbose` (lists the missing, outdated and orphaned IDs per locale) and `--json` (prints the full report as JSON for CI). Installed packages can run `node node_modules/vite-plugin-ai-auto-i18n/dist/cli/stats.js` with the same options.
|
|
351
|
+
|
|
349
352
|
๐ Set `debug: true` (default `false`) to print every translation exchange to the console: each message sent to the provider (`sent`) and each translation received back (`received`), prefixed with `[autoi18n debug]` and an ISO timestamp.
|
|
350
353
|
|
|
351
354
|
๐ To keep copy out of automatic translation, add `translate="no"` to the element (for example `<h2 translate="no">Sample code</h2>`). This is the standard HTML marker, and it also covers the element's descendants and attributes such as `title`.
|
|
@@ -393,7 +396,46 @@ In dev, saving a source file re-indexes the project, translates only new or chan
|
|
|
393
396
|
| `componentProps` | `Record<string, string[]>` | `{}` | Attributes of custom components whose string values are UI copy, keyed by component name, for example `{ Button: ["aria-label", "title"] }`. Only the listed attributes are extracted from those components. The pseudo-prop `children` also makes a component's text children translatable, including sentences with `{name}` expressions: `{ Button: ["children", "aria-label"] }` extracts `<Button>Save</Button>` and `<Button>Delete {count} files</Button>`. |
|
|
394
397
|
| `stringMarker` | `string` | `"i18n"` | Text of the block comment that marks a string literal outside JSX for translation, for example `/* i18n */ "Save"`. Set it to `"t"` to use `/* t */ "Save"` instead. |
|
|
395
398
|
|
|
396
|
-
###
|
|
399
|
+
### Locale groups
|
|
400
|
+
|
|
401
|
+
The package exports ready-made locale lists for the `locales` option. Each is a plain array of BCP 47 codes, so spread it to keep the shared export unchanged:
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
import autoI18n, { ASIA, EUROPE, WORLDWIDE_POPULAR } from "vite-plugin-ai-auto-i18n";
|
|
405
|
+
|
|
406
|
+
autoI18n({ locales: [...EUROPE] });
|
|
407
|
+
autoI18n({ locales: ["en", ...ASIA] });
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
| Group | Languages |
|
|
411
|
+
|---|---|
|
|
412
|
+
| `EU` | 23 official EU languages |
|
|
413
|
+
| `EUROPE` | `EU` plus Norwegian, Icelandic, Albanian, Bosnian, Macedonian, Serbian, Ukrainian, Russian, Belarusian, Catalan |
|
|
414
|
+
| `NORDIC` | Danish, Finnish, Icelandic, Norwegian, Swedish |
|
|
415
|
+
| `BALTIC` | Estonian, Latvian, Lithuanian |
|
|
416
|
+
| `BALKANS` | Albanian, Bosnian, Bulgarian, Croatian, Greek, Macedonian, Romanian, Serbian, Slovenian |
|
|
417
|
+
| `BENELUX` | Dutch, French, Luxembourgish |
|
|
418
|
+
| `EAST_ASIA` | Chinese, Japanese, Korean, Mongolian |
|
|
419
|
+
| `SOUTH_ASIA` | Hindi, Bengali, Urdu, Punjabi, Tamil, Telugu, Marathi, Gujarati, Kannada, Malayalam, Nepali, Sinhala |
|
|
420
|
+
| `SOUTHEAST_ASIA` | Indonesian, Malay, Burmese, Khmer, Lao, Filipino, Thai, Vietnamese |
|
|
421
|
+
| `CENTRAL_ASIA` | Kazakh, Kyrgyz, Tajik, Turkmen, Uzbek |
|
|
422
|
+
| `CAUCASUS` | Armenian, Azerbaijani, Georgian |
|
|
423
|
+
| `ASIA` | `EAST_ASIA`, `SOUTH_ASIA`, `SOUTHEAST_ASIA`, `CENTRAL_ASIA`, `CAUCASUS` |
|
|
424
|
+
| `MIDDLE_EAST` | Arabic, Persian, Hebrew, Turkish, Kurdish |
|
|
425
|
+
| `AFRICA` | Arabic, French, Portuguese, Swahili, Amharic, Hausa, Yoruba, Igbo, Zulu, Xhosa, Afrikaans, Somali, Kinyarwanda, Wolof |
|
|
426
|
+
| `NORTH_AMERICA` | English, French, Spanish |
|
|
427
|
+
| `CENTRAL_AMERICA_CARIBBEAN` | Spanish, English, French, Haitian Creole, Dutch |
|
|
428
|
+
| `SOUTH_AMERICA` | Spanish, Portuguese, Quechua, Guarani, Aymara |
|
|
429
|
+
| `AMERICAS` | `NORTH_AMERICA`, `CENTRAL_AMERICA_CARIBBEAN`, `SOUTH_AMERICA` |
|
|
430
|
+
| `OCEANIA` | English, Maori, Samoan, Tongan |
|
|
431
|
+
| `ASEAN` | `SOUTHEAST_ASIA` languages plus English (working language) |
|
|
432
|
+
| `MERCOSUR` | Spanish, Portuguese, Guarani |
|
|
433
|
+
| `GCC` | Arabic, English |
|
|
434
|
+
| `CIS` | Russian, Belarusian, Armenian, Azerbaijani, Kazakh, Kyrgyz, Tajik, Uzbek |
|
|
435
|
+
| `G7` | English, French, German, Italian, Japanese |
|
|
436
|
+
| `FRANCOPHONE` | French, Haitian Creole, Wolof, Lingala, Kinyarwanda |
|
|
437
|
+
| `WORLDWIDE_POPULAR` | The 17 languages with the most speakers worldwide, including English, Chinese, Hindi, Spanish, and Arabic |
|
|
438
|
+
|
|
397
439
|
|
|
398
440
|
Create the provider with `new OpenAICompatibleProvider(options)`.
|
|
399
441
|
|
|
@@ -504,7 +546,7 @@ interface TranslationRequest {
|
|
|
504
546
|
source: string; // English text
|
|
505
547
|
context: string; // for example "Toolbar > Save button"
|
|
506
548
|
kind: "text" | "attribute" | "mixed" | "string"; // text node, static attribute, JSX with expressions, or a marked string literal
|
|
507
|
-
placeholders?: string[]; //
|
|
549
|
+
placeholders?: string[]; // ICU argument names (for example "count") that must survive translation
|
|
508
550
|
}
|
|
509
551
|
```
|
|
510
552
|
|
|
@@ -542,7 +584,7 @@ Critical modules such as the plugin hooks (`src/index.ts`), translation pipeline
|
|
|
542
584
|
This is a beta release. Review these limitations before shipping to production:
|
|
543
585
|
|
|
544
586
|
1. Runtime delivery loads a catalogue per locale; a message's source text is used until its catalogue has loaded.
|
|
545
|
-
2. Mixed JSX sentences with simple expressions (`You have {count} documents`, where `count` is an identifier or member path) are translated as
|
|
587
|
+
2. Mixed JSX sentences with simple expressions (`You have {count} documents`, where `count` is an identifier or member path) are translated as ICU MessageFormat messages, formatted at runtime with `intl-messageformat`. Translations that are not valid ICU, or that change the set of argument names, are rejected. Argument names are sanitized to letters, digits and `_` (`doc.name` becomes `doc_name`), so translations stored under the old `{doc.name}` names fall back to the source until they are retranslated. Sentences with complex expressions (calls, JSX, computed access) are left entirely in the source language.
|
|
546
588
|
3. Duplicate identical occurrences are paired by stable source ordering. This handles ordinary line shifts, but cannot guarantee identity if indistinguishable duplicates are reordered or moved independently. Such cases are reported through `onAmbiguous`; they are not resolved automatically.
|
|
547
589
|
4. Dev-server HMR re-indexes the whole project after saved or added/removed source files (debounced by 150 ms) rather than only the changed module. This is simple and correct, but may be slow in very large projects.
|
|
548
590
|
5. Only JSX text, and the attributes `alt`, `title`, `placeholder`, `label`, `summary`, `aria-label`, `aria-description`, `aria-placeholder`, `aria-roledescription` and `aria-valuetext` on intrinsic (lowercase) elements, are extracted automatically. String literals outside JSX are extracted only when marked with the `stringMarker` comment (`/* i18n */` by default). Text inside capitalised components (for example `<Button>Save</Button>`) is extracted only when the component lists `children` in `componentProps`. Attributes of capitalised components are extracted only when listed in `componentProps`. Other attributes such as `href` are not extracted. Unmarked JS string literals, template literals, Markdown, server-rendered content and rich-text components need explicit policies.
|
package/dist/cli/prune.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{t as e}from"../debug-DVrC55pP.js";import{A as t,M as n,d as r,i,j as a,n as o,t as s}from"../registry-W7usElv-.js";import{readdir as c}from"node:fs/promises";import l from"node:path";import{existsSync as u}from"node:fs";async function d(e,r){if(!u(e))return[];let i=[];for(let o of await c(e)){if(!o.endsWith(`.json`))continue;let s=l.join(e,o),c=t(await a(s,e=>console.warn(e)),r);c.removed.length&&(await n(s,c.file),i.push({file:s,removed:c.removed}))}return i}function f(e){let t={directory:`.autoi18n`,retentionMinutes:r,debug:!1};for(let n=0;n<e.length;n++){let r=e[n];if(r===`--debug`)t.debug=!0;else if(r===`--directory`||r===`--retention-minutes`){let i=e[++n];if(i===void 0)throw Error(`${r} needs a value`);r===`--directory`?t.directory=i:t.retentionMinutes=Number(i)}else throw Error(`Unknown option ${r}`)}if(!(t.retentionMinutes>=0))throw Error(`--retention-minutes must be a number of minutes, 0 or more`);return t}async function p(){let t=f(process.argv.slice(2)),n=l.resolve(process.cwd(),t.directory,`registry.json`);if(!u(n)){console.log(`autoi18n: no registry found at ${n}; nothing to prune.`);return}let r=await s(n,e=>console.warn(e)),a=o(r,t.retentionMinutes);a.length&&await i(n,r);let c=new Set(Object.values(r.entries).filter(e=>e.status===`active`).map(e=>e.id)),p=await d(l.join(l.dirname(n),`locales`),c);if(t.debug){for(let t of a)e(`removed ${t.id} ${JSON.stringify(t.source)} (last seen ${t.lastSeen})`);for(let{file:t,removed:n}of p)for(let r of n)e(`removed ${r} from ${t}`)}console.log(`autoi18n: removed ${a.length} registry entries orphaned for more than ${t.retentionMinutes} minutes.`),p.length&&console.log(`autoi18n: removed stale keys from ${p.length} locale files.`)}p().catch(e=>{console.error(`autoi18n: ${e.message}`),process.exitCode=1});export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{j as e,t}from"../registry-W7usElv-.js";import{readdir as n}from"node:fs/promises";import r from"node:path";import{existsSync as i}from"node:fs";const a=e=>{let t={};for(let n of e)t[n]=(t[n]??0)+1;return t};function o(e,t){let n=Object.values(e.entries),r=n.filter(e=>e.status===`active`),i=new Set(r.map(e=>e.id)),o=n.filter(e=>e.status===`orphaned`).sort((e,t)=>e.lastSeen.localeCompare(t.lastSeen)||e.id.localeCompare(t.id)),s=Object.keys(t).sort((e,t)=>e.localeCompare(t)).map(e=>{let n=t[e],a=r.filter(e=>!Object.hasOwn(n.messages,e.id)).map(e=>e.id),o=r.filter(e=>Object.hasOwn(n.messages,e.id)&&n.sources[e.id]!==e.source).map(e=>e.id),s=[.../* @__PURE__ */ new Set([...Object.keys(n.messages),...Object.keys(n.sources)])].filter(e=>!i.has(e)),c=r.length,l=c-a.length;return{locale:e,translated:l,total:c,coverage:c===0?1:l/c,missing:a.sort(),outdated:o.sort(),orphanedKeys:s.sort()}}),c=s.length===0?null:s.reduce((e,t)=>e+t.coverage,0)/s.length;return{registry:{total:n.length,active:r.length,orphaned:o.length,byKind:a(r.map(e=>e.kind)),byComponent:a(r.map(e=>e.component)),orphans:o},locales:s,averageCoverage:c}}function s(e){let t={directory:`.autoi18n`,sourceLocale:`en`,json:!1,verbose:!1};for(let n=0;n<e.length;n++){let r=e[n];if(r===`--json`)t.json=!0;else if(r===`--verbose`)t.verbose=!0;else if(r===`--directory`||r===`--source-locale`){let i=e[++n];if(i===void 0||i.startsWith(`--`))throw Error(`${r} needs a value`);r===`--directory`?t.directory=i:t.sourceLocale=i}else throw Error(`Unknown option ${r}`)}return t}async function c(t,a){let o={};if(!i(t))return o;for(let i of await n(t)){if(!i.endsWith(`.json`)||i.endsWith(`.sources.json`))continue;let n=i.slice(0,-5);n!==a&&(o[n]=await e(r.join(t,i),e=>console.warn(e)))}return o}const l=e=>`${(e*100).toFixed(1)}%`;function u(e,t){let n=[],{registry:r}=e;n.push(`Registry`),n.push(` entries: ${r.total} (${r.active} active, ${r.orphaned} orphaned)`);let i=Object.entries(r.byKind).sort(([e],[t])=>e.localeCompare(t));i.length&&n.push(` by kind: ${i.map(([e,t])=>`${e} ${t}`).join(`, `)}`);let a=Object.entries(r.byComponent).sort(([e],[t])=>e.localeCompare(t));if(a.length&&n.push(` by component: ${a.map(([e,t])=>`${e} ${t}`).join(`, `)}`),n.push(``,`Coverage`),e.locales.length===0)n.push(` no locale files found.`);else{let r=[[`locale`,`translated`,`missing`,`outdated`,`orphaned keys`,`coverage`],...e.locales.map(e=>[e.locale,`${e.translated}/${e.total}`,String(e.missing.length),String(e.outdated.length),String(e.orphanedKeys.length),l(e.coverage)])],i=r[0].map((e,t)=>Math.max(...r.map(e=>e[t].length)));for(let e of r)n.push(` `+e.map((e,t)=>e.padEnd(i[t])).join(` `).trimEnd());if(n.push(` average coverage: ${l(e.averageCoverage??1)}`),t)for(let t of e.locales)t.missing.length&&n.push(` ${t.locale} missing: ${t.missing.join(`, `)}`),t.outdated.length&&n.push(` ${t.locale} outdated: ${t.outdated.join(`, `)}`),t.orphanedKeys.length&&n.push(` ${t.locale} orphaned keys: ${t.orphanedKeys.join(`, `)}`)}if(n.push(``,`Orphans`),r.orphans.length===0)n.push(` none.`);else for(let e of r.orphans)n.push(` ${e.id} ${JSON.stringify(e.source)} ${e.location} last seen ${e.lastSeen}`);return n.join(`
|
|
2
|
+
`)}async function d(){let e=s(process.argv.slice(2)),n=r.resolve(process.cwd(),e.directory,`registry.json`);if(!i(n)){console.log(`autoi18n: no registry found at ${n}; nothing to report.`);return}let a=o(await t(n,e=>console.warn(e)),await c(r.join(r.dirname(n),`locales`),e.sourceLocale));console.log(e.json?JSON.stringify(a,null,2):u(a,e.verbose))}d().catch(e=>{console.error(`autoi18n: ${e.message}`),process.exitCode=1});export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=e=>console.log(`[autoi18n debug] ${(/* @__PURE__ */ new Date()).toISOString()} ${e}`);export{e as t};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ArkErrors } from "arktype";
|
|
1
2
|
import { Plugin } from "vite";
|
|
2
3
|
/**
|
|
3
4
|
* What a message was extracted from: `text` for JSX text, `attribute` for a translatable attribute, `mixed` for JSX text
|
|
@@ -156,6 +157,11 @@ declare abstract class BatchedProvider<O extends BatchedProviderOptions> impleme
|
|
|
156
157
|
protected abstract request(input: TranslateInput): Promise<Catalog>;
|
|
157
158
|
/** POSTs a JSON body with the given headers and returns the parsed JSON reply. Non-2xx replies throw a classified error. */
|
|
158
159
|
protected post(url: string, headers: Record<string, string>, body: unknown): Promise<unknown>;
|
|
160
|
+
/**
|
|
161
|
+
* Posts one batch and reads its translations from the reply. `parse` validates the provider's response envelope and
|
|
162
|
+
* `text` extracts the model's text from it. An invalid envelope is treated as an empty reply.
|
|
163
|
+
*/
|
|
164
|
+
protected complete<E>(url: string, headers: Record<string, string>, body: unknown, parse: (data: unknown) => E | ArkErrors, text: (envelope: E) => string | undefined, input: TranslateInput): Promise<Catalog>;
|
|
159
165
|
/**
|
|
160
166
|
* Parses the model's text reply, which must be a JSON object mapping IDs to strings. A surrounding Markdown code fence
|
|
161
167
|
* is stripped first, because models sometimes add one even when told not to.
|
|
@@ -208,6 +214,58 @@ interface GeminiOptions extends BatchedProviderOptions {
|
|
|
208
214
|
export declare class GeminiProvider extends BatchedProvider<GeminiOptions> {
|
|
209
215
|
protected request(input: TranslateInput): Promise<Catalog>;
|
|
210
216
|
}
|
|
217
|
+
/** European Union: the 23 official languages (English dropped after Brexit). */
|
|
218
|
+
export declare const EU: string[];
|
|
219
|
+
/** Nordic countries: Danish, Finnish, Icelandic, Norwegian, Swedish. */
|
|
220
|
+
export declare const NORDIC: string[];
|
|
221
|
+
/** Baltic states: Estonian, Latvian, Lithuanian. */
|
|
222
|
+
export declare const BALTIC: string[];
|
|
223
|
+
/** Balkans: Albanian, Bosnian, Bulgarian, Croatian, Greek, Macedonian, Romanian, Serbian, Slovenian. */
|
|
224
|
+
export declare const BALKANS: string[];
|
|
225
|
+
/** Benelux: Dutch, French, Luxembourgish. */
|
|
226
|
+
export declare const BENELUX: string[];
|
|
227
|
+
/** Europe: EU languages plus the major non-EU languages of the continent. */
|
|
228
|
+
export declare const EUROPE: string[];
|
|
229
|
+
/** Eastern Asia: Chinese, Japanese, Korean, Mongolian. */
|
|
230
|
+
export declare const EAST_ASIA: string[];
|
|
231
|
+
/** South Asia: Hindi, Bengali, Urdu, Punjabi, Tamil, Telugu, Marathi, Gujarati, Kannada, Malayalam, Nepali, Sinhala. */
|
|
232
|
+
export declare const SOUTH_ASIA: string[];
|
|
233
|
+
/** Southeast Asia: Indonesian, Malay, Burmese, Khmer, Lao, Filipino, Thai, Vietnamese. */
|
|
234
|
+
export declare const SOUTHEAST_ASIA: string[];
|
|
235
|
+
/** Central Asia: Kazakh, Kyrgyz, Tajik, Turkmen, Uzbek. */
|
|
236
|
+
export declare const CENTRAL_ASIA: string[];
|
|
237
|
+
/** Caucasus: Armenian, Azerbaijani, Georgian. */
|
|
238
|
+
export declare const CAUCASUS: string[];
|
|
239
|
+
/** Asia: East, South, Southeast, Central Asia and the Caucasus. */
|
|
240
|
+
export declare const ASIA: string[];
|
|
241
|
+
/** Middle East: Arabic, Persian, Hebrew, Turkish, Kurdish. */
|
|
242
|
+
export declare const MIDDLE_EAST: string[];
|
|
243
|
+
/** Africa: major official and widely spoken languages. */
|
|
244
|
+
export declare const AFRICA: string[];
|
|
245
|
+
/** North America: English, French (Canada), Spanish (Mexico, US). */
|
|
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[];
|
|
211
269
|
export interface AutoI18nOptions {
|
|
212
270
|
locales: string[];
|
|
213
271
|
sourceLocale?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
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-aikqFLy2.js";import{n as ee,t as te}from"./resolve-ELDMKdoA.js";import{ArkErrors as M}from"arktype";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(`
|
|
1
|
+
import{t as e}from"./debug-DVrC55pP.js";import{A as t,C as n,D as r,E as i,F as a,I as o,L as s,M as c,N as l,O as u,P as d,R as f,S as p,T 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,p as T,r as E,t as ee,u as te,v as D,w as O,x as ne,y as k}from"./registry-W7usElv-.js";import{n as A,t as j}from"./resolve-ELDMKdoA.js";import{ArkErrors as M}from"arktype";import{glob as re,readFile as ie,rm as N,stat as ae}from"node:fs/promises";import P from"node:path";import{fileURLToPath as oe}from"node:url";import{createHash as se}from"node:crypto";import{IntlMessageFormat as ce}from"intl-messageformat";import{parseSync as le}from"rolldown/utils";import ue from"magic-string";async function de(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 fe=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++}};const pe=/* @__PURE__ */ new Set([1,2,3,4,5,6]);function me(e){return e.replace(/['{}]/g,e=>e===`'`?`''`:`'${e}'`)}function he(e){return e.replace(/[^\p{L}\p{N}_]/gu,`_`)}function ge(e){let t;try{t=new ce(e,`en`).getAst()}catch{return}let n=/* @__PURE__ */ new Set,r=e=>{for(let t of e)if(pe.has(t.type)&&t.value&&n.add(t.value),t.children&&r(t.children),t.options)for(let e of Object.values(t.options))r(e.value)};return r(t),[...n]}function _e(e,t){let n=new Set(e),r=new Set(t);return n.size===r.size&&[...n].every(e=>r.has(e))}function ve(e,t){if(e.trim()===``)return!1;let n=ge(e);return n!==void 0&&_e(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 ye(n){let{provider:r,sourceLocale:i,locale:a,requests:o,previous:s,debug:c}=n,l=new Set(o.map(e=>e.id)),{messages:u,sources:d}=t(s,l).file,f=o.filter(e=>u[e.id]===void 0||d[e.id]!==e.source),p={},m=0,h;if(f.length&&r){if(c)for(let t of f)e(`sent ${i} -> ${a} ${t.id}: ${JSON.stringify(t.source)} (context: ${t.context})`);try{p=await r.translate({sourceLocale:i,targetLocale:a,messages:f})}catch(e){if(!(e instanceof F))throw e;p=e.partial,m=e.failed,h=e.message}if(c)for(let t of f){let n=p[t.id];e(`received ${i} -> ${a} ${t.id}: ${typeof n==`string`?JSON.stringify(n):`(no translation)`}`)}}let g={...u},_={...d},v=0;for(let e of f){delete g[e.id],delete _[e.id];let t=p[e.id];typeof t==`string`&&t.trim()&&(e.kind===`mixed`&&!ve(t,e.placeholders??[])?v++:(g[e.id]=t,_[e.id]=e.source))}let y=o.filter(e=>g[e.id]===void 0).length;return{file:{messages:g,sources:_},untranslated:y,rejected:v,requested:f.length,failed:m,failure:h}}async function be(e){let t=structuredClone(e.registry),{ambiguous:n,messages:r}=E(t,e.files.flatMap(e=>e.messages)),i=C(t,e.orphanRetentionMinutes),a={};for(let e of r)a[e.id]=e.source;let o=r.map(e=>({id:e.id,source:e.source,context:`${e.component} > ${e.context}`,kind:e.kind,placeholders:e.placeholders})),s=await de(e.locales,e.localeConcurrency,t=>ye({provider:e.provider,sourceLocale:e.sourceLocale,locale:t,requests:o,previous:e.previous.get(t)??u(),debug:e.debug})),c=/* @__PURE__ */ new Map,l=/* @__PURE__ */ new Map;return s.forEach((t,n)=>{let r=e.locales[n];t.status===`fulfilled`?c.set(r,t.value):l.set(r,t.reason)}),{registry:t,ambiguous:n,pruned:i,messages:r,sourceFile:{messages:a,sources:{}},locales:c,failures:l}}function I(e){return e.replace(/\s+/g,` `).trim()}function xe(e){let t=[I(e.source),e.kind,e.tag.toLowerCase(),e.attribute??``,e.context].join(``);return se(`sha256`).update(t).digest(`hex`).slice(0,24)}function L(e,t){let n=le(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 Se(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 Ce(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(ne,(e,t,n)=>{if(n!==void 0)return k.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 we(e){return{prefix:e.match(/^\s*/)?.[0]??``,suffix:e.match(/\s*$/)?.[0]??``}}const U=e=>/[A-Za-z]/.test(e);function Te(e){return e.attributes.some(e=>e.type===`JSXAttribute`&&e.name.type===`JSXIdentifier`&&e.name.name===`translate`&&V(e.value)===`no`)}function Ee(e){return e.type===`JSXIdentifier`&&!g.test(e.name)&&!_.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 De=(e,t)=>e.type===`FunctionExpression`?!(t?.type===`MethodDefinition`||t?.type===`Property`&&(t.method||t.kind!==`init`)):e.type===`FunctionDeclaration`||e.type===`ArrowFunctionExpression`;function Oe(e,t){for(let n=e.length-1;n>=0;n--)if(t(e[n]))return n;return-1}function ke(e){let t=Oe(e,e=>e.type===`JSXElement`);return t<0?void 0:{index:t,element:e[t]}}function G(e){return e.some(e=>{let t=e.type===`JSXOpeningElement`?e:e.type===`JSXElement`?e.openingElement:void 0;return t!==void 0&&Te(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 Ae(e,t,n={},r=L(e,t)){let{program:a,commentsByEnd:o}=r,s=[],c=(e,t=e.source)=>{s.push({...e,fingerprint:xe({...e,source:t})})},l=e=>Object.prototype.hasOwnProperty.call(n.componentProps??{},e)?n.componentProps[e]:[],u=e=>{if(e.type!==`JSXIdentifier`)return!1;let t=e.name;return Ee(e)||g.test(t)&&l(t).includes(`children`)};function d(e){let t=e.length-1;for(;t>=0&&!De(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 f(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`&&U(H(e))))return;let a=e.openingElement.name;if(!u(a))return;let o=[];for(let e of i){let t=W(e.expression),n=t;for(let e=2;o.includes(n);e++)n=`${t}_${e}`;o.push(n)}let s=[];for(let e of o){let t=he(e),n=t;for(let e=2;s.includes(n);e++)n=`${t}_${e}`;s.push(n)}let l=``,f=``,p=0;for(let e of r)if(e.type===`JSXText`){let t=H(e);l+=t,f+=me(t)}else l+=`{${o[p]}}`,f+=`{${s[p]}}`,p++;let m=I(f);if(!m)return;let h=a.name;c({source:m,...we(l),kind:`mixed`,placeholders:s,file:t,start:e.start,end:e.end,tag:h,component:d(n),context:K(e.openingElement,h)},I(l))}function p(e,n){if(G([...n,e]))return;let r=H(e),i=I(r);if(!i)return;let a=ke(n);if(!a)return;let{index:o,element:s}=a;if(s.children.some(e=>e.type===`JSXExpressionContainer`))return;let l=s.openingElement.name;if(!u(l))return;let f=l.name;c({source:i,...we(r),kind:`text`,file:t,start:e.start,end:e.end,tag:f,component:d(n.slice(0,o)),context:K(s.openingElement,f)})}function m(e,n){if(e.name.type!==`JSXIdentifier`)return;let r=e.name.name,a=n[n.length-1];if(a?.type!==`JSXOpeningElement`)return;let o=a.name.type===`JSXIdentifier`?a.name.name:``;if(!(Ee(a.name)?i.has(r):l(o).includes(r))||G([...n,e])||!z(e.value))return;let s=I(B(e.value.value));if(!s||!U(s))return;let u=ke(n);if(!u)return;let{index:f,element:p}=u;c({source:s,kind:`attribute`,file:t,start:e.value.start,end:e.value.end,tag:o,component:d(n.slice(0,f)),attribute:r,context:K(p.openingElement,o,r)})}function h(r,i){if(!z(r)||i[i.length-1]?.type===`JSXAttribute`||i.some(e=>e.start===r.start)||!Ce(e,o,r.start,n.stringMarker??`i18n`))return;let a=I(r.value);a&&U(a)&&c({source:a,kind:`string`,file:t,start:r.start,end:r.end,tag:``,component:d(i),context:``})}return Se(a,(e,t)=>{switch(e.type){case`JSXElement`:return f(e,t);case`JSXText`:return p(e,t);case`JSXAttribute`:return m(e,t);case`Literal`:return h(e,t)}}),s.sort((e,t)=>e.start-t.start)}function je(e,t,r,i,a,o=!1,s=`en`,c=L(e,t)){let{program:l}=c,u={mixed:/* @__PURE__ */ new Map,text:/* @__PURE__ */ new Map,attribute:/* @__PURE__ */ new Map,string:/* @__PURE__ */ new Map};for(let e of r)u[e.kind]?.set(e.start,e);let d=r.map(e=>e.start).sort((e,t)=>e-t),f=(e,t)=>{let n=0,r=d.length;for(;n<r;){let t=n+r>>1;d[t]<e?n=t+1:r=t}return n<d.length&&d[n]<t},h=[];Se(l,e=>{if(!f(e.start,e.end))return!1;if(e.type===`JSXElement`){let t=u.mixed.get(e.start);t&&h.push({kind:`mixed`,node:e,message:t})}else if(e.type===`JSXText`){let t=u.text.get(e.start);t&&h.push({kind:`text`,node:e,message:t})}else if(e.type===`JSXAttribute`&&e.value?.type===`Literal`){let t=u.attribute.get(e.value.start);t&&h.push({kind:`attribute`,node:e.value,message:t})}else if(e.type===`Literal`&&typeof e.value==`string`){let t=u.string.get(e.start);t&&h.push({kind:`string`,node:e,message:t})}});let g=new ue(e),_=!1,v=!1,y=!1,b=e=>i===s?e.source:a[e.id]??e.source,x=e=>o?(_=!0,`${O}(${JSON.stringify(e.id)}, ${JSON.stringify(e.source)})`):JSON.stringify(b(e)),C=e=>{let t=e.prefix??``,n=e.suffix??``;return o?[t&&JSON.stringify(t),x(e),n&&JSON.stringify(n)].filter(Boolean).join(` + `):JSON.stringify(`${t}${b(e)}${n}`)};for(let e of h.reverse()){let{message:t}=e;if(e.kind===`mixed`){let r=e.node;if(!r.closingElement)continue;let a=t.placeholders??[],s=r.children.flatMap(e=>e.type===`JSXExpressionContainer`?[e.expression]:[]),c=e=>g.slice(s[e].start,s[e].end),l=r.openingElement.end,u=r.closingElement.start,d=a.map((e,t)=>`${JSON.stringify(e)}: ${c(t)}`).join(`, `);if(v=!0,o){y=!0,g.overwrite(l,u,`{${p}(${C(t)}, {${d}}, ${n}())}`);continue}let f=b(t),m=ve(f,a)?f:t.source,h=JSON.stringify(`${t.prefix??``}${m}${t.suffix??``}`);g.overwrite(l,u,`{${p}(${h}, {${d}}, ${JSON.stringify(i)})}`)}else e.kind===`text`?g.overwrite(e.node.start,e.node.end,`{${C(t)}}`):e.kind===`attribute`?g.overwrite(e.node.start,e.node.end,o?`{${x(t)}}`:x(t)):g.overwrite(e.node.start,e.node.end,x(t))}let w=[..._?[`t as ${O}`]:[],...y?[`getLocale as ${n}`]:[]];return w.length&&g.prepend(`import { ${w.join(`, `)} } from "${m}";\n`),v&&g.prepend(`import { format as ${p} } from "${S}";\n`),{code:g.toString(),map:g.generateMap({source:t,hires:!0,includeContent:!0})}}function Me(e,t,n,r,i){try{let a=i+`\0`+n,o=t.get(a);return o||(o=new e(n,i),t.set(a,o)),o.formatToParts(r).map(e=>e.value)}catch(e){return console.warn(`autoi18n: could not format `+JSON.stringify(n)+`.`,e),[n]}}function Ne(){return[`import { IntlMessageFormat } from ${JSON.stringify(T)};`,`const formatMessage = ${Me.toString()};`,`const cache = new Map();`,`export function format(text, values, locale) { return formatMessage(IntlMessageFormat, cache, text, values, locale); }`].join(`
|
|
3
2
|
`)+`
|
|
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=a(r);if(i instanceof M)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))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=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 instanceof M?void 0:i.choices[0].message.content??void 0,e.messages)}},Ne=class extends ${async request(e){let t=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 instanceof M?void 0:t.content.filter(e=>e.type===`text`).map(e=>e.text??``).join(``),e.messages)}},Pe=class extends ${async request(e){let t=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 instanceof M?void 0:(t.candidates[0]?.content?.parts??[]).map(e=>e.text??``).join(``),e.messages)}};function Fe(e){let t=i(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 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};
|
|
3
|
+
`}function Pe(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(`,
|
|
4
|
+
`)}\n};`,`const sourceLocale = ${JSON.stringify(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
|
+
`)+`
|
|
6
|
+
`}async function q(e){try{let t=await ae(e);return`${t.mtimeMs}:${t.size}`}catch{return}}var Fe=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 Ie(e,t){return J(e.messages,t.messages)&&J(e.sources,t.sources)}var Le=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 Fe;registries=new Fe;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,Ie))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))}},Re=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 (${ze(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: ${ze(i)}`,t,n.length)}};const ze=e=>e instanceof Error?e.message:String(e),Be=e=>typeof e==`object`&&!!e&&typeof e.translate==`function`;function Ve(e,t){if(e===void 0)return;if(Be(e))return e;let{primary:n,...r}=e,i=[];for(let[e,t]of Object.entries(r))if(t!==void 0){if(!Be(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 Re(i,t)}var Y=class extends Error{retryable;retryAfterMs;constructor(e,t,n){super(e),this.retryable=t,this.retryAfterMs=n}};function He(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 Ue=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 We(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,He(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 Ue(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 We(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}},Ge=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)}},Ke=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)}},qe=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 Je=[`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([...Je,`no`,`is`,`sq`,`bs`,`mk`,`sr`,`uk`,`ru`,`be`,`ca`])],$=[`zh`,`ja`,`ko`,`mn`],et=[`hi`,`bn`,`ur`,`pa`,`ta`,`te`,`mr`,`gu`,`kn`,`ml`,`ne`,`si`],tt=[`id`,`ms`,`my`,`km`,`lo`,`fil`,`th`,`vi`],nt=[`kk`,`ky`,`tg`,`tk`,`uz`],rt=[`hy`,`az`,`ka`],it=[.../* @__PURE__ */ new Set([...$,...et,...tt,...nt,...rt])],at=[`ar`,`fa`,`he`,`tr`,`ku`],ot=[`ar`,`fr`,`pt`,`sw`,`am`,`ha`,`yo`,`ig`,`zu`,`xh`,`af`,`so`,`rw`,`wo`],st=[`en`,`fr`,`es`],ct=[`es`,`en`,`fr`,`ht`,`nl`],lt=[`es`,`pt`,`qu`,`gn`,`ay`],ut=[.../* @__PURE__ */ new Set([...st,...ct,...lt])],dt=[`en`,`mi`,`sm`,`to`],ft=[`id`,`ms`,`my`,`km`,`lo`,`fil`,`th`,`vi`,`en`],pt=[`es`,`pt`,`gn`],mt=[`ar`,`en`],ht=[`ru`,`be`,`hy`,`az`,`kk`,`ky`,`tg`,`uz`],gt=[`en`,`fr`,`de`,`it`,`ja`],_t=[`fr`,`ht`,`wo`,`ln`,`rw`],vt=[`en`,`zh`,`hi`,`es`,`ar`,`bn`,`pt`,`ru`,`ja`,`de`,`fr`,`ko`,`it`,`tr`,`vi`,`id`,`ur`];function yt(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 bt=e=>({info:t=>e.info(t),warn:t=>e.warn(t),error:t=>e.error(t)});async function xt(e,t){await Promise.all(t.map(t=>N(P.join(e,`${t}.sources.json`),{force:!0})))}function St(t){yt(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=Ve(t.provider,e=>l?.warn(e)),d,p=``,h=new Le,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:Ae(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 xt(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(bt(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 Pe(h.catalogFiles,n);if(e===`\0virtual:autoi18n-format`)return Ne()},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(bt(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=je(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{ot as AFRICA,ut as AMERICAS,ft as ASEAN,it as ASIA,Ke as AnthropicProvider,Ze as BALKANS,Xe as BALTIC,Qe as BENELUX,rt as CAUCASUS,ct as CENTRAL_AMERICA_CARIBBEAN,nt as CENTRAL_ASIA,ht as CIS,$ as EAST_ASIA,Je as EU,$e as EUROPE,_t as FRANCOPHONE,Re as FallbackProvider,gt as G7,mt as GCC,qe as GeminiProvider,pt as MERCOSUR,at as MIDDLE_EAST,Ye as NORDIC,st as NORTH_AMERICA,dt as OCEANIA,Ge as OpenAICompatibleProvider,F as PartialTranslationError,tt as SOUTHEAST_ASIA,lt as SOUTH_AMERICA,et as SOUTH_ASIA,vt as WORLDWIDE_POPULAR,St as default,Ve as resolveProvider};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{ArkErrors as e,type as t}from"arktype";import{mkdir as n,readFile as r,rename as i,writeFile as a}from"node:fs/promises";import o from"node:path";import{createHash as s,randomUUID as c}from"node:crypto";const l=e=>JSON.stringify(e,null,2)+`
|
|
2
|
+
`,u=e=>s(`sha256`).update(e).digest(`hex`);async function d(e,t,n){let a;try{a=await r(e,`utf8`)}catch(r){return r.code===`ENOENT`||n?.(`autoi18n: could not read ${e}: ${r.message}`),t}try{return JSON.parse(a)}catch(r){let a=`${e}.corrupt-${Date.now()}`;return await i(e,a).catch(()=>void 0),n?.(`autoi18n: ${e} is not valid JSON (${r.message}); moved to ${a} and starting fresh.`),t}}async function f(e,t){await n(o.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${c()}.tmp`;await a(r,t,`utf8`),await i(r,e)}async function p(e,t){await r(e,`utf8`).catch(()=>void 0)!==t&&await f(e,t)}const m=t({choices:t({message:{"content?":`string | null`}}).array().atLeastLength(1)}),h=t({content:t({type:`string`,"text?":`string`}).array()}),ee=t({candidates:t({"content?":{"parts?":t({"text?":`string`}).array()}}).array()}),g=t(`Record<string, unknown>`),_=t({messages:`Record<string, unknown>`,sources:`Record<string, unknown>`}),v=t({id:`string`,source:`string`,kind:`'text' | 'attribute' | 'mixed' | 'string'`,tag:`string`,component:`string`,"attribute?":`string`,context:`string`,fingerprint:`string`,location:`string`,status:`'active' | 'orphaned'`,firstSeen:`string`,lastSeen:`string`}).onUndeclaredKey(`delete`),te=t({version:`1`,entries:`Record<string, unknown>`}),y=t(`string >= 1`),b=t({locales:y.array(),"sourceLocale?":y.or(`undefined`),"delivery?":`'build' | 'runtime' | undefined`,"locale?":y.or(`undefined`),"include?":`string[] | undefined`,"exclude?":`string[] | undefined`,"directory?":y.or(`undefined`),"provider?":`object | undefined`,"failOnMissingProvider?":`boolean | undefined`,"onAmbiguous?":`'warn' | 'error' | undefined`,"orphanRetentionMinutes?":t(`number >= 0`).or(t.unit(1/0)).or(`undefined`),"localeConcurrency?":`number >= 1 | undefined`,"componentProps?":`Record<string, string[]> | undefined`,"stringMarker?":y.or(`undefined`),"debug?":`boolean | undefined`}),x=()=>({messages:{},sources:{}});function S(e,t){return o.join(e,`${t}.json`)}const C=(e,t,n)=>{let r={};for(let[i,a]of Object.entries(e))typeof a==`string`?r[i]=a:t?.(`autoi18n: dropped non-text value for "${i}" in ${n}.`);return r};async function w(t,n){let r=await d(t,null,n),i=_(r);return i instanceof e?x():{messages:C(i.messages,n,t),sources:C(i.sources,n,t)}}const T=(e,t)=>p(e,l(t));function E(e,t){let n={},r=[];for(let[i,a]of Object.entries(e))t.has(i)?n[i]=a:r.push(i);return{catalog:n,removed:r}}function D(e,t){let n=E(e.messages,t),r=E(e.sources,t);return{file:{messages:n.catalog,sources:r.catalog},removed:[.../* @__PURE__ */ new Set([...n.removed,...r.removed])]}}const O=`autoi18n:update`,k=[`src/**/*.{tsx,jsx,ts,js}`],A=[`**/*.test.*`,`**/node_modules/**`],j=`https://api.anthropic.com/v1`,M=`2023-06-01`,N=`https://api.openai.com/v1`,P=`https://generativelanguage.googleapis.com/v1beta`,F=6e4,I=43200,ne=`virtual:autoi18n`,L=`virtual:autoi18n-format`,R=`intl-messageformat`,z=`__autoI18nT`,B=`__autoI18nFormat`,V=`__autoI18nLocale`,H=/^[A-Z]/,U=/* @__PURE__ */ new Set([`script`,`style`,`code`,`pre`]),W=`children`,G=/* @__PURE__ */ new Set([`alt`,`title`,`placeholder`,`label`,`summary`,`aria-label`,`aria-description`,`aria-placeholder`,`aria-roledescription`,`aria-valuetext`]),K=`i18n`,q=`translate`,J=new Map(`quot:0022 amp:0026 apos:0027 lt:003C gt:003E nbsp:00A0 iexcl:00A1 cent:00A2 pound:00A3 curren:00A4 yen:00A5 brvbar:00A6 sect:00A7 uml:00A8 copy:00A9 ordf:00AA laquo:00AB not:00AC shy:00AD reg:00AE macr:00AF deg:00B0 plusmn:00B1 sup2:00B2 sup3:00B3 acute:00B4 micro:00B5 para:00B6 middot:00B7 cedil:00B8 sup1:00B9 ordm:00BA raquo:00BB frac14:00BC frac12:00BD frac34:00BE iquest:00BF Agrave:00C0 Aacute:00C1 Acirc:00C2 Atilde:00C3 Auml:00C4 Aring:00C5 AElig:00C6 Ccedil:00C7 Egrave:00C8 Eacute:00C9 Ecirc:00CA Euml:00CB Igrave:00CC Iacute:00CD Icirc:00CE Iuml:00CF ETH:00D0 Ntilde:00D1 Ograve:00D2 Oacute:00D3 Ocirc:00D4 Otilde:00D5 Ouml:00D6 times:00D7 Oslash:00D8 Ugrave:00D9 Uacute:00DA Ucirc:00DB Uuml:00DC Yacute:00DD THORN:00DE szlig:00DF agrave:00E0 aacute:00E1 acirc:00E2 atilde:00E3 auml:00E4 aring:00E5 aelig:00E6 ccedil:00E7 egrave:00E8 eacute:00E9 ecirc:00EA euml:00EB igrave:00EC iacute:00ED icirc:00EE iuml:00EF eth:00F0 ntilde:00F1 ograve:00F2 oacute:00F3 ocirc:00F4 otilde:00F5 ouml:00F6 divide:00F7 oslash:00F8 ugrave:00F9 uacute:00FA ucirc:00FB uuml:00FC yacute:00FD thorn:00FE yuml:00FF OElig:0152 oelig:0153 Scaron:0160 scaron:0161 Yuml:0178 fnof:0192 circ:02C6 tilde:02DC Alpha:0391 Beta:0392 Gamma:0393 Delta:0394 Epsilon:0395 Zeta:0396 Eta:0397 Theta:0398 Iota:0399 Kappa:039A Lambda:039B Mu:039C Nu:039D Xi:039E Omicron:039F Pi:03A0 Rho:03A1 Sigma:03A3 Tau:03A4 Upsilon:03A5 Phi:03A6 Chi:03A7 Psi:03A8 Omega:03A9 alpha:03B1 beta:03B2 gamma:03B3 delta:03B4 epsilon:03B5 zeta:03B6 eta:03B7 theta:03B8 iota:03B9 kappa:03BA lambda:03BB mu:03BC nu:03BD xi:03BE omicron:03BF pi:03C0 rho:03C1 sigmaf:03C2 sigma:03C3 tau:03C4 upsilon:03C5 phi:03C6 chi:03C7 psi:03C8 omega:03C9 thetasym:03D1 upsih:03D2 piv:03D6 ensp:2002 emsp:2003 thinsp:2009 zwnj:200C zwj:200D lrm:200E rlm:200F ndash:2013 mdash:2014 lsquo:2018 rsquo:2019 sbquo:201A ldquo:201C rdquo:201D bdquo:201E dagger:2020 Dagger:2021 bull:2022 hellip:2026 permil:2030 prime:2032 Prime:2033 lsaquo:2039 rsaquo:203A oline:203E frasl:2044 euro:20AC image:2111 weierp:2118 real:211C trade:2122 alefsym:2135 larr:2190 uarr:2191 rarr:2192 darr:2193 harr:2194 crarr:21B5 lArr:21D0 uArr:21D1 rArr:21D2 dArr:21D3 hArr:21D4 forall:2200 part:2202 exist:2203 empty:2205 nabla:2207 isin:2208 notin:2209 ni:220B prod:220F sum:2211 minus:2212 lowast:2217 radic:221A prop:221D infin:221E ang:2220 and:2227 or:2228 cap:2229 cup:222A int:222B there4:2234 sim:223C cong:2245 asymp:2248 ne:2260 equiv:2261 le:2264 ge:2265 sub:2282 sup:2283 nsub:2284 sube:2286 supe:2287 oplus:2295 otimes:2297 perp:22A5 sdot:22C5 lceil:2308 rceil:2309 lfloor:230A rfloor:230B lang:2329 rang:232A loz:25CA spades:2660 clubs:2663 hearts:2665 diams:2666`.split(` `).map(e=>{let[t,n]=e.split(`:`);return[t,String.fromCharCode(parseInt(n,16))]})),Y=/&(?:#(x[0-9a-fA-F]+|[0-9]+)|([A-Za-z0-9]+));/g,X=()=>({version:1,entries:{}});async function Z(t,n){let r=await d(t,null,n);if(r===null)return X();let i=te(r);if(i instanceof e)return n?.(`autoi18n: ${t} has an unsupported format; message IDs will be regenerated.`),X();let a={};for(let[r,o]of Object.entries(i.entries)){let i=v(o);i instanceof e?n?.(`autoi18n: dropped invalid registry entry "${r}" in ${t}.`):a[r]=i}return{version:1,entries:a}}function Q(e,t){return`${e}:${String(t).padStart(12,`0`)}`}function $(e){let t=e.lastIndexOf(`:`);return Q(e.slice(0,t),Number(e.slice(t+1)))}function re(e,t){let n=(/* @__PURE__ */ new Date()).toISOString(),r=Object.values(e.entries),i=/* @__PURE__ */ new Map;for(let e of r){let t=i.get(e.fingerprint)??[];t.push(e),i.set(e.fingerprint,t)}let a=/* @__PURE__ */ new Map;for(let e of t){let t=a.get(e.fingerprint)??[];t.push(e),a.set(e.fingerprint,t)}let o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Set,l=[];for(let[t,r]of a){let a=(i.get(t)??[]).filter(e=>e.status===`active`);r.sort((e,t)=>Q(e.file,e.start).localeCompare(Q(t.file,t.start))),a.sort((e,t)=>$(e.location).localeCompare($(t.location))),r.length!==a.length&&(r.length>1||a.length>1)&&l.push(r[0].source);let u=Array(r.length),d=/* @__PURE__ */ new Set,f=/* @__PURE__ */ new Map;for(let e of a){let t=f.get(e.component)??[];t.push(e),f.set(e.component,t)}r.forEach((e,t)=>{let n=f.get(e.component)?.shift();n&&(u[t]=n,d.add(n))});let p=a.filter(e=>!d.has(e)),m=0;r.forEach((e,t)=>{u[t]===void 0&&m<p.length&&(u[t]=p[m++])});for(let[t,i]of r.entries()){let r=u[t];if(r)o.set(i,r.id),r.status=`active`,r.lastSeen=n,r.source=i.source,r.component=i.component,r.location=`${i.file}:${i.start}`,s.add(r.id);else{let t=`msg_${c().replaceAll(`-`,``).slice(0,16)}`;o.set(i,t),e.entries[t]={id:t,source:i.source,kind:i.kind,tag:i.tag,component:i.component,attribute:i.attribute,context:i.context,fingerprint:i.fingerprint,location:`${i.file}:${i.start}`,status:`active`,firstSeen:n,lastSeen:n},s.add(t)}}}for(let t of Object.values(e.entries))s.has(t.id)||(t.status=`orphaned`);return{registry:e,ambiguous:l,messages:t.map(e=>{let t=o.get(e);if(t===void 0)throw Error(`autoi18n: no message ID was assigned to ${e.file}:${e.start}`);return{...e,id:t}})}}function ie(e,t,n=Date.now()){let r=n-t*6e4,i=[];for(let[t,n]of Object.entries(e.entries))n.status===`orphaned`&&Date.parse(n.lastSeen)<=r&&(delete e.entries[t],i.push(n));return i}async function ae(e,t){await p(e,l(t))}export{D as A,V as C,O as D,G as E,ee as F,b as I,g as L,T as M,h as N,x as O,m as P,u as R,B as S,ne as T,q as _,j as a,N as b,H as c,I as d,K as f,U as g,P as h,ae as i,w as j,S as k,A as l,L as m,ie as n,M as o,R as p,re as r,W as s,Z as t,k as u,F as v,z as w,Y as x,J as y};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-ai-auto-i18n",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Implicit, build-time AI localization for JSX applications",
|
|
6
6
|
"license": "ISC",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"analyze": "ANALYZE=1 tsdown",
|
|
46
46
|
"prepublishOnly": "npm run build",
|
|
47
47
|
"prune": "npm run build && node dist/cli/prune.js --directory example/.autoi18n --retention-minutes 0",
|
|
48
|
+
"stats": "npm run build && node dist/cli/stats.js --directory example/.autoi18n",
|
|
48
49
|
"example:dev": "vite example",
|
|
49
50
|
"example:build": "vite build example",
|
|
50
51
|
"preview": "vite build example && vite preview example --port 4173 --strictPort",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
60
|
"arktype": "^2.2.9",
|
|
61
|
+
"intl-messageformat": "^12.1.5",
|
|
60
62
|
"magic-string": "^1.4.5"
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{ArkErrors as e,type as t}from"arktype";import{mkdir as n,readFile as r,rename as i,writeFile as a}from"node:fs/promises";import o from"node:path";import{createHash as s,randomUUID as c}from"node:crypto";const l=e=>console.log(`[autoi18n debug] ${(/* @__PURE__ */ new Date()).toISOString()} ${e}`),u=e=>JSON.stringify(e,null,2)+`
|
|
2
|
-
`,d=e=>s(`sha256`).update(e).digest(`hex`);async function f(e,t,n){let a;try{a=await r(e,`utf8`)}catch(r){return r.code===`ENOENT`||n?.(`autoi18n: could not read ${e}: ${r.message}`),t}try{return JSON.parse(a)}catch(r){let a=`${e}.corrupt-${Date.now()}`;return await i(e,a).catch(()=>void 0),n?.(`autoi18n: ${e} is not valid JSON (${r.message}); moved to ${a} and starting fresh.`),t}}async function p(e,t){await n(o.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${c()}.tmp`;await a(r,t,`utf8`),await i(r,e)}async function m(e,t){await r(e,`utf8`).catch(()=>void 0)!==t&&await p(e,t)}const ee=t({choices:t({message:{"content?":`string | null`}}).array().atLeastLength(1)}),h=t({content:t({type:`string`,"text?":`string`}).array()}),g=t({candidates:t({"content?":{"parts?":t({"text?":`string`}).array()}}).array()}),_=t(`Record<string, unknown>`),v=t({messages:`Record<string, unknown>`,sources:`Record<string, unknown>`}),y=t({id:`string`,source:`string`,kind:`'text' | 'attribute' | 'mixed' | 'string'`,tag:`string`,component:`string`,"attribute?":`string`,context:`string`,fingerprint:`string`,location:`string`,status:`'active' | 'orphaned'`,firstSeen:`string`,lastSeen:`string`}).onUndeclaredKey(`delete`),b=t({version:`1`,entries:`Record<string, unknown>`}),x=t(`string >= 1`),S=t({locales:x.array(),"sourceLocale?":x.or(`undefined`),"delivery?":`'build' | 'runtime' | undefined`,"locale?":x.or(`undefined`),"include?":`string[] | undefined`,"exclude?":`string[] | undefined`,"directory?":x.or(`undefined`),"provider?":`object | undefined`,"failOnMissingProvider?":`boolean | undefined`,"onAmbiguous?":`'warn' | 'error' | undefined`,"orphanRetentionMinutes?":t(`number >= 0`).or(t.unit(1/0)).or(`undefined`),"localeConcurrency?":`number >= 1 | undefined`,"componentProps?":`Record<string, string[]> | undefined`,"stringMarker?":x.or(`undefined`),"debug?":`boolean | undefined`}),C=()=>({messages:{},sources:{}});function w(e,t){return o.join(e,`${t}.json`)}const T=(e,t,n)=>{let r={};for(let[i,a]of Object.entries(e))typeof a==`string`?r[i]=a:t?.(`autoi18n: dropped non-text value for "${i}" in ${n}.`);return r};async function E(t,n){let r=await f(t,null,n),i=v(r);return i instanceof e?C():{messages:T(i.messages,n,t),sources:T(i.sources,n,t)}}const D=(e,t)=>m(e,u(t));function O(e,t){let n={},r=[];for(let[i,a]of Object.entries(e))t.has(i)?n[i]=a:r.push(i);return{catalog:n,removed:r}}const k=`autoi18n:update`,A=[`src/**/*.{tsx,jsx,ts,js}`],j=[`**/*.test.*`,`**/node_modules/**`],M=`https://api.anthropic.com/v1`,N=`2023-06-01`,P=`https://api.openai.com/v1`,F=`https://generativelanguage.googleapis.com/v1beta`,I=6e4,L=43200,te=`virtual:autoi18n`,R=`__autoI18nT`,z=`__autoI18nFormat`,B=/^[A-Z]/,V=/* @__PURE__ */ new Set([`script`,`style`,`code`,`pre`]),H=`children`,U=/* @__PURE__ */ new Set([`alt`,`title`,`placeholder`,`label`,`summary`,`aria-label`,`aria-description`,`aria-placeholder`,`aria-roledescription`,`aria-valuetext`]),W=`i18n`,G=`translate`,K=new Map(`quot:0022 amp:0026 apos:0027 lt:003C gt:003E nbsp:00A0 iexcl:00A1 cent:00A2 pound:00A3 curren:00A4 yen:00A5 brvbar:00A6 sect:00A7 uml:00A8 copy:00A9 ordf:00AA laquo:00AB not:00AC shy:00AD reg:00AE macr:00AF deg:00B0 plusmn:00B1 sup2:00B2 sup3:00B3 acute:00B4 micro:00B5 para:00B6 middot:00B7 cedil:00B8 sup1:00B9 ordm:00BA raquo:00BB frac14:00BC frac12:00BD frac34:00BE iquest:00BF Agrave:00C0 Aacute:00C1 Acirc:00C2 Atilde:00C3 Auml:00C4 Aring:00C5 AElig:00C6 Ccedil:00C7 Egrave:00C8 Eacute:00C9 Ecirc:00CA Euml:00CB Igrave:00CC Iacute:00CD Icirc:00CE Iuml:00CF ETH:00D0 Ntilde:00D1 Ograve:00D2 Oacute:00D3 Ocirc:00D4 Otilde:00D5 Ouml:00D6 times:00D7 Oslash:00D8 Ugrave:00D9 Uacute:00DA Ucirc:00DB Uuml:00DC Yacute:00DD THORN:00DE szlig:00DF agrave:00E0 aacute:00E1 acirc:00E2 atilde:00E3 auml:00E4 aring:00E5 aelig:00E6 ccedil:00E7 egrave:00E8 eacute:00E9 ecirc:00EA euml:00EB igrave:00EC iacute:00ED icirc:00EE iuml:00EF eth:00F0 ntilde:00F1 ograve:00F2 oacute:00F3 ocirc:00F4 otilde:00F5 ouml:00F6 divide:00F7 oslash:00F8 ugrave:00F9 uacute:00FA ucirc:00FB uuml:00FC yacute:00FD thorn:00FE yuml:00FF OElig:0152 oelig:0153 Scaron:0160 scaron:0161 Yuml:0178 fnof:0192 circ:02C6 tilde:02DC Alpha:0391 Beta:0392 Gamma:0393 Delta:0394 Epsilon:0395 Zeta:0396 Eta:0397 Theta:0398 Iota:0399 Kappa:039A Lambda:039B Mu:039C Nu:039D Xi:039E Omicron:039F Pi:03A0 Rho:03A1 Sigma:03A3 Tau:03A4 Upsilon:03A5 Phi:03A6 Chi:03A7 Psi:03A8 Omega:03A9 alpha:03B1 beta:03B2 gamma:03B3 delta:03B4 epsilon:03B5 zeta:03B6 eta:03B7 theta:03B8 iota:03B9 kappa:03BA lambda:03BB mu:03BC nu:03BD xi:03BE omicron:03BF pi:03C0 rho:03C1 sigmaf:03C2 sigma:03C3 tau:03C4 upsilon:03C5 phi:03C6 chi:03C7 psi:03C8 omega:03C9 thetasym:03D1 upsih:03D2 piv:03D6 ensp:2002 emsp:2003 thinsp:2009 zwnj:200C zwj:200D lrm:200E rlm:200F ndash:2013 mdash:2014 lsquo:2018 rsquo:2019 sbquo:201A ldquo:201C rdquo:201D bdquo:201E dagger:2020 Dagger:2021 bull:2022 hellip:2026 permil:2030 prime:2032 Prime:2033 lsaquo:2039 rsaquo:203A oline:203E frasl:2044 euro:20AC image:2111 weierp:2118 real:211C trade:2122 alefsym:2135 larr:2190 uarr:2191 rarr:2192 darr:2193 harr:2194 crarr:21B5 lArr:21D0 uArr:21D1 rArr:21D2 dArr:21D3 hArr:21D4 forall:2200 part:2202 exist:2203 empty:2205 nabla:2207 isin:2208 notin:2209 ni:220B prod:220F sum:2211 minus:2212 lowast:2217 radic:221A prop:221D infin:221E ang:2220 and:2227 or:2228 cap:2229 cup:222A int:222B there4:2234 sim:223C cong:2245 asymp:2248 ne:2260 equiv:2261 le:2264 ge:2265 sub:2282 sup:2283 nsub:2284 sube:2286 supe:2287 oplus:2295 otimes:2297 perp:22A5 sdot:22C5 lceil:2308 rceil:2309 lfloor:230A rfloor:230B lang:2329 rang:232A loz:25CA spades:2660 clubs:2663 hearts:2665 diams:2666`.split(` `).map(e=>{let[t,n]=e.split(`:`);return[t,String.fromCharCode(parseInt(n,16))]})),q=/&(?:#(x[0-9a-fA-F]+|[0-9]+)|([A-Za-z0-9]+));/g,J=`\\{([^{}]+)\\}`,Y=RegExp(J,`g`),X=()=>({version:1,entries:{}});async function Z(t,n){let r=await f(t,null,n);if(r===null)return X();let i=b(r);if(i instanceof e)return n?.(`autoi18n: ${t} has an unsupported format; message IDs will be regenerated.`),X();let a={};for(let[r,o]of Object.entries(i.entries)){let i=y(o);i instanceof e?n?.(`autoi18n: dropped invalid registry entry "${r}" in ${t}.`):a[r]=i}return{version:1,entries:a}}function Q(e,t){return`${e}:${String(t).padStart(12,`0`)}`}function $(e){let t=e.lastIndexOf(`:`);return Q(e.slice(0,t),Number(e.slice(t+1)))}function ne(e,t){let n=(/* @__PURE__ */ new Date()).toISOString(),r=Object.values(e.entries),i=/* @__PURE__ */ new Map;for(let e of r){let t=i.get(e.fingerprint)??[];t.push(e),i.set(e.fingerprint,t)}let a=/* @__PURE__ */ new Map;for(let e of t){let t=a.get(e.fingerprint)??[];t.push(e),a.set(e.fingerprint,t)}let o=/* @__PURE__ */ new Map,s=/* @__PURE__ */ new Set,l=[];for(let[t,r]of a){let a=(i.get(t)??[]).filter(e=>e.status===`active`);r.sort((e,t)=>Q(e.file,e.start).localeCompare(Q(t.file,t.start))),a.sort((e,t)=>$(e.location).localeCompare($(t.location))),r.length!==a.length&&(r.length>1||a.length>1)&&l.push(r[0].source);let u=Array(r.length),d=/* @__PURE__ */ new Set,f=/* @__PURE__ */ new Map;for(let e of a){let t=f.get(e.component)??[];t.push(e),f.set(e.component,t)}r.forEach((e,t)=>{let n=f.get(e.component)?.shift();n&&(u[t]=n,d.add(n))});let p=a.filter(e=>!d.has(e)),m=0;r.forEach((e,t)=>{u[t]===void 0&&m<p.length&&(u[t]=p[m++])});for(let[t,i]of r.entries()){let r=u[t];if(r)o.set(i,r.id),r.status=`active`,r.lastSeen=n,r.source=i.source,r.component=i.component,r.location=`${i.file}:${i.start}`,s.add(r.id);else{let t=`msg_${c().replaceAll(`-`,``).slice(0,16)}`;o.set(i,t),e.entries[t]={id:t,source:i.source,kind:i.kind,tag:i.tag,component:i.component,attribute:i.attribute,context:i.context,fingerprint:i.fingerprint,location:`${i.file}:${i.start}`,status:`active`,firstSeen:n,lastSeen:n},s.add(t)}}}for(let t of Object.values(e.entries))s.has(t.id)||(t.status=`orphaned`);return{registry:e,ambiguous:l,messages:t.map(e=>{let t=o.get(e);if(t===void 0)throw Error(`autoi18n: no message ID was assigned to ${e.file}:${e.start}`);return{...e,id:t}})}}function re(e,t,n=Date.now()){let r=n-t*6e4,i=[];for(let[t,n]of Object.entries(e.entries))n.status===`orphaned`&&Date.parse(n.lastSeen)<=r&&(delete e.entries[t],i.push(n));return i}async function ie(e,t){await m(e,u(t))}export{E as A,R as C,C as D,k as E,S as F,_ as I,d as L,h as M,ee as N,w as O,g as P,l as R,z as S,U as T,K as _,M as a,J as b,B as c,L as d,W as f,I as g,G as h,ie as i,D as j,O as k,j as l,V as m,re as n,N as o,F as p,ne as r,H as s,Z as t,A as u,P as v,te as w,q as x,Y as y};
|