mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-27 15:05:38 -04:00
130 lines
4.5 KiB
TypeScript
130 lines
4.5 KiB
TypeScript
import { sendRedirect } from 'h3';
|
|
|
|
import type { LazyDatabase } from '..';
|
|
|
|
import locales from '~~/locale/locales.ts';
|
|
import type { LocaleDescription } from '~~/locale/locales.ts';
|
|
import { getLocale, loadConfig } from '~~/server/data.ts';
|
|
|
|
/**
|
|
* When the current locale represents a macrolanguage, redirects the user to the
|
|
* appropriate sublanguage based on the Accept-Language header.
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
const { node: { req }, path } = event;
|
|
|
|
const locale = getLocale(event);
|
|
const config = await loadConfig(locale);
|
|
|
|
if (
|
|
!config.macrolanguage?.enabled ||
|
|
/(?:langswitch|__nuxt_error)(?:\/|#|\?|$)/.test(path) ||
|
|
path.startsWith('/api/')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const langswitchPath = path !== '/'
|
|
? `/langswitch?return=${encodeURIComponent(path)}`
|
|
: '/langswitch';
|
|
const acceptLanguage = req.headers['accept-language'];
|
|
|
|
// Map individual languages to their locales
|
|
const individualLanguages = config.macrolanguage?.languages || [];
|
|
const subLocales = new Map<string, LocaleDescription>(
|
|
// @ts-expect-error I don't have a clue why this is bugging out
|
|
individualLanguages
|
|
.map((subLanguage) => {
|
|
const foundLocale = locales.find(
|
|
(locale) => locale.code === subLanguage.code,
|
|
)!;
|
|
return foundLocale ? [subLanguage.code, foundLocale] : null;
|
|
})
|
|
.filter(
|
|
// @ts-expect-error TypeScript is confused about the type here
|
|
(locale): locale is [string, LocaleDescription][] => locale !== null,
|
|
),
|
|
);
|
|
|
|
// Only redirect to individual locale automatically if all individual languages
|
|
// have some language codes to match against the Accept-Language header
|
|
const canAutoRedirect = individualLanguages.every(
|
|
(subLanguage) => subLanguage.matches?.some(Boolean),
|
|
);
|
|
|
|
if (!acceptLanguage || !individualLanguages || !canAutoRedirect) {
|
|
return sendRedirect(event, langswitchPath);
|
|
}
|
|
|
|
// Get language codes from the Accept-Language header, ordered by quality
|
|
const requestedLangs = acceptLanguage.split(',').map((language) => {
|
|
const [lang, quality] = language.split(';q=');
|
|
return { lang, quality: quality ? Number.parseFloat(quality) : 1 };
|
|
})
|
|
.sort((a, b) => b.quality - a.quality)
|
|
.map((acceptEntry) => acceptEntry.lang);
|
|
|
|
// If the requested route is for a user profile, get which individual
|
|
// languages the user has a profile for
|
|
if (path.startsWith('/@') && event.context.db) {
|
|
const db = event.context.db as LazyDatabase;
|
|
const username = /^\/@([^/?#]+)/.exec(path)?.[1];
|
|
|
|
if (!username) {
|
|
return sendRedirect(event, langswitchPath);
|
|
}
|
|
|
|
const profiles =
|
|
(await db.all<{ id: string; locale: string; username: string }>(`
|
|
SELECT profiles.id, profiles.locale, users.username
|
|
FROM profiles
|
|
LEFT JOIN users on profiles.userId = users.id
|
|
WHERE users.username = ?
|
|
AND profiles.locale IN (${
|
|
individualLanguages.map(() => '?').join(', ')
|
|
})
|
|
`, [username, ...individualLanguages.map((lang) => lang.code)]));
|
|
|
|
if (profiles.length !== 1) {
|
|
return sendRedirect(event, langswitchPath);
|
|
}
|
|
|
|
const foundLocale = subLocales.get(profiles[0].locale);
|
|
|
|
if (foundLocale) {
|
|
return sendRedirect(event, `${foundLocale.url}${req.url}`);
|
|
}
|
|
|
|
return sendRedirect(event, langswitchPath);
|
|
}
|
|
|
|
// Find the first locale matching an individual language of the
|
|
// macrolanguage
|
|
let localeDescription: LocaleDescription | undefined;
|
|
|
|
// Iterate in order of the Accept-Language header's quality values, to
|
|
// prioritize the user's preferred languages
|
|
for (const tag of requestedLangs) {
|
|
const languageConfig = individualLanguages.find(
|
|
(lang) => lang.matches?.includes(tag),
|
|
);
|
|
|
|
if (!languageConfig) {
|
|
break;
|
|
}
|
|
|
|
const foundLocaleDescription = subLocales.get(languageConfig.code);
|
|
|
|
if (foundLocaleDescription) {
|
|
localeDescription = foundLocaleDescription;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!localeDescription) {
|
|
return sendRedirect(event, langswitchPath);
|
|
}
|
|
|
|
return sendRedirect(event, `${localeDescription.url}${req.url}`);
|
|
});
|