Files
ab a47c4a0398
Checks / test (push) Failing after 12m20s
Init
2026-09-24 14:35:05 +03:00

36 lines
2.4 KiB
JavaScript

// The complete, versioned catalog is embedded before this script by the server.
// Only textContent/Alpine x-text is used: parameters are never interpreted as HTML.
const I18n = (() => {
const supported=['en','ru','fr'];
const detect=(languages)=>{for(const raw of languages || []){const base=String(raw).toLowerCase().split(/[-_]/)[0];if(supported.includes(base))return base;}return 'en';};
const browser=detect(navigator.languages?.length?navigator.languages:[navigator.language]);
const locale=()=>globalThis.Alpine?.store('locale')?.language || browser;
const translate=(key,params={},language=locale())=>{
const entry=MEGURI_MESSAGES[key];
if(!entry)throw new Error('Missing localization key: '+key);
const pattern=entry[language] || entry.en;
return pattern.replace(/\{([\w]+)\}/g,(match,name)=>Object.hasOwn(params,name)?String(params[name]):match);
};
const exact=new Map();
for(const [key,entry]of Object.entries(MEGURI_MESSAGES))for(const text of Object.values(entry))if(text)exact.set(text,key);
const patterns=[];
for(const [key,entry]of Object.entries(MEGURI_MESSAGES)){
if(!entry.en.includes('{'))continue;
const names=[];let from=0,expression='^';
for(const match of entry.en.matchAll(/\{(\w+)\}/g)){expression+=entry.en.slice(from,match.index).replace(/[.*+?^${}()|[\]\\]/g,'\\$&')+'([\\s\\S]*?)';names.push(match[1]);from=match.index+match[0].length;}
if(names.length){expression+=entry.en.slice(from).replace(/[.*+?^${}()|[\]\\]/g,'\\$&')+'$';patterns.push({key,names,re:new RegExp(expression)});}
}
const diagnostic=(text)=>{
if(text==null)return '';
text=String(text);const key=exact.get(text);if(key)return translate(key);
for(const item of patterns){const match=item.re.exec(text);if(match)return translate(item.key,Object.fromEntries(item.names.map((name,i)=>[name,match[i+1]])));}
return text; // User values and third-party diagnostics are opaque, never guessed or rewritten.
};
document.addEventListener('alpine:init',()=>{
Alpine.store('locale',{language:browser});
Alpine.effect(()=>{const language=locale();document.documentElement.lang=language;const title=document.querySelector('[data-title-key]');if(title)document.title=translate(title.dataset.titleKey);});
});
return Object.freeze({supported,browser,detect,locale,translate,diagnostic});
})();
function tr(key,params){return I18n.translate(key,params);}