···11---
22import { DashboardData } from "../data";
33+import { processPlugins } from "../utils";
3455+const plugins = await processPlugins();
66+77+// Collect all languages with 'done' translation anywhere
88+const languagesWithTranslations = new Set<string>();
99+for (const plugin of plugins) {
1010+ for (const keyData of plugin.keys) {
1111+ for (const [lang, status] of Object.entries(keyData.locales)) {
1212+ if (status === "done") {
1313+ languagesWithTranslations.add(lang);
1414+ }
1515+ }
1616+ }
1717+}
1818+1919+// Now filter the locales array accordingly
2020+const filteredLocales = DashboardData.locales.filter((locale) =>
2121+ languagesWithTranslations.has(locale.lang)
2222+);
2323+2424+// Keep pluginLinks unchanged (or filter similarly if needed)
425const pluginLinks = DashboardData.plugins.map((plugin) => ({
526 name: plugin.name,
627 link: `/plugins/${plugin.packageName}`,
728}));
82999-const localeLinks = DashboardData.locales.map((locale) => ({
3030+const localeLinks = filteredLocales.map((locale) => ({
1031 label: locale.label,
1132 link: `/languages/${locale.lang}`,
1233}));
+27-10
src/utils.ts
···6565 // Map base normalized key -> first line number (1-based)
6666 const baseKeyToLine = new Map<string, number>();
67676868- // Simple key extractor regex: match keys in the 'en' object only
6969- // Assumes key strings are single-quoted or double-quoted, e.g. 'key': or "key":
7068 let insideEnBlock = false;
7169 for (let i = 0; i < lines.length; i++) {
7270 const line = lines[i].trim();
···7674 continue;
7775 }
7876 if (insideEnBlock && line === "},") {
7979- // end of 'en' block
8077 insideEnBlock = false;
8178 }
8279 if (insideEnBlock) {
8383- // Try to extract key string from line like:
8484- // 'starlightBlog.authors.count_one': '{{count}} post by {{author}}',
8585- // or
8686- // "some.key_here": "value",
8780 const keyMatch = line.match(/^['"]([^'"]+)['"]\s*:/);
8881 if (keyMatch) {
8982 const rawKey = keyMatch[1];
9083 const baseKey = normalizeKey(rawKey);
9184 if (!baseKeyToLine.has(baseKey)) {
9292- baseKeyToLine.set(baseKey, i + 1); // line numbers 1-based
8585+ baseKeyToLine.set(baseKey, i + 1);
9386 }
9487 }
9588 }
9689 }
97909898- // Now fetch parsed translations object for all locales
9991 const data = await fetchTranslationFile(plugin.translationFileLinkRaw);
1009210193 const defaultLang = "en";
···117109 localesStatus[lang] = hasTranslation ? "done" : "missing";
118110 }
119111120120- const line = baseKeyToLine.get(key)!; // must exist
112112+ const line = baseKeyToLine.get(key)!;
121113122114 keysStatus.push({ key, locales: localesStatus, line });
123115 }
···129121 translationFileLinkRaw: plugin.translationFileLinkRaw,
130122 keys: keysStatus,
131123 });
124124+ }
125125+126126+ // Now filter out languages with no 'done' status anywhere in any plugin
127127+ // Collect all languages that have 'done' status in any plugin's keys
128128+ const languagesWithTranslations = new Set<string>();
129129+130130+ for (const plugin of results) {
131131+ for (const keyData of plugin.keys) {
132132+ for (const [lang, status] of Object.entries(keyData.locales)) {
133133+ if (status === "done") {
134134+ languagesWithTranslations.add(lang);
135135+ }
136136+ }
137137+ }
138138+ }
139139+140140+ // Filter out locales in plugins keys for languages that never have any translation
141141+ for (const plugin of results) {
142142+ for (const keyData of plugin.keys) {
143143+ for (const lang of Object.keys(keyData.locales)) {
144144+ if (!languagesWithTranslations.has(lang)) {
145145+ delete keyData.locales[lang];
146146+ }
147147+ }
148148+ }
132149 }
133150134151 return results;