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
| @@ -9,6 +9,9 @@ var langs; | ||
| 9 | 9 | // eslint-disable-next-line prefer-const |
| 10 | 10 | var localeData; |
| 11 | 11 | |
| 12 | +/** @type {Array<string>|null} Array of translations keys if they should be tracked - if not tracked then null */ | |
| 13 | +let trackMissingDynamicTranslate = null; | |
| 14 | + | |
| 12 | 15 | export const getCurrentLocale = () => localeFile; |
| 13 | 16 | |
| 14 | 17 | /** |
| @@ -97,6 +100,9 @@ export function t(strings, ...values) { | ||
| 97 | 100 | */ |
| 98 | 101 | export function translate(text, key = null) { |
| 99 | 102 | const translationKey = key || text; |
| 103 | + if (trackMissingDynamicTranslate && localeData && !Object.hasOwn(localeData, translationKey) && !trackMissingDynamicTranslate.includes(translationKey)) { | |
| 104 | + trackMissingDynamicTranslate.push(translationKey); | |
| 105 | + } | |
| 100 | 106 | return localeData?.[translationKey] || text; |
| 101 | 107 | } |
| 102 | 108 | |
| @@ -153,13 +159,28 @@ function translateElement(element) { | ||
| 153 | 159 | } |
| 154 | 160 | } |
| 155 | 161 | |
| 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 | +} | |
| 156 | 171 | |
| 157 | 172 | async function getMissingTranslations() { |
| 173 | + /** @type {Array<{key: string, language: string, value: string}>} */ | |
| 158 | 174 | const missingData = []; |
| 159 | 175 | |
| 176 | + if (trackMissingDynamicTranslate) { | |
| 177 | + for (const key of trackMissingDynamicTranslate) { | |
| 178 | + missingData.push({ key, language: localeFile, value: key }); | |
| 179 | + } | |
| 180 | + } | |
| 181 | + | |
| 160 | 182 | // Determine locales to search for untranslated strings |
| 161 | 183 | const isNotSupportedlangsToProcess = !isSupportedNonEnglish() ? [findLang(localeFile)] : langs; |
| 162 | - const langsToProcess = (isNotSupported || localeFile == 'en') ? langs : [findLang(localeFile)]; | |
| 163 | 184 | |
| 164 | 185 | for (const language of langsToProcess) { |
| 165 | 186 | const localeData = await getLocaleData(language.lang); |
| @@ -170,7 +191,7 @@ async function getMissingTranslations() { | ||
| 170 | 191 | if (attributeMatch) { // attribute-tagged key |
| 171 | 192 | const localizedValue = localeData?.[attributeMatch[2]]; |
| 172 | 193 | if (!localizedValue) { |
| 173 | 194 | missingData.push({ key, language: language.lang, value: String($(this).attr(attributeMatch[1])) }); |
| 174 | 195 | } |
| 175 | 196 | } else { // No attribute tag, treat as 'text' |
| 176 | 197 | const localizedValue = localeData?.[key]; |
| @@ -196,15 +217,19 @@ async function getMissingTranslations() { | ||
| 196 | 217 | // Map to { language: { key: value } } |
| 197 | 218 | let missingDataMap = {}; |
| 198 | 219 | for (const { key, value } of uniqueMissingData) { |
| 199 | - if (!missingDataMap) { | |
| 200 | - missingDataMap = {}; | |
| 201 | - } | |
| 202 | 220 | missingDataMap[key] = value; |
| 203 | 221 | } |
| 204 | 222 | |
| 223 | + console.log('Missing Translations:'); | |
| 205 | 224 | console.table(uniqueMissingData); |
| 225 | + console.log('Full map of missing data:'); | |
| 206 | 226 | console.log(missingDataMap); |
| 207 | 227 | |
| 228 | + if (trackMissingDynamicTranslate) { | |
| 229 | + console.log('Dynamic translations missing:'); | |
| 230 | + console.log(trackMissingDynamicTranslate); | |
| 231 | + } | |
| 232 | + | |
| 208 | 233 | toastr.success(`Found ${uniqueMissingData.length} missing translations. See browser console for details.`); |
| 209 | 234 | } |
| 210 | 235 | |
| @@ -266,6 +291,31 @@ export async function initLocales() { | ||
| 266 | 291 | attributeFilter: ['data-i18n'], |
| 267 | 292 | }); |
| 268 | 293 | |
| 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 | + }); | |
| 270 | 320 | registerDebugFunction('applyLocale', 'Apply locale', 'Reapplies the currently selected locale to the page.', applyLocale); |
| 271 | 321 | } |