glotfile 0.5.2 → 0.5.4
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/server/cli.js +144 -185
- package/dist/server/server.js +365 -426
- package/dist/ui/assets/{index-B1UwtMSs.js → index-BGtqXjLQ.js} +293 -117
- package/dist/ui/assets/index-BrgUMyDW.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-DPfAS4pJ.css +0 -1
package/dist/server/cli.js
CHANGED
|
@@ -2734,7 +2734,7 @@ var init_local_settings = __esm({
|
|
|
2734
2734
|
isEditorId = (v) => EDITOR_IDS.includes(v);
|
|
2735
2735
|
DEFAULT_AI = {
|
|
2736
2736
|
provider: "anthropic",
|
|
2737
|
-
model: "claude-
|
|
2737
|
+
model: "claude-sonnet-4-6",
|
|
2738
2738
|
endpoint: null,
|
|
2739
2739
|
region: null,
|
|
2740
2740
|
batchSize: 25
|
|
@@ -2744,6 +2744,46 @@ var init_local_settings = __esm({
|
|
|
2744
2744
|
}
|
|
2745
2745
|
});
|
|
2746
2746
|
|
|
2747
|
+
// src/server/glossary.ts
|
|
2748
|
+
function contains(haystack, needle, caseSensitive) {
|
|
2749
|
+
return caseSensitive ? haystack.includes(needle) : haystack.toLowerCase().includes(needle.toLowerCase());
|
|
2750
|
+
}
|
|
2751
|
+
function relevantGlossary(source, targetLocale, glossary) {
|
|
2752
|
+
const hints = [];
|
|
2753
|
+
for (const entry of glossary) {
|
|
2754
|
+
if (!contains(source, entry.term, entry.caseSensitive)) continue;
|
|
2755
|
+
hints.push({
|
|
2756
|
+
term: entry.term,
|
|
2757
|
+
doNotTranslate: entry.doNotTranslate,
|
|
2758
|
+
forced: entry.translations?.[targetLocale],
|
|
2759
|
+
notes: entry.notes
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
return hints;
|
|
2763
|
+
}
|
|
2764
|
+
function glossaryViolations(source, value, targetLocale, glossary) {
|
|
2765
|
+
const out = [];
|
|
2766
|
+
for (const entry of glossary) {
|
|
2767
|
+
if (!contains(source, entry.term, entry.caseSensitive)) continue;
|
|
2768
|
+
if (entry.doNotTranslate) {
|
|
2769
|
+
if (!contains(value, entry.term, entry.caseSensitive)) {
|
|
2770
|
+
out.push({ term: entry.term, expected: entry.term, kind: "do-not-translate" });
|
|
2771
|
+
}
|
|
2772
|
+
continue;
|
|
2773
|
+
}
|
|
2774
|
+
const forced = entry.translations?.[targetLocale];
|
|
2775
|
+
if (forced && !contains(value, forced, entry.caseSensitive)) {
|
|
2776
|
+
out.push({ term: entry.term, expected: forced, kind: "forced" });
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
return out;
|
|
2780
|
+
}
|
|
2781
|
+
var init_glossary = __esm({
|
|
2782
|
+
"src/server/glossary.ts"() {
|
|
2783
|
+
"use strict";
|
|
2784
|
+
}
|
|
2785
|
+
});
|
|
2786
|
+
|
|
2747
2787
|
// src/server/glob.ts
|
|
2748
2788
|
function globToRegExp(glob) {
|
|
2749
2789
|
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
@@ -2815,21 +2855,6 @@ function selectRequests(state, opts) {
|
|
|
2815
2855
|
}
|
|
2816
2856
|
return reqs;
|
|
2817
2857
|
}
|
|
2818
|
-
function relevantGlossary(source, targetLocale, glossary) {
|
|
2819
|
-
const hints = [];
|
|
2820
|
-
for (const entry of glossary) {
|
|
2821
|
-
const haystack = entry.caseSensitive ? source : source.toLowerCase();
|
|
2822
|
-
const needle = entry.caseSensitive ? entry.term : entry.term.toLowerCase();
|
|
2823
|
-
if (!haystack.includes(needle)) continue;
|
|
2824
|
-
hints.push({
|
|
2825
|
-
term: entry.term,
|
|
2826
|
-
doNotTranslate: entry.doNotTranslate,
|
|
2827
|
-
forced: entry.translations?.[targetLocale],
|
|
2828
|
-
notes: entry.notes
|
|
2829
|
-
});
|
|
2830
|
-
}
|
|
2831
|
-
return hints;
|
|
2832
|
-
}
|
|
2833
2858
|
function attachScreenshots(reqs, state, projectRoot) {
|
|
2834
2859
|
const cache2 = /* @__PURE__ */ new Map();
|
|
2835
2860
|
for (const req of reqs) {
|
|
@@ -2936,6 +2961,7 @@ var MEDIA_TYPES, MAX_IMAGE_BYTES, DEFAULT_LOCALE_CONCURRENCY;
|
|
|
2936
2961
|
var init_run = __esm({
|
|
2937
2962
|
"src/server/ai/run.ts"() {
|
|
2938
2963
|
"use strict";
|
|
2964
|
+
init_glossary();
|
|
2939
2965
|
init_placeholders();
|
|
2940
2966
|
init_plurals();
|
|
2941
2967
|
init_state();
|
|
@@ -3113,7 +3139,7 @@ function findMissing(state) {
|
|
|
3113
3139
|
const entry = state.keys[key];
|
|
3114
3140
|
if (entry.skipTranslate) continue;
|
|
3115
3141
|
for (const locale of targets) {
|
|
3116
|
-
const v = entry.plural ? entry.values[locale]?.forms?.other?.trim() : entry.values[locale]?.value;
|
|
3142
|
+
const v = entry.plural ? entry.values[locale]?.forms?.other?.trim() : entry.values[locale]?.value?.trim();
|
|
3117
3143
|
if (!v) out.push({ key, locale });
|
|
3118
3144
|
}
|
|
3119
3145
|
}
|
|
@@ -3581,24 +3607,83 @@ var init_context = __esm({
|
|
|
3581
3607
|
}
|
|
3582
3608
|
});
|
|
3583
3609
|
|
|
3584
|
-
// src/server/
|
|
3585
|
-
function
|
|
3586
|
-
return
|
|
3610
|
+
// src/server/spell.ts
|
|
3611
|
+
function spellTokens(value) {
|
|
3612
|
+
return value.replace(ICU_BLOCK, " ").replace(MASK, " ").match(WORD) ?? [];
|
|
3587
3613
|
}
|
|
3588
|
-
function
|
|
3614
|
+
function ignoreWordsFor(glossary, customWords = []) {
|
|
3589
3615
|
const set = /* @__PURE__ */ new Set();
|
|
3590
|
-
const add = (
|
|
3591
|
-
for (const w of
|
|
3616
|
+
const add = (text) => {
|
|
3617
|
+
for (const w of text.match(WORD) ?? []) set.add(w.toLowerCase());
|
|
3592
3618
|
};
|
|
3593
|
-
for (const
|
|
3594
|
-
|
|
3619
|
+
for (const e of glossary) {
|
|
3620
|
+
add(e.term);
|
|
3621
|
+
for (const t of Object.values(e.translations ?? {})) add(t);
|
|
3622
|
+
}
|
|
3623
|
+
for (const w of customWords) add(w);
|
|
3595
3624
|
return set;
|
|
3596
3625
|
}
|
|
3626
|
+
async function getSpeller(dictId) {
|
|
3627
|
+
const key = norm(dictId);
|
|
3628
|
+
const existing = instances.get(key);
|
|
3629
|
+
if (existing) return existing;
|
|
3630
|
+
if (unavailable.has(key)) return null;
|
|
3631
|
+
try {
|
|
3632
|
+
const nspellMod = await import("nspell");
|
|
3633
|
+
const nspell = nspellMod.default ?? nspellMod;
|
|
3634
|
+
const dictMod = await import(`dictionary-${key}`);
|
|
3635
|
+
const dictExport = dictMod.default ?? dictMod;
|
|
3636
|
+
const dict = typeof dictExport === "function" ? await dictExport() : dictExport;
|
|
3637
|
+
const speller = nspell(dict);
|
|
3638
|
+
instances.set(key, speller);
|
|
3639
|
+
return speller;
|
|
3640
|
+
} catch {
|
|
3641
|
+
unavailable.add(key);
|
|
3642
|
+
return null;
|
|
3643
|
+
} finally {
|
|
3644
|
+
loading.delete(key);
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
function spellValue(dictId, value, ignore) {
|
|
3648
|
+
const key = norm(dictId);
|
|
3649
|
+
if (unavailable.has(key)) return [];
|
|
3650
|
+
const spell = instances.get(key);
|
|
3651
|
+
if (!spell) {
|
|
3652
|
+
if (!loading.has(key)) {
|
|
3653
|
+
loading.add(key);
|
|
3654
|
+
void getSpeller(key);
|
|
3655
|
+
}
|
|
3656
|
+
return null;
|
|
3657
|
+
}
|
|
3658
|
+
const cacheKey = key + " " + value;
|
|
3659
|
+
let allBad = cache.get(cacheKey);
|
|
3660
|
+
if (!allBad) {
|
|
3661
|
+
allBad = spellTokens(value).filter((w) => !spell.correct(w));
|
|
3662
|
+
cache.set(cacheKey, allBad);
|
|
3663
|
+
}
|
|
3664
|
+
return allBad.filter((w) => !ignore.has(w.toLowerCase()));
|
|
3665
|
+
}
|
|
3666
|
+
var instances, loading, unavailable, cache, norm, ICU_BLOCK, MASK, WORD;
|
|
3667
|
+
var init_spell = __esm({
|
|
3668
|
+
"src/server/spell.ts"() {
|
|
3669
|
+
"use strict";
|
|
3670
|
+
instances = /* @__PURE__ */ new Map();
|
|
3671
|
+
loading = /* @__PURE__ */ new Set();
|
|
3672
|
+
unavailable = /* @__PURE__ */ new Set();
|
|
3673
|
+
cache = /* @__PURE__ */ new Map();
|
|
3674
|
+
norm = (dictId) => dictId.toLowerCase();
|
|
3675
|
+
ICU_BLOCK = /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g;
|
|
3676
|
+
MASK = /\{[^}]*\}|<[^>]*>|:\w+|%[sd]/g;
|
|
3677
|
+
WORD = new RegExp("\\p{L}[\\p{L}''\u2019-]*", "gu");
|
|
3678
|
+
}
|
|
3679
|
+
});
|
|
3680
|
+
|
|
3681
|
+
// src/server/lint/spelling.ts
|
|
3597
3682
|
var spellingRule, defaultLoader;
|
|
3598
3683
|
var init_spelling = __esm({
|
|
3599
3684
|
"src/server/lint/spelling.ts"() {
|
|
3600
3685
|
"use strict";
|
|
3601
|
-
|
|
3686
|
+
init_spell();
|
|
3602
3687
|
spellingRule = {
|
|
3603
3688
|
id: "spelling",
|
|
3604
3689
|
run(state, ctx) {
|
|
@@ -3610,10 +3695,8 @@ var init_spelling = __esm({
|
|
|
3610
3695
|
if (!speller) continue;
|
|
3611
3696
|
const value = entry.values[locale]?.value;
|
|
3612
3697
|
if (!value) continue;
|
|
3613
|
-
const
|
|
3614
|
-
|
|
3615
|
-
const lower = word.toLowerCase();
|
|
3616
|
-
if (placeholders.has(lower) || ctx.allowWords.has(lower)) continue;
|
|
3698
|
+
for (const word of spellTokens(value)) {
|
|
3699
|
+
if (ctx.allowWords.has(word.toLowerCase())) continue;
|
|
3617
3700
|
if (!speller.correct(word)) {
|
|
3618
3701
|
out.push({ ruleId: "spelling", key, locale, message: `possible misspelling: "${word}"` });
|
|
3619
3702
|
}
|
|
@@ -3623,18 +3706,7 @@ var init_spelling = __esm({
|
|
|
3623
3706
|
return out;
|
|
3624
3707
|
}
|
|
3625
3708
|
};
|
|
3626
|
-
defaultLoader =
|
|
3627
|
-
try {
|
|
3628
|
-
const nspellMod = await import("nspell");
|
|
3629
|
-
const nspell2 = nspellMod.default ?? nspellMod;
|
|
3630
|
-
const dictMod = await import(`dictionary-${dictId.toLowerCase()}`);
|
|
3631
|
-
const dictExport = dictMod.default ?? dictMod;
|
|
3632
|
-
const dict = typeof dictExport === "function" ? await dictExport() : dictExport;
|
|
3633
|
-
return nspell2(dict);
|
|
3634
|
-
} catch {
|
|
3635
|
-
return null;
|
|
3636
|
-
}
|
|
3637
|
-
};
|
|
3709
|
+
defaultLoader = (dictId) => getSpeller(dictId);
|
|
3638
3710
|
}
|
|
3639
3711
|
});
|
|
3640
3712
|
|
|
@@ -3645,7 +3717,7 @@ var init_rules = __esm({
|
|
|
3645
3717
|
"use strict";
|
|
3646
3718
|
init_scan();
|
|
3647
3719
|
init_placeholders();
|
|
3648
|
-
|
|
3720
|
+
init_glossary();
|
|
3649
3721
|
init_spelling();
|
|
3650
3722
|
emptySourceRule = {
|
|
3651
3723
|
id: "empty-source",
|
|
@@ -3661,17 +3733,14 @@ var init_rules = __esm({
|
|
|
3661
3733
|
};
|
|
3662
3734
|
emptyTranslationRule = {
|
|
3663
3735
|
id: "empty-translation",
|
|
3664
|
-
|
|
3736
|
+
// findMissing is the shared "untranslated" walk (also behind the editor's
|
|
3737
|
+
// untranslated check and /scan/missing); a whitespace-only value counts as
|
|
3738
|
+
// missing there, so no separate whitespace pass is needed.
|
|
3739
|
+
run(state) {
|
|
3665
3740
|
const out = [];
|
|
3666
3741
|
for (const m of findMissing(state)) {
|
|
3667
3742
|
out.push({ ruleId: "empty-translation", key: m.key, locale: m.locale, message: "translation is empty or missing" });
|
|
3668
3743
|
}
|
|
3669
|
-
for (const key of Object.keys(state.keys)) {
|
|
3670
|
-
for (const locale of ctx.targetLocales) {
|
|
3671
|
-
const v = state.keys[key].values[locale]?.value;
|
|
3672
|
-
if (v && !v.trim()) out.push({ ruleId: "empty-translation", key, locale, message: "translation is whitespace-only" });
|
|
3673
|
-
}
|
|
3674
|
-
}
|
|
3675
3744
|
return out;
|
|
3676
3745
|
}
|
|
3677
3746
|
};
|
|
@@ -3799,13 +3868,13 @@ var init_rules = __esm({
|
|
|
3799
3868
|
for (const locale of ctx.targetLocales) {
|
|
3800
3869
|
const v = entry.values[locale]?.value;
|
|
3801
3870
|
if (!v) continue;
|
|
3802
|
-
for (const
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
}
|
|
3871
|
+
for (const viol of glossaryViolations(src, v, locale, ctx.glossary)) {
|
|
3872
|
+
out.push({
|
|
3873
|
+
ruleId: "glossary-violation",
|
|
3874
|
+
key,
|
|
3875
|
+
locale,
|
|
3876
|
+
message: viol.kind === "do-not-translate" ? `do-not-translate term "${viol.term}" is missing or altered` : `expected glossary translation "${viol.expected}" for "${viol.term}"`
|
|
3877
|
+
});
|
|
3809
3878
|
}
|
|
3810
3879
|
}
|
|
3811
3880
|
}
|
|
@@ -3866,7 +3935,7 @@ async function runLint(state, options = {}) {
|
|
|
3866
3935
|
const active = rules.filter(isActive);
|
|
3867
3936
|
const spellingOn = active.some((r) => r.id === "spelling");
|
|
3868
3937
|
const spellers = spellingOn ? await loadSpellers(targetLocales, config, load, warn) : /* @__PURE__ */ new Map();
|
|
3869
|
-
const allowWords = spellingOn ?
|
|
3938
|
+
const allowWords = spellingOn ? ignoreWordsFor(state.glossary, state.config.spelling?.customWords) : /* @__PURE__ */ new Set();
|
|
3870
3939
|
const ctx = {
|
|
3871
3940
|
config,
|
|
3872
3941
|
sourceLocale: state.config.sourceLocale,
|
|
@@ -3904,6 +3973,7 @@ var init_run2 = __esm({
|
|
|
3904
3973
|
init_registry();
|
|
3905
3974
|
init_rules();
|
|
3906
3975
|
init_spelling();
|
|
3976
|
+
init_spell();
|
|
3907
3977
|
init_suppress();
|
|
3908
3978
|
}
|
|
3909
3979
|
});
|
|
@@ -4718,120 +4788,22 @@ var init_stats = __esm({
|
|
|
4718
4788
|
}
|
|
4719
4789
|
});
|
|
4720
4790
|
|
|
4721
|
-
// node_modules/dictionary-en/index.js
|
|
4722
|
-
var dictionary_en_exports = {};
|
|
4723
|
-
__export(dictionary_en_exports, {
|
|
4724
|
-
default: () => dictionary_en_default
|
|
4725
|
-
});
|
|
4726
|
-
import fs from "fs/promises";
|
|
4727
|
-
var aff, dic, dictionary, dictionary_en_default;
|
|
4728
|
-
var init_dictionary_en = __esm({
|
|
4729
|
-
async "node_modules/dictionary-en/index.js"() {
|
|
4730
|
-
"use strict";
|
|
4731
|
-
aff = await fs.readFile(new URL("index.aff", import.meta.url));
|
|
4732
|
-
dic = await fs.readFile(new URL("index.dic", import.meta.url));
|
|
4733
|
-
dictionary = { aff, dic };
|
|
4734
|
-
dictionary_en_default = dictionary;
|
|
4735
|
-
}
|
|
4736
|
-
});
|
|
4737
|
-
|
|
4738
|
-
// src/server/spell.ts
|
|
4739
|
-
import nspell from "nspell";
|
|
4740
|
-
async function loadDictionary(locale) {
|
|
4741
|
-
const key = norm(locale);
|
|
4742
|
-
if (instances.has(key) || unavailable.has(key)) return;
|
|
4743
|
-
const loader = LOADERS[key];
|
|
4744
|
-
if (!loader) {
|
|
4745
|
-
unavailable.add(key);
|
|
4746
|
-
return;
|
|
4747
|
-
}
|
|
4748
|
-
try {
|
|
4749
|
-
const { default: dict } = await loader();
|
|
4750
|
-
instances.set(key, nspell(dict));
|
|
4751
|
-
} catch {
|
|
4752
|
-
unavailable.add(key);
|
|
4753
|
-
} finally {
|
|
4754
|
-
loading.delete(key);
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4757
|
-
function spellValue(locale, value, ignore) {
|
|
4758
|
-
const key = norm(locale);
|
|
4759
|
-
if (unavailable.has(key)) return [];
|
|
4760
|
-
const spell = instances.get(key);
|
|
4761
|
-
if (!spell) {
|
|
4762
|
-
if (!LOADERS[key]) {
|
|
4763
|
-
unavailable.add(key);
|
|
4764
|
-
return [];
|
|
4765
|
-
}
|
|
4766
|
-
if (!loading.has(key)) {
|
|
4767
|
-
loading.add(key);
|
|
4768
|
-
void loadDictionary(key);
|
|
4769
|
-
}
|
|
4770
|
-
return null;
|
|
4771
|
-
}
|
|
4772
|
-
const cacheKey = key + " " + value;
|
|
4773
|
-
let allBad = cache.get(cacheKey);
|
|
4774
|
-
if (!allBad) {
|
|
4775
|
-
const words = value.replace(ICU_BLOCK, " ").replace(MASK, " ").match(WORD) ?? [];
|
|
4776
|
-
allBad = words.filter((w) => !spell.correct(w));
|
|
4777
|
-
cache.set(cacheKey, allBad);
|
|
4778
|
-
}
|
|
4779
|
-
return allBad.filter((w) => !ignore.has(w.toLowerCase()));
|
|
4780
|
-
}
|
|
4781
|
-
var LOADERS, instances, loading, unavailable, cache, norm, ICU_BLOCK, MASK, WORD;
|
|
4782
|
-
var init_spell = __esm({
|
|
4783
|
-
"src/server/spell.ts"() {
|
|
4784
|
-
"use strict";
|
|
4785
|
-
LOADERS = {
|
|
4786
|
-
en: () => init_dictionary_en().then(() => dictionary_en_exports),
|
|
4787
|
-
es: () => import("dictionary-es"),
|
|
4788
|
-
fr: () => import("dictionary-fr")
|
|
4789
|
-
};
|
|
4790
|
-
instances = /* @__PURE__ */ new Map();
|
|
4791
|
-
loading = /* @__PURE__ */ new Set();
|
|
4792
|
-
unavailable = /* @__PURE__ */ new Set();
|
|
4793
|
-
cache = /* @__PURE__ */ new Map();
|
|
4794
|
-
norm = (locale) => locale.toLowerCase();
|
|
4795
|
-
ICU_BLOCK = /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g;
|
|
4796
|
-
MASK = /\{[^}]*\}|<[^>]*>|:\w+|%[sd]/g;
|
|
4797
|
-
WORD = new RegExp("\\p{L}[\\p{L}''\u2019-]*", "gu");
|
|
4798
|
-
}
|
|
4799
|
-
});
|
|
4800
|
-
|
|
4801
4791
|
// src/server/checks.ts
|
|
4802
|
-
function contains(haystack, needle, caseSensitive) {
|
|
4803
|
-
return caseSensitive ? haystack.includes(needle) : haystack.toLowerCase().includes(needle.toLowerCase());
|
|
4804
|
-
}
|
|
4805
4792
|
function runChecks(state, opts = {}) {
|
|
4806
4793
|
const ruleOff = (id) => state.config.lint?.rules?.[CHECK_RULE[id]] === "off";
|
|
4807
4794
|
const on = (id) => (!opts.only || opts.only.includes(id)) && !ruleOff(id);
|
|
4808
4795
|
const issues = [];
|
|
4809
4796
|
let spellPending = false;
|
|
4810
4797
|
const { sourceLocale } = state.config;
|
|
4811
|
-
const
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
for (const t of Object.values(e.translations ?? {})) {
|
|
4816
|
-
for (const w of t.toLowerCase().split(/\s+/)) if (w) ignore.add(w);
|
|
4798
|
+
const ignore = ignoreWordsFor(state.glossary, state.config.spelling?.customWords);
|
|
4799
|
+
if (on("untranslated")) {
|
|
4800
|
+
for (const m of findMissing(state)) {
|
|
4801
|
+
issues.push({ key: m.key, locale: m.locale, check: "untranslated", message: "Not translated yet" });
|
|
4817
4802
|
}
|
|
4818
4803
|
}
|
|
4819
|
-
for (const word of state.config.spelling?.customWords ?? []) {
|
|
4820
|
-
const w = word.trim().toLowerCase();
|
|
4821
|
-
if (w) ignore.add(w);
|
|
4822
|
-
}
|
|
4823
|
-
const targetLocales = state.config.locales.filter((l) => l !== sourceLocale);
|
|
4824
4804
|
for (const key of Object.keys(state.keys).sort()) {
|
|
4825
4805
|
const entry = state.keys[key];
|
|
4826
4806
|
const source = entry.values[sourceLocale]?.value ?? "";
|
|
4827
|
-
if (on("untranslated") && !entry.skipTranslate) {
|
|
4828
|
-
for (const locale of targetLocales) {
|
|
4829
|
-
const translated = entry.plural ? (entry.values[locale]?.forms?.other ?? "").trim() !== "" : (entry.values[locale]?.value ?? "").trim() !== "";
|
|
4830
|
-
if (!translated) {
|
|
4831
|
-
issues.push({ key, locale, check: "untranslated", message: "Not translated yet" });
|
|
4832
|
-
}
|
|
4833
|
-
}
|
|
4834
|
-
}
|
|
4835
4807
|
if (entry.plural) {
|
|
4836
4808
|
if (on("placeholder")) {
|
|
4837
4809
|
const sourceForm = entry.values[sourceLocale]?.forms?.other ?? "";
|
|
@@ -4875,7 +4847,8 @@ function runChecks(state, opts = {}) {
|
|
|
4875
4847
|
});
|
|
4876
4848
|
}
|
|
4877
4849
|
if (on("spelling") && !blank) {
|
|
4878
|
-
const
|
|
4850
|
+
const dictId = state.config.lint?.spelling?.locales?.[locale] ?? locale;
|
|
4851
|
+
const bad = spellValue(dictId, value, ignore);
|
|
4879
4852
|
if (bad === null) spellPending = true;
|
|
4880
4853
|
else if (bad.length) {
|
|
4881
4854
|
issues.push({
|
|
@@ -4905,29 +4878,14 @@ function runChecks(state, opts = {}) {
|
|
|
4905
4878
|
});
|
|
4906
4879
|
}
|
|
4907
4880
|
if (on("glossary") && source) {
|
|
4908
|
-
for (const
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
message: `Do-not-translate term "${hint.term}" is missing from the translation`,
|
|
4917
|
-
detail: [hint.term]
|
|
4918
|
-
});
|
|
4919
|
-
}
|
|
4920
|
-
} else if (hint.forced) {
|
|
4921
|
-
if (!contains(value, hint.forced, cs)) {
|
|
4922
|
-
issues.push({
|
|
4923
|
-
key,
|
|
4924
|
-
locale,
|
|
4925
|
-
check: "glossary",
|
|
4926
|
-
message: `Should use "${hint.forced}" for "${hint.term}"`,
|
|
4927
|
-
detail: [hint.forced]
|
|
4928
|
-
});
|
|
4929
|
-
}
|
|
4930
|
-
}
|
|
4881
|
+
for (const viol of glossaryViolations(source, value, locale, state.glossary)) {
|
|
4882
|
+
issues.push({
|
|
4883
|
+
key,
|
|
4884
|
+
locale,
|
|
4885
|
+
check: "glossary",
|
|
4886
|
+
message: viol.kind === "do-not-translate" ? `Do-not-translate term "${viol.term}" is missing from the translation` : `Should use "${viol.expected}" for "${viol.term}"`,
|
|
4887
|
+
detail: [viol.expected]
|
|
4888
|
+
});
|
|
4931
4889
|
}
|
|
4932
4890
|
}
|
|
4933
4891
|
}
|
|
@@ -4943,7 +4901,8 @@ var init_checks = __esm({
|
|
|
4943
4901
|
"src/server/checks.ts"() {
|
|
4944
4902
|
"use strict";
|
|
4945
4903
|
init_placeholders();
|
|
4946
|
-
|
|
4904
|
+
init_glossary();
|
|
4905
|
+
init_scan();
|
|
4947
4906
|
init_spell();
|
|
4948
4907
|
init_suppress();
|
|
4949
4908
|
CHECK_IDS = ["untranslated", "placeholder", "spelling", "length", "glossary"];
|