@x18n/core 0.1.7 → 0.1.8
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/index.d.mts +40 -2
- package/dist/index.mjs +103 -18
- package/package.json +1 -1
- package/schema/config.json +15 -0
package/dist/index.d.mts
CHANGED
|
@@ -336,6 +336,21 @@ interface LocaleFinder {
|
|
|
336
336
|
readonly namespace: string | undefined;
|
|
337
337
|
readonly model: Resource | undefined;
|
|
338
338
|
}, ctx: FinderContext) => string | undefined) | undefined;
|
|
339
|
+
/**
|
|
340
|
+
* A file of its own for one key in a locale, for layouts with one file per message. A new message of the key goes
|
|
341
|
+
* there, created with `format` (default: by extension) when it does not exist; a rename moves the message to the
|
|
342
|
+
* file of the new key; a delete that leaves the file empty deletes it.
|
|
343
|
+
*/
|
|
344
|
+
readonly resolveKey?: ((target: {
|
|
345
|
+
readonly key: KeyString;
|
|
346
|
+
readonly locale: LocaleCode;
|
|
347
|
+
}, ctx: FinderContext) => KeyFile | undefined) | undefined;
|
|
348
|
+
}
|
|
349
|
+
/** The file of one key, from a locale finder's `resolveKey`. */
|
|
350
|
+
interface KeyFile {
|
|
351
|
+
readonly path: string;
|
|
352
|
+
/** Format id of a new file. Default: the format of its extension. */
|
|
353
|
+
readonly format?: string | undefined;
|
|
339
354
|
}
|
|
340
355
|
interface FormatContext {
|
|
341
356
|
readonly path: string;
|
|
@@ -349,6 +364,10 @@ interface ParseResult {
|
|
|
349
364
|
readonly ranges?: ReadonlyMap<string, NodeRange> | undefined;
|
|
350
365
|
/** What the file says about a message for translators (a note or comment next to it), keyed by `pathId(storedPath)`. */
|
|
351
366
|
readonly descriptions?: ReadonlyMap<string, string> | undefined;
|
|
367
|
+
/** The translation state the file records for a message (XLIFF `state`, PO `fuzzy`), keyed by `pathId(storedPath)`. */
|
|
368
|
+
readonly states?: ReadonlyMap<string, MessageState> | undefined;
|
|
369
|
+
/** Display labels of keys that do not read well, such as hashes, keyed by `pathId(storedPath)`. */
|
|
370
|
+
readonly labels?: ReadonlyMap<string, string> | undefined;
|
|
352
371
|
}
|
|
353
372
|
type MessageOp = {
|
|
354
373
|
readonly type: "set";
|
|
@@ -480,6 +499,10 @@ interface LocaleCoverage {
|
|
|
480
499
|
readonly empty: readonly KeyString[];
|
|
481
500
|
/** Translated keys whose message last changed before the source message. */
|
|
482
501
|
readonly outdated: readonly KeyString[];
|
|
502
|
+
/** Translated keys that the format marks `needs-review`. */
|
|
503
|
+
readonly needsReview: readonly KeyString[];
|
|
504
|
+
/** Translated keys that the format marks `reviewed`. */
|
|
505
|
+
readonly reviewed: readonly KeyString[];
|
|
483
506
|
}
|
|
484
507
|
/** When a message last changed, as a history provider knows it. */
|
|
485
508
|
interface MessageChange {
|
|
@@ -775,11 +798,15 @@ interface GlossaryEntry {
|
|
|
775
798
|
readonly translations?: {
|
|
776
799
|
readonly [locale: string]: string;
|
|
777
800
|
} | undefined;
|
|
801
|
+
/** Other forms per locale, matched at the start of a word: inflections and compound stems of the translation, or of the term under the source locale. */
|
|
802
|
+
readonly forms?: {
|
|
803
|
+
readonly [locale: string]: readonly string[];
|
|
804
|
+
} | undefined;
|
|
778
805
|
readonly note?: string | undefined;
|
|
779
806
|
readonly doNotTranslate?: boolean | undefined;
|
|
780
807
|
}
|
|
781
808
|
interface ExtractTemplate {
|
|
782
|
-
/** Replacement code
|
|
809
|
+
/** Replacement code. `{key}` is the key, `{attr}` the attribute name, `{args}` the arguments of a template string, `{text}` the message of the hard string, for a default value, `{source}` the text as written in the file, and `{parts}` the message as the body of a template string. `{key}` and `{text}` are escaped for the quotes around them. */
|
|
783
810
|
readonly template: string;
|
|
784
811
|
/** Hard-string kinds the template applies to, for example `jsx-text`. */
|
|
785
812
|
readonly kinds?: readonly string[] | undefined;
|
|
@@ -813,6 +840,8 @@ interface ExtractOptions {
|
|
|
813
840
|
interface UsageOptions {
|
|
814
841
|
readonly include?: readonly string[] | undefined;
|
|
815
842
|
readonly exclude?: readonly string[] | undefined;
|
|
843
|
+
/** Skip the files that `.gitignore` files ignore. Default `true`. Set `false` to scan a checkout that git ignores, such as a clone of the sources in a translations repository. */
|
|
844
|
+
readonly gitignore?: boolean | undefined;
|
|
816
845
|
readonly calls?: readonly CallSpec[] | undefined;
|
|
817
846
|
readonly regex?: readonly RegexScannerSpec[] | undefined;
|
|
818
847
|
/** Callee names never counted as translate calls. */
|
|
@@ -952,6 +981,7 @@ interface ResolvedConfig {
|
|
|
952
981
|
readonly usage: {
|
|
953
982
|
readonly include: readonly string[];
|
|
954
983
|
readonly exclude: readonly string[];
|
|
984
|
+
readonly gitignore: boolean;
|
|
955
985
|
readonly calls: readonly CallSpec[];
|
|
956
986
|
readonly regex: readonly RegexScannerSpec[];
|
|
957
987
|
readonly ignoreCallees: readonly string[];
|
|
@@ -1051,6 +1081,8 @@ interface Resource {
|
|
|
1051
1081
|
/** Key separator of the resource when it differs from the config's `keySeparator` (from its resource spec). */
|
|
1052
1082
|
readonly keySeparator?: KeySeparator | undefined;
|
|
1053
1083
|
}
|
|
1084
|
+
/** How far a translation got, as in XLIFF: a value can exist before anyone reviews it. */
|
|
1085
|
+
type MessageState = "translated" | "needs-review" | "reviewed";
|
|
1054
1086
|
/** One stored value of one key in one locale. */
|
|
1055
1087
|
interface Message {
|
|
1056
1088
|
readonly key: KeyString;
|
|
@@ -1063,6 +1095,10 @@ interface Message {
|
|
|
1063
1095
|
readonly range: NodeRange | undefined;
|
|
1064
1096
|
/** What the resource says about the message for translators, such as a note or a comment beside it. */
|
|
1065
1097
|
readonly description?: string | undefined;
|
|
1098
|
+
/** The translation state the resource records, such as XLIFF `state` or PO `fuzzy`. */
|
|
1099
|
+
readonly state?: MessageState | undefined;
|
|
1100
|
+
/** A display label of the key from the resource, for keys that do not read well, such as hashes. */
|
|
1101
|
+
readonly label?: string | undefined;
|
|
1066
1102
|
}
|
|
1067
1103
|
type UsageKind = "call" | "selector" | "accessor" | "constant" | "marker";
|
|
1068
1104
|
interface Usage {
|
|
@@ -1523,6 +1559,8 @@ interface KeyInfo {
|
|
|
1523
1559
|
readonly key: KeyString;
|
|
1524
1560
|
readonly namespace: string | undefined;
|
|
1525
1561
|
readonly path: readonly string[];
|
|
1562
|
+
/** Display label of the key: the first `label` of its messages, source locale first. */
|
|
1563
|
+
readonly label: string | undefined;
|
|
1526
1564
|
/** One message per locale that has the key, source locale first. */
|
|
1527
1565
|
readonly messages: readonly Message[];
|
|
1528
1566
|
readonly missing: readonly LocaleCode[];
|
|
@@ -1876,4 +1914,4 @@ export declare function rangeContains(range: TextRange, offset: number): boolean
|
|
|
1876
1914
|
/** Fast non-cryptographic string hash (cyrb53), as hex. */
|
|
1877
1915
|
export declare function hashText(text: string): string;
|
|
1878
1916
|
//#endregion
|
|
1879
|
-
export type { Analysis, Answers, ApplyResult, ArgSource, BaselineSummary, Builtins, CacheStore, CallSpec, CapabilityConflict, Check, CheckContext, CheckIds, CheckLevel, CheckSetting, ConfigContext, ConfigDraft, ConfigOrigin, CreateProjectOptions, Decision, DecisionOption, DetectContext, Diagnostic, DiagnosticInput, Disposable, EditOp, Envelope, ErrorCode, ErrorInfo, ExtractItem, ExtractOptions, ExtractTemplate, Extractor, FileChange, FileEvent, FileScan, FinderContext, Fix, Format, FormatContext, FormatEntry, GlobOptions, GlossaryEntry, HardString, HistoryConfigs, HistoryContext, HistoryOptions, HistoryProvider, HistorySettings, Host, JsonObject, JsonPrimitive, JsonValue, KeptUnusedKey, KeyContext, KeyId, KeyInfo, KeyQuery, KeySeparator, KeyString, KeyTarget, LazyFormat, LazyScanner, LibraryOptions, LineIndex, LinePosition, LocaleCode, LocaleCoverage, LocaleFinder, LocaleInfo, Location, Logger, ManifestReader, MemoryHost, Message, MessageChange, MessageHistory, MessageLeaf, MessageOp, MessageTree, NamespaceFromPath, NamespaceOptions, NodeRange, OverlayHost, ParamsSource, ParseResult, Placeholder, PlaceholderComparer, PlaceholderProblem, PlaceholderSyntax, Plan, Plugin, PluginConfigs, PluginFile, PluginLoader, PluginOption, PluginRegistry, PluginSpecifier, PluralForm, Preset, PresetDetection, PresetEntry, PresetExplanation, PresetFactory, PresetLoader, PresetOption, Project, ProjectChange, ProjectExplanation, PromptContext, PromptData, PromptDraft, PromptIssue, PromptItem, PromptOptions, RegexScannerSpec, RelatedValue, ReportInput, ReportSummary, Reporter, ReporterContext, ReporterLoader, ResolveConfigOptions, ResolvedConfig, Resource, ResourceBlock, ResourceCandidate, ResourceId, ResourceNode, ResourceSpec, ResourceTransform, ResourceTransformContext, ReviewAuthor, ReviewComment, ReviewData, ReviewFile, ReviewFilter, ReviewOptions, ReviewState, ReviewStore, ReviewThread, ReviewType, ScanContext, ScanResult, Scanner, ScannerEntry, SchemaIssue, Scope, SearchMatch, SearchOptions, SerializableConfig, Setup, Severity, SourceFile, TextEdit, TextRange, ThreadComment, TreeLeaf, UnusedDeletion, UnusedKeyDeletion, UnusedQuery, Usage, UsageCandidate, UsageContext, UsageKind, UsageMatch, UsageOptions, UsageQuery, UserConfig, WireDiagnostic, WireRange, WriteOptions, X18nError };
|
|
1917
|
+
export type { Analysis, Answers, ApplyResult, ArgSource, BaselineSummary, Builtins, CacheStore, CallSpec, CapabilityConflict, Check, CheckContext, CheckIds, CheckLevel, CheckSetting, ConfigContext, ConfigDraft, ConfigOrigin, CreateProjectOptions, Decision, DecisionOption, DetectContext, Diagnostic, DiagnosticInput, Disposable, EditOp, Envelope, ErrorCode, ErrorInfo, ExtractItem, ExtractOptions, ExtractTemplate, Extractor, FileChange, FileEvent, FileScan, FinderContext, Fix, Format, FormatContext, FormatEntry, GlobOptions, GlossaryEntry, HardString, HistoryConfigs, HistoryContext, HistoryOptions, HistoryProvider, HistorySettings, Host, JsonObject, JsonPrimitive, JsonValue, KeptUnusedKey, KeyContext, KeyFile, KeyId, KeyInfo, KeyQuery, KeySeparator, KeyString, KeyTarget, LazyFormat, LazyScanner, LibraryOptions, LineIndex, LinePosition, LocaleCode, LocaleCoverage, LocaleFinder, LocaleInfo, Location, Logger, ManifestReader, MemoryHost, Message, MessageChange, MessageHistory, MessageLeaf, MessageOp, MessageState, MessageTree, NamespaceFromPath, NamespaceOptions, NodeRange, OverlayHost, ParamsSource, ParseResult, Placeholder, PlaceholderComparer, PlaceholderProblem, PlaceholderSyntax, Plan, Plugin, PluginConfigs, PluginFile, PluginLoader, PluginOption, PluginRegistry, PluginSpecifier, PluralForm, Preset, PresetDetection, PresetEntry, PresetExplanation, PresetFactory, PresetLoader, PresetOption, Project, ProjectChange, ProjectExplanation, PromptContext, PromptData, PromptDraft, PromptIssue, PromptItem, PromptOptions, RegexScannerSpec, RelatedValue, ReportInput, ReportSummary, Reporter, ReporterContext, ReporterLoader, ResolveConfigOptions, ResolvedConfig, Resource, ResourceBlock, ResourceCandidate, ResourceId, ResourceNode, ResourceSpec, ResourceTransform, ResourceTransformContext, ReviewAuthor, ReviewComment, ReviewData, ReviewFile, ReviewFilter, ReviewOptions, ReviewState, ReviewStore, ReviewThread, ReviewType, ScanContext, ScanResult, Scanner, ScannerEntry, SchemaIssue, Scope, SearchMatch, SearchOptions, SerializableConfig, Setup, Severity, SourceFile, TextEdit, TextRange, ThreadComment, TreeLeaf, UnusedDeletion, UnusedKeyDeletion, UnusedQuery, Usage, UsageCandidate, UsageContext, UsageKind, UsageMatch, UsageOptions, UsageQuery, UserConfig, WireDiagnostic, WireRange, WriteOptions, X18nError };
|
package/dist/index.mjs
CHANGED
|
@@ -882,6 +882,7 @@ const extractOptions = v.strictObject({
|
|
|
882
882
|
const glossaryEntry = v.strictObject({
|
|
883
883
|
term: v.string(),
|
|
884
884
|
translations: v.optional(v.record(v.string(), v.string())),
|
|
885
|
+
forms: v.optional(v.record(v.string(), v.array(v.string()))),
|
|
885
886
|
note: v.optional(v.string()),
|
|
886
887
|
doNotTranslate: v.optional(v.boolean())
|
|
887
888
|
});
|
|
@@ -924,6 +925,7 @@ const ConfigSchema = v.strictObject({
|
|
|
924
925
|
usage: v.optional(v.strictObject({
|
|
925
926
|
include: v.optional(strings),
|
|
926
927
|
exclude: v.optional(strings),
|
|
928
|
+
gitignore: v.optional(v.boolean()),
|
|
927
929
|
calls: v.optional(v.array(callSpec)),
|
|
928
930
|
regex: v.optional(v.array(regexSpec)),
|
|
929
931
|
ignoreCallees: v.optional(strings),
|
|
@@ -1370,12 +1372,19 @@ function computeCoverage(state, options, sourceKeys) {
|
|
|
1370
1372
|
const missing = [];
|
|
1371
1373
|
const empty = [];
|
|
1372
1374
|
const outdated = [];
|
|
1375
|
+
const needsReview = [];
|
|
1376
|
+
const reviewed = [];
|
|
1373
1377
|
for (const key of reference) {
|
|
1374
|
-
const
|
|
1378
|
+
const message = messages.get(key)?.get(locale)?.[0];
|
|
1379
|
+
const value = message?.value;
|
|
1375
1380
|
if (value === void 0 || value === "") {
|
|
1376
1381
|
if (inherited(key)) continue;
|
|
1377
1382
|
(value === void 0 ? missing : empty).push(key);
|
|
1378
|
-
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (locale !== sourceLocale && isOutdated(state.history(key, sourceLocale), state.history(key, locale)) && !state.fresh(key, locale)) outdated.push(key);
|
|
1386
|
+
if (message?.state === "needs-review") needsReview.push(key);
|
|
1387
|
+
else if (message?.state === "reviewed") reviewed.push(key);
|
|
1379
1388
|
}
|
|
1380
1389
|
return {
|
|
1381
1390
|
locale,
|
|
@@ -1383,7 +1392,9 @@ function computeCoverage(state, options, sourceKeys) {
|
|
|
1383
1392
|
translated: reference.length - missing.length - empty.length,
|
|
1384
1393
|
missing,
|
|
1385
1394
|
empty,
|
|
1386
|
-
outdated
|
|
1395
|
+
outdated,
|
|
1396
|
+
needsReview,
|
|
1397
|
+
reviewed
|
|
1387
1398
|
};
|
|
1388
1399
|
});
|
|
1389
1400
|
}
|
|
@@ -1771,6 +1782,10 @@ var PlanBuilder = class {
|
|
|
1771
1782
|
answers;
|
|
1772
1783
|
work = /* @__PURE__ */ new Map();
|
|
1773
1784
|
created = /* @__PURE__ */ new Map();
|
|
1785
|
+
/** New files that a finder's `resolveKey` gave one key. */
|
|
1786
|
+
keyFiles = /* @__PURE__ */ new Map();
|
|
1787
|
+
/** Resources that are the file of a key deleted from them: removed once empty. */
|
|
1788
|
+
keyFilesLeft = /* @__PURE__ */ new Set();
|
|
1774
1789
|
sourceEdits = /* @__PURE__ */ new Map();
|
|
1775
1790
|
sourceImports = /* @__PURE__ */ new Map();
|
|
1776
1791
|
decisions = [];
|
|
@@ -1898,8 +1913,40 @@ var PlanBuilder = class {
|
|
|
1898
1913
|
this.created.set(path, target);
|
|
1899
1914
|
return target;
|
|
1900
1915
|
}
|
|
1901
|
-
/**
|
|
1916
|
+
/** The file a locale finder gives the key, created when it does not exist. */
|
|
1917
|
+
keyTarget(key, locale) {
|
|
1918
|
+
const file = this.options.resolveKey(key, locale);
|
|
1919
|
+
if (!file) return void 0;
|
|
1920
|
+
const existing = this.state.resources.find((s) => s.resource.path === file.path && !s.resource.block);
|
|
1921
|
+
if (existing) {
|
|
1922
|
+
if (existing.resource.locale !== locale || this.relativePath(key, existing.resource) === void 0) throw errors.X18N4005({
|
|
1923
|
+
key,
|
|
1924
|
+
locale
|
|
1925
|
+
});
|
|
1926
|
+
return this.targetOf(existing);
|
|
1927
|
+
}
|
|
1928
|
+
const pending = this.keyFiles.get(file.path);
|
|
1929
|
+
if (pending) return pending;
|
|
1930
|
+
const format = file.format === void 0 ? this.options.registry.formatFor(file.path) : this.options.registry.formatById(file.format);
|
|
1931
|
+
if (!format?.serialize) throw errors.X18N4005({
|
|
1932
|
+
key,
|
|
1933
|
+
locale
|
|
1934
|
+
});
|
|
1935
|
+
const target = {
|
|
1936
|
+
id: file.path,
|
|
1937
|
+
path: file.path,
|
|
1938
|
+
locale,
|
|
1939
|
+
namespace: this.state.namespaced ? this.keyId(key).namespace ?? this.config.namespace.default : void 0,
|
|
1940
|
+
format,
|
|
1941
|
+
state: void 0
|
|
1942
|
+
};
|
|
1943
|
+
this.keyFiles.set(file.path, target);
|
|
1944
|
+
return target;
|
|
1945
|
+
}
|
|
1946
|
+
/** Placement: the key's own file, then the namespace's resource, then the longest shared prefix, then the first; a tie is a decision. */
|
|
1902
1947
|
pickTarget(key, locale) {
|
|
1948
|
+
const own = this.keyTarget(key, locale);
|
|
1949
|
+
if (own) return own;
|
|
1903
1950
|
let inLocale = this.state.resources.filter((s) => s.resource.locale === locale && this.relativePath(key, s.resource) !== void 0);
|
|
1904
1951
|
const source = this.options.message(key, this.state.sourceLocale);
|
|
1905
1952
|
const sourceNamespace = source && this.stateById.get(source.resource)?.resource.namespace;
|
|
@@ -1958,7 +2005,9 @@ var PlanBuilder = class {
|
|
|
1958
2005
|
});
|
|
1959
2006
|
}
|
|
1960
2007
|
deleteMessage(m) {
|
|
1961
|
-
const
|
|
2008
|
+
const s = this.writable(m.resource);
|
|
2009
|
+
if (!s.resource.block && this.options.resolveKey(m.key, m.locale)?.path === s.resource.path) this.keyFilesLeft.add(s.resource.id);
|
|
2010
|
+
const entry = this.workFor(this.targetOf(s));
|
|
1962
2011
|
let path = [...m.storedPath];
|
|
1963
2012
|
while (path.length > 1) {
|
|
1964
2013
|
const parent = nodeAt(entry.tree, path.slice(0, -1));
|
|
@@ -2017,7 +2066,8 @@ var PlanBuilder = class {
|
|
|
2017
2066
|
if (renamed.has(m.resource)) continue;
|
|
2018
2067
|
const s = this.writable(m.resource);
|
|
2019
2068
|
const relative = this.relativePath(to, s.resource);
|
|
2020
|
-
|
|
2069
|
+
const own = this.options.resolveKey(to, locale);
|
|
2070
|
+
if (!relative || own && own.path !== s.resource.path) {
|
|
2021
2071
|
this.deleteMessage(m);
|
|
2022
2072
|
this.setValue(to, locale, m.value);
|
|
2023
2073
|
continue;
|
|
@@ -2326,6 +2376,14 @@ var PlanBuilder = class {
|
|
|
2326
2376
|
texts.set(target.path, void 0);
|
|
2327
2377
|
continue;
|
|
2328
2378
|
}
|
|
2379
|
+
if (this.keyFilesLeft.has(s.resource.id) && Object.keys(entry.tree).length === 0) {
|
|
2380
|
+
changes.push({
|
|
2381
|
+
type: "delete",
|
|
2382
|
+
path: target.path
|
|
2383
|
+
});
|
|
2384
|
+
texts.set(target.path, this.state.fileTexts.get(target.path));
|
|
2385
|
+
continue;
|
|
2386
|
+
}
|
|
2329
2387
|
const edits = diffText(s.content, this.nextContent(entry, s)).map((e) => ({
|
|
2330
2388
|
range: {
|
|
2331
2389
|
start: e.range.start + s.offset,
|
|
@@ -2462,7 +2520,7 @@ function createPlanner(options) {
|
|
|
2462
2520
|
for (const change of fresh.plan.changes) {
|
|
2463
2521
|
const current = await options.host.readFile(change.path);
|
|
2464
2522
|
const expected = fresh.texts.get(change.path);
|
|
2465
|
-
if (change.type
|
|
2523
|
+
if (change.type !== "create" && expected !== void 0 && current !== expected || change.type === "create" && current !== void 0) throw errors.X18N4004({ path: change.path });
|
|
2466
2524
|
}
|
|
2467
2525
|
const events = [];
|
|
2468
2526
|
for (const change of fresh.plan.changes) events.push(await write(change));
|
|
@@ -2871,12 +2929,15 @@ function createProject(options) {
|
|
|
2871
2929
|
const source = message(key, sourceLocale)?.value;
|
|
2872
2930
|
return recorded !== void 0 && source !== void 0 && recorded === sourceHash(source);
|
|
2873
2931
|
}
|
|
2874
|
-
|
|
2875
|
-
|
|
2932
|
+
function finderContext() {
|
|
2933
|
+
return {
|
|
2876
2934
|
host,
|
|
2877
2935
|
config,
|
|
2878
2936
|
extensions: [...new Set(registry.formats.flatMap((format) => format.extensions))]
|
|
2879
2937
|
};
|
|
2938
|
+
}
|
|
2939
|
+
async function discover() {
|
|
2940
|
+
const ctx = finderContext();
|
|
2880
2941
|
const seen = /* @__PURE__ */ new Map();
|
|
2881
2942
|
const out = [];
|
|
2882
2943
|
duplicates = [];
|
|
@@ -2973,6 +3034,8 @@ function createProject(options) {
|
|
|
2973
3034
|
rawTree: {},
|
|
2974
3035
|
ranges: /* @__PURE__ */ new Map(),
|
|
2975
3036
|
descriptions: /* @__PURE__ */ new Map(),
|
|
3037
|
+
states: /* @__PURE__ */ new Map(),
|
|
3038
|
+
labels: /* @__PURE__ */ new Map(),
|
|
2976
3039
|
error: errors.X18N2001({ path: candidate.path })
|
|
2977
3040
|
};
|
|
2978
3041
|
try {
|
|
@@ -2991,6 +3054,8 @@ function createProject(options) {
|
|
|
2991
3054
|
rawTree: parsed.tree,
|
|
2992
3055
|
ranges: parsed.ranges ?? /* @__PURE__ */ new Map(),
|
|
2993
3056
|
descriptions,
|
|
3057
|
+
states: parsed.states ?? /* @__PURE__ */ new Map(),
|
|
3058
|
+
labels: parsed.labels ?? /* @__PURE__ */ new Map(),
|
|
2994
3059
|
error: void 0
|
|
2995
3060
|
};
|
|
2996
3061
|
} catch (error) {
|
|
@@ -3005,6 +3070,8 @@ function createProject(options) {
|
|
|
3005
3070
|
rawTree: {},
|
|
3006
3071
|
ranges: /* @__PURE__ */ new Map(),
|
|
3007
3072
|
descriptions: /* @__PURE__ */ new Map(),
|
|
3073
|
+
states: /* @__PURE__ */ new Map(),
|
|
3074
|
+
labels: /* @__PURE__ */ new Map(),
|
|
3008
3075
|
error: coded
|
|
3009
3076
|
};
|
|
3010
3077
|
}
|
|
@@ -3022,11 +3089,7 @@ function createProject(options) {
|
|
|
3022
3089
|
}
|
|
3023
3090
|
/** A path for a new resource, from the locale finders. */
|
|
3024
3091
|
function newResourcePath(locale, namespace, model) {
|
|
3025
|
-
const ctx =
|
|
3026
|
-
host,
|
|
3027
|
-
config,
|
|
3028
|
-
extensions: [...new Set(registry.formats.flatMap((format) => format.extensions))]
|
|
3029
|
-
};
|
|
3092
|
+
const ctx = finderContext();
|
|
3030
3093
|
for (const finder of registry.finders) {
|
|
3031
3094
|
const path = finder.resolveNew?.({
|
|
3032
3095
|
locale,
|
|
@@ -3036,6 +3099,19 @@ function createProject(options) {
|
|
|
3036
3099
|
if (path) return path;
|
|
3037
3100
|
}
|
|
3038
3101
|
}
|
|
3102
|
+
/** The file of one key, from the locale finders. */
|
|
3103
|
+
function keyFile(key, locale) {
|
|
3104
|
+
const finders = registry.finders.filter((finder) => finder.resolveKey);
|
|
3105
|
+
if (finders.length === 0) return void 0;
|
|
3106
|
+
const ctx = finderContext();
|
|
3107
|
+
for (const finder of finders) {
|
|
3108
|
+
const file = finder.resolveKey?.({
|
|
3109
|
+
key,
|
|
3110
|
+
locale
|
|
3111
|
+
}, ctx);
|
|
3112
|
+
if (file) return file;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3039
3115
|
async function loadResources(candidates, reread) {
|
|
3040
3116
|
const next = /* @__PURE__ */ new Map();
|
|
3041
3117
|
await forEachLimit([...new Set(candidates.map((c) => c.path))], ioConcurrency, async (path) => {
|
|
@@ -3165,7 +3241,7 @@ function createProject(options) {
|
|
|
3165
3241
|
usageEligible = (path) => registry.scannersFor(path).length > 0 && include(path) && !exclude(path);
|
|
3166
3242
|
await scanPaths((await host.glob(config.usage.include, {
|
|
3167
3243
|
ignore: config.usage.exclude,
|
|
3168
|
-
gitignore:
|
|
3244
|
+
gitignore: config.usage.gitignore
|
|
3169
3245
|
})).filter((path) => registry.scannersFor(path).length > 0));
|
|
3170
3246
|
}
|
|
3171
3247
|
let index;
|
|
@@ -3187,7 +3263,8 @@ function createProject(options) {
|
|
|
3187
3263
|
const path = [...stored, segment];
|
|
3188
3264
|
const key = keyFor(resource, path);
|
|
3189
3265
|
const storedPath = state.toStored(path);
|
|
3190
|
-
const
|
|
3266
|
+
const id = pathId(storedPath);
|
|
3267
|
+
const raw = state.ranges.get(id);
|
|
3191
3268
|
const range = raw ? {
|
|
3192
3269
|
key: shift(raw.key),
|
|
3193
3270
|
value: shift(raw.value)
|
|
@@ -3204,7 +3281,9 @@ function createProject(options) {
|
|
|
3204
3281
|
messages.set(key, byLocale);
|
|
3205
3282
|
const list = byLocale.get(resource.locale) ?? [];
|
|
3206
3283
|
byLocale.set(resource.locale, list);
|
|
3207
|
-
const description = state.descriptions.get(
|
|
3284
|
+
const description = state.descriptions.get(id);
|
|
3285
|
+
const translation = state.states.get(id);
|
|
3286
|
+
const label = state.labels.get(id);
|
|
3208
3287
|
list.push({
|
|
3209
3288
|
key,
|
|
3210
3289
|
locale: resource.locale,
|
|
@@ -3212,7 +3291,9 @@ function createProject(options) {
|
|
|
3212
3291
|
resource: resource.id,
|
|
3213
3292
|
storedPath,
|
|
3214
3293
|
range,
|
|
3215
|
-
...description === void 0 ? {} : { description }
|
|
3294
|
+
...description === void 0 ? {} : { description },
|
|
3295
|
+
...translation === void 0 ? {} : { state: translation },
|
|
3296
|
+
...label === void 0 ? {} : { label }
|
|
3216
3297
|
});
|
|
3217
3298
|
if (!keyPaths.has(key)) keyPaths.set(key, keyIdFor(resource, path));
|
|
3218
3299
|
} else {
|
|
@@ -3413,6 +3494,7 @@ function createProject(options) {
|
|
|
3413
3494
|
key,
|
|
3414
3495
|
namespace: id.namespace,
|
|
3415
3496
|
path: id.path,
|
|
3497
|
+
label: list.find((m) => m.label !== void 0)?.label,
|
|
3416
3498
|
messages: list,
|
|
3417
3499
|
missing: locales.filter((locale) => !byLocale.has(locale) && needs(key, locale)),
|
|
3418
3500
|
usages: usageMatches({ key }),
|
|
@@ -3565,6 +3647,7 @@ function createProject(options) {
|
|
|
3565
3647
|
basesOf,
|
|
3566
3648
|
toStored: (target, path) => storedPathOf(target)(path),
|
|
3567
3649
|
resolveNew: newResourcePath,
|
|
3650
|
+
resolveKey: keyFile,
|
|
3568
3651
|
update
|
|
3569
3652
|
});
|
|
3570
3653
|
const ready = (async () => {
|
|
@@ -3722,6 +3805,7 @@ function defaultDraft() {
|
|
|
3722
3805
|
usage: {
|
|
3723
3806
|
include: ["**/*"],
|
|
3724
3807
|
exclude: defaultExclude,
|
|
3808
|
+
gitignore: true,
|
|
3725
3809
|
calls: [],
|
|
3726
3810
|
regex: [],
|
|
3727
3811
|
ignoreCallees: [],
|
|
@@ -3823,6 +3907,7 @@ function applyLayer(draft, layer, origin, origins) {
|
|
|
3823
3907
|
draft.usage = {
|
|
3824
3908
|
include: usage.include ?? draft.usage.include,
|
|
3825
3909
|
exclude: [...draft.usage.exclude, ...usage.exclude ?? []],
|
|
3910
|
+
gitignore: usage.gitignore ?? draft.usage.gitignore,
|
|
3826
3911
|
calls: [...draft.usage.calls, ...usage.calls ?? []],
|
|
3827
3912
|
regex: [...draft.usage.regex, ...usage.regex ?? []],
|
|
3828
3913
|
ignoreCallees: [...draft.usage.ignoreCallees, ...usage.ignoreCallees ?? []],
|
package/package.json
CHANGED
package/schema/config.json
CHANGED
|
@@ -217,6 +217,9 @@
|
|
|
217
217
|
"type": "string"
|
|
218
218
|
}
|
|
219
219
|
},
|
|
220
|
+
"gitignore": {
|
|
221
|
+
"type": "boolean"
|
|
222
|
+
},
|
|
220
223
|
"calls": {
|
|
221
224
|
"type": "array",
|
|
222
225
|
"items": {
|
|
@@ -632,6 +635,18 @@
|
|
|
632
635
|
"type": "string"
|
|
633
636
|
}
|
|
634
637
|
},
|
|
638
|
+
"forms": {
|
|
639
|
+
"type": "object",
|
|
640
|
+
"propertyNames": {
|
|
641
|
+
"type": "string"
|
|
642
|
+
},
|
|
643
|
+
"additionalProperties": {
|
|
644
|
+
"type": "array",
|
|
645
|
+
"items": {
|
|
646
|
+
"type": "string"
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
},
|
|
635
650
|
"note": {
|
|
636
651
|
"type": "string"
|
|
637
652
|
},
|