First commit
This commit is contained in:
60
frontend/app/composables/useAppLocale.ts
Normal file
60
frontend/app/composables/useAppLocale.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user