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 { MeiliSearch } from 'meilisearch'
|
||||||
import { locale } from 'svelte-i18n'
|
import { locale } from 'svelte-i18n'
|
||||||
import type { Actor } from '$lib/models/activitypub/actor'
|
import type { Actor } from '$lib/models/activitypub/actor'
|
||||||
|
import { normalizeLocale } from '$lib/i18n/locales'
|
||||||
|
|
||||||
|
|
||||||
function csrf(allowedPaths: string[]): Handle {
|
function csrf(allowedPaths: string[]): Handle {
|
||||||
@@ -109,12 +110,14 @@ const auth: Handle = async ({ event, resolve }) => {
|
|||||||
}
|
}
|
||||||
event.locals.settings = settings
|
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) {
|
if (lang) {
|
||||||
locale.set(lang)
|
const normalizedLocale = normalizeLocale(lang)
|
||||||
|
locale.set(normalizedLocale)
|
||||||
if (pb.authStore.record) {
|
if (pb.authStore.record) {
|
||||||
pb.authStore.record!.language = lang;
|
pb.authStore.record!.language = normalizedLocale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { getPb } from '$lib/pocketbase';
|
import { getPb } from '$lib/pocketbase';
|
||||||
import { init, register } from 'svelte-i18n';
|
import { init, register } from 'svelte-i18n';
|
||||||
|
import { defaultLocale, LOCALE_LOADERS, normalizeLocale } from './locales';
|
||||||
|
|
||||||
const defaultLocale = 'en'
|
for (const [localeKey, loader] of Object.entries(LOCALE_LOADERS)) {
|
||||||
register('cs', () => import('./locales/cs.json'))
|
register(localeKey, loader)
|
||||||
register('en', () => import('./locales/en.json'))
|
}
|
||||||
register('de', () => import('./locales/de.json'))
|
|
||||||
register('es', () => import('./locales/es.json'))
|
const userLang = browser ? getPb().authStore.record?.language : null;
|
||||||
register('eu', () => import('./locales/eu.json'))
|
const navigatorLang = browser ? window.navigator.language : null;
|
||||||
register('fr', () => import('./locales/fr.json'))
|
|
||||||
register('hu', () => import('./locales/hu.json'))
|
const initial = normalizeLocale(userLang ?? navigatorLang ?? defaultLocale);
|
||||||
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'))
|
|
||||||
|
|
||||||
init({
|
init({
|
||||||
fallbackLocale: defaultLocale,
|
fallbackLocale: defaultLocale,
|
||||||
initialLocale: browser ? getPb().authStore.record?.language ?? window.navigator.language : defaultLocale,
|
initialLocale: initial,
|
||||||
formats: {
|
formats: {
|
||||||
date: {
|
date: {
|
||||||
monthName: { month: 'long' }
|
monthName: { month: 'long' }
|
||||||
@@ -28,4 +22,4 @@ init({
|
|||||||
number: {},
|
number: {},
|
||||||
time: {}
|
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