ticket-selector
Version:
A professional stadium seat selection widget with multi-language support
156 lines (125 loc) • 3.75 kB
JavaScript
import { en, az, tr } from './translations.js';
class I18n {
constructor(lang = null) {
this.languages = {
en: en,
az: az,
tr: tr,
};
this.defaultLanguage = 'az';
this.currentLanguage = this.detectLanguage(lang);
this.translations =
this.languages[this.currentLanguage] ||
this.languages[this.defaultLanguage];
}
detectLanguage(providedLang) {
if (providedLang && this.languages[providedLang]) {
return providedLang;
}
const browserLang = this.getBrowserLanguage();
if (this.languages[browserLang]) {
return browserLang;
}
return this.defaultLanguage;
}
// Extract base language from locale codes (en-US -> en, tr-TR -> tr, etc.)
extractLanguageCode(langString) {
if (!langString || typeof langString !== 'string') {
return null;
}
// Handle locale codes like en-US, tr-TR, az-AZ, en_US, etc.
return langString.split(/[-_]/)[0].toLowerCase().trim();
}
getBrowserLanguage() {
// Check HTML lang attribute first (highest priority)
if (typeof document !== 'undefined') {
const htmlLang = document.documentElement.lang;
if (htmlLang) {
const baseLang = this.extractLanguageCode(htmlLang);
if (baseLang && this.languages[baseLang]) {
return baseLang;
}
}
}
// Check browser language (fallback)
if (typeof navigator !== 'undefined') {
const navigatorLang = navigator.language || navigator.userLanguage;
if (navigatorLang) {
const baseLang = this.extractLanguageCode(navigatorLang);
if (baseLang && this.languages[baseLang]) {
return baseLang;
}
}
}
return this.defaultLanguage;
}
t(keyPath, params = {}, count = null) {
const keys = keyPath.split('.');
let value = this.translations;
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key];
} else {
value = this.getFallbackTranslation(keyPath);
break;
}
}
if (value && typeof value === 'object' && count !== null) {
value = this.getPlural(value, count);
}
if (typeof value !== 'string') {
console.warn(`Translation key not found: ${keyPath}`);
return keyPath;
}
return this.replaceParams(value, params, count);
}
getFallbackTranslation(keyPath) {
const keys = keyPath.split('.');
let value = this.languages[this.defaultLanguage];
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key];
} else {
return keyPath;
}
}
return value;
}
getPlural(pluralObject, count) {
const num = parseInt(count);
if (num === 0 && pluralObject.zero) {
return pluralObject.zero;
} else if (num === 1 && pluralObject.one) {
return pluralObject.one;
} else if (pluralObject.other) {
return pluralObject.other;
}
return pluralObject.other || pluralObject.one || pluralObject.zero || '';
}
replaceParams(text, params, count) {
let result = text;
if (count !== null) {
params.count = count;
}
Object.keys(params).forEach((key) => {
const placeholder = `{${key}}`;
result = result.replace(new RegExp(placeholder, 'g'), params[key]);
});
return result;
}
setLanguage(lang) {
if (this.languages[lang]) {
this.currentLanguage = lang;
this.translations = this.languages[lang];
return true;
}
return false;
}
getCurrentLanguage() {
return this.currentLanguage;
}
getAvailableLanguages() {
return Object.keys(this.languages);
}
}
export default I18n;