2023-07-06 21:29:36 +02:00
class Localization {
constructor ( ) {
Localization . defaultLocale = "en" ;
2023-10-11 17:49:49 +02:00
Localization . supportedLocales = [ "ar" , "de" , "en" , "es" , "fr" , "id" , "it" , "ja" , "nb" , "nl" , "ro" , "ru" , "zh-CN" ] ;
Localization . supportedLocalesRTL = [ "ar" ] ;
2023-10-05 17:11:16 +02:00
2023-07-06 21:29:36 +02:00
Localization . translations = { } ;
2023-07-07 14:58:15 +02:00
Localization . defaultTranslations = { } ;
2023-07-06 21:29:36 +02:00
2023-10-05 17:11:16 +02:00
Localization . systemLocale = Localization . getSupportedOrDefault ( navigator . languages ) ;
2023-07-06 21:29:36 +02:00
2023-08-30 14:57:40 +02:00
let storedLanguageCode = localStorage . getItem ( "language-code" ) ;
Localization . initialLocale = storedLanguageCode && Localization . isSupported ( storedLanguageCode )
? storedLanguageCode
: Localization . systemLocale ;
Localization . setTranslation ( Localization . initialLocale )
2023-07-06 21:29:36 +02:00
. then ( _ => {
2023-08-30 14:57:40 +02:00
console . log ( "Initial translation successful." ) ;
2023-09-14 20:06:17 +02:00
Events . fire ( "initial-translation-loaded" ) ;
2023-08-30 14:57:40 +02:00
} ) ;
2023-07-06 21:29:36 +02:00
}
static isSupported ( locale ) {
return Localization . supportedLocales . indexOf ( locale ) > - 1 ;
}
2023-10-05 17:11:16 +02:00
static isRTLLanguage ( locale ) {
return Localization . supportedLocalesRTL . indexOf ( locale ) > - 1 ;
}
static getSupportedOrDefault ( locales ) {
2023-07-06 21:29:36 +02:00
return locales . find ( Localization . isSupported ) || Localization . defaultLocale ;
}
2023-08-30 14:57:40 +02:00
static async setTranslation ( locale ) {
if ( ! locale ) locale = Localization . systemLocale ;
await Localization . setLocale ( locale )
await Localization . translatePage ( ) ;
2023-10-05 17:11:16 +02:00
const htmlRootNode = document . querySelector ( 'html' ) ;
if ( Localization . isRTLLanguage ( locale ) ) {
htmlRootNode . setAttribute ( 'dir' , 'rtl' ) ;
} else {
htmlRootNode . removeAttribute ( 'dir' ) ;
}
htmlRootNode . setAttribute ( 'lang' , locale ) ;
2023-08-30 14:57:40 +02:00
console . log ( "Page successfully translated" ,
` System language: ${ Localization . systemLocale } ` ,
` Selected language: ${ locale } `
) ;
2023-09-14 20:06:17 +02:00
Events . fire ( "translation-loaded" ) ;
2023-08-30 14:57:40 +02:00
}
2023-07-06 21:29:36 +02:00
static async setLocale ( newLocale ) {
if ( newLocale === Localization . locale ) return false ;
2023-07-07 14:58:15 +02:00
Localization . defaultTranslations = await Localization . fetchTranslationsFor ( Localization . defaultLocale ) ;
2023-07-07 14:58:15 +02:00
2023-07-06 21:29:36 +02:00
const newTranslations = await Localization . fetchTranslationsFor ( newLocale ) ;
if ( ! newTranslations ) return false ;
Localization . locale = newLocale ;
Localization . translations = newTranslations ;
2023-08-30 14:57:40 +02:00
}
2023-07-06 21:29:36 +02:00
2023-08-30 14:57:40 +02:00
static getLocale ( ) {
return Localization . locale ;
}
static isSystemLocale ( ) {
return ! localStorage . getItem ( 'language-code' ) ;
2023-07-06 21:29:36 +02:00
}
static async fetchTranslationsFor ( newLocale ) {
const response = await fetch ( ` lang/ ${ newLocale } .json ` )
if ( response . redirected === true || response . status !== 200 ) return false ;
return await response . json ( ) ;
}
2023-08-30 14:57:40 +02:00
static async translatePage ( ) {
2023-07-06 21:29:36 +02:00
document
. querySelectorAll ( "[data-i18n-key]" )
. forEach ( element => Localization . translateElement ( element ) ) ;
}
static async translateElement ( element ) {
const key = element . getAttribute ( "data-i18n-key" ) ;
const attrs = element . getAttribute ( "data-i18n-attrs" ) . split ( " " ) ;
for ( let i in attrs ) {
let attr = attrs [ i ] ;
if ( attr === "text" ) {
2023-07-07 14:58:15 +02:00
element . innerText = Localization . getTranslation ( key ) ;
2023-07-06 21:29:36 +02:00
} else {
2023-08-30 14:57:40 +02:00
if ( attr . startsWith ( "data-" ) ) {
let dataAttr = attr . substring ( 5 ) ;
element . dataset . dataAttr = Localization . getTranslation ( key , attr ) ;
} {
element . setAttribute ( attr , Localization . getTranslation ( key , attr ) ) ;
}
2023-07-06 21:29:36 +02:00
}
}
}
2023-09-13 17:08:57 +02:00
static getTranslation ( key , attr = null , data = { } , useDefault = false ) {
2023-07-06 21:29:36 +02:00
const keys = key . split ( "." ) ;
2023-07-07 14:58:15 +02:00
let translationCandidates = useDefault
? Localization . defaultTranslations
: Localization . translations ;
2023-07-06 21:29:36 +02:00
2023-08-29 02:32:54 +02:00
let translation ;
try {
for ( let i = 0 ; i < keys . length - 1 ; i ++ ) {
translationCandidates = translationCandidates [ keys [ i ] ]
}
2023-07-06 21:29:36 +02:00
2023-08-29 02:32:54 +02:00
let lastKey = keys [ keys . length - 1 ] ;
2023-07-07 14:58:15 +02:00
2023-08-29 02:32:54 +02:00
if ( attr ) lastKey += "_" + attr ;
2023-07-06 21:29:36 +02:00
2023-08-29 02:32:54 +02:00
translation = translationCandidates [ lastKey ] ;
2023-07-06 21:29:36 +02:00
2023-08-29 02:32:54 +02:00
for ( let j in data ) {
translation = translation . replace ( ` {{ ${ j } }} ` , data [ j ] ) ;
}
} catch ( e ) {
translation = "" ;
2023-07-07 14:58:15 +02:00
}
if ( ! translation ) {
if ( ! useDefault ) {
translation = this . getTranslation ( key , attr , data , true ) ;
console . warn ( ` Missing translation entry for your language ${ Localization . locale . toUpperCase ( ) } . Using ${ Localization . defaultLocale . toUpperCase ( ) } instead. ` , key , attr ) ;
2023-09-19 00:23:19 +02:00
console . warn ( ` Translate this string here: https://hosted.weblate.org/browse/pairdrop/pairdrop-spa/ ${ Localization . locale . toLowerCase ( ) } /?q= ${ key } ` )
console . log ( "Help translating PairDrop: https://hosted.weblate.org/engage/pairdrop/" ) ;
2023-07-07 14:58:15 +02:00
} else {
console . warn ( "Missing translation in default language:" , key , attr ) ;
}
2023-07-06 21:29:36 +02:00
}
return Localization . escapeHTML ( translation ) ;
}
static escapeHTML ( unsafeText ) {
let div = document . createElement ( 'div' ) ;
div . innerText = unsafeText ;
return div . innerHTML ;
}
}