110 lines
3.5 KiB
JavaScript
110 lines
3.5 KiB
JavaScript
const I18n = {
|
|
currentLang: 'ru',
|
|
translations: {},
|
|
|
|
async init() {
|
|
const savedLang = localStorage.getItem('lang') || 'ru';
|
|
await this.setLang(savedLang);
|
|
},
|
|
|
|
async setLang(lang) {
|
|
try {
|
|
const res = await fetch(`/api/translations/${lang}`);
|
|
if (!res.ok) throw new Error('Failed to load translations');
|
|
this.translations = await res.json();
|
|
this.currentLang = lang;
|
|
localStorage.setItem('lang', lang);
|
|
this.updateUI();
|
|
this.updateNavLabels();
|
|
} catch (err) {
|
|
console.error('I18n error:', err);
|
|
if (lang !== 'ru') {
|
|
await this.setLang('ru');
|
|
}
|
|
}
|
|
},
|
|
|
|
t(key, replacements = {}) {
|
|
let text = this.translations[key] || key;
|
|
Object.entries(replacements).forEach(([k, v]) => {
|
|
text = text.replace(`{${k}}`, v);
|
|
});
|
|
return text;
|
|
},
|
|
|
|
updateUI() {
|
|
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
const key = el.getAttribute('data-i18n');
|
|
const attr = el.getAttribute('data-i18n-attr');
|
|
const text = this.t(key);
|
|
|
|
if (attr) {
|
|
el.setAttribute(attr, text);
|
|
} else {
|
|
el.textContent = text;
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
|
const key = el.getAttribute('data-i18n-placeholder');
|
|
el.placeholder = this.t(key);
|
|
});
|
|
},
|
|
|
|
updateNavLabels() {
|
|
const langBtns = document.querySelectorAll('.lang-btn');
|
|
langBtns.forEach(btn => {
|
|
btn.classList.toggle('active', btn.dataset.lang === this.currentLang);
|
|
});
|
|
},
|
|
|
|
getInitials(name) {
|
|
if (!name) return '?';
|
|
const parts = name.trim().split(/\s+/);
|
|
if (parts.length >= 2) {
|
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
}
|
|
return name.substring(0, 2).toUpperCase();
|
|
},
|
|
|
|
formatDate(dateStr) {
|
|
const date = new Date(dateStr);
|
|
const months = this.currentLang === 'ru'
|
|
? ['Января', 'Февраля', 'Марта', 'Апреля', 'Мая', 'Июня', 'Июля', 'Августа', 'Сентября', 'Октября', 'Ноября', 'Декабря']
|
|
: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
return `${months[date.getMonth()]} ${date.getFullYear()}`;
|
|
},
|
|
|
|
renderStars(count, max = 5) {
|
|
let html = '';
|
|
for (let i = 1; i <= max; i++) {
|
|
if (count >= i) {
|
|
html += '<i class="fas fa-star filled"></i>';
|
|
} else if (count >= i - 0.5) {
|
|
html += '<i class="fas fa-star-half-alt filled"></i>';
|
|
} else {
|
|
html += '<i class="far fa-star"></i>';
|
|
}
|
|
}
|
|
return html;
|
|
},
|
|
|
|
renderStarsStatic(count) {
|
|
let html = '';
|
|
const fullStars = Math.floor(count);
|
|
const hasHalf = count % 1 >= 0.5;
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
if (i < fullStars) {
|
|
html += '<i class="fas fa-star"></i>';
|
|
} else if (i === fullStars && hasHalf) {
|
|
html += '<i class="fas fa-star-half-alt"></i>';
|
|
} else {
|
|
html += '<i class="far fa-star"></i>';
|
|
}
|
|
}
|
|
return html;
|
|
}
|
|
};
|
|
|
|
window.I18n = I18n; |