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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
54
web/src/lib/i18n/locales.ts
Normal file
54
web/src/lib/i18n/locales.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user