fix locale web error logs (#656)

* fix locale web error logs

* refactor(i18n): derive supported locales dynamically from locale files
This commit is contained in:
slothful-vassal
2026-02-17 18:30:10 +01:00
committed by GitHub
parent 0c715ad5ba
commit 0368892014
3 changed files with 71 additions and 20 deletions

View File

@@ -9,6 +9,7 @@ import { sequence } from '@sveltejs/kit/hooks'
import { MeiliSearch } from 'meilisearch'
import { locale } from 'svelte-i18n'
import type { Actor } from '$lib/models/activitypub/actor'
import { normalizeLocale } from '$lib/i18n/locales'
function csrf(allowedPaths: string[]): Handle {
@@ -109,12 +110,14 @@ const auth: Handle = async ({ event, resolve }) => {
}
event.locals.settings = settings
const lang = settings?.language ?? event.request.headers.get('accept-language')?.split(',')[0]
const langHeader = event.request.headers.get('accept-language')?.split(',')[0]
const lang = settings?.language ?? langHeader
if (lang) {
locale.set(lang)
const normalizedLocale = normalizeLocale(lang)
locale.set(normalizedLocale)
if (pb.authStore.record) {
pb.authStore.record!.language = lang;
pb.authStore.record!.language = normalizedLocale;
}
}

View File

@@ -1,26 +1,20 @@
import { browser } from '$app/environment';
import { getPb } from '$lib/pocketbase';
import { init, register } from 'svelte-i18n';
import { defaultLocale, LOCALE_LOADERS, normalizeLocale } from './locales';
const defaultLocale = 'en'
register('cs', () => import('./locales/cs.json'))
register('en', () => import('./locales/en.json'))
register('de', () => import('./locales/de.json'))
register('es', () => import('./locales/es.json'))
register('eu', () => import('./locales/eu.json'))
register('fr', () => import('./locales/fr.json'))
register('hu', () => import('./locales/hu.json'))
register('it', () => import('./locales/it.json'))
register('nl', () => import('./locales/nl.json'))
register('no', () => import('./locales/no.json'))
register('pl', () => import('./locales/pl.json'))
register('pt', () => import('./locales/pt.json'))
register('ru', () => import('./locales/ru.json'))
register('zh', () => import('./locales/zh.json'))
for (const [localeKey, loader] of Object.entries(LOCALE_LOADERS)) {
register(localeKey, loader)
}
const userLang = browser ? getPb().authStore.record?.language : null;
const navigatorLang = browser ? window.navigator.language : null;
const initial = normalizeLocale(userLang ?? navigatorLang ?? defaultLocale);
init({
fallbackLocale: defaultLocale,
initialLocale: browser ? getPb().authStore.record?.language ?? window.navigator.language : defaultLocale,
initialLocale: initial,
formats: {
date: {
monthName: { month: 'long' }
@@ -28,4 +22,4 @@ init({
number: {},
time: {}
},
})
})

View File

@@ -0,0 +1,54 @@
type LocaleMessages = Record<string, unknown>;
type LocaleModule = { default: LocaleMessages };
type LocaleLoader = () => Promise<LocaleMessages>;
const localeModules = import.meta.glob<LocaleModule>("./locales/*.json");
const localeEntries = Object.entries(localeModules)
.map(([path, loadModule]) => {
const match = path.match(/\/([a-z]{2,5}(?:-[a-z]{2,4})?)\.json$/i);
if (!match) return null;
const locale = match[1].toLowerCase();
const loader: LocaleLoader = async () => (await loadModule()).default;
return [locale, loader] as const;
})
.filter((entry): entry is readonly [string, LocaleLoader] => entry !== null);
export const LOCALE_LOADERS: Record<string, LocaleLoader> = Object.fromEntries(
localeEntries,
);
export const SUPPORTED_LOCALES = Object.keys(LOCALE_LOADERS).sort();
export type SupportedLocale = string;
export const defaultLocale: SupportedLocale = SUPPORTED_LOCALES.includes("en")
? "en"
: (SUPPORTED_LOCALES[0] ?? "en");
export function normalizeLocale(raw: string | null | undefined): SupportedLocale {
if (!raw) return defaultLocale;
// remove spaces and any ";q=0.7" part
let tag = raw.trim().split(";")[0];
// Turn "de_CH" into "de-CH"
tag = tag.replace("_", "-");
// Lowercase language, uppercase region if present
const parts = tag.split("-");
if (parts.length === 2) {
tag = `${parts[0].toLowerCase()}-${parts[1].toUpperCase()}`;
} else {
tag = tag.toLowerCase();
}
// Map "de-CH" -> "de", "en-US" -> "en", etc.
const base = tag.split("-")[0];
if (SUPPORTED_LOCALES.includes(base)) {
return base;
}
return defaultLocale;
}