// Tiny reactive i18n for the SPA (vue-i18n deliberately not pulled in) —
// копия hard-panel: locale ref, t() с {param} и славянскими плюралями,
// плоские словари. Дефолт ставится из backend locale_effective.
import { ref, watch } from 'vue'
import en from './messages/en.js'
import uk from './messages/uk.js'
import ru from './messages/ru.js'
const messages = { en, uk, ru }
// "en-US" -> "en"; отсутствие локали в словарях откатывается на en
export const SUPPORTED_LOCALES = Object.keys(messages)
export const locale = ref('en')
function normalizeTag(tag) {
const prefix = String(tag || '').trim().toLowerCase().split(/[-_]/)[0]
return messages[prefix] ? prefix : null
}
export function setLocale(tag) {
locale.value = normalizeTag(tag) || 'en'
}
watch(locale, (lang) => {
if (typeof document !== 'undefined') {
document.documentElement.lang = lang
document.title = t('app.docTitle')
}
})
function pluralCategory(lang, n) {
// ru/uk: n%10==1 (кроме 11) -> one; 2-4 (кроме 12-14) -> few; иначе many
const mod10 = n % 10
const mod100 = n % 100
if (lang === 'ru' || lang === 'uk') {
if (mod10 === 1 && mod100 !== 11) return 'one'
if (mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14)) return 'few'
return 'many'
}
return n === 1 ? 'one' : 'other'
}
function interpolate(text, params) {
return text.replace(/\{(\w+)\}/g, (match, key) =>
params && key in params ? String(params[key]) : match
)
}
function lookup(slug) {
return messages[locale.value]?.[slug] ?? messages.en[slug]
}
export function t(slug, params) {
let text = lookup(slug)
if (text != null && typeof text === 'object') {
// плюраль {one, few, many, other}; отсутствующая форма падает в other
const n = Number(params?.n ?? 0)
text = text[pluralCategory(locale.value, n)] ?? text.other
}
return text != null ? interpolate(String(text), params) : slug
}