import deErrors from '@dorfteich/shared/i18n/de/errors.json'; import deLegal from '@dorfteich/shared/i18n/de/legal.json'; import deMails from '@dorfteich/shared/i18n/de/mails.json'; import enErrors from '@dorfteich/shared/i18n/en/errors.json'; import enLegal from '@dorfteich/shared/i18n/en/legal.json'; import enMails from '@dorfteich/shared/i18n/en/mails.json'; import { createInstance, type i18n as I18n } from 'i18next'; /** * Minimal i18next instance for user-facing api texts (error messages, * later e-mails). Kept separate from any request context — callers pass * the language explicitly. */ export const apiI18n: I18n = createInstance(); void apiI18n.init({ resources: { en: { errors: enErrors, mails: enMails, legal: enLegal }, de: { errors: deErrors, mails: deMails, legal: deLegal }, }, fallbackLng: 'en', supportedLngs: ['de', 'en'], interpolation: { escapeValue: false }, }); /** Best supported language for an Accept-Language header value. */ export function negotiateLanguage(acceptLanguage: string | undefined): 'de' | 'en' { if (!acceptLanguage) return 'en'; // First matching language tag wins; quality factors are ignored on // purpose — with two languages the added complexity buys nothing. for (const part of acceptLanguage.split(',')) { const tag = part.trim().toLowerCase(); if (tag.startsWith('de')) return 'de'; if (tag.startsWith('en')) return 'en'; } return 'en'; } /** Localized message for an error code, or undefined when uncatalogued. */ export function translateErrorCode(code: string, language: 'de' | 'en'): string | undefined { const key = `errors:${code}`; return apiI18n.exists(key) ? apiI18n.t(key, { lng: language }) : undefined; }