61 lines
2.4 KiB
TypeScript
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) =>
|
|
(({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }) 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 }
|
|
}
|