@x18n/core 0.0.9 → 0.0.12
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 +9 -0
- package/dist/index.mjs +49 -4
- package/package.json +1 -1
- package/schema/config.json +16 -0
package/dist/index.d.mts
CHANGED
|
@@ -575,6 +575,8 @@ interface CheckContext {
|
|
|
575
575
|
readonly config: ResolvedConfig;
|
|
576
576
|
readonly sourceLocale: LocaleCode;
|
|
577
577
|
readonly locales: readonly LocaleCode[];
|
|
578
|
+
/** The locales a locale falls back to, nearest first. Empty for a locale without a fallback. */
|
|
579
|
+
readonly fallbacks: (locale: LocaleCode) => readonly LocaleCode[];
|
|
578
580
|
readonly resources: readonly Resource[];
|
|
579
581
|
/** Options from `[severity, options]` in config. */
|
|
580
582
|
readonly options: JsonObject;
|
|
@@ -832,6 +834,10 @@ interface SerializableConfig {
|
|
|
832
834
|
/** Locale codes as written in paths. Filters the found locales. */
|
|
833
835
|
readonly locales?: readonly string[] | undefined;
|
|
834
836
|
readonly ignoreLocales?: readonly string[] | undefined;
|
|
837
|
+
/** Locale to the locale it falls back to, such as `{ 'en-GB': 'en' }`. A locale with a fallback holds only overrides: a key it lacks or leaves empty is not missing when the fallback chain has it. `'auto'` falls back to the nearest parent language that exists: `en-US` to `en`, `zh-Hant-TW` to `zh-Hant` or `zh`. */
|
|
838
|
+
readonly localeFallbacks?: "auto" | {
|
|
839
|
+
readonly [locale: string]: string;
|
|
840
|
+
} | undefined;
|
|
835
841
|
/** `'auto'` detects built-in presets from the project's dependencies. */
|
|
836
842
|
readonly presets?: "auto" | readonly PluginSpecifier[] | undefined;
|
|
837
843
|
readonly plugins?: readonly PluginSpecifier[] | undefined;
|
|
@@ -892,6 +898,9 @@ interface ResolvedConfig {
|
|
|
892
898
|
readonly sourceLocale: string;
|
|
893
899
|
readonly locales: readonly string[] | undefined;
|
|
894
900
|
readonly ignoreLocales: readonly string[];
|
|
901
|
+
readonly localeFallbacks: "auto" | {
|
|
902
|
+
readonly [locale: string]: string;
|
|
903
|
+
};
|
|
895
904
|
readonly resources: readonly ResourceSpec[];
|
|
896
905
|
readonly formats: {
|
|
897
906
|
readonly [extension: string]: string;
|
package/dist/index.mjs
CHANGED
|
@@ -363,6 +363,28 @@ const defaultRegions = {
|
|
|
363
363
|
vi: "VN",
|
|
364
364
|
zh: "CN"
|
|
365
365
|
};
|
|
366
|
+
/** The fallback of each locale: from the config map, or for `'auto'` the nearest parent language among `locales`. The source locale has none. */
|
|
367
|
+
function resolveFallbacks(setting, locales, sourceLocale) {
|
|
368
|
+
if (setting !== "auto") return Object.fromEntries(Object.entries(setting).filter(([locale]) => locale !== sourceLocale));
|
|
369
|
+
const fallbacks = {};
|
|
370
|
+
for (const locale of locales) {
|
|
371
|
+
if (locale === sourceLocale) continue;
|
|
372
|
+
for (let cut = locale.search(/[-_][^-_]*$/); cut > 0; cut = locale.slice(0, cut).search(/[-_][^-_]*$/)) {
|
|
373
|
+
const parent = locales.find((code) => code === locale.slice(0, cut));
|
|
374
|
+
if (parent) {
|
|
375
|
+
fallbacks[locale] = parent;
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return fallbacks;
|
|
381
|
+
}
|
|
382
|
+
/** The locales a locale falls back to, nearest first. Stops at a cycle. */
|
|
383
|
+
function fallbackChain(fallbacks, locale) {
|
|
384
|
+
const chain = [];
|
|
385
|
+
for (let next = fallbacks[locale]; next !== void 0 && next !== locale && !chain.includes(next); next = fallbacks[next]) chain.push(next);
|
|
386
|
+
return chain;
|
|
387
|
+
}
|
|
366
388
|
const pluralOrder = [
|
|
367
389
|
"zero",
|
|
368
390
|
"one",
|
|
@@ -712,6 +734,7 @@ const ConfigSchema = v.strictObject({
|
|
|
712
734
|
sourceLocale: v.optional(v.string()),
|
|
713
735
|
locales: v.optional(strings),
|
|
714
736
|
ignoreLocales: v.optional(strings),
|
|
737
|
+
localeFallbacks: v.optional(v.union([v.literal("auto"), v.record(v.string(), v.string())])),
|
|
715
738
|
presets: v.optional(v.union([v.literal("auto"), v.array(pluginSpecifier)])),
|
|
716
739
|
plugins: v.optional(v.array(pluginSpecifier)),
|
|
717
740
|
resources: v.optional(v.array(resourceSpec)),
|
|
@@ -930,14 +953,20 @@ function levelOf(setting) {
|
|
|
930
953
|
function computeCoverage(state, reference) {
|
|
931
954
|
const { messages, sourceLocale } = state;
|
|
932
955
|
return state.locales.map((locale) => {
|
|
956
|
+
const chain = fallbackChain(state.fallbacks, locale);
|
|
957
|
+
const inherited = (key) => chain.some((code) => {
|
|
958
|
+
const value = messages.get(key)?.get(code)?.[0]?.value;
|
|
959
|
+
return value !== void 0 && value !== "";
|
|
960
|
+
});
|
|
933
961
|
const missing = [];
|
|
934
962
|
const empty = [];
|
|
935
963
|
const outdated = [];
|
|
936
964
|
for (const key of reference) {
|
|
937
965
|
const value = messages.get(key)?.get(locale)?.[0]?.value;
|
|
938
|
-
if (value === void 0)
|
|
939
|
-
|
|
940
|
-
|
|
966
|
+
if (value === void 0 || value === "") {
|
|
967
|
+
if (inherited(key)) continue;
|
|
968
|
+
(value === void 0 ? missing : empty).push(key);
|
|
969
|
+
} else if (locale !== sourceLocale && isOutdated(state.history(key, sourceLocale), state.history(key, locale))) outdated.push(key);
|
|
941
970
|
}
|
|
942
971
|
return {
|
|
943
972
|
locale,
|
|
@@ -1069,6 +1098,7 @@ function createAnalyzer(options) {
|
|
|
1069
1098
|
config,
|
|
1070
1099
|
sourceLocale,
|
|
1071
1100
|
locales,
|
|
1101
|
+
fallbacks: (locale) => fallbackChain(state.fallbacks, locale),
|
|
1072
1102
|
resources: state.resources.map((s) => s.resource),
|
|
1073
1103
|
hasScanners,
|
|
1074
1104
|
keys: () => allKeys,
|
|
@@ -1543,7 +1573,7 @@ var PlanBuilder = class {
|
|
|
1543
1573
|
case "set":
|
|
1544
1574
|
this.setValue(op.key, op.locale, op.value);
|
|
1545
1575
|
if (this.config.write.keepFilled) {
|
|
1546
|
-
for (const locale of this.state.locales) if (locale !== op.locale && !this.options.message(op.key, locale)) this.setValue(op.key, locale, "");
|
|
1576
|
+
for (const locale of this.state.locales) if (locale !== op.locale && !this.state.fallbacks[locale] && !this.options.message(op.key, locale)) this.setValue(op.key, locale, "");
|
|
1547
1577
|
}
|
|
1548
1578
|
break;
|
|
1549
1579
|
case "delete":
|
|
@@ -1993,6 +2023,7 @@ function createProject(options) {
|
|
|
1993
2023
|
let revision = 0;
|
|
1994
2024
|
let namespaced = false;
|
|
1995
2025
|
let locales = [];
|
|
2026
|
+
let fallbacks = {};
|
|
1996
2027
|
let sourceLocale = config.sourceLocale;
|
|
1997
2028
|
let resolver;
|
|
1998
2029
|
let watcher;
|
|
@@ -2184,6 +2215,7 @@ function createProject(options) {
|
|
|
2184
2215
|
if (!found.includes(sourceLocale) && config.origins.sourceLocale === void 0 && found.length > 0) sourceLocale = found.find((code) => code.toLowerCase().startsWith("en")) ?? [...found].sort()[0];
|
|
2185
2216
|
locales = [sourceLocale, ...found.filter((code) => code !== sourceLocale).sort()];
|
|
2186
2217
|
if (!found.includes(sourceLocale) && found.length === 0) locales = [sourceLocale];
|
|
2218
|
+
fallbacks = resolveFallbacks(config.localeFallbacks, locales, sourceLocale);
|
|
2187
2219
|
resolver = createUsageResolver(config, namespaced, normalizers);
|
|
2188
2220
|
resolved = /* @__PURE__ */ new WeakMap();
|
|
2189
2221
|
resourceIndex = void 0;
|
|
@@ -2456,6 +2488,7 @@ function createProject(options) {
|
|
|
2456
2488
|
revision,
|
|
2457
2489
|
sourceLocale,
|
|
2458
2490
|
locales,
|
|
2491
|
+
fallbacks,
|
|
2459
2492
|
resources: [...resourceStates.values()],
|
|
2460
2493
|
resourceErrors: [...resourceStates.values()].flatMap((s) => s.error ? [{
|
|
2461
2494
|
error: s.error,
|
|
@@ -2630,6 +2663,7 @@ function createProject(options) {
|
|
|
2630
2663
|
revision,
|
|
2631
2664
|
sourceLocale,
|
|
2632
2665
|
locales,
|
|
2666
|
+
fallbacks,
|
|
2633
2667
|
namespaced,
|
|
2634
2668
|
resources: [...resourceStates.values()],
|
|
2635
2669
|
fileTexts
|
|
@@ -2779,6 +2813,7 @@ function defaultDraft() {
|
|
|
2779
2813
|
sourceLocale: "en",
|
|
2780
2814
|
locales: void 0,
|
|
2781
2815
|
ignoreLocales: [],
|
|
2816
|
+
localeFallbacks: {},
|
|
2782
2817
|
resources: [],
|
|
2783
2818
|
formats: {},
|
|
2784
2819
|
keyStyle: "auto",
|
|
@@ -2822,6 +2857,12 @@ function serializablePart(config) {
|
|
|
2822
2857
|
history: options
|
|
2823
2858
|
};
|
|
2824
2859
|
}
|
|
2860
|
+
function mergeFallbacks(base, layer) {
|
|
2861
|
+
return base === "auto" || layer === "auto" ? layer : {
|
|
2862
|
+
...base,
|
|
2863
|
+
...layer
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2825
2866
|
/** Applies a user config layer: scalars replace, lists of rules append. */
|
|
2826
2867
|
function applyLayer(draft, layer, origin, origins) {
|
|
2827
2868
|
const mark = (key) => {
|
|
@@ -2839,6 +2880,10 @@ function applyLayer(draft, layer, origin, origins) {
|
|
|
2839
2880
|
draft.ignoreLocales = [...draft.ignoreLocales, ...layer.ignoreLocales];
|
|
2840
2881
|
mark("ignoreLocales");
|
|
2841
2882
|
}
|
|
2883
|
+
if (layer.localeFallbacks !== void 0) {
|
|
2884
|
+
draft.localeFallbacks = mergeFallbacks(draft.localeFallbacks, layer.localeFallbacks);
|
|
2885
|
+
mark("localeFallbacks");
|
|
2886
|
+
}
|
|
2842
2887
|
if (layer.resources !== void 0) {
|
|
2843
2888
|
draft.resources = layer.resources;
|
|
2844
2889
|
mark("resources");
|
package/package.json
CHANGED
package/schema/config.json
CHANGED
|
@@ -25,6 +25,22 @@
|
|
|
25
25
|
"type": "string"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
|
+
"localeFallbacks": {
|
|
29
|
+
"anyOf": [
|
|
30
|
+
{
|
|
31
|
+
"const": "auto"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "object",
|
|
35
|
+
"propertyNames": {
|
|
36
|
+
"type": "string"
|
|
37
|
+
},
|
|
38
|
+
"additionalProperties": {
|
|
39
|
+
"type": "string"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
28
44
|
"presets": {
|
|
29
45
|
"anyOf": [
|
|
30
46
|
{
|