fix(app,api): kick reasons, blocked counts and locale fallbacks (#3334)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-09 01:40:08 +02:00
1 parent a5c1362f4a
commit 4db13b0375
157 files changed
+3080 -1995

No files matched your search

@@ -14,6 +14,9 @@ const SUPPORTED_LOCALE_SET: ReadonlySet<LocaleCode> = new Set<LocaleCode>(SUPPOR
const LANGUAGE_FALLBACK_BY_LANGUAGE_CODE: Record<string, LocaleCode> = {
en: Locales.EN_US,
es: Locales.ES_ES,
nb: Locales.NO,
nn: Locales.NO,
no: Locales.NO,
pt: Locales.PT_BR,
zh: Locales.ZH_CN,
sv: Locales.SV_SE,
@@ -111,6 +114,7 @@ const LOCALE_METADATA_BY_CODE: Record<LocaleCode, LocaleMetadata> = {
[Locales.NO]: {
displayName: 'Norsk',
flagCode: '1f1f3-1f1f4',
aliases: ['nb', 'nn'],
},
[Locales.PL]: {
displayName: 'Polski',
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {LocaleCode} from '@fluxer/constants/src/Locales';
import {type LocaleCode, Locales} from '@fluxer/constants/src/Locales';
import {
DEFAULT_LOCALE,
findLocaleByLanguagePrefix,
@@ -14,6 +14,9 @@ interface AcceptLanguagePreference {
quality: number;
}
const TRADITIONAL_CHINESE_SUBTAGS: ReadonlySet<string> = new Set(['hant', 'tw', 'hk', 'mo']);
const SIMPLIFIED_CHINESE_SUBTAGS: ReadonlySet<string> = new Set(['hans', 'cn', 'sg']);
export function resolveLocaleFromAcceptLanguageHeader(acceptLanguageHeader: string | null | undefined): LocaleCode {
if (!acceptLanguageHeader) {
return DEFAULT_LOCALE;
@@ -75,7 +78,15 @@ function findExactLocaleMatch(preferences: ReadonlyArray<AcceptLanguagePreferenc
function findLanguageFallbackMatch(preferences: ReadonlyArray<AcceptLanguagePreference>): LocaleCode | null {
for (const {locale} of preferences) {
const languageCode = getLanguageCode(locale);
const [languageCode = '', ...subtags] = normalizeLocaleCode(locale).split('-');
const chineseVariantLocale = findChineseVariantLocale(languageCode, subtags);
if (chineseVariantLocale) {
return chineseVariantLocale;
}
const languageLocale = getLocaleByCode(languageCode);
if (languageLocale) {
return languageLocale;
}
const preferredFallbackLocale = getPreferredLocaleForLanguageCode(languageCode);
if (preferredFallbackLocale) {
return preferredFallbackLocale;
@@ -88,8 +99,17 @@ function findLanguageFallbackMatch(preferences: ReadonlyArray<AcceptLanguagePref
return null;
}
function getLanguageCode(locale: string): string {
const normalizedLocale = normalizeLocaleCode(locale);
const [languageCode] = normalizedLocale.split('-');
return languageCode ?? '';
function findChineseVariantLocale(languageCode: string, subtags: ReadonlyArray<string>): LocaleCode | null {
if (languageCode !== 'zh') {
return null;
}
for (const subtag of subtags) {
if (TRADITIONAL_CHINESE_SUBTAGS.has(subtag)) {
return Locales.ZH_TW;
}
if (SIMPLIFIED_CHINESE_SUBTAGS.has(subtag)) {
return Locales.ZH_CN;
}
}
return null;
}
@@ -62,6 +62,9 @@ const localeLookupCases: Array<LocaleLookupCase> = [
{input: 'pt-br', expected: Locales.PT_BR},
{input: 'sv', expected: Locales.SV_SE},
{input: 'sv-se', expected: Locales.SV_SE},
{input: 'nb', expected: Locales.NO},
{input: 'nn', expected: Locales.NO},
{input: 'NB', expected: Locales.NO},
{input: 'xx', expected: null},
{input: '', expected: null},
{input: 'en-AU', expected: null},
@@ -98,7 +101,31 @@ const acceptLanguageCases: Array<AcceptLanguageCase> = [
{header: 'de;q=0.5, ja;q=0.9, fr;q=0.7', expected: Locales.JA},
{header: 'de, fr;q=0.5', expected: Locales.DE},
{header: 'xx-YY', expected: Locales.EN_US},
{header: 'fr-CA', expected: Locales.EN_US},
{header: 'fr-CA', expected: Locales.FR},
{header: 'de-DE', expected: Locales.DE},
{header: 'de-CH, fr-FR;q=0.8', expected: Locales.DE},
{header: 'sv-FI', expected: Locales.SV_SE},
{header: 'pt-PT', expected: Locales.PT_BR},
{header: 'es-MX', expected: Locales.ES_ES},
{header: 'nb', expected: Locales.NO},
{header: 'nn', expected: Locales.NO},
{header: 'nb-NO', expected: Locales.NO},
{header: 'nn_NO', expected: Locales.NO},
{header: 'no-NO', expected: Locales.NO},
{header: 'nb-NO,nb;q=0.9,en-US;q=0.8,en;q=0.7', expected: Locales.NO},
{header: 'zh-Hant', expected: Locales.ZH_TW},
{header: 'zh-Hant-TW', expected: Locales.ZH_TW},
{header: 'zh-Hant-HK', expected: Locales.ZH_TW},
{header: 'zh-HK', expected: Locales.ZH_TW},
{header: 'zh-MO', expected: Locales.ZH_TW},
{header: 'zh_hant_tw', expected: Locales.ZH_TW},
{header: 'zh-Hans', expected: Locales.ZH_CN},
{header: 'zh-Hans-CN', expected: Locales.ZH_CN},
{header: 'zh-SG', expected: Locales.ZH_CN},
{header: 'zh-Hans-HK', expected: Locales.ZH_CN},
{header: 'zh-Hant-CN', expected: Locales.ZH_TW},
{header: 'zh-HK, zh-Hans;q=0.9', expected: Locales.ZH_TW},
{header: 'zh-HK,zh;q=0.9', expected: Locales.ZH_TW},
{header: ' de , fr ', expected: Locales.DE},
{header: 'zh-TW', expected: Locales.ZH_TW},
{header: 'zh-CN', expected: Locales.ZH_CN},