@x18n/builtins 0.0.0 → 0.0.3
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/checks.mjs +10 -11
- package/dist/extractors.mjs +23 -1
- package/dist/{finders-Cf-IEaDm.mjs → finders-CqTGh4OJ.mjs} +17 -8
- package/dist/finders.d.mts +3 -1
- package/dist/finders.mjs +3 -3
- package/dist/index.mjs +5 -5
- package/dist/{presets-CxJ8CsTD.mjs → presets-DNnE_2MQ.mjs} +35 -7
- package/dist/presets.mjs +1 -1
- package/dist/{regex-350J_An-.mjs → regex-B9TBGgXC.mjs} +11 -3
- package/dist/{scanners--prozYaE.mjs → scanners-B27IEH4_.mjs} +1 -1
- package/dist/scanners.d.mts +2 -1
- package/dist/scanners.mjs +3 -3
- package/dist/{sfc-blocks-CQhgEeT6.mjs → sfc-blocks-BwJwCUka.mjs} +1 -1
- package/dist/{vue-6FYzIrQl.mjs → vue-5GFR5hft.mjs} +19 -22
- package/dist/yaml-plugin.mjs +3 -2
- package/package.json +2 -2
package/dist/checks.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { shouldExtract } from "./extractors.mjs";
|
|
2
|
-
import { localeSpellingKey } from "@x18n/core";
|
|
2
|
+
import { createPlaceholderComparer, localeSpellingKey } from "@x18n/core";
|
|
3
3
|
//#region src/checks/index.ts
|
|
4
4
|
function list(items) {
|
|
5
5
|
return items.join(", ");
|
|
@@ -159,25 +159,20 @@ const unusedKey = {
|
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
};
|
|
162
|
-
function placeholderNames(ctx, value) {
|
|
163
|
-
return ctx.placeholders(value).map((p) => p.name).sort();
|
|
164
|
-
}
|
|
165
162
|
const placeholderMismatch = {
|
|
166
163
|
id: "placeholder-mismatch",
|
|
167
164
|
defaultSeverity: "error",
|
|
168
165
|
description: "Target value placeholders differ from the source value.",
|
|
169
166
|
*run(ctx) {
|
|
167
|
+
const compare = createPlaceholderComparer(ctx.placeholderSyntaxes, ctx.placeholders);
|
|
170
168
|
for (const key of ctx.keys()) {
|
|
171
169
|
const source = ctx.message(key, ctx.sourceLocale);
|
|
172
170
|
if (typeof source?.value !== "string") continue;
|
|
173
|
-
const expected = placeholderNames(ctx, source.value);
|
|
174
171
|
for (const locale of ctx.locales) {
|
|
175
172
|
if (locale === ctx.sourceLocale) continue;
|
|
176
173
|
const target = ctx.message(key, locale);
|
|
177
174
|
if (typeof target?.value !== "string" || target.value === "") continue;
|
|
178
|
-
const
|
|
179
|
-
const missing = expected.filter((name) => !actual.includes(name));
|
|
180
|
-
const extra = actual.filter((name) => !expected.includes(name));
|
|
175
|
+
const { missing, extra } = compare(source.value, target.value);
|
|
181
176
|
if (missing.length || extra.length) {
|
|
182
177
|
const parts = [missing.length ? `missing ${list(missing)}` : "", extra.length ? `unexpected ${list(extra)}` : ""].filter(Boolean);
|
|
183
178
|
yield {
|
|
@@ -279,16 +274,20 @@ const hardString = {
|
|
|
279
274
|
};
|
|
280
275
|
}
|
|
281
276
|
};
|
|
277
|
+
/** A term as a whole word, so "Home" does not match "Homepage". */
|
|
278
|
+
function wordPattern(term) {
|
|
279
|
+
return new RegExp(`(?<![\\p{L}\\p{N}])${term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\p{L}\\p{N}])`, "iu");
|
|
280
|
+
}
|
|
282
281
|
function* glossaryDiagnostics(ctx, entry) {
|
|
283
|
-
const term = entry.term
|
|
282
|
+
const term = wordPattern(entry.term);
|
|
284
283
|
for (const key of ctx.keys()) {
|
|
285
284
|
const source = ctx.message(key, ctx.sourceLocale)?.value;
|
|
286
|
-
if (typeof source !== "string" || !
|
|
285
|
+
if (typeof source !== "string" || !term.test(source)) continue;
|
|
287
286
|
for (const locale of ctx.locales) {
|
|
288
287
|
const value = ctx.message(key, locale)?.value;
|
|
289
288
|
const expected = entry.doNotTranslate ? entry.term : entry.translations?.[locale];
|
|
290
289
|
if (locale === ctx.sourceLocale || typeof value !== "string" || value === "" || !expected) continue;
|
|
291
|
-
if (!
|
|
290
|
+
if (!wordPattern(expected).test(value)) yield {
|
|
292
291
|
key,
|
|
293
292
|
locale,
|
|
294
293
|
message: `"${key}" in ${locale} should use "${expected}" for "${entry.term}".`
|
package/dist/extractors.mjs
CHANGED
|
@@ -48,6 +48,10 @@ const rules = [
|
|
|
48
48
|
id: "single-word",
|
|
49
49
|
test: (text, kind) => !textKinds.has(kind) && !kind.endsWith("attribute") && !/\s/.test(text.trim()) ? "should-exclude" : "none"
|
|
50
50
|
},
|
|
51
|
+
{
|
|
52
|
+
id: "attribute-token",
|
|
53
|
+
test: (text, kind) => kind.endsWith("attribute") && /^[a-z][\w-]*$/.test(text.trim()) ? "must-exclude" : "none"
|
|
54
|
+
},
|
|
51
55
|
{
|
|
52
56
|
id: "identifier",
|
|
53
57
|
test: (text) => /^[a-z]+(?:[A-Z][a-z\d]*)+$/.test(text) || /^[A-Z\d]+(?:_[A-Z\d]+)+$/.test(text) || /^[a-z\d]+(?:[-_.:][a-z\d]+)+$/.test(text) ? "should-exclude" : "none"
|
|
@@ -187,6 +191,20 @@ function rawKeyFor(project, key) {
|
|
|
187
191
|
const ns = project.config.namespace.default;
|
|
188
192
|
return project.namespaced && ns && key.startsWith(`${ns}:`) ? key.slice(ns.length + 1) : key;
|
|
189
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Escapes a key for the quotes around `{key}` in a template, since `keygen: 'source'` keys hold any text:
|
|
196
|
+
* backslashes inside a JS string, entities inside a double-quoted attribute (`:title="$t('{key}')"`).
|
|
197
|
+
*/
|
|
198
|
+
function quoteKey(before, key) {
|
|
199
|
+
const quote = before.at(-1);
|
|
200
|
+
const inAttribute = /="[^"]*$/.test(before);
|
|
201
|
+
let out = key;
|
|
202
|
+
if (quote === "'" || quote === "`" || quote === "\"" && !inAttribute) out = out.replace(quote === "`" ? /[\\`]|\$\{/g : new RegExp(`[\\\\${quote}]`, "g"), (c) => `\\${c}`);
|
|
203
|
+
return inAttribute ? out.replace(/&/g, "&").replace(/"/g, """) : out;
|
|
204
|
+
}
|
|
205
|
+
function fillTemplate(template, values) {
|
|
206
|
+
return template.replace(/\{(key|attr|args)\}/g, (_, name, at) => name === "key" ? quoteKey(template.slice(0, at), values.key) : values[name]);
|
|
207
|
+
}
|
|
190
208
|
/** Hard strings worth extracting, with proposed keys, values and replacements. */
|
|
191
209
|
function findCandidates(project, options = {}) {
|
|
192
210
|
const inFiles = options.files?.length ? pathMatcher(options.files) : void 0;
|
|
@@ -214,7 +232,11 @@ function findCandidates(project, options = {}) {
|
|
|
214
232
|
key,
|
|
215
233
|
reused,
|
|
216
234
|
value,
|
|
217
|
-
replacement: chosen
|
|
235
|
+
replacement: fillTemplate(chosen, {
|
|
236
|
+
key: rawKeyFor(project, key),
|
|
237
|
+
attr: hard.attribute ?? "",
|
|
238
|
+
args
|
|
239
|
+
}),
|
|
218
240
|
imports: template?.imports ?? [],
|
|
219
241
|
alternatives: templates.slice(1)
|
|
220
242
|
});
|
|
@@ -83,7 +83,7 @@ function compilePattern(pattern, extensions) {
|
|
|
83
83
|
const extension = groups.ext ?? path.slice(path.lastIndexOf(".") + 1);
|
|
84
84
|
return {
|
|
85
85
|
locale: groups.locale,
|
|
86
|
-
namespace: groups.namespace
|
|
86
|
+
namespace: groups.namespace,
|
|
87
87
|
ext: extension
|
|
88
88
|
};
|
|
89
89
|
}
|
|
@@ -135,7 +135,13 @@ async function guessResources(ctx) {
|
|
|
135
135
|
}
|
|
136
136
|
return specs;
|
|
137
137
|
}
|
|
138
|
-
|
|
138
|
+
/** A namespace from a path in the style of `namespace.fromPath`. Transloco camelCases scope folders, as its `toCamelCase`. */
|
|
139
|
+
function namespaceFromPath(path, style) {
|
|
140
|
+
if (style === "slash") return path;
|
|
141
|
+
if (style === "camel") return path.replace(/[-_/\s]+(.)/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toLowerCase());
|
|
142
|
+
return path.replace(/\//g, ".");
|
|
143
|
+
}
|
|
144
|
+
async function findBySpecs(ctx, specs, strictLocales) {
|
|
139
145
|
const out = [];
|
|
140
146
|
for (const spec of specs) {
|
|
141
147
|
const compiled = compilePattern(spec.pattern, ctx.extensions);
|
|
@@ -148,11 +154,12 @@ async function findBySpecs(ctx, specs, guessed) {
|
|
|
148
154
|
const matched = compiled.match(path);
|
|
149
155
|
if (!matched) continue;
|
|
150
156
|
const locale = fixed ?? matched.locale ?? (spec.pattern.includes("{locale?}") ? ctx.config.sourceLocale : void 0);
|
|
151
|
-
if (!locale ||
|
|
157
|
+
if (!locale || strictLocales && !isLocaleCode(locale)) continue;
|
|
158
|
+
const namespace = spec.namespace ?? (matched.namespace === void 0 ? void 0 : namespaceFromPath(matched.namespace, ctx.config.namespace.fromPath));
|
|
152
159
|
out.push({
|
|
153
160
|
path,
|
|
154
161
|
locale,
|
|
155
|
-
namespace
|
|
162
|
+
namespace,
|
|
156
163
|
format: spec.format,
|
|
157
164
|
readonly: spec.execute === true
|
|
158
165
|
});
|
|
@@ -163,16 +170,18 @@ async function findBySpecs(ctx, specs, guessed) {
|
|
|
163
170
|
const patternFinder = {
|
|
164
171
|
id: "pattern",
|
|
165
172
|
resolveNew(target, ctx) {
|
|
173
|
+
const style = ctx.config.namespace.fromPath;
|
|
166
174
|
for (const spec of ctx.config.resources) {
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
const wantsNamespace = /\{namespaces?\}/.test(spec.pattern);
|
|
176
|
+
if (wantsNamespace !== (target.namespace !== void 0) || wantsNamespace && style === "camel" || spec.execute || !spec.pattern.includes("{locale")) continue;
|
|
177
|
+
const path = spec.pattern.replace(/^\.\//, "").replace(/\{locale\??\}/g, target.locale).replace("{namespaces}", style === "slash" ? target.namespace ?? "" : (target.namespace ?? "").replace(/\./g, "/")).replace("{namespace}", target.namespace ?? "").replace("{ext}", spec.format === "yaml" ? "yml" : spec.format ?? "json");
|
|
169
178
|
if (!/[*?{}[\]]/.test(path)) return path;
|
|
170
179
|
}
|
|
171
180
|
},
|
|
172
181
|
async find(ctx) {
|
|
173
182
|
const configured = ctx.config.resources;
|
|
174
|
-
const found = configured.length > 0 ? await findBySpecs(ctx, configured, false) : [];
|
|
175
183
|
const fromUser = ctx.config.origins.resources === "user" || ctx.config.origins.resources === "parent";
|
|
184
|
+
const found = configured.length > 0 ? await findBySpecs(ctx, configured, !fromUser) : [];
|
|
176
185
|
if (found.length > 0 || fromUser) return found;
|
|
177
186
|
return findBySpecs(ctx, await guessResources(ctx), true);
|
|
178
187
|
}
|
|
@@ -185,4 +194,4 @@ function pattern() {
|
|
|
185
194
|
};
|
|
186
195
|
}
|
|
187
196
|
//#endregion
|
|
188
|
-
export { patternFinder as i, guessResources as n,
|
|
197
|
+
export { patternFinder as a, pattern as i, guessResources as n, namespaceFromPath as r, compilePattern as t };
|
package/dist/finders.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FinderContext, LocaleFinder, Plugin, ResourceSpec } from "@x18n/core";
|
|
1
|
+
import { FinderContext, LocaleFinder, NamespaceFromPath, Plugin, ResourceSpec } from "@x18n/core";
|
|
2
2
|
//#region src/finders/pattern.d.ts
|
|
3
3
|
interface CompiledPattern {
|
|
4
4
|
/** Glob for the host. */
|
|
@@ -16,6 +16,8 @@ interface CompiledPattern {
|
|
|
16
16
|
export declare function compilePattern(pattern: string, extensions: readonly string[]): CompiledPattern;
|
|
17
17
|
/** Guesses resource patterns from directories named like `locales`, as i18n-ally did without `localesPaths`. */
|
|
18
18
|
export declare function guessResources(ctx: FinderContext): Promise<ResourceSpec[]>;
|
|
19
|
+
/** A namespace from a path in the style of `namespace.fromPath`. Transloco camelCases scope folders, as its `toCamelCase`. */
|
|
20
|
+
export declare function namespaceFromPath(path: string, style: NamespaceFromPath): string;
|
|
19
21
|
export declare const patternFinder: LocaleFinder;
|
|
20
22
|
/** The `pattern` locale finder: files that match `resources[].pattern`, or guessed locale directories. */
|
|
21
23
|
export declare function pattern(): Plugin;
|
package/dist/finders.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as sfcFluentBlocks, t as sfcBlocks } from "./sfc-blocks-
|
|
3
|
-
export { compilePattern, guessResources, pattern, patternFinder, sfcBlocks, sfcFluentBlocks };
|
|
1
|
+
import { a as patternFinder, i as pattern, n as guessResources, r as namespaceFromPath, t as compilePattern } from "./finders-CqTGh4OJ.mjs";
|
|
2
|
+
import { n as sfcFluentBlocks, t as sfcBlocks } from "./sfc-blocks-BwJwCUka.mjs";
|
|
3
|
+
export { compilePattern, guessResources, namespaceFromPath, pattern, patternFinder, sfcBlocks, sfcFluentBlocks };
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { recommended } from "./checks.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { i as pattern } from "./finders-CqTGh4OJ.mjs";
|
|
3
3
|
import { a as json5, c as ini, i as php, l as ftl, n as properties, o as json, r as po, s as jsonc, t as yaml, u as ecmascript } from "./formats-BAogSrvw.mjs";
|
|
4
|
-
import { i as js, n as vue } from "./vue-
|
|
4
|
+
import { i as js, n as vue } from "./vue-5GFR5hft.mjs";
|
|
5
5
|
import { manifests } from "./manifests.mjs";
|
|
6
|
-
import { n as regex } from "./regex-
|
|
7
|
-
import { t as presets } from "./presets-
|
|
6
|
+
import { n as regex } from "./regex-B9TBGgXC.mjs";
|
|
7
|
+
import { t as presets } from "./presets-DNnE_2MQ.mjs";
|
|
8
8
|
import { reporters } from "./reporters.mjs";
|
|
9
|
-
import { t as svelte } from "./scanners
|
|
9
|
+
import { t as svelte } from "./scanners-B27IEH4_.mjs";
|
|
10
10
|
//#region src/index.ts
|
|
11
11
|
/** Plugins that are always on, and the presets that `'auto'` detects. */
|
|
12
12
|
function createBuiltins() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defaultTemplates } from "./extractors.mjs";
|
|
2
|
-
import { n as sfcFluentBlocks, t as sfcBlocks } from "./sfc-blocks-
|
|
2
|
+
import { n as sfcFluentBlocks, t as sfcBlocks } from "./sfc-blocks-BwJwCUka.mjs";
|
|
3
3
|
import { braces, colon, dollars, doubleBraces, fluent, icu, linked, nesting, percentBraces, placeholders, printf } from "./placeholders.mjs";
|
|
4
|
-
import { r as regexScopes } from "./regex-
|
|
4
|
+
import { r as regexScopes } from "./regex-B9TBGgXC.mjs";
|
|
5
5
|
import { isMessageLeaf } from "@x18n/core";
|
|
6
6
|
//#region src/presets/shared.ts
|
|
7
7
|
/** One plugin holding a library's call specs, regexes, namespace rules, defaults and extraction templates. */
|
|
@@ -10,8 +10,9 @@ function library(options) {
|
|
|
10
10
|
name: `x18n:${options.name}`,
|
|
11
11
|
async config(draft, ctx) {
|
|
12
12
|
if (options.namespace) draft.namespace = {
|
|
13
|
-
separator: options.namespace.separator,
|
|
14
|
-
default: draft.namespace.default ?? options.namespace.default
|
|
13
|
+
separator: options.namespace.separator ?? draft.namespace.separator,
|
|
14
|
+
default: draft.namespace.default ?? options.namespace.default,
|
|
15
|
+
fromPath: options.namespace.fromPath ?? draft.namespace.fromPath
|
|
15
16
|
};
|
|
16
17
|
if (options.resources) draft.resources = [...draft.resources, ...options.resources.filter((spec) => !draft.resources.some((r) => r.pattern === spec.pattern))];
|
|
17
18
|
if (options.keyStyle && draft.keyStyle === "auto" && !ctx.user.keyStyle) draft.keyStyle = options.keyStyle;
|
|
@@ -21,7 +22,7 @@ function library(options) {
|
|
|
21
22
|
regex: [...draft.usage.regex, ...options.regex ?? []],
|
|
22
23
|
derivedKeys: [...draft.usage.derivedKeys, ...options.derivedKeys ?? []]
|
|
23
24
|
};
|
|
24
|
-
await options.extra?.(draft);
|
|
25
|
+
await options.extra?.(draft, ctx);
|
|
25
26
|
},
|
|
26
27
|
localeFinders: options.finders,
|
|
27
28
|
normalizeKey: options.normalizeKey,
|
|
@@ -96,6 +97,7 @@ function transloco() {
|
|
|
96
97
|
plugins: [
|
|
97
98
|
library({
|
|
98
99
|
name: "transloco",
|
|
100
|
+
namespace: { fromPath: "camel" },
|
|
99
101
|
calls: [{ callee: [
|
|
100
102
|
"translate",
|
|
101
103
|
"translocoService.translate",
|
|
@@ -213,7 +215,10 @@ function vueI18n() {
|
|
|
213
215
|
}
|
|
214
216
|
]
|
|
215
217
|
}),
|
|
216
|
-
placeholders(
|
|
218
|
+
placeholders({
|
|
219
|
+
...braces,
|
|
220
|
+
implicit: ["n", "count"]
|
|
221
|
+
}, linked),
|
|
217
222
|
sfcBlocks()
|
|
218
223
|
]
|
|
219
224
|
};
|
|
@@ -237,11 +242,30 @@ function nuxtI18n() {
|
|
|
237
242
|
}]
|
|
238
243
|
};
|
|
239
244
|
}
|
|
245
|
+
/** `defaultNS` from the i18next init code (`i18n.ts`, `next-i18next.config.js`), as keys without a namespace use it. */
|
|
246
|
+
async function findDefaultNamespace(host) {
|
|
247
|
+
const files = await host.glob(["**/*i18n*.{js,mjs,cjs,ts,mts,cts}", "**/i18n/index.{js,ts}"], {
|
|
248
|
+
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
249
|
+
gitignore: true
|
|
250
|
+
});
|
|
251
|
+
for (const file of files.slice(0, 20)) {
|
|
252
|
+
const found = /\bdefaultNS\s*:\s*['"`]([^'"`]+)['"`]/.exec(await host.readFile(file) ?? "");
|
|
253
|
+
if (found) return found[1];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
240
256
|
const i18nextPlugin = library({
|
|
241
257
|
name: "i18next",
|
|
242
258
|
namespace: {
|
|
243
259
|
separator: ":",
|
|
244
|
-
default: "translation"
|
|
260
|
+
default: "translation",
|
|
261
|
+
fromPath: "slash"
|
|
262
|
+
},
|
|
263
|
+
extra: async (draft, ctx) => {
|
|
264
|
+
const found = ctx.user.namespace?.default ? void 0 : await findDefaultNamespace(ctx.host);
|
|
265
|
+
if (found) draft.namespace = {
|
|
266
|
+
...draft.namespace,
|
|
267
|
+
default: found
|
|
268
|
+
};
|
|
245
269
|
},
|
|
246
270
|
calls: [
|
|
247
271
|
{
|
|
@@ -338,6 +362,10 @@ function reactI18next() {
|
|
|
338
362
|
kinds: ["jsx-attribute"],
|
|
339
363
|
imports: ["import i18next from 'i18next'"]
|
|
340
364
|
}]
|
|
365
|
+
}),
|
|
366
|
+
regexScopes("\\bwithTranslation\\(\\s*\\[?\\s*['\"`]([^'\"`]+)['\"`]", "react-i18next-hoc", {
|
|
367
|
+
wholeFile: true,
|
|
368
|
+
extensions: scriptExtensions
|
|
341
369
|
})
|
|
342
370
|
]
|
|
343
371
|
};
|
package/dist/presets.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { n as chromeExtension, r as emberIntl, i as fluentVue, b as flutter, x as general, a as globalize, o as i18nJs, s as i18nTag, S as i18next, c as i18nextShopify, m as jekyll, h as laravel, l as lingui, u as nextInternational, C as nextIntl, d as nextTranslate, A as ngxTranslate, w as nuxtI18n, g as phpGettext, _ as phpJoomla, f as polyglot, t as presets, v as railsLazyKey, T as reactI18next, E as reactIntl, y as rubyRails, D as svelteI18n, j as transloco, p as ui5, O as vscodeExtension, k as vueI18n } from "./presets-
|
|
1
|
+
export { n as chromeExtension, r as emberIntl, i as fluentVue, b as flutter, x as general, a as globalize, o as i18nJs, s as i18nTag, S as i18next, c as i18nextShopify, m as jekyll, h as laravel, l as lingui, u as nextInternational, C as nextIntl, d as nextTranslate, A as ngxTranslate, w as nuxtI18n, g as phpGettext, _ as phpJoomla, f as polyglot, t as presets, v as railsLazyKey, T as reactI18next, E as reactIntl, y as rubyRails, D as svelteI18n, j as transloco, p as ui5, O as vscodeExtension, k as vueI18n } from "./presets-DNnE_2MQ.mjs";
|
|
@@ -47,7 +47,11 @@ function regex(specs = [], id = "regex") {
|
|
|
47
47
|
},
|
|
48
48
|
scan(file, ctx) {
|
|
49
49
|
const all = [...specs, ...ctx.config.usage.regex];
|
|
50
|
-
|
|
50
|
+
const usages = scanWithRegex(file.text, regexesFor(file.extension, all));
|
|
51
|
+
return {
|
|
52
|
+
usages,
|
|
53
|
+
keySlots: usages.map((usage) => usage.range)
|
|
54
|
+
};
|
|
51
55
|
}
|
|
52
56
|
};
|
|
53
57
|
return {
|
|
@@ -60,7 +64,7 @@ function regex(specs = [], id = "regex") {
|
|
|
60
64
|
}
|
|
61
65
|
/**
|
|
62
66
|
* Regions after each match of `pattern` (group 1 = namespace, or key prefix with `as: 'keyPrefix'`),
|
|
63
|
-
* up to the next match: i18n-ally `scopeRangeRegex`.
|
|
67
|
+
* up to the next match: i18n-ally `scopeRangeRegex`. With `wholeFile`, the first match scopes the whole file.
|
|
64
68
|
*/
|
|
65
69
|
function regexScopes(pattern, id = "scopes", options = {}) {
|
|
66
70
|
const re = new RegExp(pattern, "g");
|
|
@@ -71,10 +75,14 @@ function regexScopes(pattern, id = "scopes", options = {}) {
|
|
|
71
75
|
extensions: options.extensions ?? ["*"],
|
|
72
76
|
scan(file) {
|
|
73
77
|
const matches = [...file.text.matchAll(re)];
|
|
78
|
+
if (options.wholeFile) matches.splice(1);
|
|
74
79
|
return {
|
|
75
80
|
usages: [],
|
|
76
81
|
scopes: matches.map((match, i) => ({
|
|
77
|
-
range: {
|
|
82
|
+
range: options.wholeFile ? {
|
|
83
|
+
start: 0,
|
|
84
|
+
end: file.text.length
|
|
85
|
+
} : {
|
|
78
86
|
start: match.index + match[0].length,
|
|
79
87
|
end: matches[i + 1]?.index ?? file.text.length
|
|
80
88
|
},
|
package/dist/scanners.d.mts
CHANGED
|
@@ -30,11 +30,12 @@ export declare function scanWithRegex(text: string, regexes: readonly RegExp[]):
|
|
|
30
30
|
export declare function regex(specs?: readonly RegexScannerSpec[], id?: string): Plugin;
|
|
31
31
|
/**
|
|
32
32
|
* Regions after each match of `pattern` (group 1 = namespace, or key prefix with `as: 'keyPrefix'`),
|
|
33
|
-
* up to the next match: i18n-ally `scopeRangeRegex`.
|
|
33
|
+
* up to the next match: i18n-ally `scopeRangeRegex`. With `wholeFile`, the first match scopes the whole file.
|
|
34
34
|
*/
|
|
35
35
|
export declare function regexScopes(pattern: string, id?: string, options?: {
|
|
36
36
|
readonly as?: "namespace" | "keyPrefix";
|
|
37
37
|
readonly extensions?: readonly string[];
|
|
38
|
+
readonly wholeFile?: boolean;
|
|
38
39
|
}): Plugin;
|
|
39
40
|
//#endregion
|
|
40
41
|
//#region src/scanners/svelte.d.ts
|
package/dist/scanners.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as loadParser, i as js, n as vue, o as scanSource, r as calleeName } from "./vue-
|
|
2
|
-
import { i as scanWithRegex, n as regex, r as regexScopes, t as compileUsagePattern } from "./regex-
|
|
3
|
-
import { t as svelte } from "./scanners
|
|
1
|
+
import { a as loadParser, i as js, n as vue, o as scanSource, r as calleeName } from "./vue-5GFR5hft.mjs";
|
|
2
|
+
import { i as scanWithRegex, n as regex, r as regexScopes, t as compileUsagePattern } from "./regex-B9TBGgXC.mjs";
|
|
3
|
+
import { t as svelte } from "./scanners-B27IEH4_.mjs";
|
|
4
4
|
export { calleeName, compileUsagePattern, js, loadParser, regex, regexScopes, scanSource, scanWithRegex, svelte, vue };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as json5, o as json, t as yaml } from "./formats-BAogSrvw.mjs";
|
|
2
|
-
import { t as loadVueCompiler } from "./vue-
|
|
2
|
+
import { t as loadVueCompiler } from "./vue-5GFR5hft.mjs";
|
|
3
3
|
import { isMessageLeaf, pathId } from "@x18n/core";
|
|
4
4
|
//#region src/finders/sfc-blocks.ts
|
|
5
5
|
const formatOfLang = {
|
|
@@ -549,12 +549,10 @@ const TEXT = 2;
|
|
|
549
549
|
const SIMPLE_EXPRESSION = 4;
|
|
550
550
|
const INTERPOLATION = 5;
|
|
551
551
|
const ATTRIBUTE = 6;
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
["i18n-d", "path"]
|
|
557
|
-
]);
|
|
552
|
+
/** `keypath` is the vue-i18n prop for a key, on `<i18n-t>` and on the app's own wrappers of it; `path` on the legacy components. */
|
|
553
|
+
function isKeyAttribute(tag, name) {
|
|
554
|
+
return name === "keypath" || name === "path" && (tag === "i18n" || tag === "i18n-d");
|
|
555
|
+
}
|
|
558
556
|
const ignoredAttributes = /* @__PURE__ */ new Set([
|
|
559
557
|
"class",
|
|
560
558
|
"style",
|
|
@@ -603,6 +601,18 @@ var TemplateScan = class {
|
|
|
603
601
|
}));
|
|
604
602
|
} catch {}
|
|
605
603
|
}
|
|
604
|
+
/** `:keypath="cond ? 'a' : 'b'"`: the expression is read as the key argument of a translate call. */
|
|
605
|
+
keyExpression(content, offset) {
|
|
606
|
+
const callee = "__x18nKey(";
|
|
607
|
+
try {
|
|
608
|
+
merge(this.out, scanSource(this.parse, `${callee}${content})`, {
|
|
609
|
+
calls: [{ callee: "__x18nKey" }],
|
|
610
|
+
offset: offset - 10,
|
|
611
|
+
filename: "template.ts",
|
|
612
|
+
hardStrings: false
|
|
613
|
+
}));
|
|
614
|
+
} catch {}
|
|
615
|
+
}
|
|
606
616
|
/** `v-t="'key'"` and `v-t="{ path: 'key' }"`. */
|
|
607
617
|
vt(dir) {
|
|
608
618
|
const exp = dir.exp;
|
|
@@ -631,7 +641,7 @@ var TemplateScan = class {
|
|
|
631
641
|
start: value.loc.start.offset + 1,
|
|
632
642
|
end: value.loc.end.offset - 1
|
|
633
643
|
};
|
|
634
|
-
if (
|
|
644
|
+
if (isKeyAttribute(element.tag, prop.name)) {
|
|
635
645
|
this.out.usages.push({
|
|
636
646
|
raw: value.content,
|
|
637
647
|
range,
|
|
@@ -656,21 +666,8 @@ var TemplateScan = class {
|
|
|
656
666
|
}
|
|
657
667
|
const exp = prop.exp;
|
|
658
668
|
if (exp && "content" in exp) {
|
|
659
|
-
if (prop.name === "bind" && prop.arg && "content" in prop.arg &&
|
|
660
|
-
|
|
661
|
-
const raw = exp.content.trim().slice(1, -1);
|
|
662
|
-
this.out.usages.push({
|
|
663
|
-
raw,
|
|
664
|
-
range: {
|
|
665
|
-
start,
|
|
666
|
-
end: start + raw.length
|
|
667
|
-
},
|
|
668
|
-
kind: "call",
|
|
669
|
-
dynamic: false
|
|
670
|
-
});
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
this.expression(exp.content, exp.loc.start.offset);
|
|
669
|
+
if (prop.name === "bind" && prop.arg && "content" in prop.arg && isKeyAttribute(element.tag, prop.arg.content)) this.keyExpression(exp.content, exp.loc.start.offset);
|
|
670
|
+
else this.expression(exp.content, exp.loc.start.offset);
|
|
674
671
|
}
|
|
675
672
|
}
|
|
676
673
|
node(node) {
|
package/dist/yaml-plugin.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { placeholderFromShape } from "./placeholders.mjs";
|
|
2
|
-
import { r as regexScopes } from "./regex-
|
|
2
|
+
import { r as regexScopes } from "./regex-B9TBGgXC.mjs";
|
|
3
3
|
//#region src/yaml-plugin/index.ts
|
|
4
4
|
/**
|
|
5
5
|
* Compiles a validated YAML plugin into plugin objects. Each section maps to the
|
|
@@ -21,7 +21,8 @@ function compileYamlPlugin(data) {
|
|
|
21
21
|
if (defaults?.keyStyle) draft.keyStyle = defaults.keyStyle;
|
|
22
22
|
if (defaults?.namespace) draft.namespace = {
|
|
23
23
|
separator: defaults.namespace.separator ?? draft.namespace.separator,
|
|
24
|
-
default: defaults.namespace.default ?? draft.namespace.default
|
|
24
|
+
default: defaults.namespace.default ?? draft.namespace.default,
|
|
25
|
+
fromPath: defaults.namespace.fromPath ?? draft.namespace.fromPath
|
|
25
26
|
};
|
|
26
27
|
draft.usage = {
|
|
27
28
|
...draft.usage,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x18n/builtins",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.3",
|
|
5
5
|
"description": "Built-in x18n plugins and presets: formats, locale finders, scanners, checks and reporters",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@vue/compiler-sfc": "^3.5.44",
|
|
36
|
-
"@x18n/core": "0.0.
|
|
36
|
+
"@x18n/core": "0.0.3",
|
|
37
37
|
"json5": "^2.2.3",
|
|
38
38
|
"jsonc-parser": "^3.3.1",
|
|
39
39
|
"oxc-parser": "^0.154.0",
|