@x18n/builtins 0.0.13 → 0.0.14
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/dist/angular-templates-DqIOGfzT.mjs +8 -0
- package/dist/extractors.mjs +7 -3
- package/dist/finders.d.mts +4 -1
- package/dist/finders.mjs +1 -1
- package/dist/formats/ecmascript.d.mts +5 -0
- package/dist/formats/ecmascript.mjs +211 -0
- package/dist/formats/ftl.d.mts +5 -0
- package/dist/formats/ftl.mjs +234 -0
- package/dist/formats/ini.d.mts +5 -0
- package/dist/formats/ini.mjs +158 -0
- package/dist/formats/json.d.mts +6 -0
- package/dist/formats/json.mjs +10 -0
- package/dist/formats/json5.d.mts +5 -0
- package/dist/formats/json5.mjs +30 -0
- package/dist/formats/jsonc.d.mts +6 -0
- package/dist/formats/jsonc.mjs +10 -0
- package/dist/formats/php.d.mts +5 -0
- package/dist/formats/php.mjs +200 -0
- package/dist/formats/po.d.mts +5 -0
- package/dist/formats/po.mjs +177 -0
- package/dist/formats/properties.d.mts +5 -0
- package/dist/formats/properties.mjs +123 -0
- package/dist/formats/resx.d.mts +6 -0
- package/dist/formats/resx.mjs +202 -0
- package/dist/formats/yaml.d.mts +5 -0
- package/dist/formats/yaml.mjs +113 -0
- package/dist/formats.d.mts +10 -34
- package/dist/formats.mjs +61 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +28 -22
- package/dist/{vue-CF4pe-Yr.mjs → js-CV-ECNJG.mjs} +67 -222
- package/dist/json-CyjI8bjp.d.mts +8 -0
- package/dist/json-Db-8qSbE.mjs +73 -0
- package/dist/jsx-templates-B1J0no3i.mjs +17 -0
- package/dist/locale-root-C_rch3IQ.mjs +15 -0
- package/dist/manifests.d.mts +8 -0
- package/dist/manifests.mjs +66 -2
- package/dist/placeholders.d.mts +11 -0
- package/dist/placeholders.mjs +28 -1
- package/dist/plugin-Cb1b0axD.mjs +83 -0
- package/dist/plural-forms-B7_QX4nG.mjs +12 -0
- package/dist/presets/cargo-i18n.d.mts +6 -0
- package/dist/presets/cargo-i18n.mjs +28 -0
- package/dist/presets/chrome-extension.d.mts +6 -0
- package/dist/presets/chrome-extension.mjs +52 -0
- package/dist/presets/dotnet-resx.d.mts +6 -0
- package/dist/presets/dotnet-resx.mjs +64 -0
- package/dist/presets/easy-localization.d.mts +6 -0
- package/dist/presets/easy-localization.mjs +36 -0
- package/dist/presets/ember-intl.d.mts +5 -0
- package/dist/presets/ember-intl.mjs +31 -0
- package/dist/presets/fluent-react.d.mts +5 -0
- package/dist/presets/fluent-react.mjs +46 -0
- package/dist/presets/fluent-vue.d.mts +5 -0
- package/dist/presets/fluent-vue.mjs +51 -0
- package/dist/presets/flutter-translate.d.mts +6 -0
- package/dist/presets/flutter-translate.mjs +30 -0
- package/dist/presets/flutter.d.mts +6 -0
- package/dist/presets/flutter.mjs +21 -0
- package/dist/presets/general.d.mts +6 -0
- package/dist/presets/general.mjs +17 -0
- package/dist/presets/globalize.d.mts +5 -0
- package/dist/presets/globalize.mjs +25 -0
- package/dist/presets/gravity-ui-i18n.d.mts +6 -0
- package/dist/presets/gravity-ui-i18n.mjs +41 -0
- package/dist/presets/i18n-js.d.mts +5 -0
- package/dist/presets/i18n-js.mjs +19 -0
- package/dist/presets/i18n-tag.d.mts +5 -0
- package/dist/presets/i18n-tag.mjs +21 -0
- package/dist/presets/i18next-shopify.d.mts +6 -0
- package/dist/presets/i18next-shopify.mjs +23 -0
- package/dist/presets/i18next.d.mts +5 -0
- package/dist/presets/i18next.mjs +10 -0
- package/dist/presets/i18nice.d.mts +6 -0
- package/dist/presets/i18nice.mjs +29 -0
- package/dist/presets/jekyll.d.mts +5 -0
- package/dist/presets/jekyll.mjs +23 -0
- package/dist/presets/laravel-vue-i18n.d.mts +6 -0
- package/dist/presets/laravel-vue-i18n.mjs +54 -0
- package/dist/presets/laravel.d.mts +6 -0
- package/dist/presets/laravel.mjs +23 -0
- package/dist/presets/lingui.d.mts +5 -0
- package/dist/presets/lingui.mjs +61 -0
- package/dist/presets/next-international.d.mts +5 -0
- package/dist/presets/next-international.mjs +27 -0
- package/dist/presets/next-intl.d.mts +5 -0
- package/dist/presets/next-intl.mjs +55 -0
- package/dist/presets/next-translate.d.mts +5 -0
- package/dist/presets/next-translate.mjs +53 -0
- package/dist/presets/ngx-translate.d.mts +5 -0
- package/dist/presets/ngx-translate.mjs +41 -0
- package/dist/presets/nuxt-i18n-micro.d.mts +5 -0
- package/dist/presets/nuxt-i18n-micro.mjs +81 -0
- package/dist/presets/nuxt-i18n.d.mts +5 -0
- package/dist/presets/nuxt-i18n.mjs +23 -0
- package/dist/presets/paraglide.d.mts +5 -0
- package/dist/presets/paraglide.mjs +38 -0
- package/dist/presets/php-gettext.d.mts +6 -0
- package/dist/presets/php-gettext.mjs +27 -0
- package/dist/presets/php-joomla.d.mts +6 -0
- package/dist/presets/php-joomla.mjs +22 -0
- package/dist/presets/polyglot.d.mts +5 -0
- package/dist/presets/polyglot.mjs +23 -0
- package/dist/presets/python-gettext.d.mts +6 -0
- package/dist/presets/python-gettext.mjs +44 -0
- package/dist/presets/react-i18next.d.mts +5 -0
- package/dist/presets/react-i18next.mjs +67 -0
- package/dist/presets/react-intl.d.mts +5 -0
- package/dist/presets/react-intl.mjs +47 -0
- package/dist/presets/ruby-rails.d.mts +7 -0
- package/dist/presets/ruby-rails.mjs +44 -0
- package/dist/presets/solid-primitives-i18n.d.mts +5 -0
- package/dist/presets/solid-primitives-i18n.mjs +29 -0
- package/dist/presets/svelte-i18n.d.mts +5 -0
- package/dist/presets/svelte-i18n.mjs +33 -0
- package/dist/presets/svelte-i18next.d.mts +5 -0
- package/dist/presets/svelte-i18next.mjs +27 -0
- package/dist/presets/transloco.d.mts +5 -0
- package/dist/presets/transloco.mjs +44 -0
- package/dist/presets/typesafe-i18n.d.mts +6 -0
- package/dist/presets/typesafe-i18n.mjs +61 -0
- package/dist/presets/ui5.d.mts +5 -0
- package/dist/presets/ui5.mjs +35 -0
- package/dist/presets/vscode-extension.d.mts +5 -0
- package/dist/presets/vscode-extension.mjs +53 -0
- package/dist/presets/vue-i18n.d.mts +5 -0
- package/dist/presets/vue-i18n.mjs +59 -0
- package/dist/presets.d.mts +7 -54
- package/dist/presets.mjs +346 -1
- package/dist/reporters/github.d.mts +6 -0
- package/dist/reporters/github.mjs +27 -0
- package/dist/reporters/json.d.mts +6 -0
- package/dist/reporters/json.mjs +24 -0
- package/dist/reporters/markdown.d.mts +6 -0
- package/dist/reporters/markdown.mjs +37 -0
- package/dist/reporters/pretty.d.mts +6 -0
- package/dist/reporters/pretty.mjs +64 -0
- package/dist/reporters/sarif.d.mts +6 -0
- package/dist/reporters/sarif.mjs +48 -0
- package/dist/reporters.d.mts +5 -12
- package/dist/reporters.mjs +8 -218
- package/dist/scanners/astro.d.mts +6 -0
- package/dist/scanners/astro.mjs +256 -0
- package/dist/scanners/js.d.mts +6 -0
- package/dist/scanners/js.mjs +33 -0
- package/dist/scanners/regex-scopes.d.mts +14 -0
- package/dist/scanners/regex-scopes.mjs +41 -0
- package/dist/scanners/regex.d.mts +9 -0
- package/dist/{regex-AYVhuSb6.mjs → scanners/regex.mjs} +2 -40
- package/dist/scanners/svelte.d.mts +6 -0
- package/dist/{scanners-DhnnVp7s.mjs → scanners/svelte.mjs} +3 -3
- package/dist/scanners/vue.d.mts +6 -0
- package/dist/scanners/vue.mjs +172 -0
- package/dist/scanners.d.mts +9 -50
- package/dist/scanners.mjs +36 -4
- package/dist/sfc-blocks-DaGKDZsb.mjs +213 -0
- package/dist/shared-BWaXUmwA.mjs +70 -0
- package/dist/shared-D-tbsYmc.mjs +30 -0
- package/dist/shared-D4NJBKEP.mjs +17 -0
- package/dist/shared-Tq06G0gp.mjs +6 -0
- package/dist/shared-meYeVckh.mjs +11 -0
- package/dist/vue-compiler-0sfLIiiL.mjs +8 -0
- package/package.json +67 -3
- package/dist/formats-CFkM5co0.mjs +0 -1370
- package/dist/presets-Co5SlnKr.mjs +0 -1187
- package/dist/sfc-blocks-BqQ8m9rX.mjs +0 -161
- package/dist/yaml-plugin.d.mts +0 -8
- package/dist/yaml-plugin.mjs +0 -68
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t as createJsonFormat } from "../json-Db-8qSbE.mjs";
|
|
2
|
+
//#region src/formats/json/index.ts
|
|
3
|
+
function json(options = {}) {
|
|
4
|
+
return {
|
|
5
|
+
name: "x18n:format-json",
|
|
6
|
+
formats: [createJsonFormat("json", ["json"], true, options.indent)]
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { json as default, json };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { errors, isMessageTree } from "@x18n/core";
|
|
2
|
+
import JSON5 from "json5";
|
|
3
|
+
//#region src/formats/json5/index.ts
|
|
4
|
+
function json5() {
|
|
5
|
+
return {
|
|
6
|
+
name: "x18n:format-json5",
|
|
7
|
+
formats: [{
|
|
8
|
+
id: "json5",
|
|
9
|
+
extensions: ["json5"],
|
|
10
|
+
parse(text, ctx) {
|
|
11
|
+
if (text.trim() === "") return { tree: {} };
|
|
12
|
+
let data;
|
|
13
|
+
try {
|
|
14
|
+
data = JSON5.parse(text);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
throw errors.X18N2002({
|
|
17
|
+
path: ctx.path,
|
|
18
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
19
|
+
cause: error
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (!isMessageTree(data)) throw errors.X18N2003({ path: ctx.path });
|
|
23
|
+
return { tree: data };
|
|
24
|
+
},
|
|
25
|
+
serialize: (tree, ctx) => `${JSON5.stringify(tree, null, ctx.indent === "tab" ? " " : ctx.indent ?? 2)}\n`
|
|
26
|
+
}]
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { json5 as default, json5 };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t as createJsonFormat } from "../json-Db-8qSbE.mjs";
|
|
2
|
+
//#region src/formats/jsonc/index.ts
|
|
3
|
+
function jsonc(options = {}) {
|
|
4
|
+
return {
|
|
5
|
+
name: "x18n:format-jsonc",
|
|
6
|
+
formats: [createJsonFormat("jsonc", ["jsonc"], true, options.indent)]
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { jsonc as default, jsonc };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { a as splice } from "../shared-BWaXUmwA.mjs";
|
|
2
|
+
import { applyMessageOps, diffText, errors, isMessageLeaf, pathId } from "@x18n/core";
|
|
3
|
+
//#region src/formats/php/index.ts
|
|
4
|
+
const doubleEscapes = {
|
|
5
|
+
"n": "\n",
|
|
6
|
+
"t": " ",
|
|
7
|
+
"r": "\r",
|
|
8
|
+
"v": "\v",
|
|
9
|
+
"e": "\x1B",
|
|
10
|
+
"f": "\f",
|
|
11
|
+
"\\": "\\",
|
|
12
|
+
"$": "$",
|
|
13
|
+
"\"": "\""
|
|
14
|
+
};
|
|
15
|
+
function decodeDouble(raw, fail) {
|
|
16
|
+
if (/(?:^|[^\\])(?:\\\\)*(?:\$[a-z_{]|\{\$)/i.test(raw)) fail("X18N2004", "variable interpolation in a string");
|
|
17
|
+
return raw.replace(/\\(u\{[0-9a-f]+\}|x[0-9a-f]{1,2}|[0-7]{1,3}|[\s\S])/gi, (match, escape) => {
|
|
18
|
+
if (/^u\{/i.test(escape)) return String.fromCodePoint(Number.parseInt(escape.slice(2, -1), 16));
|
|
19
|
+
if (/^x[0-9a-f]/i.test(escape)) return String.fromCharCode(Number.parseInt(escape.slice(1), 16));
|
|
20
|
+
if (/^[0-7]/.test(escape)) return String.fromCharCode(Number.parseInt(escape, 8));
|
|
21
|
+
return doubleEscapes[escape] ?? match;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function parseFile(text, path) {
|
|
25
|
+
let pos = 0;
|
|
26
|
+
const fail = (code, reason) => {
|
|
27
|
+
throw errors[code]({
|
|
28
|
+
path,
|
|
29
|
+
line: text.slice(0, pos).split("\n").length,
|
|
30
|
+
reason
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const skip = () => {
|
|
34
|
+
for (;;) {
|
|
35
|
+
const rest = text.slice(pos);
|
|
36
|
+
const blank = /^(?:\s+|\/\/[^\n]*|#(?!\[)[^\n]*|\/\*[\s\S]*?\*\/)/.exec(rest);
|
|
37
|
+
if (!blank) return;
|
|
38
|
+
pos += blank[0].length;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const eat = (pattern) => {
|
|
42
|
+
skip();
|
|
43
|
+
const match = pattern.exec(text.slice(pos));
|
|
44
|
+
if (match) pos += match[0].length;
|
|
45
|
+
return match?.[0];
|
|
46
|
+
};
|
|
47
|
+
const parseString = (quote) => {
|
|
48
|
+
const start = pos;
|
|
49
|
+
const match = (quote === "'" ? /^'(?:[^'\\]|\\[\s\S])*'/ : /^"(?:[^"\\]|\\[\s\S])*"/).exec(text.slice(pos));
|
|
50
|
+
if (!match) return fail("X18N2002", "unterminated string");
|
|
51
|
+
pos += match[0].length;
|
|
52
|
+
const raw = match[0].slice(1, -1);
|
|
53
|
+
const value = quote === "'" ? raw.replace(/\\([\\'])/g, "$1") : decodeDouble(raw, fail);
|
|
54
|
+
return {
|
|
55
|
+
kind: "scalar",
|
|
56
|
+
range: {
|
|
57
|
+
start,
|
|
58
|
+
end: pos
|
|
59
|
+
},
|
|
60
|
+
value,
|
|
61
|
+
quote
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const parseArray = (start, close) => {
|
|
65
|
+
const closing = close === "]" ? /^\]/ : /^\)/;
|
|
66
|
+
const items = [];
|
|
67
|
+
while (!eat(closing)) {
|
|
68
|
+
const key = parseValue();
|
|
69
|
+
if (!eat(/^=>/)) return fail("X18N2003", "array item without a key");
|
|
70
|
+
if (key.kind !== "scalar" || typeof key.value !== "string" && typeof key.value !== "number") return fail("X18N2004", "array keys must be strings or integers");
|
|
71
|
+
items.push({
|
|
72
|
+
key: String(key.value),
|
|
73
|
+
keyRange: key.quote ? {
|
|
74
|
+
start: key.range.start + 1,
|
|
75
|
+
end: key.range.end - 1
|
|
76
|
+
} : key.range,
|
|
77
|
+
value: parseValue()
|
|
78
|
+
});
|
|
79
|
+
if (eat(/^,/)) continue;
|
|
80
|
+
skip();
|
|
81
|
+
if (text[pos] !== close) return fail("X18N2004", `expected "," or "${close}"; expressions are not static`);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
kind: "array",
|
|
85
|
+
range: {
|
|
86
|
+
start,
|
|
87
|
+
end: pos
|
|
88
|
+
},
|
|
89
|
+
items
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
function parseValue() {
|
|
93
|
+
skip();
|
|
94
|
+
const start = pos;
|
|
95
|
+
if (eat(/^\[/)) return parseArray(start, "]");
|
|
96
|
+
if (eat(/^array\s*\(/i)) return parseArray(start, ")");
|
|
97
|
+
if (text[pos] === "'" || text[pos] === "\"") return parseString(text[pos]);
|
|
98
|
+
const literal = eat(/^(?:-?\d+(?:\.\d+)?(?:e[+-]?\d+)?|true|false|null)\b/i);
|
|
99
|
+
if (literal === void 0) return fail("X18N2004", `"${text.slice(pos, pos + 20).split("\n")[0]}" is not a static value`);
|
|
100
|
+
const lower = literal.toLowerCase();
|
|
101
|
+
const value = lower === "null" ? null : lower === "true" ? true : lower === "false" ? false : Number(literal);
|
|
102
|
+
return {
|
|
103
|
+
kind: "scalar",
|
|
104
|
+
range: {
|
|
105
|
+
start,
|
|
106
|
+
end: pos
|
|
107
|
+
},
|
|
108
|
+
value,
|
|
109
|
+
quote: void 0
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
eat(/^<\?php\b/i);
|
|
113
|
+
if (eat(/^$/) !== void 0) return void 0;
|
|
114
|
+
eat(/^declare\s*\([^)]*\)\s*;/i);
|
|
115
|
+
if (!eat(/^return\b/i)) fail("X18N2004", "expected `return [ ... ];`");
|
|
116
|
+
const root = parseValue();
|
|
117
|
+
if (root.kind !== "array") fail("X18N2003", "the file must return an array");
|
|
118
|
+
if (!eat(/^;/)) fail("X18N2004", "expected \";\" after the array; expressions are not static");
|
|
119
|
+
eat(/^\?>/);
|
|
120
|
+
if (eat(/^$/) === void 0) fail("X18N2002", "unexpected text after the array");
|
|
121
|
+
return root;
|
|
122
|
+
}
|
|
123
|
+
function readTree(value, path, ranges) {
|
|
124
|
+
const out = {};
|
|
125
|
+
if (value.kind !== "array") return out;
|
|
126
|
+
for (const item of value.items) {
|
|
127
|
+
const childPath = [...path, item.key];
|
|
128
|
+
const inner = item.value.kind === "scalar" && item.value.quote ? {
|
|
129
|
+
start: item.value.range.start + 1,
|
|
130
|
+
end: item.value.range.end - 1
|
|
131
|
+
} : item.value.range;
|
|
132
|
+
ranges.set(pathId(childPath), {
|
|
133
|
+
key: item.keyRange,
|
|
134
|
+
value: inner
|
|
135
|
+
});
|
|
136
|
+
out[item.key] = item.value.kind === "array" ? readTree(item.value, childPath, ranges) : item.value.value;
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
const doubleEncodings = {
|
|
141
|
+
"\n": "\\n",
|
|
142
|
+
" ": "\\t",
|
|
143
|
+
"\r": "\\r",
|
|
144
|
+
"\v": "\\v",
|
|
145
|
+
"\f": "\\f"
|
|
146
|
+
};
|
|
147
|
+
function literal(value, quote) {
|
|
148
|
+
if (typeof value !== "string") return String(value);
|
|
149
|
+
if (quote === "\"" || /[\n\r\t\v\f]/.test(value)) return `"${value.replace(/[\\"$\n\t\r\v\f]/g, (char) => doubleEncodings[char] ?? `\\${char}`)}"`;
|
|
150
|
+
return `'${value.replace(/[\\']/g, "\\$&")}'`;
|
|
151
|
+
}
|
|
152
|
+
function arrayText(tree, unit, depth) {
|
|
153
|
+
const entries = Object.entries(tree);
|
|
154
|
+
if (entries.length === 0) return "[]";
|
|
155
|
+
const pad = unit.repeat(depth + 1);
|
|
156
|
+
return `[\n${entries.map(([key, child]) => `${pad}${literal(key, "'")} => ${isMessageLeaf(child) ? literal(child, "'") : arrayText(child, unit, depth + 1)},\n`).join("")}${unit.repeat(depth)}]`;
|
|
157
|
+
}
|
|
158
|
+
function find(root, path) {
|
|
159
|
+
let node = root;
|
|
160
|
+
for (const segment of path) node = node?.kind === "array" ? node.items.findLast((item) => item.key === segment)?.value : void 0;
|
|
161
|
+
return node;
|
|
162
|
+
}
|
|
163
|
+
/** Indent unit: configured, else the file's (first indented item), else 4 spaces. */
|
|
164
|
+
function unitOf(text, indent) {
|
|
165
|
+
if (indent !== void 0) return indent === "tab" ? " " : " ".repeat(indent);
|
|
166
|
+
return /^([ \t]+)['"\]]/m.exec(text)?.[1] ?? " ";
|
|
167
|
+
}
|
|
168
|
+
function applyOp(text, op, path, unit) {
|
|
169
|
+
const root = parseFile(text, path);
|
|
170
|
+
const node = root && find(root, op.path);
|
|
171
|
+
if (op.type === "set" && node?.kind === "scalar") return splice(text, node.range, literal(op.value, node.quote));
|
|
172
|
+
const tree = applyMessageOps(root ? readTree(root, [], /* @__PURE__ */ new Map()) : {}, [op]);
|
|
173
|
+
return root ? splice(text, root.range, arrayText(tree, unit, 0)) : `${text.trim() === "" ? "<?php" : text.trimEnd()}\n\nreturn ${arrayText(tree, unit, 0)};\n`;
|
|
174
|
+
}
|
|
175
|
+
function php() {
|
|
176
|
+
return {
|
|
177
|
+
name: "x18n:format-php",
|
|
178
|
+
formats: [{
|
|
179
|
+
id: "php",
|
|
180
|
+
extensions: ["php"],
|
|
181
|
+
parse(text, ctx) {
|
|
182
|
+
const root = parseFile(text, ctx.path);
|
|
183
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
184
|
+
return {
|
|
185
|
+
tree: root ? readTree(root, [], ranges) : {},
|
|
186
|
+
ranges
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
edit(text, ops, ctx) {
|
|
190
|
+
const unit = unitOf(text, ctx.indent);
|
|
191
|
+
let next = text;
|
|
192
|
+
for (const op of ops) next = applyOp(next, op, ctx.path, unit);
|
|
193
|
+
return diffText(text, next);
|
|
194
|
+
},
|
|
195
|
+
serialize: (tree, ctx) => `<?php\n\nreturn ${arrayText(tree, unitOf("", ctx.indent), 0)};\n`
|
|
196
|
+
}]
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
export { php as default, php };
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { a as splice, c as withBlankLines, i as leafText, n as detectEol, o as splitLines, t as appendLines } from "../shared-BWaXUmwA.mjs";
|
|
2
|
+
import { diffText, errors, pathId, walkLeaves } from "@x18n/core";
|
|
3
|
+
//#region src/formats/po/index.ts
|
|
4
|
+
/** gettext joins msgctxt and msgid with EOT. */
|
|
5
|
+
const CONTEXT_SEPARATOR = "";
|
|
6
|
+
const decodings = {
|
|
7
|
+
n: "\n",
|
|
8
|
+
t: " ",
|
|
9
|
+
r: "\r",
|
|
10
|
+
a: "\x07",
|
|
11
|
+
b: "\b",
|
|
12
|
+
f: "\f",
|
|
13
|
+
v: "\v"
|
|
14
|
+
};
|
|
15
|
+
const encodings = {
|
|
16
|
+
"\\": "\\\\",
|
|
17
|
+
"\"": "\\\"",
|
|
18
|
+
"\n": "\\n",
|
|
19
|
+
" ": "\\t",
|
|
20
|
+
"\r": "\\r"
|
|
21
|
+
};
|
|
22
|
+
/** Reads one `"..."` string starting at `start`; returns its value and the offset after the closing quote. */
|
|
23
|
+
function readString(text, start, end, fail) {
|
|
24
|
+
if (text[start] !== "\"") fail("expected a string");
|
|
25
|
+
let value = "";
|
|
26
|
+
for (let i = start + 1; i < end; i++) {
|
|
27
|
+
const char = text[i];
|
|
28
|
+
if (char === "\"") {
|
|
29
|
+
if (text.slice(i + 1, end).trim() !== "") fail("unexpected text after string");
|
|
30
|
+
return {
|
|
31
|
+
value,
|
|
32
|
+
end: i + 1
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (char !== "\\") {
|
|
36
|
+
value += char;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const escape = /^(?:[0-7]{1,3}|x[0-9a-f]{1,2}|[\s\S])/i.exec(text.slice(i + 1, end))?.[0] ?? "";
|
|
40
|
+
value += /^[0-7]/.test(escape) ? String.fromCharCode(Number.parseInt(escape, 8)) : escape.length > 1 ? String.fromCharCode(Number.parseInt(escape.slice(1), 16)) : decodings[escape] ?? escape;
|
|
41
|
+
i += escape.length;
|
|
42
|
+
}
|
|
43
|
+
return fail("unterminated string");
|
|
44
|
+
}
|
|
45
|
+
function toEntry(text, draft, fail) {
|
|
46
|
+
const field = (name) => draft.fields.find((f) => f.name === name);
|
|
47
|
+
const msgid = field("msgid");
|
|
48
|
+
if (!msgid) return draft.fields.length > 0 ? fail("entry without msgid") : void 0;
|
|
49
|
+
const msgctxt = field("msgctxt");
|
|
50
|
+
const msgstr = field("msgstr") ?? field("msgstr[0]");
|
|
51
|
+
if (!msgstr) return fail(`msgid "${msgid.value}" without msgstr`);
|
|
52
|
+
if (msgid.value === "" && !msgctxt) return void 0;
|
|
53
|
+
const inner = (f) => ({
|
|
54
|
+
start: f.range.start + 1,
|
|
55
|
+
end: f.range.end - 1
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
key: msgctxt ? `${msgctxt.value}${CONTEXT_SEPARATOR}${msgid.value}` : msgid.value,
|
|
59
|
+
value: msgstr.value,
|
|
60
|
+
range: {
|
|
61
|
+
key: inner(msgid),
|
|
62
|
+
value: inner(msgstr)
|
|
63
|
+
},
|
|
64
|
+
source: msgstr.range,
|
|
65
|
+
block: withBlankLines(text, {
|
|
66
|
+
start: draft.start,
|
|
67
|
+
end: draft.end
|
|
68
|
+
})
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function read(text, path) {
|
|
72
|
+
const entries = [];
|
|
73
|
+
let lineNumber = 0;
|
|
74
|
+
const fail = (message) => {
|
|
75
|
+
throw errors.X18N2002({
|
|
76
|
+
path,
|
|
77
|
+
line: lineNumber,
|
|
78
|
+
reason: message
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
let draft;
|
|
82
|
+
let field;
|
|
83
|
+
const flush = () => {
|
|
84
|
+
const entry = draft && toEntry(text, draft, fail);
|
|
85
|
+
if (entry) entries.push(entry);
|
|
86
|
+
draft = void 0;
|
|
87
|
+
field = void 0;
|
|
88
|
+
};
|
|
89
|
+
for (const line of splitLines(text)) {
|
|
90
|
+
lineNumber++;
|
|
91
|
+
const offset = line.start + /^\s*/.exec(text.slice(line.start, line.end))[0].length;
|
|
92
|
+
const content = text.slice(offset, line.end);
|
|
93
|
+
const keyword = /^(msgctxt|msgid_plural|msgid|msgstr(?:\[\d+\])?)\s*/.exec(content);
|
|
94
|
+
if (content === "" || content.startsWith("#") && draft?.fields.some((f) => f.name.startsWith("msgstr"))) flush();
|
|
95
|
+
if (content === "") continue;
|
|
96
|
+
if (keyword && (keyword[1] === "msgctxt" || keyword[1] === "msgid") && draft?.fields.some((f) => f.name !== "msgctxt")) flush();
|
|
97
|
+
draft ??= {
|
|
98
|
+
start: line.start,
|
|
99
|
+
end: line.next,
|
|
100
|
+
fields: []
|
|
101
|
+
};
|
|
102
|
+
draft.end = line.next;
|
|
103
|
+
if (content.startsWith("#")) {
|
|
104
|
+
field = void 0;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const stringStart = keyword ? offset + keyword[0].length : offset;
|
|
108
|
+
if (!keyword && (!field || !content.startsWith("\""))) fail(`unexpected "${content.slice(0, 20)}"`);
|
|
109
|
+
const string = readString(text, stringStart, line.end, fail);
|
|
110
|
+
if (keyword) {
|
|
111
|
+
field = {
|
|
112
|
+
name: keyword[1],
|
|
113
|
+
range: {
|
|
114
|
+
start: stringStart,
|
|
115
|
+
end: string.end
|
|
116
|
+
},
|
|
117
|
+
value: string.value
|
|
118
|
+
};
|
|
119
|
+
draft.fields.push(field);
|
|
120
|
+
} else {
|
|
121
|
+
field.range.end = string.end;
|
|
122
|
+
field.value += string.value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
flush();
|
|
126
|
+
return entries;
|
|
127
|
+
}
|
|
128
|
+
const quote = (value) => `"${value.replace(/[\\"\n\t\r]/g, (char) => encodings[char])}"`;
|
|
129
|
+
/** One string, or gettext's style of an empty first string and one string per line. */
|
|
130
|
+
function encode(value, eol) {
|
|
131
|
+
if (!value.slice(0, -1).includes("\n")) return quote(value);
|
|
132
|
+
return ["\"\"", ...value.split(/(?<=\n)/).map(quote)].join(eol);
|
|
133
|
+
}
|
|
134
|
+
function entryText(key, value) {
|
|
135
|
+
const index = key.indexOf(CONTEXT_SEPARATOR);
|
|
136
|
+
return `${index === -1 ? "" : `msgctxt ${quote(key.slice(0, index))}\n`}msgid ${encode(key.slice(index + 1), "\n")}\nmsgstr ${encode(value, "\n")}\n`;
|
|
137
|
+
}
|
|
138
|
+
function applyOp(text, op, path) {
|
|
139
|
+
const key = op.path.join(".");
|
|
140
|
+
const entry = read(text, path).findLast((e) => e.key === key);
|
|
141
|
+
if (op.type === "delete") return entry ? splice(text, entry.block, "") : text;
|
|
142
|
+
const value = leafText(op.value);
|
|
143
|
+
if (entry) return splice(text, entry.source, encode(value, detectEol(text)));
|
|
144
|
+
const separated = text.trim() === "" || /\n[ \t]*\r?\n$/.test(text);
|
|
145
|
+
return appendLines(text, `${separated ? "" : "\n"}${entryText(key, value)}`);
|
|
146
|
+
}
|
|
147
|
+
function po() {
|
|
148
|
+
return {
|
|
149
|
+
name: "x18n:format-po",
|
|
150
|
+
formats: [{
|
|
151
|
+
id: "po",
|
|
152
|
+
extensions: ["po", "pot"],
|
|
153
|
+
parse(text, ctx) {
|
|
154
|
+
const tree = {};
|
|
155
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
156
|
+
for (const entry of read(text, ctx.path)) {
|
|
157
|
+
tree[entry.key] = entry.value;
|
|
158
|
+
ranges.set(pathId([entry.key]), entry.range);
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
tree,
|
|
162
|
+
ranges
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
edit(text, ops, ctx) {
|
|
166
|
+
let next = text;
|
|
167
|
+
for (const op of ops) next = applyOp(next, op, ctx.path);
|
|
168
|
+
return diffText(text, next);
|
|
169
|
+
},
|
|
170
|
+
serialize(tree, ctx) {
|
|
171
|
+
return [`msgid ""\nmsgstr ""\n"Language: ${ctx.locale}\\n"\n"MIME-Version: 1.0\\n"\n"Content-Type: text/plain; charset=UTF-8\\n"\n"Content-Transfer-Encoding: 8bit\\n"\n`, ...[...walkLeaves(tree)].map((leaf) => entryText(leaf.path.join("."), leafText(leaf.value)))].join("\n");
|
|
172
|
+
}
|
|
173
|
+
}]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
export { po as default, po };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { a as splice, i as leafText, o as splitLines, t as appendLines } from "../shared-BWaXUmwA.mjs";
|
|
2
|
+
import { diffText, errors, pathId, walkLeaves } from "@x18n/core";
|
|
3
|
+
//#region src/formats/properties/index.ts
|
|
4
|
+
const escapes = {
|
|
5
|
+
t: " ",
|
|
6
|
+
n: "\n",
|
|
7
|
+
r: "\r",
|
|
8
|
+
f: "\f"
|
|
9
|
+
};
|
|
10
|
+
const isSpace = (char) => char === " " || char === " " || char === "\f";
|
|
11
|
+
function unescape(raw, path) {
|
|
12
|
+
let out = "";
|
|
13
|
+
for (let i = 0; i < raw.length; i++) {
|
|
14
|
+
const char = raw[i];
|
|
15
|
+
if (char !== "\\") {
|
|
16
|
+
out += char;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const next = raw[++i];
|
|
20
|
+
if (next === void 0) break;
|
|
21
|
+
if (next === "\r" || next === "\n") {
|
|
22
|
+
if (next === "\r" && raw[i + 1] === "\n") i++;
|
|
23
|
+
while (isSpace(raw[i + 1])) i++;
|
|
24
|
+
} else if (next === "u") {
|
|
25
|
+
const hex = raw.slice(i + 1, i + 5);
|
|
26
|
+
if (!/^[0-9a-f]{4}$/i.test(hex)) throw errors.X18N2002({
|
|
27
|
+
path,
|
|
28
|
+
reason: `malformed \\u escape "\\u${hex}"`
|
|
29
|
+
});
|
|
30
|
+
out += String.fromCharCode(Number.parseInt(hex, 16));
|
|
31
|
+
i += 4;
|
|
32
|
+
} else out += escapes[next] ?? next;
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
function read(text, path) {
|
|
37
|
+
const entries = [];
|
|
38
|
+
const lines = splitLines(text);
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
let p = lines[i].start;
|
|
41
|
+
const lineStart = p;
|
|
42
|
+
while (isSpace(text[p])) p++;
|
|
43
|
+
if (p >= lines[i].end || text[p] === "#" || text[p] === "!") continue;
|
|
44
|
+
while (i < lines.length - 1 && /(?:^|[^\\])(?:\\\\)*\\$/.test(text.slice(lines[i].start, lines[i].end))) i++;
|
|
45
|
+
const end = lines[i].end;
|
|
46
|
+
const keyStart = p;
|
|
47
|
+
while (p < end && !"=: \f".includes(text[p])) p += text[p] === "\\" ? 2 : 1;
|
|
48
|
+
const keyEnd = Math.min(p, end);
|
|
49
|
+
while (isSpace(text[p])) p++;
|
|
50
|
+
if (text[p] === "=" || text[p] === ":") p++;
|
|
51
|
+
while (isSpace(text[p])) p++;
|
|
52
|
+
const valueStart = Math.min(p, end);
|
|
53
|
+
entries.push({
|
|
54
|
+
key: unescape(text.slice(keyStart, keyEnd), path),
|
|
55
|
+
value: unescape(text.slice(valueStart, end), path),
|
|
56
|
+
range: {
|
|
57
|
+
key: {
|
|
58
|
+
start: keyStart,
|
|
59
|
+
end: keyEnd
|
|
60
|
+
},
|
|
61
|
+
value: {
|
|
62
|
+
start: valueStart,
|
|
63
|
+
end
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
line: {
|
|
67
|
+
start: lineStart,
|
|
68
|
+
end: lines[i].next
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return entries;
|
|
73
|
+
}
|
|
74
|
+
const encodings = {
|
|
75
|
+
"\\": "\\\\",
|
|
76
|
+
" ": "\\t",
|
|
77
|
+
"\n": "\\n",
|
|
78
|
+
"\r": "\\r",
|
|
79
|
+
"\f": "\\f"
|
|
80
|
+
};
|
|
81
|
+
const escapeText = (text) => text.replace(/[\\\n\r\t\f]/g, (char) => encodings[char]);
|
|
82
|
+
const escapeKey = (key) => escapeText(key).replace(/[=: #!]/g, "\\$&");
|
|
83
|
+
const escapeValue = (value) => escapeText(value).replace(/^ /, "\\ ");
|
|
84
|
+
function applyOp(text, op, path) {
|
|
85
|
+
const key = op.path.join(".");
|
|
86
|
+
const entries = read(text, path);
|
|
87
|
+
const entry = entries.findLast((e) => e.key === key);
|
|
88
|
+
if (op.type === "delete") return entry ? splice(text, entry.line, "") : text;
|
|
89
|
+
const value = escapeValue(leafText(op.value));
|
|
90
|
+
if (entry) return splice(text, entry.range.value, value);
|
|
91
|
+
const last = entries.findLast((e) => e.value !== "");
|
|
92
|
+
const separator = last ? text.slice(last.range.key.end, last.range.value.start) : "";
|
|
93
|
+
return appendLines(text, `${escapeKey(key)}${/[=:\s]/.test(separator) ? separator : "="}${value}\n`);
|
|
94
|
+
}
|
|
95
|
+
function properties() {
|
|
96
|
+
return {
|
|
97
|
+
name: "x18n:format-properties",
|
|
98
|
+
formats: [{
|
|
99
|
+
id: "properties",
|
|
100
|
+
extensions: ["properties"],
|
|
101
|
+
parse(text, ctx) {
|
|
102
|
+
const tree = {};
|
|
103
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const entry of read(text, ctx.path)) {
|
|
105
|
+
tree[entry.key] = entry.value;
|
|
106
|
+
ranges.set(pathId([entry.key]), entry.range);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
tree,
|
|
110
|
+
ranges
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
edit(text, ops, ctx) {
|
|
114
|
+
let next = text;
|
|
115
|
+
for (const op of ops) next = applyOp(next, op, ctx.path);
|
|
116
|
+
return diffText(text, next);
|
|
117
|
+
},
|
|
118
|
+
serialize: (tree) => [...walkLeaves(tree)].map((leaf) => `${escapeKey(leaf.path.join("."))}=${escapeValue(leafText(leaf.value))}\n`).join("")
|
|
119
|
+
}]
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { properties as default, properties };
|