Debug Func to track missing dynamic translations - Add debug function to the debug menu that enables tracking of missing dynamic translations (via `t` or `translate`) once enabled - Only works with non-English language loaded - Prints it together with the existing debug print

08e184de261491810f31a2724bc8ec86c445ae91

Wolfsblvt <wolfsblvt@gmail.com>

1 files changed, +57 -7Showing whitespace changes
public/scripts/i18n.js+57 -7
@@ -9,6 +9,9 @@ var langs;
99// eslint-disable-next-line prefer-const
1010var localeData;
1111
12+/** @type {Array<string>|null} Array of translations keys if they should be tracked - if not tracked then null */
13+let trackMissingDynamicTranslate = null;
14+
1215export const getCurrentLocale = () => localeFile;
1316
1417/**
@@ -97,6 +100,9 @@ export function t(strings, ...values) {
97100 */
98101export function translate(text, key = null) {
99102 const translationKey = key || text;
103+ if (trackMissingDynamicTranslate && localeData && !Object.hasOwn(localeData, translationKey) && !trackMissingDynamicTranslate.includes(translationKey)) {
104+ trackMissingDynamicTranslate.push(translationKey);
105+ }
100106 return localeData?.[translationKey] || text;
101107}
102108
@@ -153,13 +159,28 @@ function translateElement(element) {
153159 }
154160}
155161
162+/**
163+ * Checks if the given locale is supported and not English.
164+ * @param {string} [locale=null] The locale to check (defaults to the current locale)
165+ * @returns {boolean} True if the locale is not English and supported
166+ */
167+function isSupportedNonEnglish(locale = null) {
168+ const lang = locale || localeFile;
169+ return lang && lang != 'en' && findLang(lang);
170+}
156171
157172async function getMissingTranslations() {
173+ /** @type {Array<{key: string, language: string, value: string}>} */
158174 const missingData = [];
159175
176+ if (trackMissingDynamicTranslate) {
177+ for (const key of trackMissingDynamicTranslate) {
178+ missingData.push({ key, language: localeFile, value: key });
179+ }
180+ }
181+
160182 // Determine locales to search for untranslated strings
161183 const isNotSupportedlangsToProcess = !isSupportedNonEnglish() ? [findLang(localeFile)] : langs;
162- const langsToProcess = (isNotSupported || localeFile == 'en') ? langs : [findLang(localeFile)];
163184
164185 for (const language of langsToProcess) {
165186 const localeData = await getLocaleData(language.lang);
@@ -170,7 +191,7 @@ async function getMissingTranslations() {
170191 if (attributeMatch) { // attribute-tagged key
171192 const localizedValue = localeData?.[attributeMatch[2]];
172193 if (!localizedValue) {
173194 missingData.push({ key, language: language.lang, value: String($(this).attr(attributeMatch[1])) });
174195 }
175196 } else { // No attribute tag, treat as 'text'
176197 const localizedValue = localeData?.[key];
@@ -196,15 +217,19 @@ async function getMissingTranslations() {
196217 // Map to { language: { key: value } }
197218 let missingDataMap = {};
198219 for (const { key, value } of uniqueMissingData) {
199- if (!missingDataMap) {
200- missingDataMap = {};
201- }
202220 missingDataMap[key] = value;
203221 }
204222
223+ console.log('Missing Translations:');
205224 console.table(uniqueMissingData);
225+ console.log('Full map of missing data:');
206226 console.log(missingDataMap);
207227
228+ if (trackMissingDynamicTranslate) {
229+ console.log('Dynamic translations missing:');
230+ console.log(trackMissingDynamicTranslate);
231+ }
232+
208233 toastr.success(`Found ${uniqueMissingData.length} missing translations. See browser console for details.`);
209234}
210235
@@ -266,6 +291,31 @@ export async function initLocales() {
266291 attributeFilter: ['data-i18n'],
267292 });
268293
269- registerDebugFunction('getMissingTranslations', 'Get missing translations', 'Detects missing localization data in the current locale and dumps the data into the browser console. If the current locale is English, searches all other locales.', getMissingTranslations);
294+ if (localStorage.getItem('trackDynamicTranslate') === 'true' && isSupportedNonEnglish()) {
295+ trackMissingDynamicTranslate = [];
296+ }
297+
298+ registerDebugFunction('getMissingTranslations', 'Get missing translations',
299+ 'Detects missing localization data in the current locale and dumps the data into the browser console. ' +
300+ 'If the current locale is English, searches all other locales.',
301+ getMissingTranslations);
302+ registerDebugFunction('trackDynamicTranslate', 'Track dynamic translation',
303+ 'Toggles tracking of dynamic translations, which will be dumped into the missing translations translations too. ' +
304+ 'This includes things translated via the t`...` function and translate(). It will only track strings translated <b>after</b> this is toggled on, '
305+ + 'and when they actually pop up, so refreshing the page and opening popups, etc, is needed. Will only track if the current locale is not English.',
306+ () => {
307+ const isTracking = localStorage.getItem('trackDynamicTranslate') !== 'true';
308+ localStorage.setItem('trackDynamicTranslate', isTracking ? 'true' : 'false');
309+ if (isTracking && isSupportedNonEnglish()) {
310+ trackMissingDynamicTranslate = [];
311+ toastr.success('Dynamic translation tracking enabled.');
312+ } else if (isTracking) {
313+ trackMissingDynamicTranslate = null;
314+ toastr.warning('Dynamic translation tracking enabled, but will not be tracked with locale English.');
315+ } else {
316+ trackMissingDynamicTranslate = null;
317+ toastr.info('Dynamic translation tracking disabled.');
318+ }
319+ });
270320 registerDebugFunction('applyLocale', 'Apply locale', 'Reapplies the currently selected locale to the page.', applyLocale);
271321}