Files
fx-test/fluxer/packages/locale/src/catalog/LocaleCatalog.tsx
Vish 3b9d759b4b feat: add fluxer upstream source and self-hosting documentation
- Clone of github.com/fluxerapp/fluxer (official upstream)
- SELF_HOSTING.md: full VM rebuild procedure, architecture overview,
  service reference, step-by-step setup, troubleshooting, seattle reference
- dev/.env.example: all env vars with secrets redacted and generation instructions
- dev/livekit.yaml: LiveKit config template with placeholder keys
- fluxer-seattle/: existing seattle deployment setup scripts
2026-03-13 00:55:14 -07:00

264 lines
6.1 KiB
TypeScript

/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {AllLocales, type LocaleCode, Locales} from '@fluxer/constants/src/Locales';
interface LocaleMetadata {
displayName: string;
flagCode: string;
aliases?: ReadonlyArray<string>;
}
export const DEFAULT_LOCALE: LocaleCode = Locales.EN_US;
export const SUPPORTED_LOCALES: ReadonlyArray<LocaleCode> = AllLocales;
const SUPPORTED_LOCALE_SET: ReadonlySet<LocaleCode> = new Set<LocaleCode>(SUPPORTED_LOCALES);
const LANGUAGE_FALLBACK_BY_LANGUAGE_CODE: Record<string, LocaleCode> = {
en: Locales.EN_US,
es: Locales.ES_ES,
pt: Locales.PT_BR,
zh: Locales.ZH_CN,
sv: Locales.SV_SE,
};
const LOCALE_METADATA_BY_CODE: Record<LocaleCode, LocaleMetadata> = {
[Locales.AR]: {
displayName: 'العربية',
flagCode: '1f1f8-1f1e6',
},
[Locales.BG]: {
displayName: 'Български',
flagCode: '1f1e7-1f1ec',
},
[Locales.CS]: {
displayName: 'Čeština',
flagCode: '1f1e8-1f1ff',
},
[Locales.DA]: {
displayName: 'Dansk',
flagCode: '1f1e9-1f1f0',
},
[Locales.DE]: {
displayName: 'Deutsch',
flagCode: '1f1e9-1f1ea',
},
[Locales.EL]: {
displayName: 'Ελληνικά',
flagCode: '1f1ec-1f1f7',
},
[Locales.EN_GB]: {
displayName: 'English',
flagCode: '1f1ec-1f1e7',
},
[Locales.EN_US]: {
displayName: 'English (US)',
flagCode: '1f1fa-1f1f8',
aliases: ['en'],
},
[Locales.ES_ES]: {
displayName: 'Español (España)',
flagCode: '1f1ea-1f1f8',
},
[Locales.ES_419]: {
displayName: 'Español (Latinoamérica)',
flagCode: '1f30e',
},
[Locales.FI]: {
displayName: 'Suomi',
flagCode: '1f1eb-1f1ee',
},
[Locales.FR]: {
displayName: 'Français',
flagCode: '1f1eb-1f1f7',
},
[Locales.HE]: {
displayName: 'עברית',
flagCode: '1f1ee-1f1f1',
},
[Locales.HI]: {
displayName: 'हिन्दी',
flagCode: '1f1ee-1f1f3',
},
[Locales.HR]: {
displayName: 'Hrvatski',
flagCode: '1f1ed-1f1f7',
},
[Locales.HU]: {
displayName: 'Magyar',
flagCode: '1f1ed-1f1fa',
},
[Locales.ID]: {
displayName: 'Bahasa Indonesia',
flagCode: '1f1ee-1f1e9',
},
[Locales.IT]: {
displayName: 'Italiano',
flagCode: '1f1ee-1f1f9',
},
[Locales.JA]: {
displayName: '日本語',
flagCode: '1f1ef-1f1f5',
},
[Locales.KO]: {
displayName: '한국어',
flagCode: '1f1f0-1f1f7',
},
[Locales.LT]: {
displayName: 'Lietuvių',
flagCode: '1f1f1-1f1f9',
},
[Locales.NL]: {
displayName: 'Nederlands',
flagCode: '1f1f3-1f1f1',
},
[Locales.NO]: {
displayName: 'Norsk',
flagCode: '1f1f3-1f1f4',
},
[Locales.PL]: {
displayName: 'Polski',
flagCode: '1f1f5-1f1f1',
},
[Locales.PT_BR]: {
displayName: 'Português (Brasil)',
flagCode: '1f1e7-1f1f7',
},
[Locales.RO]: {
displayName: 'Română',
flagCode: '1f1f7-1f1f4',
},
[Locales.RU]: {
displayName: 'Русский',
flagCode: '1f1f7-1f1fa',
},
[Locales.SV_SE]: {
displayName: 'Svenska',
flagCode: '1f1f8-1f1ea',
aliases: ['sv'],
},
[Locales.TH]: {
displayName: 'ไทย',
flagCode: '1f1f9-1f1ed',
},
[Locales.TR]: {
displayName: 'Türkçe',
flagCode: '1f1f9-1f1f7',
},
[Locales.UK]: {
displayName: 'Українська',
flagCode: '1f1fa-1f1e6',
},
[Locales.VI]: {
displayName: 'Tiếng Việt',
flagCode: '1f1fb-1f1f3',
},
[Locales.ZH_CN]: {
displayName: '简体中文',
flagCode: '1f1e8-1f1f3',
},
[Locales.ZH_TW]: {
displayName: '繁體中文',
flagCode: '1f1f9-1f1fc',
},
};
const NORMALIZED_LOCALE_TO_CODE = createNormalizedLocaleLookup();
export function normalizeLocaleCode(code: string): string {
return code.trim().replace(/_/g, '-').toLowerCase();
}
export function isSupportedLocale(locale: LocaleCode): boolean {
return SUPPORTED_LOCALE_SET.has(locale);
}
export function getLocaleDisplayName(locale: LocaleCode): string {
return LOCALE_METADATA_BY_CODE[locale].displayName;
}
export function getLocaleFlagCode(locale: LocaleCode): string {
return LOCALE_METADATA_BY_CODE[locale].flagCode;
}
export function getLocaleByCode(code: string): LocaleCode | null {
const normalizedCode = normalizeLocaleCode(code);
if (!normalizedCode) {
return null;
}
const locale = NORMALIZED_LOCALE_TO_CODE.get(normalizedCode);
return locale ?? null;
}
export function getLocaleLanguageCode(locale: LocaleCode): string {
return locale.split('-')[0];
}
export function getPreferredLocaleForLanguageCode(languageCode: string): LocaleCode | null {
const normalizedLanguageCode = normalizeLocaleCode(languageCode);
if (!normalizedLanguageCode) {
return null;
}
const preferredLocale = LANGUAGE_FALLBACK_BY_LANGUAGE_CODE[normalizedLanguageCode];
if (!preferredLocale) {
return null;
}
if (!isSupportedLocale(preferredLocale)) {
return null;
}
return preferredLocale;
}
export function findLocaleByLanguagePrefix(languageCode: string): LocaleCode | null {
const normalizedLanguageCode = normalizeLocaleCode(languageCode);
if (!normalizedLanguageCode) {
return null;
}
const languagePrefix = `${normalizedLanguageCode}-`;
for (const locale of SUPPORTED_LOCALES) {
if (locale.toLowerCase().startsWith(languagePrefix)) {
return locale;
}
}
return null;
}
function createNormalizedLocaleLookup(): ReadonlyMap<string, LocaleCode> {
const lookup = new Map<string, LocaleCode>();
for (const locale of SUPPORTED_LOCALES) {
lookup.set(normalizeLocaleCode(locale), locale);
}
for (const locale of SUPPORTED_LOCALES) {
const aliases = LOCALE_METADATA_BY_CODE[locale].aliases ?? [];
for (const alias of aliases) {
lookup.set(normalizeLocaleCode(alias), locale);
}
}
return lookup;
}