@x18n/builtins 0.0.13 → 0.0.15
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/checks.d.mts +1 -0
- package/dist/checks.mjs +26 -1
- 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 +232 -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 +143 -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 +198 -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 +140 -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 +30 -22
- package/dist/{vue-CF4pe-Yr.mjs → js-CV-ECNJG.mjs} +67 -222
- package/dist/json-CGNSGS97.mjs +109 -0
- package/dist/json-CyjI8bjp.d.mts +8 -0
- package/dist/jsx-templates-B1J0no3i.mjs +17 -0
- package/dist/literal-gIdB1VGy.mjs +125 -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-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-i18n-js.d.mts +5 -0
- package/dist/presets/ruby-i18n-js.mjs +19 -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/reviews.d.mts +6 -0
- package/dist/reviews.mjs +35 -0
- 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-C7ceptwi.mjs +55 -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 +68 -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,125 @@
|
|
|
1
|
+
import { a as splice, n as detectEol } from "./shared-C7ceptwi.mjs";
|
|
2
|
+
import { isMessageLeaf } from "@x18n/core";
|
|
3
|
+
//#region src/formats/shared/literal.ts
|
|
4
|
+
/** Prints a value at `depth`. Objects span lines, one entry per line. */
|
|
5
|
+
function literalText(value, syntax, depth = 0) {
|
|
6
|
+
if (isMessageLeaf(value)) return syntax.leaf(value, void 0);
|
|
7
|
+
const entries = Object.entries(value);
|
|
8
|
+
if (entries.length === 0) return `${syntax.open}${syntax.close}`;
|
|
9
|
+
const pad = syntax.unit.repeat(depth + 1);
|
|
10
|
+
const lines = entries.map(([key, child]) => `${pad}${syntax.key(key)}${syntax.separator}${literalText(child, syntax, depth + 1)}`);
|
|
11
|
+
return `${syntax.open}\n${lines.join(",\n")}${syntax.trailingComma ? "," : ""}\n${syntax.unit.repeat(depth)}${syntax.close}`;
|
|
12
|
+
}
|
|
13
|
+
/** Applies one op to the text of `root` and leaves everything else as it is: comments, quotes, order and layout. */
|
|
14
|
+
function editLiteral(text, root, op, syntax) {
|
|
15
|
+
const last = root.items.at(-1);
|
|
16
|
+
const style = last ? {
|
|
17
|
+
...syntax,
|
|
18
|
+
trailingComma: commaAfter(text, last.range.end) !== void 0
|
|
19
|
+
} : syntax;
|
|
20
|
+
let node = root;
|
|
21
|
+
let depth = 0;
|
|
22
|
+
while (depth < op.path.length && node.kind === "object") {
|
|
23
|
+
const object = node;
|
|
24
|
+
const item = object.items.findLast((i) => i.key === op.path[depth]);
|
|
25
|
+
if (!item) break;
|
|
26
|
+
if (op.type === "delete" && depth === op.path.length - 1) return removeItem(text, object, item);
|
|
27
|
+
node = item.value;
|
|
28
|
+
depth++;
|
|
29
|
+
}
|
|
30
|
+
if (op.type === "delete") return text;
|
|
31
|
+
const rest = op.path.slice(depth);
|
|
32
|
+
if (node.kind === "object" && rest.length > 0) return insertItem(text, node, rest, op.value, style);
|
|
33
|
+
const value = nest(rest, op.value);
|
|
34
|
+
return splice(text, node.range, isMessageLeaf(value) ? style.leaf(value, node.kind === "scalar" ? node.quote : void 0) : reindent(literalText(value, style), indentAt(text, node.range.start), detectEol(text)));
|
|
35
|
+
}
|
|
36
|
+
function nest(path, value) {
|
|
37
|
+
return path.reduceRight((child, segment) => ({ [segment]: child }), value);
|
|
38
|
+
}
|
|
39
|
+
function reindent(text, indent, eol) {
|
|
40
|
+
return text.replace(/\n/g, `${eol}${indent}`);
|
|
41
|
+
}
|
|
42
|
+
function lineStart(text, pos) {
|
|
43
|
+
return text.lastIndexOf("\n", pos - 1) + 1;
|
|
44
|
+
}
|
|
45
|
+
function indentAt(text, pos) {
|
|
46
|
+
return /^[ \t]*/.exec(text.slice(lineStart(text, pos)))[0];
|
|
47
|
+
}
|
|
48
|
+
/** The `,` after a value, past blanks and comments. */
|
|
49
|
+
function commaAfter(text, pos) {
|
|
50
|
+
const at = pos + /^(?:\s|\/\/[^\n]*|#[^\n]*|\/\*[\s\S]*?\*\/)*/.exec(text.slice(pos))[0].length;
|
|
51
|
+
return text[at] === "," ? at : void 0;
|
|
52
|
+
}
|
|
53
|
+
/** The end of the line at `pos`, before the line break, when only blanks and a comment follow. */
|
|
54
|
+
function lineEnd(text, pos) {
|
|
55
|
+
const end = pos + /^[ \t]*(?:(?:\/\/|#).*|\/\*.*?\*\/[ \t]*)?/.exec(text.slice(pos))[0].length;
|
|
56
|
+
return end === text.length || text.startsWith("\n", end) || text.startsWith("\r\n", end) ? end : void 0;
|
|
57
|
+
}
|
|
58
|
+
function insertItem(text, object, path, value, syntax) {
|
|
59
|
+
const eol = detectEol(text);
|
|
60
|
+
const entry = (indent) => `${syntax.key(path[0])}${syntax.separator}${reindent(literalText(nest(path.slice(1), value), syntax), indent, eol)}`;
|
|
61
|
+
const first = object.items[0];
|
|
62
|
+
const last = object.items.at(-1);
|
|
63
|
+
if (!first || !last) {
|
|
64
|
+
const base = indentAt(text, object.range.start);
|
|
65
|
+
const indent = base + syntax.unit;
|
|
66
|
+
const close = object.range.end - 1;
|
|
67
|
+
const blank = /\s*$/.exec(text.slice(object.range.start, close))[0].length;
|
|
68
|
+
return splice(text, {
|
|
69
|
+
start: close - blank,
|
|
70
|
+
end: close
|
|
71
|
+
}, `${eol}${indent}${entry(indent)}${syntax.trailingComma ? "," : ""}${eol}${base}`);
|
|
72
|
+
}
|
|
73
|
+
const comma = commaAfter(text, last.range.end);
|
|
74
|
+
const end = comma === void 0 ? last.range.end : comma + 1;
|
|
75
|
+
if (!text.slice(object.range.start, first.range.start).includes("\n")) {
|
|
76
|
+
const indent = indentAt(text, object.range.start);
|
|
77
|
+
return splice(text, {
|
|
78
|
+
start: end,
|
|
79
|
+
end
|
|
80
|
+
}, comma === void 0 ? `, ${entry(indent)}` : ` ${entry(indent)},`);
|
|
81
|
+
}
|
|
82
|
+
const indent = indentAt(text, first.range.start);
|
|
83
|
+
const at = lineEnd(text, end) ?? end;
|
|
84
|
+
const next = splice(text, {
|
|
85
|
+
start: at,
|
|
86
|
+
end: at
|
|
87
|
+
}, `${eol}${indent}${entry(indent)}${comma === void 0 ? "" : ","}`);
|
|
88
|
+
return comma === void 0 ? splice(next, {
|
|
89
|
+
start: end,
|
|
90
|
+
end
|
|
91
|
+
}, ",") : next;
|
|
92
|
+
}
|
|
93
|
+
function removeItem(text, object, item) {
|
|
94
|
+
const comma = commaAfter(text, item.range.end);
|
|
95
|
+
const end = comma === void 0 ? item.range.end : comma + 1;
|
|
96
|
+
const previous = object.items[object.items.indexOf(item) - 1];
|
|
97
|
+
const before = comma === void 0 && previous ? commaAfter(text, previous.range.end) : void 0;
|
|
98
|
+
const close = lineEnd(text, end);
|
|
99
|
+
const opening = text.slice(object.range.start, item.range.start);
|
|
100
|
+
if (object.items.length === 1 && !/\/\/|\/\*|#/.test(opening) && text.slice(close ?? end, object.range.end - 1).trim() === "") return splice(text, {
|
|
101
|
+
start: object.range.start + opening.trimEnd().length,
|
|
102
|
+
end: object.range.end - 1
|
|
103
|
+
}, "");
|
|
104
|
+
const start = lineStart(text, item.range.start);
|
|
105
|
+
if (close !== void 0 && /^[ \t]*$/.test(text.slice(start, item.range.start))) {
|
|
106
|
+
const next = splice(text, {
|
|
107
|
+
start,
|
|
108
|
+
end: close + (/^\r?\n/.exec(text.slice(close))?.[0].length ?? 0)
|
|
109
|
+
}, "");
|
|
110
|
+
return before === void 0 ? next : splice(next, {
|
|
111
|
+
start: before,
|
|
112
|
+
end: before + 1
|
|
113
|
+
}, "");
|
|
114
|
+
}
|
|
115
|
+
if (before !== void 0) return splice(text, {
|
|
116
|
+
start: before,
|
|
117
|
+
end
|
|
118
|
+
}, "");
|
|
119
|
+
return splice(text, {
|
|
120
|
+
start: item.range.start,
|
|
121
|
+
end: end + /^[ \t]*/.exec(text.slice(end))[0].length
|
|
122
|
+
}, "");
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { literalText as n, editLiteral as t };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isMessageLeaf } from "@x18n/core";
|
|
2
|
+
//#region src/presets/shared/locale-root.ts
|
|
3
|
+
/** Files that start with their locale, `en: { users: ... }`, as in Rails and i18nice. */
|
|
4
|
+
function localeRoot(applies) {
|
|
5
|
+
return {
|
|
6
|
+
applies,
|
|
7
|
+
read(tree, ctx) {
|
|
8
|
+
const root = tree[ctx.locale];
|
|
9
|
+
return root !== void 0 && !isMessageLeaf(root) ? root : {};
|
|
10
|
+
},
|
|
11
|
+
toStored: (path, ctx) => [ctx.locale, ...path]
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { localeRoot as t };
|
package/dist/manifests.d.mts
CHANGED
|
@@ -6,5 +6,13 @@ export declare const composerJson: ManifestReader;
|
|
|
6
6
|
export declare const pubspecYaml: ManifestReader;
|
|
7
7
|
/** Every `gem 'name'` line, not just `rails-i18n` as in i18n-ally. */
|
|
8
8
|
export declare const gemfile: ManifestReader;
|
|
9
|
+
/** Reads the PEP 621, PEP 735 and Poetry dependency tables; dependency names need no full TOML parser. */
|
|
10
|
+
export declare const pyprojectToml: ManifestReader;
|
|
11
|
+
export declare const requirementsTxt: ManifestReader;
|
|
12
|
+
/**
|
|
13
|
+
* Dependency names from `[dependencies]`, `[dev-dependencies]`, `[build-dependencies]`, `[workspace.dependencies]`
|
|
14
|
+
* and target tables, and `[dependencies.name]` headers. A renamed dependency reports its alias, not `package`. TOML puts each key on its own line, so a line scan is enough.
|
|
15
|
+
*/
|
|
16
|
+
export declare const cargoToml: ManifestReader;
|
|
9
17
|
export declare function manifests(): Plugin;
|
|
10
18
|
//#endregion
|
package/dist/manifests.mjs
CHANGED
|
@@ -34,6 +34,67 @@ const gemfile = {
|
|
|
34
34
|
file: "Gemfile",
|
|
35
35
|
read: (text) => [...text.matchAll(/^\s*gem\s+["']([^"']+)["']/gm)].map((match) => match[1])
|
|
36
36
|
};
|
|
37
|
+
/** The PEP 503 normalized name of a PEP 508 requirement: `Flask_Babel[async]>=4` is `flask-babel`. */
|
|
38
|
+
function requirementName(requirement) {
|
|
39
|
+
const name = /^\s*([a-z\d][\w.-]*)/i.exec(requirement)?.[1];
|
|
40
|
+
return name ? [name.toLowerCase().replace(/[-_.]+/g, "-")] : [];
|
|
41
|
+
}
|
|
42
|
+
/** TOML strings outside comments, up to the first `]` outside a string (`"i18nice[YAML]"` has none) when `toClose`. */
|
|
43
|
+
function tomlStrings(text, toClose) {
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const match of text.matchAll(/"([^"\n]*)"|'([^'\n]*)'|#.*|(\])/g)) {
|
|
46
|
+
if (match[3] && toClose) break;
|
|
47
|
+
const value = match[1] ?? match[2];
|
|
48
|
+
if (value !== void 0) out.push(value);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/** Reads the PEP 621, PEP 735 and Poetry dependency tables; dependency names need no full TOML parser. */
|
|
53
|
+
const pyprojectToml = {
|
|
54
|
+
id: "pyproject.toml",
|
|
55
|
+
file: "pyproject.toml",
|
|
56
|
+
read: (text) => text.replace(/"""[\s\S]*?"""|'''[\s\S]*?'''/g, "\"\"").split(/^[ \t]*\[/m).flatMap((table) => {
|
|
57
|
+
const header = /^\[?([^\]\n]+)\]/.exec(table)?.[1]?.trim();
|
|
58
|
+
const body = table.slice(table.indexOf("\n") + 1);
|
|
59
|
+
if (header === "project") {
|
|
60
|
+
const start = /^\s*dependencies\s*=\s*\[/m.exec(body);
|
|
61
|
+
return start ? tomlStrings(body.slice(start.index + start[0].length), true).flatMap(requirementName) : [];
|
|
62
|
+
}
|
|
63
|
+
if (header === "project.optional-dependencies" || header === "dependency-groups") return tomlStrings(body, false).flatMap(requirementName);
|
|
64
|
+
if (header && /^tool\.poetry\.(?:group\.[\w-]+\.)?(?:dev-)?dependencies$/.test(header)) return [...body.matchAll(/^\s*(?:"([^"\n]+)"|'([^'\n]+)'|([\w-]+))\s*[.=]/gm)].flatMap((match) => requirementName(match[1] ?? match[2] ?? match[3])).filter((name) => name !== "python");
|
|
65
|
+
return [];
|
|
66
|
+
})
|
|
67
|
+
};
|
|
68
|
+
const requirementsTxt = {
|
|
69
|
+
id: "requirements.txt",
|
|
70
|
+
file: "requirements.txt",
|
|
71
|
+
read: (text) => text.split("\n").flatMap((line) => line.trim().startsWith("-") ? [] : requirementName(line))
|
|
72
|
+
};
|
|
73
|
+
const cargoSection = /^\[\s*((?:workspace\.|target\.[^\]]+\.)?(?:dev-|build-)?dependencies)(?:\.([\w-]+))?\s*\]/;
|
|
74
|
+
const cargoKey = /^\s*"?([\w-]+)"?\s*[.=]/;
|
|
75
|
+
/**
|
|
76
|
+
* Dependency names from `[dependencies]`, `[dev-dependencies]`, `[build-dependencies]`, `[workspace.dependencies]`
|
|
77
|
+
* and target tables, and `[dependencies.name]` headers. A renamed dependency reports its alias, not `package`. TOML puts each key on its own line, so a line scan is enough.
|
|
78
|
+
*/
|
|
79
|
+
const cargoToml = {
|
|
80
|
+
id: "Cargo.toml",
|
|
81
|
+
file: "Cargo.toml",
|
|
82
|
+
read(text) {
|
|
83
|
+
const names = [];
|
|
84
|
+
let inDependencies = false;
|
|
85
|
+
for (const line of text.split("\n")) {
|
|
86
|
+
if (line.trimStart().startsWith("[")) {
|
|
87
|
+
const section = cargoSection.exec(line.trim());
|
|
88
|
+
inDependencies = section !== null && section[2] === void 0;
|
|
89
|
+
if (section?.[2]) names.push(section[2]);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const name = inDependencies ? cargoKey.exec(line)?.[1] : void 0;
|
|
93
|
+
if (name) names.push(name);
|
|
94
|
+
}
|
|
95
|
+
return names;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
37
98
|
function manifests() {
|
|
38
99
|
return {
|
|
39
100
|
name: "x18n:manifests",
|
|
@@ -41,9 +102,12 @@ function manifests() {
|
|
|
41
102
|
packageJson,
|
|
42
103
|
composerJson,
|
|
43
104
|
pubspecYaml,
|
|
44
|
-
gemfile
|
|
105
|
+
gemfile,
|
|
106
|
+
pyprojectToml,
|
|
107
|
+
requirementsTxt,
|
|
108
|
+
cargoToml
|
|
45
109
|
]
|
|
46
110
|
};
|
|
47
111
|
}
|
|
48
112
|
//#endregion
|
|
49
|
-
export { composerJson, gemfile, manifests, packageJson, pubspecYaml };
|
|
113
|
+
export { cargoToml, composerJson, gemfile, manifests, packageJson, pubspecYaml, pyprojectToml, requirementsTxt };
|
package/dist/placeholders.d.mts
CHANGED
|
@@ -2,12 +2,21 @@ import { PlaceholderSyntax, Plugin } from "@x18n/core";
|
|
|
2
2
|
//#region src/placeholders/index.d.ts
|
|
3
3
|
/** `{name}`: vue-i18n named interpolation and similar. */
|
|
4
4
|
export declare const braces: PlaceholderSyntax;
|
|
5
|
+
/** `{}`: easy_localization positional arguments. */
|
|
6
|
+
export declare const positionalBraces: PlaceholderSyntax;
|
|
7
|
+
/** `{name}` with an optional type, formatters or switch-case: `{name:string}`, `{date|weekday}`, `{gender|{male: his, *: their}}`. `{{a|b}}` plurals are not placeholders. */
|
|
8
|
+
export declare const typedBraces: PlaceholderSyntax;
|
|
5
9
|
/** `{{name}}`: i18next interpolation, including `{{name, format}}` and `{{- raw}}`. */
|
|
6
10
|
export declare const doubleBraces: PlaceholderSyntax;
|
|
7
11
|
/** `%{name}`: Rails, Polyglot. */
|
|
8
12
|
export declare const percentBraces: PlaceholderSyntax;
|
|
9
13
|
/** `%s`, `%d`, `%1$s`: printf. */
|
|
10
14
|
export declare const printf: PlaceholderSyntax;
|
|
15
|
+
/**
|
|
16
|
+
* `%(name)s`, `%s`, `%d`: Python percent formatting, as in gettext catalogs. No space flag, so `50% of` is text.
|
|
17
|
+
* URL escapes are text too: those with an uppercase hex letter (`%2F`), and any `%XX` after `://` in the same word (`%20a`).
|
|
18
|
+
*/
|
|
19
|
+
export declare const pythonPercent: PlaceholderSyntax;
|
|
11
20
|
/** `@:key` and `@.modifier:key`: vue-i18n linked messages. */
|
|
12
21
|
export declare const linked: PlaceholderSyntax;
|
|
13
22
|
/** `{ $name }`: Fluent variables. */
|
|
@@ -16,6 +25,8 @@ export declare const fluent: PlaceholderSyntax;
|
|
|
16
25
|
export declare const colon: PlaceholderSyntax;
|
|
17
26
|
/** `$name$`: Chrome extension messages. */
|
|
18
27
|
export declare const dollars: PlaceholderSyntax;
|
|
28
|
+
/** `{0}`, `{0:C}`, `{0,-10}`, `{name}`: .NET composite format items. `{{` and `}}` are literal braces. */
|
|
29
|
+
export declare const dotnet: PlaceholderSyntax;
|
|
19
30
|
/** `$t(key)` nesting in i18next. */
|
|
20
31
|
export declare const nesting: PlaceholderSyntax;
|
|
21
32
|
/** ICU MessageFormat arguments: `{name}`, `{count, plural, ...}`. Nested sub-messages are scanned too. */
|
package/dist/placeholders.mjs
CHANGED
|
@@ -20,12 +20,21 @@ function byRegex(id, regex, example) {
|
|
|
20
20
|
}
|
|
21
21
|
/** `{name}`: vue-i18n named interpolation and similar. */
|
|
22
22
|
const braces = byRegex("braces", /(?<![{$])\{\s*([\w.-]+)\s*\}(?!\})/g, "{name}");
|
|
23
|
+
/** `{}`: easy_localization positional arguments. */
|
|
24
|
+
const positionalBraces = byRegex("positional-braces", /\{\}/g, "{}");
|
|
25
|
+
/** `{name}` with an optional type, formatters or switch-case: `{name:string}`, `{date|weekday}`, `{gender|{male: his, *: their}}`. `{{a|b}}` plurals are not placeholders. */
|
|
26
|
+
const typedBraces = byRegex("typed-braces", /(?<![{$])\{\s*(\w+)\??\s*(?::[^{}|]*)?(?:\|(?:\{[^{}]*\}|[^{}]*))?\}(?!\})/g, "{name}");
|
|
23
27
|
/** `{{name}}`: i18next interpolation, including `{{name, format}}` and `{{- raw}}`. */
|
|
24
28
|
const doubleBraces = byRegex("double-braces", /\{\{-?\s*([\w.]+)\s*(?:,[^}]*)?\}\}/g, "{{name}}");
|
|
25
29
|
/** `%{name}`: Rails, Polyglot. */
|
|
26
30
|
const percentBraces = byRegex("percent-braces", /%\{(\w+)\}/g, "%{name}");
|
|
27
31
|
/** `%s`, `%d`, `%1$s`: printf. */
|
|
28
32
|
const printf = byRegex("printf", /%(?:(\d+)\$)?[-+ 0#]*(?:[1-9]\d*)?(?:\.\d+)?[sdifoxXeEgGc@]/g, "%s");
|
|
33
|
+
/**
|
|
34
|
+
* `%(name)s`, `%s`, `%d`: Python percent formatting, as in gettext catalogs. No space flag, so `50% of` is text.
|
|
35
|
+
* URL escapes are text too: those with an uppercase hex letter (`%2F`), and any `%XX` after `://` in the same word (`%20a`).
|
|
36
|
+
*/
|
|
37
|
+
const pythonPercent = byRegex("python-percent", /(?<!%)%(?![\dA-F][A-F])(?!(?<=:\/\/\S*%)[\dA-Fa-f]{2})(?:\((\w+)\))?[-#0+]*(?:[1-9]\d*|\*)?(?:\.\d+)?[c-giouxXEFGrsa]/g, "%(name)s");
|
|
29
38
|
/** `@:key` and `@.modifier:key`: vue-i18n linked messages. */
|
|
30
39
|
const linked = {
|
|
31
40
|
...byRegex("linked", /@(?:\.\w+)?:\(?([\w.-]+)\)?/g, "@:key"),
|
|
@@ -37,6 +46,24 @@ const fluent = byRegex("fluent", /\{\s*\$([\w-]+)(?:\s[^}]*)?\}/g, "{ $name }");
|
|
|
37
46
|
const colon = byRegex("colon", /(?<![\w:]):([a-z_]\w*)/gi, ":name");
|
|
38
47
|
/** `$name$`: Chrome extension messages. */
|
|
39
48
|
const dollars = byRegex("dollars", /\$(\w+)\$/g, "$name$");
|
|
49
|
+
/** `{0}`, `{0:C}`, `{0,-10}`, `{name}`: .NET composite format items. `{{` and `}}` are literal braces. */
|
|
50
|
+
const dotnet = {
|
|
51
|
+
id: "dotnet",
|
|
52
|
+
example: "{0}",
|
|
53
|
+
find(value) {
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const match of value.matchAll(/\{\{|\}\}|\{\s*(\w+)\s*(?:,\s*-?\d+\s*)?(?::[^{}]*)?\}/g)) if (match[1] !== void 0) out.push({
|
|
56
|
+
syntax: "dotnet",
|
|
57
|
+
text: match[0],
|
|
58
|
+
name: match[1],
|
|
59
|
+
range: {
|
|
60
|
+
start: match.index,
|
|
61
|
+
end: match.index + match[0].length
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
40
67
|
/** `$t(key)` nesting in i18next. */
|
|
41
68
|
const nesting = {
|
|
42
69
|
...byRegex("nesting", /\$t\(([\w.:-]+)(?:,[^)]*)?\)/g, "$t(key)"),
|
|
@@ -128,4 +155,4 @@ function placeholderFromShape(shape) {
|
|
|
128
155
|
return byRegex(`shape:${shape}`, new RegExp(`${escape(before ?? "")}\\s*([\\w.-]+)\\s*${escape(after ?? "")}`, "g"), shape);
|
|
129
156
|
}
|
|
130
157
|
//#endregion
|
|
131
|
-
export { braces, colon, dollars, doubleBraces, fluent, icu, linked, nesting, percentBraces, placeholderFromShape, placeholders, printf };
|
|
158
|
+
export { braces, colon, dollars, dotnet, doubleBraces, fluent, icu, linked, nesting, percentBraces, placeholderFromShape, placeholders, positionalBraces, printf, pythonPercent, typedBraces };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { doubleBraces, nesting } from "./placeholders.mjs";
|
|
2
|
+
import { createLibraryPlugin } from "@x18n/core";
|
|
3
|
+
//#region src/presets/i18next/plugin.ts
|
|
4
|
+
/** `defaultNS` and `fallbackNS` from the i18next init code (`i18n.ts`, `next-i18next.config.js`), as keys without a namespace use them. */
|
|
5
|
+
async function findNamespaces(host) {
|
|
6
|
+
const files = await host.glob(["**/*i18n*.{js,mjs,cjs,ts,mts,cts}", "**/i18n/index.{js,ts}"], {
|
|
7
|
+
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
8
|
+
gitignore: true
|
|
9
|
+
});
|
|
10
|
+
const found = {
|
|
11
|
+
default: void 0,
|
|
12
|
+
fallback: void 0
|
|
13
|
+
};
|
|
14
|
+
for (const file of files.slice(0, 20)) {
|
|
15
|
+
const text = await host.readFile(file) ?? "";
|
|
16
|
+
found.default ??= /\bdefaultNS\s*:\s*['"`]([^'"`]+)['"`]/.exec(text)?.[1];
|
|
17
|
+
const fallback = /\bfallbackNS\s*:\s*(\[[^\]]*\]|['"`][^'"`]+['"`])/.exec(text)?.[1];
|
|
18
|
+
found.fallback ??= fallback ? [...fallback.matchAll(/['"`]([^'"`]+)['"`]/g)].map((m) => m[1]) : void 0;
|
|
19
|
+
}
|
|
20
|
+
return found;
|
|
21
|
+
}
|
|
22
|
+
const i18nextPlugin = createLibraryPlugin({
|
|
23
|
+
name: "x18n:i18next",
|
|
24
|
+
namespace: {
|
|
25
|
+
separator: ":",
|
|
26
|
+
default: "translation",
|
|
27
|
+
fromPath: "slash"
|
|
28
|
+
},
|
|
29
|
+
extra: async (draft, ctx) => {
|
|
30
|
+
const user = ctx.user.namespace;
|
|
31
|
+
if (user?.default && user.fallback) return;
|
|
32
|
+
const found = await findNamespaces(ctx.host);
|
|
33
|
+
draft.namespace = {
|
|
34
|
+
...draft.namespace,
|
|
35
|
+
default: user?.default ?? found.default ?? draft.namespace.default,
|
|
36
|
+
fallback: user?.fallback ?? found.fallback ?? draft.namespace.fallback
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
calls: [
|
|
40
|
+
{
|
|
41
|
+
callee: [
|
|
42
|
+
"t",
|
|
43
|
+
"i18next.t",
|
|
44
|
+
"i18n.t"
|
|
45
|
+
],
|
|
46
|
+
namespace: {
|
|
47
|
+
arg: 1,
|
|
48
|
+
property: "ns"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
callee: [
|
|
53
|
+
"getFixedT",
|
|
54
|
+
"i18next.getFixedT",
|
|
55
|
+
"i18n.getFixedT"
|
|
56
|
+
],
|
|
57
|
+
factory: true,
|
|
58
|
+
returns: ["t"],
|
|
59
|
+
namespace: { arg: 1 },
|
|
60
|
+
keyPrefix: { arg: 2 }
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
callee: "tr",
|
|
64
|
+
types: ["TFunction"]
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
placeholders: [doubleBraces, nesting],
|
|
68
|
+
derivedKeys: [
|
|
69
|
+
"{key}_ordinal_{ordinal}",
|
|
70
|
+
"{key}_{plural}",
|
|
71
|
+
"{key}_*"
|
|
72
|
+
],
|
|
73
|
+
translatePrompt: (draft, ctx) => {
|
|
74
|
+
draft.notes.push(`i18next plural keys end in _${ctx.localeInfo.pluralCategories.join(", _")} for ${ctx.targetLocale}; write one key per category.`);
|
|
75
|
+
},
|
|
76
|
+
templates: [{
|
|
77
|
+
template: "i18next.t('{key}'{args})",
|
|
78
|
+
kinds: ["js-string", "js-template"],
|
|
79
|
+
imports: ["import i18next from 'i18next'"]
|
|
80
|
+
}]
|
|
81
|
+
});
|
|
82
|
+
//#endregion
|
|
83
|
+
export { i18nextPlugin as t };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/presets/shared/plural-forms.ts
|
|
2
|
+
/** Not `{plural}`: easy_localization ignores CLDR plural rules by default, and both libraries fall back to `other`. */
|
|
3
|
+
const pluralForms = [
|
|
4
|
+
"zero",
|
|
5
|
+
"one",
|
|
6
|
+
"two",
|
|
7
|
+
"few",
|
|
8
|
+
"many",
|
|
9
|
+
"other"
|
|
10
|
+
];
|
|
11
|
+
//#endregion
|
|
12
|
+
export { pluralForms as t };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Preset } from "@x18n/core";
|
|
2
|
+
//#region src/presets/cargo-i18n/index.d.ts
|
|
3
|
+
/** cargo-i18n with the Fluent system: `i18n.toml`, `{assets_dir}/{locale}/{crate}.ftl` and the `fl!` macro of `i18n-embed-fl`. */
|
|
4
|
+
export declare function cargoI18n(): Preset;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { cargoI18n as default };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { fluent } from "../placeholders.mjs";
|
|
2
|
+
import { createLibraryPlugin } from "@x18n/core";
|
|
3
|
+
//#region src/presets/cargo-i18n/index.ts
|
|
4
|
+
/** cargo-i18n with the Fluent system: `i18n.toml`, `{assets_dir}/{locale}/{crate}.ftl` and the `fl!` macro of `i18n-embed-fl`. */
|
|
5
|
+
function cargoI18n() {
|
|
6
|
+
return {
|
|
7
|
+
name: "cargo-i18n",
|
|
8
|
+
plugins: [createLibraryPlugin({
|
|
9
|
+
name: "x18n:cargo-i18n",
|
|
10
|
+
regex: [{
|
|
11
|
+
extensions: ["rs"],
|
|
12
|
+
patterns: ["\\bfl!\\(\\s*(?:[^,\"]+,\\s*)?\"({key})\""]
|
|
13
|
+
}],
|
|
14
|
+
derivedKeys: ["{key}.*"],
|
|
15
|
+
extra: async (draft, ctx) => {
|
|
16
|
+
const toml = await ctx.host.readFile("i18n.toml") ?? "";
|
|
17
|
+
const fallback = /^\s*fallback_language\s*=\s*["']([^"']+)["']/m.exec(toml)?.[1];
|
|
18
|
+
if (fallback) draft.sourceLocale = fallback;
|
|
19
|
+
const assetsDir = /^\s*assets_dir\s*=\s*["']([^"']+)["']/m.exec(toml)?.[1]?.replace(/^\.\/|\/$/g, "") ?? "i18n";
|
|
20
|
+
draft.resources = [...draft.resources, { pattern: `${assetsDir}/{locale}/*.ftl` }];
|
|
21
|
+
},
|
|
22
|
+
templates: [{ template: "fl!(LANGUAGE_LOADER, \"{key}\")" }],
|
|
23
|
+
placeholders: [fluent]
|
|
24
|
+
})]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { cargoI18n, cargoI18n as default };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Preset } from "@x18n/core";
|
|
2
|
+
//#region src/presets/chrome-extension/index.d.ts
|
|
3
|
+
/** Chrome and WebExtension `_locales/{locale}/messages.json`: `{ "name": { "message": "..." } }` reads as `name`. */
|
|
4
|
+
export declare function chromeExtension(): Preset;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { chromeExtension as default };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { dollars } from "../placeholders.mjs";
|
|
2
|
+
import { createLibraryPlugin, isMessageLeaf } from "@x18n/core";
|
|
3
|
+
//#region src/presets/chrome-extension/index.ts
|
|
4
|
+
/** Chrome and WebExtension `_locales/{locale}/messages.json`: `{ "name": { "message": "..." } }` reads as `name`. */
|
|
5
|
+
function chromeExtension() {
|
|
6
|
+
return {
|
|
7
|
+
name: "chrome-extension",
|
|
8
|
+
plugins: [createLibraryPlugin({
|
|
9
|
+
name: "x18n:chrome-extension",
|
|
10
|
+
calls: [{ callee: [
|
|
11
|
+
"chrome.i18n.getMessage",
|
|
12
|
+
"browser.i18n.getMessage",
|
|
13
|
+
"i18n.getMessage"
|
|
14
|
+
] }],
|
|
15
|
+
regex: [{
|
|
16
|
+
extensions: [
|
|
17
|
+
"json",
|
|
18
|
+
"html",
|
|
19
|
+
"css"
|
|
20
|
+
],
|
|
21
|
+
patterns: ["__MSG_({key})__"]
|
|
22
|
+
}],
|
|
23
|
+
resources: [{ pattern: "_locales/{locale}/messages.json" }, { pattern: "{src,public,extension}/_locales/{locale}/messages.json" }],
|
|
24
|
+
transformResource: {
|
|
25
|
+
applies: (ctx) => /(?:^|\/)_locales\/[^/]+\/messages\.json$/.test(ctx.path),
|
|
26
|
+
read(tree) {
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const [name, entry] of Object.entries(tree)) {
|
|
29
|
+
const message = isMessageLeaf(entry) ? void 0 : entry.message;
|
|
30
|
+
if (message !== void 0 && isMessageLeaf(message)) out[name] = message;
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
},
|
|
34
|
+
toStored: (path) => [path.join("."), "message"]
|
|
35
|
+
},
|
|
36
|
+
templates: [{
|
|
37
|
+
template: "chrome.i18n.getMessage('{key}')",
|
|
38
|
+
kinds: ["js-string", "js-template"]
|
|
39
|
+
}],
|
|
40
|
+
placeholders: [dollars]
|
|
41
|
+
}), {
|
|
42
|
+
name: "x18n:chrome-extension-source-locale",
|
|
43
|
+
async config(draft, ctx) {
|
|
44
|
+
const manifest = await ctx.host.readFile("manifest.json") ?? await ctx.host.readFile("src/manifest.json") ?? await ctx.host.readFile("public/manifest.json");
|
|
45
|
+
const locale = manifest ? /"default_locale"\s*:\s*"([^"]+)"/.exec(manifest)?.[1] : void 0;
|
|
46
|
+
if (locale) draft.sourceLocale = locale;
|
|
47
|
+
}
|
|
48
|
+
}]
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { chromeExtension, chromeExtension as default };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Preset } from "@x18n/core";
|
|
2
|
+
//#region src/presets/dotnet-resx/index.d.ts
|
|
3
|
+
/** .NET `IStringLocalizer` and strongly typed resources over `.resx` files: `Strings.resx` holds the source locale, `Strings.de.resx` German. */
|
|
4
|
+
export declare function dotnetResx(): Preset;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { dotnetResx as default };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { dotnet } from "../placeholders.mjs";
|
|
2
|
+
import { createLibraryPlugin } from "@x18n/core";
|
|
3
|
+
//#region src/presets/dotnet-resx/index.ts
|
|
4
|
+
/** .NET satellite assemblies: `X.<culture>.resx` translates `X.resx`, so `Views.Shared.Nav.resx` alone is a source file. */
|
|
5
|
+
const resxFinder = {
|
|
6
|
+
id: "dotnet-resx",
|
|
7
|
+
async find(ctx) {
|
|
8
|
+
const paths = new Set(await ctx.host.glob(["**/*.resx"], {
|
|
9
|
+
ignore: [
|
|
10
|
+
"**/node_modules/**",
|
|
11
|
+
"**/bin/**",
|
|
12
|
+
"**/obj/**"
|
|
13
|
+
],
|
|
14
|
+
gitignore: false
|
|
15
|
+
}));
|
|
16
|
+
return [...paths].map((path) => {
|
|
17
|
+
const satellite = /^(.+)\.([^./]+)\.resx$/.exec(path);
|
|
18
|
+
return {
|
|
19
|
+
path,
|
|
20
|
+
locale: satellite && paths.has(`${satellite[1]}.resx`) ? satellite[2] : ctx.config.sourceLocale,
|
|
21
|
+
namespace: void 0
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
resolveNew({ locale, model }, ctx) {
|
|
26
|
+
if (model?.finder !== "dotnet-resx") return void 0;
|
|
27
|
+
const suffix = model.path.endsWith(`.${model.locale}.resx`) ? `.${model.locale}.resx` : ".resx";
|
|
28
|
+
const base = model.path.slice(0, -suffix.length);
|
|
29
|
+
return locale === ctx.config.sourceLocale ? `${base}.resx` : `${base}.${locale}.resx`;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/** .NET `IStringLocalizer` and strongly typed resources over `.resx` files: `Strings.resx` holds the source locale, `Strings.de.resx` German. */
|
|
33
|
+
function dotnetResx() {
|
|
34
|
+
return {
|
|
35
|
+
name: "dotnet-resx",
|
|
36
|
+
plugins: [createLibraryPlugin({
|
|
37
|
+
name: "x18n:dotnet-resx",
|
|
38
|
+
regex: [{
|
|
39
|
+
extensions: [
|
|
40
|
+
"cs",
|
|
41
|
+
"cshtml",
|
|
42
|
+
"razor"
|
|
43
|
+
],
|
|
44
|
+
patterns: [
|
|
45
|
+
"\\b(?:\\w*[Ll]ocali[sz]er|L|Loc)\\[\\s*\"([^\"]+)\"",
|
|
46
|
+
"\\b\\w*[Ll]ocali[sz]er\\.Get(?:String|Html)\\(\\s*\"([^\"]+)\"",
|
|
47
|
+
"(?<![\\w.])(?:Properties\\.)?Resources\\.(?!ResourceManager\\b|Culture\\b)([A-Za-z_]\\w*)\\b(?![.(<])"
|
|
48
|
+
]
|
|
49
|
+
}],
|
|
50
|
+
finders: [resxFinder],
|
|
51
|
+
keyStyle: "flat",
|
|
52
|
+
templates: [{
|
|
53
|
+
template: "@Localizer[\"{key}\"]",
|
|
54
|
+
files: ["**/*.cshtml", "**/*.razor"]
|
|
55
|
+
}, {
|
|
56
|
+
template: "_localizer[\"{key}\"]",
|
|
57
|
+
files: ["**/*.cs"]
|
|
58
|
+
}],
|
|
59
|
+
placeholders: [dotnet]
|
|
60
|
+
})]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { dotnetResx as default, dotnetResx };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Preset } from "@x18n/core";
|
|
2
|
+
//#region src/presets/easy-localization/index.d.ts
|
|
3
|
+
/** `easy_localization`: `'key'.tr()`, `tr('key')`, `context.tr('key')`, plural and gender forms as nested keys. */
|
|
4
|
+
export declare function easyLocalization(): Preset;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { easyLocalization as default };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { braces, linked, positionalBraces } from "../placeholders.mjs";
|
|
2
|
+
import { t as pluralForms } from "../plural-forms-B7_QX4nG.mjs";
|
|
3
|
+
import { createLibraryPlugin } from "@x18n/core";
|
|
4
|
+
//#region src/presets/easy-localization/index.ts
|
|
5
|
+
/** `easy_localization`: `'key'.tr()`, `tr('key')`, `context.tr('key')`, plural and gender forms as nested keys. */
|
|
6
|
+
function easyLocalization() {
|
|
7
|
+
return {
|
|
8
|
+
name: "easy-localization",
|
|
9
|
+
plugins: [createLibraryPlugin({
|
|
10
|
+
name: "x18n:easy-localization",
|
|
11
|
+
regex: [{
|
|
12
|
+
extensions: ["dart"],
|
|
13
|
+
patterns: [
|
|
14
|
+
"['\"]({key})['\"]\\s*\\.(?:tr|plural)\\(",
|
|
15
|
+
"(?<![\\w$])Text\\(\\s*['\"]({key})['\"]\\s*\\)\\s*\\.(?:tr|plural)\\(",
|
|
16
|
+
"(?<![\\w$.])(?:context\\.)?(?:tr|plural)\\(\\s*['\"]({key})['\"]"
|
|
17
|
+
]
|
|
18
|
+
}],
|
|
19
|
+
derivedKeys: [
|
|
20
|
+
...pluralForms,
|
|
21
|
+
"male",
|
|
22
|
+
"female"
|
|
23
|
+
].map((form) => `{key}.${form}`),
|
|
24
|
+
resources: [{ pattern: "assets/translations/{locale}.{ext}" }],
|
|
25
|
+
keyStyle: "nested",
|
|
26
|
+
templates: [{ template: "'{key}'.tr()" }],
|
|
27
|
+
placeholders: [
|
|
28
|
+
positionalBraces,
|
|
29
|
+
braces,
|
|
30
|
+
linked
|
|
31
|
+
]
|
|
32
|
+
})]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { easyLocalization as default, easyLocalization };
|