Files
mathew/frontend/app/composables/useAppLocale.ts
2026-09-30 01:49:35 +02:00

61 lines
2.4 KiB
TypeScript

/* The interface language — the thing the settings popup offers, distinct from
the language an *article* is written in (see utils/languages.ts).
Where the choice lives: a `mathew-locale` cookie, which the server reads so
pages render in the right language from the first draw, and — for a signed-in
person — the account itself, so the choice follows them across devices. */
export const APP_LANGUAGES = [
{ code: 'en', language: 'en-US', name: 'English' },
{ code: 'es', language: 'es-ES', name: 'Español' },
{ code: 'ca', language: 'ca-ES', name: 'Català' },
{ code: 'fr', language: 'fr-FR', name: 'Français' },
{ code: 'de', language: 'de-DE', name: 'Deutsch' },
] as const
const SUPPORTED = new Set<string>(APP_LANGUAGES.map((l) => l.code))
function escapeHtml(text: string): string {
return text.replace(
/[&<>"']/g,
(c) =>
(({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }) as Record<string, string>)[c]!
)
}
export function useAppLocale() {
const { locale, setLocale, t } = useI18n()
/* Translate a message that is drawn with v-html. Every interpolated value
is escaped — tags, slugs and usernames wander into these — while the
messages themselves are ours and carry their own <code>/<span> markup. */
function th(key: string, params?: Record<string, unknown>): string {
if (!params) return t(key)
const escaped: Record<string, unknown> = {}
for (const [k, v] of Object.entries(params)) escaped[k] = typeof v === 'string' ? escapeHtml(v) : v
return t(key, escaped)
}
/* Turn the interface to another language. setLocale loads the messages and
keeps the detection cookie in step; where the person also has an account,
the caller saves it there too (see SettingsModal). */
async function choose(code: string) {
if (!SUPPORTED.has(code) || code === locale.value) return
await setLocale(code)
}
/* Follow an account's own choice — at startup or on signing in, where the
account outranks whatever this device was showing. */
async function applyAccount(code?: string) {
if (!code || !SUPPORTED.has(code) || code === locale.value) return
try {
await setLocale(code)
} catch {
/* the device keeps its own language; nothing else about the session
depends on this */
}
}
return { locale, th, choose, applyAccount }
}