@menapo/vi18n 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +49 -0
- package/README.md +110 -2
- package/dist/apply-Bre-lY9J.js +228 -0
- package/dist/captured-Bj8bO6vd.js +1034 -0
- package/dist/check-BIKEuu6B.js +201 -0
- package/dist/check-HsmmpJZg.js +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +41 -0
- package/dist/codec-DuvU2VQy.js +207 -0
- package/dist/collect-Cx4_SKOe.js +13 -0
- package/dist/collisions-CfpZU8e4.js +217 -0
- package/dist/compile-BaDh0cHG.js +245 -0
- package/dist/compile-Cc97JaW2.js +2502 -0
- package/dist/concurrency-C9UUWuDC.js +96 -0
- package/dist/concurrency-DONL7t4V.js +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.js +39 -0
- package/dist/dir-zw6bGMKQ.js +290 -0
- package/dist/duplicates-yuRljQ6h.js +185 -0
- package/dist/engine-C2ENBRJe.js +531 -0
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +1241 -0
- package/dist/exclude-Du-Zd_qI.js +26 -0
- package/dist/export-C9Sy6UWv.js +103 -0
- package/dist/extract-C3S8btx0.js +298 -0
- package/dist/formats-9unqj_fZ.js +781 -0
- package/dist/i18next-9HAoxwUI.js +1 -0
- package/dist/i18next-BEJgv_S8.js +1596 -0
- package/dist/import-nANvQutw.js +225 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.js +239 -0
- package/dist/init-C2ScuW6a.js +792 -0
- package/dist/library-D5CWvTQs.js +1882 -0
- package/dist/lingui-zUiCl9Um.js +833 -0
- package/dist/lint.d.ts +105 -0
- package/dist/lint.js +971 -0
- package/dist/load-CQRz4PpM.js +2950 -0
- package/dist/load-gW-oJt1Q.js +747 -0
- package/dist/locales-DX4MOBFE.js +827 -0
- package/dist/lsp-RMMMc88P.js +31 -0
- package/dist/match-BGr8EXB3.js +89 -0
- package/dist/mcp-CHVwBoRP.js +28 -0
- package/dist/merge-driver-BTX87aVm.js +422 -0
- package/dist/messages-BfjIExyB.js +166 -0
- package/dist/migrate-IpLDMXmq.js +1484 -0
- package/dist/migrate-rCQuOGsf.js +1 -0
- package/dist/openai-CTaMDcl-.js +346 -0
- package/dist/output-DWE3SIUA.js +164 -0
- package/dist/parse.d.ts +62 -0
- package/dist/parse.js +156 -0
- package/dist/pipeline-C9Dbotkh.js +2031 -0
- package/dist/pipeline-DbX6rlo5.js +1 -0
- package/dist/project-Bw9UPIdx.js +1046 -0
- package/dist/project-C4GpaN-n.js +381 -0
- package/dist/pseudo-DRv30iN5.js +145 -0
- package/dist/raw-C4Y6vyGY.js +4708 -0
- package/dist/react-intl-BP0u-D5m.js +519 -0
- package/dist/restore-CiYv-jxS.js +175 -0
- package/dist/rewrite-EsIw_lsq.js +803 -0
- package/dist/rich-DCUKl5HM.js +97 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/run-BEcnBbu-.js +694 -0
- package/dist/runtime-DTMmliT1.js +64 -0
- package/dist/scan-worker.d.ts +62 -0
- package/dist/scan-worker.js +1 -0
- package/dist/scoped-BMYpSthj.js +44 -0
- package/dist/server-Da1Ot9IN.js +10485 -0
- package/dist/server-deBOq2vq.js +11823 -0
- package/dist/server.d.ts +169 -0
- package/dist/server.js +1234 -0
- package/dist/sitemap-DJXzKoza.js +159 -0
- package/dist/sources-D19czDSA.js +2242 -0
- package/dist/status-zr4jY59Y.js +149 -0
- package/dist/stdio-CVaJfxA7.js +29 -0
- package/dist/stdio-FPOKQAgu.js +95 -0
- package/dist/sync-B9V2KFwb.js +658 -0
- package/dist/t-DC-c9zE5.js +454 -0
- package/dist/test.d.ts +116 -0
- package/dist/test.js +319 -0
- package/dist/translate-CQnJfLjO.js +299 -0
- package/dist/ts-BaU5yOf0.js +4863 -0
- package/dist/types-CgUCkFU1.d.ts +707 -0
- package/dist/types-D3QGM8XO.js +146 -0
- package/dist/types-DsdWBG0b.d.ts +558 -0
- package/dist/types-DtvQ7UdD.js +1948 -0
- package/dist/types-wSh9fCBY.js +11 -0
- package/dist/units-CQ5Q99RC.js +150 -0
- package/dist/url-CRfCOhUI.js +172 -0
- package/dist/url-CsBkRaJy.js +225 -0
- package/dist/validate-BxpupdAf.js +2615 -0
- package/dist/validate-D6DJepZv.js +365 -0
- package/dist/version-Byqi2guX.js +28 -0
- package/dist/vite-scan-CT_vsp4g.js +242 -0
- package/dist/vite.d.ts +34 -0
- package/dist/vite.js +7558 -0
- package/dist/vue-i18n-g0iGy6CZ.js +651 -0
- package/dist/which-BOemZ5Q8.js +116 -0
- package/dist/worker-84U6uYB3.js +1656 -0
- package/package.json +97 -3
- package/schemas/catalog.json +57 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { m as updateCatalogFile } from "./library-D5CWvTQs.js";
|
|
2
|
+
import { t as VERSION } from "./version-Byqi2guX.js";
|
|
3
|
+
import { c as sourceIndexOf } from "./units-CQ5Q99RC.js";
|
|
4
|
+
import { i as rootRelative, r as plural } from "./output-DWE3SIUA.js";
|
|
5
|
+
import { t as EXIT } from "./types-wSh9fCBY.js";
|
|
6
|
+
import { a as loadCatalogs, o as loadProject, t as CliExit, u as readErrors } from "./project-C4GpaN-n.js";
|
|
7
|
+
import { t as readImport } from "./formats-9unqj_fZ.js";
|
|
8
|
+
import { t as applyImport } from "./apply-Bre-lY9J.js";
|
|
9
|
+
import { basename, extname, resolve } from "node:path";
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
//#region src/cli/commands/import.ts
|
|
12
|
+
/**
|
|
13
|
+
* `vi18n import [--format f] [--locale l] [--dry-run] <file>…` (PLAN §4.4,
|
|
14
|
+
* §5.19, INTERNALS §2.6.9): translations from exchange files into the
|
|
15
|
+
* catalogs. A row is taken only for the exact source it was made for (D10),
|
|
16
|
+
* and only into an entry the catalog already has, so an import never adds
|
|
17
|
+
* messages or touches the source locale; everything left out is reported
|
|
18
|
+
* with its reason. Writes go through the compare-and-swap writer, so a dev
|
|
19
|
+
* server or another command writing meanwhile loses nothing.
|
|
20
|
+
*/
|
|
21
|
+
/** Skipped rows of these reasons are listed one by one; the rest are counted. */
|
|
22
|
+
const LISTED = /* @__PURE__ */ new Set([
|
|
23
|
+
"source-changed",
|
|
24
|
+
"no-source",
|
|
25
|
+
"unsupported",
|
|
26
|
+
"duplicate"
|
|
27
|
+
]);
|
|
28
|
+
/** Per reason and file, so one stale export does not flood the terminal. */
|
|
29
|
+
const LIST_LIMIT = 20;
|
|
30
|
+
const WHY = {
|
|
31
|
+
unknown: "no entry has this key; run vi18n extract, or the message is gone",
|
|
32
|
+
"source-changed": "translated for other source text than the code has now",
|
|
33
|
+
"no-source": "nothing says which source text it was made for",
|
|
34
|
+
empty: "empty",
|
|
35
|
+
draft: "marked as needing work",
|
|
36
|
+
invalid: "not a valid translation of its source",
|
|
37
|
+
unsupported: "uses markup vi18n cannot read back",
|
|
38
|
+
duplicate: "a second row for the same key with another translation"
|
|
39
|
+
};
|
|
40
|
+
const UTF8 = new TextDecoder("utf-8", { fatal: true });
|
|
41
|
+
async function importCatalogs(ctx, flags) {
|
|
42
|
+
const project = await loadProject(ctx);
|
|
43
|
+
const { config, codec, layout } = project;
|
|
44
|
+
const loaded = await loadCatalogs(project);
|
|
45
|
+
const unreadable = readErrors(loaded.diagnostics);
|
|
46
|
+
if (unreadable.length > 0) throw new CliExit(EXIT.failure, unreadable);
|
|
47
|
+
const context = {
|
|
48
|
+
layout,
|
|
49
|
+
codec,
|
|
50
|
+
sourceIndex: sourceIndexOf(loaded.set.get(config.sourceLocale)?.catalog ?? null, layout, codec)
|
|
51
|
+
};
|
|
52
|
+
const data = {
|
|
53
|
+
dryRun: flags.dryRun,
|
|
54
|
+
files: [],
|
|
55
|
+
changed: [],
|
|
56
|
+
skipped: []
|
|
57
|
+
};
|
|
58
|
+
let failed = false;
|
|
59
|
+
for (const name of flags.files) {
|
|
60
|
+
const path = resolve(ctx.global.cwd, name);
|
|
61
|
+
const shown = rootRelative(config.root, path);
|
|
62
|
+
const problem = (message, line = 0) => {
|
|
63
|
+
failed = true;
|
|
64
|
+
const diagnostic = {
|
|
65
|
+
code: "import-parse",
|
|
66
|
+
severity: "error",
|
|
67
|
+
message,
|
|
68
|
+
file: path
|
|
69
|
+
};
|
|
70
|
+
if (line > 0) diagnostic.line = line;
|
|
71
|
+
ctx.out.report([diagnostic]);
|
|
72
|
+
};
|
|
73
|
+
let text;
|
|
74
|
+
try {
|
|
75
|
+
text = UTF8.decode(await readFile(path));
|
|
76
|
+
} catch (error) {
|
|
77
|
+
const code = error.code;
|
|
78
|
+
problem(code === "ENOENT" ? "no such file" : error instanceof TypeError ? "the file is not valid UTF-8; export it as UTF-8" : `cannot read it: ${error instanceof Error ? error.message : String(error)}`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const format = flags.format ?? inferFormat(path, text);
|
|
82
|
+
if (format === null) {
|
|
83
|
+
problem(`cannot tell the format from the extension ${JSON.stringify(extname(path))}; pass --format`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const read = readImport(format, text, { msgid: config.catalogs.msgid });
|
|
87
|
+
if (read.error !== null) {
|
|
88
|
+
problem(`not a ${format} file vi18n can read: ${read.error.message}`, read.error.line);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (read.srcLang != null && read.srcLang !== config.sourceLocale) {
|
|
92
|
+
problem(`its source language is ${read.srcLang}, but sourceLocale is ${config.sourceLocale}`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const fromName = basename(path, extname(path));
|
|
96
|
+
const named = read.locale ?? (config.locales.includes(fromName) ? fromName : null);
|
|
97
|
+
if (flags.locale !== null && named !== null && read.locale !== null && flags.locale !== named) {
|
|
98
|
+
problem(`the file is for ${named}, not --locale ${flags.locale}`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const locale = flags.locale ?? named;
|
|
102
|
+
if (locale === null) {
|
|
103
|
+
problem("the file does not name its locale; pass --locale");
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (locale === config.sourceLocale) {
|
|
107
|
+
problem(`${locale} is the source locale, which is compiled from code and takes no translations`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (!config.locales.includes(locale)) {
|
|
111
|
+
problem(`${locale} is not in locales (${config.locales.join(", ")})`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const target = loaded.set.get(locale);
|
|
115
|
+
const apply = (catalog) => applyImport({
|
|
116
|
+
catalog,
|
|
117
|
+
records: read.records,
|
|
118
|
+
context,
|
|
119
|
+
locale
|
|
120
|
+
});
|
|
121
|
+
let outcome = null;
|
|
122
|
+
if (target === void 0 || target.catalog === null) {
|
|
123
|
+
problem(`${locale} has no catalog yet; run vi18n extract first`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (flags.dryRun) outcome = apply(target.catalog);
|
|
127
|
+
else await updateCatalogFile(target.path, target.hash, (current) => {
|
|
128
|
+
if (current === null) return null;
|
|
129
|
+
outcome = apply(current);
|
|
130
|
+
return outcome.catalog === current ? null : outcome.catalog;
|
|
131
|
+
}, {
|
|
132
|
+
locale,
|
|
133
|
+
layout,
|
|
134
|
+
isSourceLocale: false,
|
|
135
|
+
version: VERSION
|
|
136
|
+
});
|
|
137
|
+
if (outcome === null) {
|
|
138
|
+
problem(`${locale}'s catalog disappeared while importing; run vi18n extract`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
report(ctx, data, {
|
|
142
|
+
file: shown,
|
|
143
|
+
format,
|
|
144
|
+
locale,
|
|
145
|
+
outcome,
|
|
146
|
+
path
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
exitCode: failed ? EXIT.failure : EXIT.ok,
|
|
151
|
+
data
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function report(ctx, data, input) {
|
|
155
|
+
const { file, format, locale, outcome, path } = input;
|
|
156
|
+
const counts = {};
|
|
157
|
+
const listed = {};
|
|
158
|
+
const warnings = [];
|
|
159
|
+
for (const row of outcome.skipped) {
|
|
160
|
+
counts[row.reason] = (counts[row.reason] ?? 0) + 1;
|
|
161
|
+
data.skipped.push({
|
|
162
|
+
file,
|
|
163
|
+
locale,
|
|
164
|
+
line: row.line,
|
|
165
|
+
ref: row.ref,
|
|
166
|
+
id: row.id,
|
|
167
|
+
reason: row.reason
|
|
168
|
+
});
|
|
169
|
+
if (!LISTED.has(row.reason)) continue;
|
|
170
|
+
if ((listed[row.reason] = (listed[row.reason] ?? 0) + 1) > LIST_LIMIT) continue;
|
|
171
|
+
const diagnostic = {
|
|
172
|
+
code: "import-skipped",
|
|
173
|
+
severity: "warning",
|
|
174
|
+
message: `${row.ref}: ${WHY[row.reason]}; not imported`,
|
|
175
|
+
file: path,
|
|
176
|
+
locale
|
|
177
|
+
};
|
|
178
|
+
if (row.line > 0) diagnostic.line = row.line;
|
|
179
|
+
if (row.id !== null) diagnostic.id = row.id;
|
|
180
|
+
warnings.push(diagnostic);
|
|
181
|
+
}
|
|
182
|
+
ctx.out.report(warnings);
|
|
183
|
+
ctx.out.report(outcome.diagnostics.map((diagnostic) => ({
|
|
184
|
+
...diagnostic,
|
|
185
|
+
file: path
|
|
186
|
+
})));
|
|
187
|
+
for (const change of outcome.changed) data.changed.push({
|
|
188
|
+
file,
|
|
189
|
+
locale,
|
|
190
|
+
...change
|
|
191
|
+
});
|
|
192
|
+
data.files.push({
|
|
193
|
+
file,
|
|
194
|
+
format,
|
|
195
|
+
locale,
|
|
196
|
+
changed: outcome.changed.length,
|
|
197
|
+
unchanged: outcome.unchanged,
|
|
198
|
+
skipped: counts
|
|
199
|
+
});
|
|
200
|
+
const parts = Object.entries(counts).map(([reason, count]) => `${count} ${reason}`);
|
|
201
|
+
ctx.out.result(`${file} → ${locale}: ${outcome.changed.length} ${data.dryRun ? "to change" : "changed"}, ${outcome.unchanged} unchanged` + (parts.length === 0 ? "" : `, ${plural(outcome.skipped.length, "skipped row")} (${parts.join(", ")})`));
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The format from the extension. `.json` is vi18n's catalog format when the
|
|
205
|
+
* file has a `messages` object (a flat file's values are all strings), else
|
|
206
|
+
* flat JSON.
|
|
207
|
+
*/
|
|
208
|
+
function inferFormat(path, text) {
|
|
209
|
+
switch (extname(path).toLowerCase()) {
|
|
210
|
+
case ".po": return "po";
|
|
211
|
+
case ".xlf":
|
|
212
|
+
case ".xliff": return "xliff2";
|
|
213
|
+
case ".arb": return "arb";
|
|
214
|
+
case ".csv": return "csv";
|
|
215
|
+
case ".json": try {
|
|
216
|
+
const messages = JSON.parse(text.charCodeAt(0) === 65279 ? text.slice(1) : text)?.messages;
|
|
217
|
+
return typeof messages === "object" && messages !== null ? "json" : "flat";
|
|
218
|
+
} catch {
|
|
219
|
+
return "flat";
|
|
220
|
+
}
|
|
221
|
+
default: return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
export { importCatalogs };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { C as Locale, D as ValuesOf, E as RichValuesOf, S as LibraryMessages, T as RichPartsValuesOf, _ as TagHandler, a as Message, b as Values, c as MissingHandler, d as RichHtmlFunction, f as RichPart, g as TFunction, i as HtmlTagHandler, l as MissingInfo, n as Descriptor, o as MessageKey, p as RichPartsFunction, r as Fmt, s as MessageRef, t as DateInput, u as RichFunction, v as TemporalInput, w as Register, x as DefaultTags, y as Translator } from "./types-DsdWBG0b.js";
|
|
2
|
+
//#region src/runtime/t.d.ts
|
|
3
|
+
export declare const t: TFunction;
|
|
4
|
+
/**
|
|
5
|
+
* Declares a message without rendering it; the build turns the call into its
|
|
6
|
+
* id. `msg({ id })` alone names a message an opted-in package declares, typed
|
|
7
|
+
* from its text (`vi18n.gen.ts`).
|
|
8
|
+
*/
|
|
9
|
+
export declare function msg<const M extends string>(message: M): MessageRef<ValuesOf<M>>;
|
|
10
|
+
export declare function msg<const D extends Descriptor>(descriptor: D): MessageRef<ValuesOf<D["message"]>>;
|
|
11
|
+
export declare function msg<const K extends Extract<keyof LibraryMessages, string>>(descriptor: {
|
|
12
|
+
readonly id: K;
|
|
13
|
+
}): MessageRef<ValuesOf<LibraryMessages[K]>>;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/runtime/rich.d.ts
|
|
16
|
+
/**
|
|
17
|
+
* Tag handlers come from `values` and receive `(children, key)`; React and
|
|
18
|
+
* Preact glue keys their results, and nodes passed as values, through
|
|
19
|
+
* `State.richKey`. A tag without a handler renders its children.
|
|
20
|
+
*/
|
|
21
|
+
export declare const rich: RichFunction;
|
|
22
|
+
export declare const richHtml: RichHtmlFunction;
|
|
23
|
+
export declare const richParts: RichPartsFunction;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/runtime/macro.d.ts
|
|
26
|
+
/**
|
|
27
|
+
* The JSX `<T>` macro's runtime face (PLAN §4.1, INTERNALS §6 D215): the
|
|
28
|
+
* plugin's `vi18n:macro` pass replaces every `<T>…</T>` with a `rich()` call
|
|
29
|
+
* before React, Preact or Solid compile the JSX, so this function exists only
|
|
30
|
+
* to give the import a type and a value. Rendering it means no pass ran.
|
|
31
|
+
*/
|
|
32
|
+
/** What `<T>` takes: its children are the message; the rest feed extraction. */
|
|
33
|
+
interface TProps {
|
|
34
|
+
children?: unknown;
|
|
35
|
+
/** Values for the arguments of an ICU string child, `{"{n, plural, …}"}`. */
|
|
36
|
+
values?: {
|
|
37
|
+
readonly [name: string]: unknown;
|
|
38
|
+
};
|
|
39
|
+
/** A manual id, as in `rich({ id, message })`. */
|
|
40
|
+
id?: string;
|
|
41
|
+
/** Context for translators. */
|
|
42
|
+
context?: string;
|
|
43
|
+
/** Length limit for translations. */
|
|
44
|
+
maxLength?: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function T(props: TProps): never;
|
|
47
|
+
declare namespace fmt_d_exports {
|
|
48
|
+
export { collator, compact, compare, currency, currencyMinor, date, dateRange, displayName, duration, list, number, percent, relative, segment, time, unit };
|
|
49
|
+
}
|
|
50
|
+
declare const number: Fmt["number"];
|
|
51
|
+
declare const currency: Fmt["currency"];
|
|
52
|
+
declare const currencyMinor: Fmt["currencyMinor"];
|
|
53
|
+
declare const unit: Fmt["unit"];
|
|
54
|
+
declare const compact: Fmt["compact"];
|
|
55
|
+
declare const percent: Fmt["percent"];
|
|
56
|
+
declare const date: Fmt["date"];
|
|
57
|
+
declare const time: Fmt["time"];
|
|
58
|
+
declare const dateRange: Fmt["dateRange"];
|
|
59
|
+
declare const relative: Fmt["relative"];
|
|
60
|
+
declare const list: Fmt["list"];
|
|
61
|
+
declare const displayName: Fmt["displayName"];
|
|
62
|
+
declare const duration: Fmt["duration"];
|
|
63
|
+
declare const compare: Fmt["compare"];
|
|
64
|
+
declare const collator: Fmt["collator"];
|
|
65
|
+
declare const segment: Fmt["segment"];
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/runtime/name.d.ts
|
|
68
|
+
/**
|
|
69
|
+
* `localeName(locale)` (PLAN §4.1, §4.16): what a locale picker shows for a
|
|
70
|
+
* locale, its language's own name, and the `lang` of the text it shows, so
|
|
71
|
+
* screen readers read "Deutsch" in German inside an English page. It reads
|
|
72
|
+
* no state: an own-language name never depends on the current locale (the
|
|
73
|
+
* name in the current locale is `fmt.displayName`).
|
|
74
|
+
*/
|
|
75
|
+
/** `localeName()`'s result. */
|
|
76
|
+
interface LocaleName {
|
|
77
|
+
/** The language's own name, first letter upper-cased as menus show it: "Deutsch", "Français". */
|
|
78
|
+
name: string;
|
|
79
|
+
/**
|
|
80
|
+
* The language `name` is in, for the element's `lang`: `locale` itself, the
|
|
81
|
+
* engine's default locale when it has no data for that language (the name
|
|
82
|
+
* is then in that language), or `""` (unknown) when the name is the code.
|
|
83
|
+
*/
|
|
84
|
+
lang: string;
|
|
85
|
+
}
|
|
86
|
+
type LocaleNameOptions = Pick<Intl.DisplayNamesOptions, "style" | "languageDisplay">;
|
|
87
|
+
export declare function localeName(locale: string, options?: LocaleNameOptions): LocaleName;
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/runtime/zod.d.ts
|
|
90
|
+
/**
|
|
91
|
+
* `vi18nZod()` (PLAN §4.1, INTERNALS §6 D206): a zod 4 `customError` that
|
|
92
|
+
* words issues through vi18n messages in the current locale, so
|
|
93
|
+
* `z.config({ customError: vi18nZod() })` localises every schema without a
|
|
94
|
+
* message of its own. It imports nothing from zod: the issue is typed
|
|
95
|
+
* structurally and zod only ever calls the function.
|
|
96
|
+
*
|
|
97
|
+
* The texts are not here. The extractor treats a `vi18nZod()` call as the
|
|
98
|
+
* `msg()` of each text in `src/core/extract/zod.ts` (catalog references
|
|
99
|
+
* point at the call) and the transform passes them in that order, as ids in
|
|
100
|
+
* builds and descriptors in dev, so they ship in the bundle of the module
|
|
101
|
+
* that calls it. Called unprocessed, it has none and zod keeps its own.
|
|
102
|
+
*/
|
|
103
|
+
/** The issue fields the bridge reads (zod 4's `$ZodRawIssue`, structurally). */
|
|
104
|
+
interface ZodIssueLike {
|
|
105
|
+
readonly code?: string;
|
|
106
|
+
readonly input?: unknown;
|
|
107
|
+
readonly expected?: string;
|
|
108
|
+
readonly origin?: string;
|
|
109
|
+
readonly format?: string;
|
|
110
|
+
readonly minimum?: number | bigint;
|
|
111
|
+
readonly maximum?: number | bigint;
|
|
112
|
+
readonly inclusive?: boolean;
|
|
113
|
+
readonly exact?: boolean;
|
|
114
|
+
readonly keys?: readonly string[];
|
|
115
|
+
}
|
|
116
|
+
/** What `vi18nZod()` returns: zod 4's error-map shape. */
|
|
117
|
+
type ZodErrorMap = (issue: ZodIssueLike) => string | undefined;
|
|
118
|
+
export declare const vi18nZod: () => ZodErrorMap;
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/runtime/locale.d.ts
|
|
121
|
+
/** The active scope's locale: the request's on the server, the page's in the browser. */
|
|
122
|
+
export declare const getLocale: () => Locale;
|
|
123
|
+
/** Switches the page in place (or by document navigation, §2.11.7). Client only. */
|
|
124
|
+
export declare const setLocale: (locale: Locale) => Promise<void>;
|
|
125
|
+
/** Called with the new locale after every switch; returns an unsubscribe. */
|
|
126
|
+
export declare const subscribe: (listener: (locale: Locale) => void) => (() => void);
|
|
127
|
+
/** The app router's navigate function, for URL strategies (`setNavigator((href) => router.navigate(…))`). */
|
|
128
|
+
export declare const setNavigator: (navigate: ((href: string) => unknown) | null) => void;
|
|
129
|
+
/** Configured (and active pseudo-) locales, filled at registration; the same array forever. */
|
|
130
|
+
export declare const locales: readonly Locale[];
|
|
131
|
+
/** `"rtl"` or `"ltr"`; defaults to the current locale. */
|
|
132
|
+
export declare const dir: (locale?: string) => "ltr" | "rtl";
|
|
133
|
+
/** Settles once the page locale's boot and pending loads have settled. */
|
|
134
|
+
export declare const ready: () => Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* For router intent hooks: loads the route's message bundles. The build fills
|
|
137
|
+
* `keys` from the `import()` inside `load`, which is never called: routers own
|
|
138
|
+
* code prefetching (§6 D45).
|
|
139
|
+
*/
|
|
140
|
+
export declare const preload: (_load: () => Promise<unknown>, keys?: readonly string[]) => Promise<void>;
|
|
141
|
+
/** Missing-message reports, once per (key, locale); returns an unsubscribe. */
|
|
142
|
+
export declare const onMissing: (handler: MissingHandler) => (() => void);
|
|
143
|
+
/** A copy of `url` with its path in `locale` (default: the current one). */
|
|
144
|
+
export declare const localizeUrl: (url: URL | string, locale?: Locale) => URL;
|
|
145
|
+
/** A copy of `url` without its locale segment (router `rewrite`/`reroute` input). */
|
|
146
|
+
export declare const delocalizeUrl: (url: URL | string) => URL;
|
|
147
|
+
/**
|
|
148
|
+
* `href` in `locale` (default: the current one). Path-absolute and
|
|
149
|
+
* same-origin absolute hrefs are localized (with domains, hrefs on a listed
|
|
150
|
+
* host too, and a locale served by another listed host gives an absolute
|
|
151
|
+
* URL there); relative (`./x`, `x`, `?q`, `#h`) and cross-origin ones are
|
|
152
|
+
* returned unchanged.
|
|
153
|
+
*/
|
|
154
|
+
export declare function localizeHref(href: string, locale?: Locale): string;
|
|
155
|
+
//#endregion
|
|
156
|
+
export { type DateInput, type DefaultTags, type Descriptor, type Fmt, type HtmlTagHandler, type Locale, type LocaleName, type LocaleNameOptions, type Message, type MessageKey, type MessageRef, type MissingHandler, type MissingInfo, type Register, type RichPart, type RichPartsValuesOf, type RichValuesOf, type TFunction, type TProps, type TagHandler, type TemporalInput, type Translator, type Values, type ValuesOf, type ZodErrorMap, type ZodIssueLike, fmt_d_exports as fmt };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { b as trim, d as resolved, n as S, t as textDirection } from "./dir-zw6bGMKQ.js";
|
|
3
|
+
import { a as hostLocale, d as splitLocalePath, i as awayHref, n as atHome, r as atHost, s as localizePath, t as ROUTES } from "./url-CsBkRaJy.js";
|
|
4
|
+
import { C as fTime, S as fSegment, _ as fDuration, b as fPercent, d as fCompare, f as fCurrency, g as fDisplayName, h as fDateRange, l as fCollator, m as fDate, n as msg, p as fCurrencyMinor, r as t, u as fCompact, v as fList, w as fUnit, x as fRelative, y as fNumber } from "./t-DC-c9zE5.js";
|
|
5
|
+
import { i as richParts, n as richHtml, t as rich } from "./rich-DCUKl5HM.js";
|
|
6
|
+
//#region src/runtime/macro.ts
|
|
7
|
+
function T(props) {
|
|
8
|
+
throw new Error("vi18n: <T> is a compile-time macro and was not compiled: add vi18n() to the plugins of this Vite or Vitest config");
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/runtime/fmt.ts
|
|
12
|
+
/**
|
|
13
|
+
* The `fmt` namespace (PLAN §5.17): exactly the members of `Fmt`, each in the
|
|
14
|
+
* active scope's locale, zone and `now`. Separate exports, so unused members
|
|
15
|
+
* tree-shake out of `fmt.*` imports.
|
|
16
|
+
*/
|
|
17
|
+
var fmt_exports = /* @__PURE__ */ __exportAll({
|
|
18
|
+
collator: () => collator,
|
|
19
|
+
compact: () => compact,
|
|
20
|
+
compare: () => compare,
|
|
21
|
+
currency: () => currency,
|
|
22
|
+
currencyMinor: () => currencyMinor,
|
|
23
|
+
date: () => date,
|
|
24
|
+
dateRange: () => dateRange,
|
|
25
|
+
displayName: () => displayName,
|
|
26
|
+
duration: () => duration,
|
|
27
|
+
list: () => list,
|
|
28
|
+
number: () => number,
|
|
29
|
+
percent: () => percent,
|
|
30
|
+
relative: () => relative,
|
|
31
|
+
segment: () => segment,
|
|
32
|
+
time: () => time,
|
|
33
|
+
unit: () => unit
|
|
34
|
+
});
|
|
35
|
+
const scope = () => {
|
|
36
|
+
const s = S.resolve();
|
|
37
|
+
S.track();
|
|
38
|
+
return s;
|
|
39
|
+
};
|
|
40
|
+
const number = (v, o) => fNumber(scope(), v, o);
|
|
41
|
+
const currency = (v, c, o) => fCurrency(scope(), v, c, o);
|
|
42
|
+
const currencyMinor = (v, c, o) => fCurrencyMinor(scope(), v, c, o);
|
|
43
|
+
const unit = (v, u, o) => fUnit(scope(), v, u, o);
|
|
44
|
+
const compact = (v, o) => fCompact(scope(), v, o);
|
|
45
|
+
const percent = (v, o) => fPercent(scope(), v, o);
|
|
46
|
+
const date = (v, o) => fDate(scope(), v, o);
|
|
47
|
+
const time = (v, o) => fTime(scope(), v, o);
|
|
48
|
+
const dateRange = (a, b, o) => fDateRange(scope(), a, b, o);
|
|
49
|
+
const relative = (v, o) => fRelative(scope(), v, o);
|
|
50
|
+
const list = (items, o) => fList(scope(), items, o);
|
|
51
|
+
const displayName = (code, o) => fDisplayName(scope(), code, o);
|
|
52
|
+
const duration = (d, o) => fDuration(scope(), d, o);
|
|
53
|
+
const compare = (a, b, o) => fCompare(scope(), a, b, o);
|
|
54
|
+
const collator = (o) => fCollator(scope(), o);
|
|
55
|
+
const segment = (text, o) => fSegment(scope(), text, o);
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/runtime/name.ts
|
|
58
|
+
/**
|
|
59
|
+
* `localeName(locale)` (PLAN §4.1, §4.16): what a locale picker shows for a
|
|
60
|
+
* locale, its language's own name, and the `lang` of the text it shows, so
|
|
61
|
+
* screen readers read "Deutsch" in German inside an English page. It reads
|
|
62
|
+
* no state: an own-language name never depends on the current locale (the
|
|
63
|
+
* name in the current locale is `fmt.displayName`).
|
|
64
|
+
*/
|
|
65
|
+
const names = /* @__PURE__ */ new Map();
|
|
66
|
+
function localeName(locale, options) {
|
|
67
|
+
const key = locale + JSON.stringify(options);
|
|
68
|
+
let n = names.get(key);
|
|
69
|
+
if (!n) {
|
|
70
|
+
try {
|
|
71
|
+
const d = new Intl.DisplayNames(locale, {
|
|
72
|
+
...options,
|
|
73
|
+
type: "language",
|
|
74
|
+
fallback: "none"
|
|
75
|
+
});
|
|
76
|
+
const lang = d.resolvedOptions().locale;
|
|
77
|
+
const name = d.of(locale);
|
|
78
|
+
n = {
|
|
79
|
+
name: name[0].toLocaleUpperCase(lang) + name.slice(1),
|
|
80
|
+
lang
|
|
81
|
+
};
|
|
82
|
+
} catch {
|
|
83
|
+
n = {
|
|
84
|
+
name: locale,
|
|
85
|
+
lang: ""
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
trim(names, 99);
|
|
89
|
+
names.set(key, n);
|
|
90
|
+
}
|
|
91
|
+
return n;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/runtime/zod.ts
|
|
95
|
+
/**
|
|
96
|
+
* `vi18nZod()` (PLAN §4.1, INTERNALS §6 D206): a zod 4 `customError` that
|
|
97
|
+
* words issues through vi18n messages in the current locale, so
|
|
98
|
+
* `z.config({ customError: vi18nZod() })` localises every schema without a
|
|
99
|
+
* message of its own. It imports nothing from zod: the issue is typed
|
|
100
|
+
* structurally and zod only ever calls the function.
|
|
101
|
+
*
|
|
102
|
+
* The texts are not here. The extractor treats a `vi18nZod()` call as the
|
|
103
|
+
* `msg()` of each text in `src/core/extract/zod.ts` (catalog references
|
|
104
|
+
* point at the call) and the transform passes them in that order, as ids in
|
|
105
|
+
* builds and descriptors in dev, so they ship in the bundle of the module
|
|
106
|
+
* that calls it. Called unprocessed, it has none and zod keeps its own.
|
|
107
|
+
*/
|
|
108
|
+
const SIZES = {
|
|
109
|
+
string: 2,
|
|
110
|
+
array: 5,
|
|
111
|
+
set: 5,
|
|
112
|
+
map: 5,
|
|
113
|
+
file: 8
|
|
114
|
+
};
|
|
115
|
+
const FORMATS = [
|
|
116
|
+
"email",
|
|
117
|
+
"url",
|
|
118
|
+
"starts_with",
|
|
119
|
+
"ends_with",
|
|
120
|
+
"includes"
|
|
121
|
+
];
|
|
122
|
+
const vi18nZod = ((...m) => (issue) => {
|
|
123
|
+
if (!m.length) return;
|
|
124
|
+
const { code, origin = "", inclusive } = issue;
|
|
125
|
+
let i = 28;
|
|
126
|
+
let values = issue;
|
|
127
|
+
if (code === "invalid_type") i = issue.input == null ? 0 : 1;
|
|
128
|
+
else if (code === "too_small" || code === "too_big") {
|
|
129
|
+
const big = +(code === "too_big");
|
|
130
|
+
const n = big ? issue.maximum : issue.minimum;
|
|
131
|
+
const size = SIZES[origin];
|
|
132
|
+
if (size) {
|
|
133
|
+
i = size + (issue.exact ? 2 : big);
|
|
134
|
+
values = { count: inclusive === false ? Number(n) + 1 - 2 * big : n };
|
|
135
|
+
} else i = (origin === "date" ? 15 : 11) + 2 * big + +(inclusive === false);
|
|
136
|
+
} else if (code === "not_multiple_of") i = 19;
|
|
137
|
+
else if (code === "invalid_format") {
|
|
138
|
+
const f = FORMATS.indexOf(issue.format);
|
|
139
|
+
i = f < 0 ? 25 : 20 + f;
|
|
140
|
+
} else if (code === "invalid_value") i = 26;
|
|
141
|
+
else if (code === "unrecognized_keys") {
|
|
142
|
+
i = 27;
|
|
143
|
+
values = {
|
|
144
|
+
count: issue.keys.length,
|
|
145
|
+
keys: issue.keys.join(", ")
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return t(m[i], values) || void 0;
|
|
149
|
+
});
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/runtime/locale.ts
|
|
152
|
+
/**
|
|
153
|
+
* The public locale API (PLAN §5.12, §5.14, §5.15). Every function goes
|
|
154
|
+
* through the global state, so a duplicate runtime copy acts on the same page.
|
|
155
|
+
*/
|
|
156
|
+
/** The active scope's locale: the request's on the server, the page's in the browser. */
|
|
157
|
+
const getLocale = () => S.resolve().locale;
|
|
158
|
+
/** Switches the page in place (or by document navigation, §2.11.7). Client only. */
|
|
159
|
+
const setLocale = (locale) => S.setLocale(locale);
|
|
160
|
+
/** Called with the new locale after every switch; returns an unsubscribe. */
|
|
161
|
+
const subscribe = (listener) => S.subscribe(listener);
|
|
162
|
+
/** The app router's navigate function, for URL strategies (`setNavigator((href) => router.navigate(…))`). */
|
|
163
|
+
const setNavigator = (navigate) => S.setNavigator(navigate);
|
|
164
|
+
const localesOf = (s) => s.locales;
|
|
165
|
+
/** Configured (and active pseudo-) locales, filled at registration; the same array forever. */
|
|
166
|
+
const locales = /* #__PURE__ */ localesOf(S);
|
|
167
|
+
/** `"rtl"` or `"ltr"`; defaults to the current locale. */
|
|
168
|
+
const dir = (locale) => textDirection(locale ?? getLocale());
|
|
169
|
+
/** Settles once the page locale's boot and pending loads have settled. */
|
|
170
|
+
const ready = () => S.ready();
|
|
171
|
+
/**
|
|
172
|
+
* For router intent hooks: loads the route's message bundles. The build fills
|
|
173
|
+
* `keys` from the `import()` inside `load`, which is never called: routers own
|
|
174
|
+
* code prefetching (§6 D45).
|
|
175
|
+
*/
|
|
176
|
+
const preload = (_load, keys) => Array.isArray(keys) ? S.need(keys) : resolved;
|
|
177
|
+
/** Missing-message reports, once per (key, locale); returns an unsubscribe. */
|
|
178
|
+
const onMissing = (handler) => S.onMissing(handler);
|
|
179
|
+
const origin = () => globalThis.location?.origin;
|
|
180
|
+
/**
|
|
181
|
+
* A copy of `url` (resolved against the page) with its pathname edited, once
|
|
182
|
+
* registered; with domains, as the codec reads it on the URL's host.
|
|
183
|
+
*/
|
|
184
|
+
function edit(url, f) {
|
|
185
|
+
const u = new URL(url, origin());
|
|
186
|
+
const config = S.registration?.config;
|
|
187
|
+
if (config) u.pathname = f(u.pathname, ROUTES ? atHost(config, u.host) : config);
|
|
188
|
+
return u;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* `localizeUrl` where localized paths or domains may be configured: a locale
|
|
192
|
+
* served by another listed host moves the URL there (INTERNALS §6 D135).
|
|
193
|
+
*/
|
|
194
|
+
function routedUrl(url, l) {
|
|
195
|
+
let away = null;
|
|
196
|
+
const u = edit(url, (p, c) => (away = awayHref(splitLocalePath(p, c).path, l, c)) ?? localizePath(p, l, c));
|
|
197
|
+
if (away === null) return u;
|
|
198
|
+
const to = new URL(away);
|
|
199
|
+
to.search = u.search;
|
|
200
|
+
to.hash = u.hash;
|
|
201
|
+
return to;
|
|
202
|
+
}
|
|
203
|
+
/** A copy of `url` with its path in `locale` (default: the current one). */
|
|
204
|
+
const localizeUrl = (url, locale) => ROUTES ? routedUrl(url, locale ?? getLocale()) : edit(url, (p, c) => localizePath(p, locale ?? getLocale(), c));
|
|
205
|
+
/**
|
|
206
|
+
* A path-absolute href's path in `l` from the current page (whose host's
|
|
207
|
+
* locale the client config carries) or request (`Scope.home`, servers):
|
|
208
|
+
* absolute when `l` lives on another listed host.
|
|
209
|
+
*/
|
|
210
|
+
function routedPath(path, l, config) {
|
|
211
|
+
const c = atHome(config, S.serverResolver?.()?.home ?? config.routing.home);
|
|
212
|
+
return awayHref(splitLocalePath(path, c).path, l, c) ?? localizePath(path, l, c);
|
|
213
|
+
}
|
|
214
|
+
/** A copy of `url` without its locale segment (router `rewrite`/`reroute` input). */
|
|
215
|
+
const delocalizeUrl = (url) => edit(url, (p, c) => splitLocalePath(p, c).path);
|
|
216
|
+
/**
|
|
217
|
+
* `href` in `locale` (default: the current one). Path-absolute and
|
|
218
|
+
* same-origin absolute hrefs are localized (with domains, hrefs on a listed
|
|
219
|
+
* host too, and a locale served by another listed host gives an absolute
|
|
220
|
+
* URL there); relative (`./x`, `x`, `?q`, `#h`) and cross-origin ones are
|
|
221
|
+
* returned unchanged.
|
|
222
|
+
*/
|
|
223
|
+
function localizeHref(href, locale) {
|
|
224
|
+
const config = S.registration?.config;
|
|
225
|
+
if (!config) return href;
|
|
226
|
+
if (href[0] === "/" && href[1] !== "/") {
|
|
227
|
+
const i = href.search(/[?#]/) >>> 0;
|
|
228
|
+
if (ROUTES) return routedPath(href.slice(0, i), locale ?? getLocale(), config) + href.slice(i);
|
|
229
|
+
return localizePath(href.slice(0, i), locale ?? getLocale(), config) + href.slice(i);
|
|
230
|
+
}
|
|
231
|
+
const base = globalThis.location?.href;
|
|
232
|
+
if (base && /^[\x00-\x20]*(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(href)) try {
|
|
233
|
+
const u = new URL(href, base);
|
|
234
|
+
if (u.origin === origin() || ROUTES && hostLocale(u.host, config) !== void 0) return localizeUrl(u, locale).href;
|
|
235
|
+
} catch {}
|
|
236
|
+
return href;
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
export { T, delocalizeUrl, dir, fmt_exports as fmt, getLocale, localeName, locales, localizeHref, localizeUrl, msg, onMissing, preload, ready, rich, richHtml, richParts, setLocale, setNavigator, subscribe, t, vi18nZod };
|