2023-12-02 19:04:51 +01:00
import { registerDebugFunction } from './power-user.js' ;
import { waitUntilCondition } from './utils.js' ;
2023-08-22 09:37:18 +02:00
2023-12-02 19:04:51 +01:00
const storageKey = 'language' ;
2024-03-10 00:03:51 +01:00
const overrideLanguage = localStorage . getItem ( 'language' ) ;
const localeFile = overrideLanguage || navigator . userLanguage || 'lang' ;
export const localeData = await fetch ( ` ./locales/ ${ localeFile } .json ` ) . then ( response => {
console . log ( ` Loading locale data from ./locales/ ${ localeFile } .json ` ) ;
if ( ! response . ok ) {
throw new Error ( ` Failed to load locale data: ${ response . status } ${ response . statusText } ` ) ;
}
return response . json ( ) ;
} ) ;
2023-08-22 09:37:18 +02:00
2023-08-24 14:13:04 +02:00
function getMissingTranslations ( ) {
const missingData = [ ] ;
2024-03-10 00:03:51 +01:00
for ( const language of localeData ) {
2023-12-02 19:04:51 +01:00
$ ( document ) . find ( '[data-i18n]' ) . each ( function ( ) {
const keys = $ ( this ) . data ( 'i18n' ) . split ( ';' ) ; // Multi-key entries are ; delimited
2023-08-24 14:13:04 +02:00
for ( const key of keys ) {
const attributeMatch = key . match ( /\[(\S+)\](.+)/ ) ; // [attribute]key
if ( attributeMatch ) { // attribute-tagged key
2024-03-10 00:03:51 +01:00
const localizedValue = localeData ? . [ attributeMatch [ 2 ] ] ;
2023-08-24 14:13:04 +02:00
if ( ! localizedValue ) {
missingData . push ( { key , language , value : $ ( this ) . attr ( attributeMatch [ 1 ] ) } ) ;
}
} else { // No attribute tag, treat as 'text'
2024-03-10 00:03:51 +01:00
const localizedValue = localeData ? . [ key ] ;
2023-08-24 14:13:04 +02:00
if ( ! localizedValue ) {
missingData . push ( { key , language , value : $ ( this ) . text ( ) . trim ( ) } ) ;
}
}
}
} ) ;
}
// Remove duplicates
const uniqueMissingData = [ ] ;
for ( const { key , language , value } of missingData ) {
if ( ! uniqueMissingData . some ( x => x . key === key && x . language === language && x . value === value ) ) {
uniqueMissingData . push ( { key , language , value } ) ;
}
}
// Sort by language, then key
uniqueMissingData . sort ( ( a , b ) => a . language . localeCompare ( b . language ) || a . key . localeCompare ( b . key ) ) ;
// Map to { language: { key: value } }
2024-03-10 00:03:51 +01:00
let missingDataMap = { } ;
for ( const { key , value } of uniqueMissingData ) {
if ( ! missingDataMap ) {
missingDataMap = { } ;
2023-08-24 14:13:04 +02:00
}
2024-03-10 00:03:51 +01:00
missingDataMap [ key ] = value ;
2023-08-24 14:13:04 +02:00
}
console . table ( uniqueMissingData ) ;
console . log ( missingDataMap ) ;
2023-08-27 22:20:43 +02:00
toastr . success ( ` Found ${ uniqueMissingData . length } missing translations. See browser console for details. ` ) ;
}
2023-08-24 14:13:04 +02:00
2023-08-22 09:37:18 +02:00
export function applyLocale ( root = document ) {
2023-12-02 19:04:51 +01:00
const $root = root instanceof Document ? $ ( root ) : $ ( new DOMParser ( ) . parseFromString ( root , 'text/html' ) ) ;
2023-08-22 09:37:18 +02:00
//find all the elements with `data-i18n` attribute
2023-12-02 19:04:51 +01:00
$root . find ( '[data-i18n]' ) . each ( function ( ) {
2023-08-22 09:37:18 +02:00
//read the translation from the language data
2023-12-02 19:04:51 +01:00
const keys = $ ( this ) . data ( 'i18n' ) . split ( ';' ) ; // Multi-key entries are ; delimited
2023-08-22 09:37:18 +02:00
for ( const key of keys ) {
const attributeMatch = key . match ( /\[(\S+)\](.+)/ ) ; // [attribute]key
if ( attributeMatch ) { // attribute-tagged key
2024-03-10 00:03:51 +01:00
const localizedValue = localeData ? . [ attributeMatch [ 2 ] ] ;
2023-08-22 09:37:18 +02:00
if ( localizedValue ) {
$ ( this ) . attr ( attributeMatch [ 1 ] , localizedValue ) ;
}
} else { // No attribute tag, treat as 'text'
2024-03-10 00:03:51 +01:00
const localizedValue = localeData ? . [ key ] ;
2023-08-22 09:37:18 +02:00
if ( localizedValue ) {
$ ( this ) . text ( localizedValue ) ;
}
}
}
} ) ;
if ( root !== document ) {
return $root . get ( 0 ) . body . innerHTML ;
}
}
2024-03-10 00:03:51 +01:00
async function addLanguagesToDropdown ( ) {
const langs = await fetch ( '/locales/lang.json' ) . then ( response => response . json ( ) ) ;
2023-08-22 09:37:18 +02:00
2024-03-10 00:03:51 +01:00
for ( const lang of langs ) {
2023-08-22 09:37:18 +02:00
const option = document . createElement ( 'option' ) ;
option . value = lang ;
option . innerText = lang ;
$ ( '#ui_language_select' ) . append ( option ) ;
}
const selectedLanguage = localStorage . getItem ( storageKey ) ;
if ( selectedLanguage ) {
$ ( '#ui_language_select' ) . val ( selectedLanguage ) ;
}
}
2023-11-28 00:44:13 +01:00
export function initLocales ( ) {
2023-08-22 09:37:18 +02:00
waitUntilCondition ( ( ) => ! ! localeData ) ;
applyLocale ( ) ;
addLanguagesToDropdown ( ) ;
$ ( '#ui_language_select' ) . on ( 'change' , async function ( ) {
2023-08-22 13:30:49 +02:00
const language = String ( $ ( this ) . val ( ) ) ;
2023-08-22 09:37:18 +02:00
if ( language ) {
localStorage . setItem ( storageKey , language ) ;
} else {
localStorage . removeItem ( storageKey ) ;
}
location . reload ( ) ;
} ) ;
2023-08-27 22:20:43 +02:00
registerDebugFunction ( 'getMissingTranslations' , 'Get missing translations' , 'Detects missing localization data and dumps the data into the browser console.' , getMissingTranslations ) ;
registerDebugFunction ( 'applyLocale' , 'Apply locale' , 'Reapplies the currently selected locale to the page.' , applyLocale ) ;
2023-12-02 16:15:03 +01:00
}