mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-08-01 17:59:40 -04:00
Update identity generator language list references (#776)
This commit is contained in:
@@ -6,7 +6,8 @@ import { useDb } from '@/entrypoints/popup/context/DbContext';
|
||||
import { useLoading } from '@/entrypoints/popup/context/LoadingContext';
|
||||
import { useVaultMutate } from '@/entrypoints/popup/hooks/useVaultMutate';
|
||||
|
||||
import { getAvailableLanguages, getAvailableAgeRanges, ILanguageOption, IAgeRangeOption } from '@/utils/dist/core/identity-generator';
|
||||
import { getAvailableLanguages, getAvailableAgeRanges, IAgeRangeOption } from '@/utils/dist/core/identity-generator';
|
||||
import { getLanguageInfo } from '@/utils/dist/core/models/defaults';
|
||||
|
||||
/**
|
||||
* Identity Generator Settings page component.
|
||||
@@ -21,7 +22,7 @@ const IdentityGeneratorSettings: React.FC = () => {
|
||||
const [language, setLanguage] = useState<string>('en');
|
||||
const [gender, setGender] = useState<string>('random');
|
||||
const [ageRange, setAgeRange] = useState<string>('random');
|
||||
const [languageOptions, setLanguageOptions] = useState<ILanguageOption[]>([]);
|
||||
const [languageOptions, setLanguageOptions] = useState<string[]>([]);
|
||||
const [ageRangeOptions, setAgeRangeOptions] = useState<IAgeRangeOption[]>([]);
|
||||
|
||||
const GENDER_OPTIONS = [
|
||||
@@ -34,7 +35,9 @@ const IdentityGeneratorSettings: React.FC = () => {
|
||||
* Load settings on mount.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const languages = getAvailableLanguages();
|
||||
const languages = getAvailableLanguages().sort(
|
||||
(a, b) => getLanguageInfo(a).label.localeCompare(getLanguageInfo(b).label)
|
||||
);
|
||||
const ranges = getAvailableAgeRanges();
|
||||
setLanguageOptions(languages);
|
||||
setAgeRangeOptions(ranges);
|
||||
@@ -109,11 +112,14 @@ const IdentityGeneratorSettings: React.FC = () => {
|
||||
onChange={(e) => handleLanguageChange(e.target.value)}
|
||||
className="w-full px-3 py-2 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md text-gray-900 dark:text-white focus:ring-primary-500 focus:border-primary-500"
|
||||
>
|
||||
{languageOptions.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.flag} {option.label}
|
||||
</option>
|
||||
))}
|
||||
{languageOptions.map((code) => {
|
||||
const info = getLanguageInfo(code);
|
||||
return (
|
||||
<option key={code} value={code}>
|
||||
{info.flag} {info.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Add new languages here to make them available throughout the application
|
||||
*/
|
||||
|
||||
import { getLanguageInfo } from '@/utils/dist/core/models/defaults';
|
||||
|
||||
import daTranslations from './locales/da.json';
|
||||
import deTranslations from './locales/de.json';
|
||||
import enTranslations from './locales/en.json';
|
||||
@@ -76,121 +78,13 @@ export const LANGUAGE_RESOURCES = {
|
||||
};
|
||||
|
||||
/**
|
||||
* List of all available languages with their code, name, native name and flag.
|
||||
* When adding a new language, add the language to the map here.
|
||||
* List of all available UI languages with their code, native name and flag.
|
||||
*/
|
||||
export const AVAILABLE_LANGUAGES: ILanguageConfig[] = [
|
||||
{
|
||||
code: 'da',
|
||||
name: 'Danish',
|
||||
nativeName: 'Dansk',
|
||||
flag: '🇩🇰'
|
||||
},
|
||||
{
|
||||
code: 'de',
|
||||
name: 'German',
|
||||
nativeName: 'Deutsch',
|
||||
flag: '🇩🇪'
|
||||
},
|
||||
{
|
||||
code: 'en',
|
||||
name: 'English',
|
||||
nativeName: 'English',
|
||||
flag: '🇺🇸'
|
||||
},
|
||||
{
|
||||
code: 'es',
|
||||
name: 'Spanish',
|
||||
nativeName: 'Español',
|
||||
flag: '🇪🇸'
|
||||
},
|
||||
{
|
||||
code: 'fi',
|
||||
name: 'Finnish',
|
||||
nativeName: 'Suomi',
|
||||
flag: '🇫🇮'
|
||||
},
|
||||
{
|
||||
code: 'fr',
|
||||
name: 'French',
|
||||
nativeName: 'Français',
|
||||
flag: '🇫🇷'
|
||||
},
|
||||
{
|
||||
code: 'he',
|
||||
name: 'Hebrew',
|
||||
nativeName: 'עברית',
|
||||
flag: '🇮🇱'
|
||||
},
|
||||
{
|
||||
code: 'it',
|
||||
name: 'Italian',
|
||||
nativeName: 'Italiano',
|
||||
flag: '🇮🇹'
|
||||
},
|
||||
{
|
||||
code: 'nl',
|
||||
name: 'Dutch',
|
||||
nativeName: 'Nederlands',
|
||||
flag: '🇳🇱'
|
||||
},
|
||||
{
|
||||
code: 'pl',
|
||||
name: 'Polish',
|
||||
nativeName: 'Polski',
|
||||
flag: '🇵🇱'
|
||||
},
|
||||
{
|
||||
code: 'pt',
|
||||
name: 'Portuguese Brazilian',
|
||||
nativeName: 'Português Brasileiro',
|
||||
flag: '🇧🇷'
|
||||
},
|
||||
{
|
||||
code: 'ro',
|
||||
name: 'Romanian',
|
||||
nativeName: 'Română',
|
||||
flag: '🇷🇴'
|
||||
},
|
||||
{
|
||||
code: 'ru',
|
||||
name: 'Russian',
|
||||
nativeName: 'Русский',
|
||||
flag: '🇷🇺'
|
||||
},
|
||||
{
|
||||
code: 'sv',
|
||||
name: 'Swedish',
|
||||
nativeName: 'Svenska',
|
||||
flag: '🇸🇪'
|
||||
},
|
||||
{
|
||||
code: 'uk',
|
||||
name: 'Ukrainian',
|
||||
nativeName: 'Українська',
|
||||
flag: '🇺🇦'
|
||||
},
|
||||
{
|
||||
code: 'zh',
|
||||
name: 'Chinese',
|
||||
nativeName: '简体中文',
|
||||
flag: '🇨🇳'
|
||||
},
|
||||
/*
|
||||
* {
|
||||
* code: 'es',
|
||||
* name: 'Spanish',
|
||||
* nativeName: 'Español',
|
||||
* flag: '🇪🇸'
|
||||
* },
|
||||
* {
|
||||
* code: 'fr',
|
||||
* name: 'French',
|
||||
* nativeName: 'Français',
|
||||
* flag: '🇫🇷'
|
||||
* },
|
||||
*/
|
||||
];
|
||||
export const AVAILABLE_LANGUAGES: ILanguageConfig[] =
|
||||
(Object.keys(LANGUAGE_RESOURCES) as Array<keyof typeof LANGUAGE_RESOURCES>).map((code) => {
|
||||
const info = getLanguageInfo(code);
|
||||
return { code, nativeName: info.label, flag: info.flag };
|
||||
});
|
||||
|
||||
/**
|
||||
* Default language that is used when no language is set in the browser or when a localized string is not found for the current language.
|
||||
@@ -201,7 +95,6 @@ export const LANGUAGE_CODES = AVAILABLE_LANGUAGES.map(lang => lang.code);
|
||||
|
||||
export interface ILanguageConfig {
|
||||
code: string;
|
||||
name: string;
|
||||
nativeName: string;
|
||||
flag?: string;
|
||||
}
|
||||
|
||||
@@ -395,35 +395,10 @@ declare function getAvailableAgeRanges(): IAgeRangeOption[];
|
||||
declare function convertAgeRangeToBirthdateOptions(ageRange: string): IBirthdateOptions | null;
|
||||
|
||||
/**
|
||||
* Represents a language option for identity generation.
|
||||
* Gets all available language codes for identity generation.
|
||||
* @returns Array of ISO language codes (e.g. ["da", "de", "en", ...]).
|
||||
*/
|
||||
interface ILanguageOption {
|
||||
/**
|
||||
* The language code (e.g., "en", "nl", "de")
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* The display label in the native language (e.g., "English", "Nederlands", "Deutsch")
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The flag emoji for the language (e.g., "🇬🇧", "🇳🇱", "🇩🇪")
|
||||
*/
|
||||
flag: string;
|
||||
/**
|
||||
* Alternative language codes that map to this identity generator language.
|
||||
* Used for matching UI locale codes to identity generator languages.
|
||||
* For example, "en-US", "en-GB", "en-CA" all map to "en"
|
||||
*/
|
||||
alternativeCodes?: string[];
|
||||
}
|
||||
/**
|
||||
* Gets all available languages for identity generation.
|
||||
* Display labels are in the native language, with optional flag emoji that clients can choose to display.
|
||||
* Languages are sorted alphabetically by label for consistent display across all platforms.
|
||||
* @returns Array of language options sorted alphabetically by label
|
||||
*/
|
||||
declare function getAvailableLanguages(): ILanguageOption[];
|
||||
declare function getAvailableLanguages(): string[];
|
||||
/**
|
||||
* Maps a UI language code to an identity generator language code.
|
||||
* If no explicit match is found, returns null to indicate no preference.
|
||||
@@ -435,7 +410,7 @@ declare function getAvailableLanguages(): ILanguageOption[];
|
||||
* mapUiLanguageToIdentityLanguage("en-US") // returns "en"
|
||||
* mapUiLanguageToIdentityLanguage("nl") // returns "nl"
|
||||
* mapUiLanguageToIdentityLanguage("de-CH") // returns "de"
|
||||
* mapUiLanguageToIdentityLanguage("fr") // returns null (no French identity generator)
|
||||
* mapUiLanguageToIdentityLanguage("ja") // returns null (no Japanese identity generator)
|
||||
*/
|
||||
declare function mapUiLanguageToIdentityLanguage(uiLanguageCode: string | null | undefined): string | null;
|
||||
|
||||
@@ -454,4 +429,4 @@ declare const CreateIdentityGenerator: (language: string) => IdentityGenerator;
|
||||
*/
|
||||
declare const CreateUsernameEmailGenerator: () => UsernameEmailGenerator;
|
||||
|
||||
export { CreateIdentityGenerator, CreateUsernameEmailGenerator, Gender, type IAgeRangeOption, type IBirthdateOptions, type IDecadeFirstnames, type IIdentityGenerator, type ILanguageOption, type Identity, IdentityGenerator, IdentityGeneratorDa, IdentityGeneratorDe, IdentityGeneratorEn, IdentityGeneratorEs, IdentityGeneratorFa, IdentityGeneratorFr, IdentityGeneratorIt, IdentityGeneratorNl, IdentityGeneratorRo, IdentityGeneratorSv, IdentityGeneratorUr, IdentityHelperUtils, UsernameEmailGenerator, convertAgeRangeToBirthdateOptions, getAvailableAgeRanges, getAvailableLanguages, mapUiLanguageToIdentityLanguage };
|
||||
export { CreateIdentityGenerator, CreateUsernameEmailGenerator, Gender, type IAgeRangeOption, type IBirthdateOptions, type IDecadeFirstnames, type IIdentityGenerator, type Identity, IdentityGenerator, IdentityGeneratorDa, IdentityGeneratorDe, IdentityGeneratorEn, IdentityGeneratorEs, IdentityGeneratorFa, IdentityGeneratorFr, IdentityGeneratorIt, IdentityGeneratorNl, IdentityGeneratorRo, IdentityGeneratorSv, IdentityGeneratorUr, IdentityHelperUtils, UsernameEmailGenerator, convertAgeRangeToBirthdateOptions, getAvailableAgeRanges, getAvailableLanguages, mapUiLanguageToIdentityLanguage };
|
||||
|
||||
@@ -11763,95 +11763,39 @@ function convertAgeRangeToBirthdateOptions(ageRange) {
|
||||
}
|
||||
|
||||
// src/utils/LanguageProvider.ts
|
||||
var LANGUAGE_DEFS = [
|
||||
{ value: "da", alternativeCodes: ["da-DK"] },
|
||||
{ value: "de", alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"] },
|
||||
{ value: "en", alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"] },
|
||||
{ value: "es", alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"] },
|
||||
{ value: "fr", alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"] },
|
||||
{ value: "it", alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"] },
|
||||
{ value: "nl", alternativeCodes: ["nl-NL", "nl-BE"] },
|
||||
{ value: "ro", alternativeCodes: ["ro-RO", "ro-MD"] },
|
||||
{ value: "sv", alternativeCodes: ["sv-SE", "sv-FI"] },
|
||||
{ value: "ur", alternativeCodes: ["ur-PK", "ur-IN"] },
|
||||
{ value: "fa", alternativeCodes: ["fa-IR", "fa-AF"] }
|
||||
];
|
||||
function getAvailableLanguages() {
|
||||
const languages = [
|
||||
{
|
||||
value: "da",
|
||||
label: "Dansk",
|
||||
flag: "\u{1F1E9}\u{1F1F0}",
|
||||
alternativeCodes: ["da-DK"]
|
||||
},
|
||||
{
|
||||
value: "de",
|
||||
label: "Deutsch",
|
||||
flag: "\u{1F1E9}\u{1F1EA}",
|
||||
alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"]
|
||||
},
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
flag: "\u{1F1EC}\u{1F1E7}",
|
||||
alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"]
|
||||
},
|
||||
{
|
||||
value: "es",
|
||||
label: "Espa\xF1ol",
|
||||
flag: "\u{1F1EA}\u{1F1F8}",
|
||||
alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"]
|
||||
},
|
||||
{
|
||||
value: "fr",
|
||||
label: "Fran\xE7ais",
|
||||
flag: "\u{1F1EB}\u{1F1F7}",
|
||||
alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"]
|
||||
},
|
||||
{
|
||||
value: "it",
|
||||
label: "Italiano",
|
||||
flag: "\u{1F1EE}\u{1F1F9}",
|
||||
alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"]
|
||||
},
|
||||
{
|
||||
value: "nl",
|
||||
label: "Nederlands",
|
||||
flag: "\u{1F1F3}\u{1F1F1}",
|
||||
alternativeCodes: ["nl-NL", "nl-BE"]
|
||||
},
|
||||
{
|
||||
value: "ro",
|
||||
label: "Rom\xE2n\u0103",
|
||||
flag: "\u{1F1F7}\u{1F1F4}",
|
||||
alternativeCodes: ["ro-RO", "ro-MD"]
|
||||
},
|
||||
{
|
||||
value: "sv",
|
||||
label: "Svenska",
|
||||
flag: "\u{1F1F8}\u{1F1EA}",
|
||||
alternativeCodes: ["sv-SE", "sv-FI"]
|
||||
},
|
||||
{
|
||||
value: "ur",
|
||||
label: "\u0627\u0631\u062F\u0648",
|
||||
flag: "\u{1F1F5}\u{1F1F0}",
|
||||
alternativeCodes: ["ur-PK", "ur-IN"]
|
||||
},
|
||||
{
|
||||
value: "fa",
|
||||
label: "\u0641\u0627\u0631\u0633\u06CC",
|
||||
flag: "\u{1F1EE}\u{1F1F7}",
|
||||
alternativeCodes: ["fa-IR", "fa-AF"]
|
||||
}
|
||||
];
|
||||
return languages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return LANGUAGE_DEFS.map((lang) => lang.value);
|
||||
}
|
||||
function mapUiLanguageToIdentityLanguage(uiLanguageCode) {
|
||||
if (!uiLanguageCode) {
|
||||
return null;
|
||||
}
|
||||
const normalizedCode = uiLanguageCode.toLowerCase();
|
||||
const availableLanguages = getAvailableLanguages();
|
||||
const exactMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
const exactMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
if (exactMatch) {
|
||||
return exactMatch.value;
|
||||
}
|
||||
const alternativeMatch = availableLanguages.find(
|
||||
const alternativeMatch = LANGUAGE_DEFS.find(
|
||||
(lang) => lang.alternativeCodes?.some((code) => code.toLowerCase() === normalizedCode)
|
||||
);
|
||||
if (alternativeMatch) {
|
||||
return alternativeMatch.value;
|
||||
}
|
||||
const baseCode = normalizedCode.split("-")[0];
|
||||
const baseMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
const baseMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
if (baseMatch) {
|
||||
return baseMatch.value;
|
||||
}
|
||||
|
||||
@@ -11718,95 +11718,39 @@ function convertAgeRangeToBirthdateOptions(ageRange) {
|
||||
}
|
||||
|
||||
// src/utils/LanguageProvider.ts
|
||||
var LANGUAGE_DEFS = [
|
||||
{ value: "da", alternativeCodes: ["da-DK"] },
|
||||
{ value: "de", alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"] },
|
||||
{ value: "en", alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"] },
|
||||
{ value: "es", alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"] },
|
||||
{ value: "fr", alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"] },
|
||||
{ value: "it", alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"] },
|
||||
{ value: "nl", alternativeCodes: ["nl-NL", "nl-BE"] },
|
||||
{ value: "ro", alternativeCodes: ["ro-RO", "ro-MD"] },
|
||||
{ value: "sv", alternativeCodes: ["sv-SE", "sv-FI"] },
|
||||
{ value: "ur", alternativeCodes: ["ur-PK", "ur-IN"] },
|
||||
{ value: "fa", alternativeCodes: ["fa-IR", "fa-AF"] }
|
||||
];
|
||||
function getAvailableLanguages() {
|
||||
const languages = [
|
||||
{
|
||||
value: "da",
|
||||
label: "Dansk",
|
||||
flag: "\u{1F1E9}\u{1F1F0}",
|
||||
alternativeCodes: ["da-DK"]
|
||||
},
|
||||
{
|
||||
value: "de",
|
||||
label: "Deutsch",
|
||||
flag: "\u{1F1E9}\u{1F1EA}",
|
||||
alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"]
|
||||
},
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
flag: "\u{1F1EC}\u{1F1E7}",
|
||||
alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"]
|
||||
},
|
||||
{
|
||||
value: "es",
|
||||
label: "Espa\xF1ol",
|
||||
flag: "\u{1F1EA}\u{1F1F8}",
|
||||
alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"]
|
||||
},
|
||||
{
|
||||
value: "fr",
|
||||
label: "Fran\xE7ais",
|
||||
flag: "\u{1F1EB}\u{1F1F7}",
|
||||
alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"]
|
||||
},
|
||||
{
|
||||
value: "it",
|
||||
label: "Italiano",
|
||||
flag: "\u{1F1EE}\u{1F1F9}",
|
||||
alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"]
|
||||
},
|
||||
{
|
||||
value: "nl",
|
||||
label: "Nederlands",
|
||||
flag: "\u{1F1F3}\u{1F1F1}",
|
||||
alternativeCodes: ["nl-NL", "nl-BE"]
|
||||
},
|
||||
{
|
||||
value: "ro",
|
||||
label: "Rom\xE2n\u0103",
|
||||
flag: "\u{1F1F7}\u{1F1F4}",
|
||||
alternativeCodes: ["ro-RO", "ro-MD"]
|
||||
},
|
||||
{
|
||||
value: "sv",
|
||||
label: "Svenska",
|
||||
flag: "\u{1F1F8}\u{1F1EA}",
|
||||
alternativeCodes: ["sv-SE", "sv-FI"]
|
||||
},
|
||||
{
|
||||
value: "ur",
|
||||
label: "\u0627\u0631\u062F\u0648",
|
||||
flag: "\u{1F1F5}\u{1F1F0}",
|
||||
alternativeCodes: ["ur-PK", "ur-IN"]
|
||||
},
|
||||
{
|
||||
value: "fa",
|
||||
label: "\u0641\u0627\u0631\u0633\u06CC",
|
||||
flag: "\u{1F1EE}\u{1F1F7}",
|
||||
alternativeCodes: ["fa-IR", "fa-AF"]
|
||||
}
|
||||
];
|
||||
return languages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return LANGUAGE_DEFS.map((lang) => lang.value);
|
||||
}
|
||||
function mapUiLanguageToIdentityLanguage(uiLanguageCode) {
|
||||
if (!uiLanguageCode) {
|
||||
return null;
|
||||
}
|
||||
const normalizedCode = uiLanguageCode.toLowerCase();
|
||||
const availableLanguages = getAvailableLanguages();
|
||||
const exactMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
const exactMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
if (exactMatch) {
|
||||
return exactMatch.value;
|
||||
}
|
||||
const alternativeMatch = availableLanguages.find(
|
||||
const alternativeMatch = LANGUAGE_DEFS.find(
|
||||
(lang) => lang.alternativeCodes?.some((code) => code.toLowerCase() === normalizedCode)
|
||||
);
|
||||
if (alternativeMatch) {
|
||||
return alternativeMatch.value;
|
||||
}
|
||||
const baseCode = normalizedCode.split("-")[0];
|
||||
const baseMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
const baseMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
if (baseMatch) {
|
||||
return baseMatch.value;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var MAX_WORD_COUNT = 10;
|
||||
// src/defaults/Languages.ts
|
||||
var DEFAULT_LANGUAGE_CODE = "en";
|
||||
var LANGUAGES = [
|
||||
{ code: "en", flag: "\u{1F1EC}\u{1F1E7}", label: "English" },
|
||||
{ code: "en", flag: "\u{1F1FA}\u{1F1F8}", label: "English" },
|
||||
{ code: "nl", flag: "\u{1F1F3}\u{1F1F1}", label: "Nederlands" },
|
||||
{ code: "de", flag: "\u{1F1E9}\u{1F1EA}", label: "Deutsch" },
|
||||
{ code: "fr", flag: "\u{1F1EB}\u{1F1F7}", label: "Fran\xE7ais" },
|
||||
@@ -23,12 +23,14 @@ var LANGUAGES = [
|
||||
{ code: "fi", flag: "\u{1F1EB}\u{1F1EE}", label: "Suomi" },
|
||||
{ code: "he", flag: "\u{1F1EE}\u{1F1F1}", label: "\u05E2\u05D1\u05E8\u05D9\u05EA" },
|
||||
{ code: "pl", flag: "\u{1F1F5}\u{1F1F1}", label: "Polski" },
|
||||
{ code: "pt", flag: "\u{1F1E7}\u{1F1F7}", label: "Portugu\xEAs" },
|
||||
{ code: "pt", flag: "\u{1F1E7}\u{1F1F7}", label: "Portugu\xEAs Brasileiro" },
|
||||
{ code: "ro", flag: "\u{1F1F7}\u{1F1F4}", label: "Rom\xE2n\u0103" },
|
||||
{ code: "ru", flag: "\u{1F1F7}\u{1F1FA}", label: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" },
|
||||
{ code: "sv", flag: "\u{1F1F8}\u{1F1EA}", label: "Svenska" },
|
||||
{ code: "uk", flag: "\u{1F1FA}\u{1F1E6}", label: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430" },
|
||||
{ code: "zh", flag: "\u{1F1E8}\u{1F1F3}", label: "\u4E2D\u6587" }
|
||||
{ code: "zh", flag: "\u{1F1E8}\u{1F1F3}", label: "\u7B80\u4F53\u4E2D\u6587" },
|
||||
{ code: "ur", flag: "\u{1F1F5}\u{1F1F0}", label: "\u0627\u0631\u062F\u0648" },
|
||||
{ code: "fa", flag: "\u{1F1EE}\u{1F1F7}", label: "\u0641\u0627\u0631\u0633\u06CC" }
|
||||
];
|
||||
function normalizeLanguageCode(code) {
|
||||
return (code ?? "").slice(0, 2).toLowerCase();
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useState, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { StyleSheet, View, TouchableOpacity } from 'react-native';
|
||||
|
||||
import { getAvailableLanguages, ILanguageOption } from '@/utils/dist/core/identity-generator';
|
||||
import { getAvailableLanguages } from '@/utils/dist/core/identity-generator';
|
||||
import { getLanguageInfo } from '@/utils/dist/core/models/defaults';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { useVaultMutate } from '@/hooks/useVaultMutate';
|
||||
@@ -28,7 +29,9 @@ export default function IdentityGeneratorLanguageScreen(): React.ReactNode {
|
||||
const { executeVaultMutation } = useVaultMutate();
|
||||
|
||||
const [language, setLanguage] = useState<string>('en');
|
||||
const [languageOptions] = useState<ILanguageOption[]>(() => getAvailableLanguages());
|
||||
const [languageOptions] = useState<string[]>(() =>
|
||||
getAvailableLanguages().sort((a, b) => getLanguageInfo(a).label.localeCompare(getLanguageInfo(b).label))
|
||||
);
|
||||
|
||||
// Store pending changes and initial values
|
||||
const pendingChanges = useRef<{ language?: string }>({});
|
||||
@@ -130,14 +133,15 @@ export default function IdentityGeneratorLanguageScreen(): React.ReactNode {
|
||||
<View style={styles.optionContainer}>
|
||||
{languageOptions.map((option, index) => {
|
||||
const isLast = index === languageOptions.length - 1;
|
||||
const info = getLanguageInfo(option);
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={option.value}
|
||||
key={option}
|
||||
style={[styles.option, isLast && styles.optionLast]}
|
||||
onPress={() => handleLanguageSelect(option.value)}
|
||||
onPress={() => handleLanguageSelect(option)}
|
||||
>
|
||||
<ThemedText style={styles.optionText}>{option.flag} {option.label}</ThemedText>
|
||||
{language === option.value && (
|
||||
<ThemedText style={styles.optionText}>{info.flag} {info.label}</ThemedText>
|
||||
{language === option && (
|
||||
<Ionicons name="checkmark" size={20} style={styles.selectedIcon} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useState, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { StyleSheet, View, TouchableOpacity } from 'react-native';
|
||||
|
||||
import { getAvailableAgeRanges, IAgeRangeOption, getAvailableLanguages, ILanguageOption } from '@/utils/dist/core/identity-generator';
|
||||
import { getAvailableAgeRanges, IAgeRangeOption } from '@/utils/dist/core/identity-generator';
|
||||
import { getLanguageInfo } from '@/utils/dist/core/models/defaults';
|
||||
|
||||
import { useColors } from '@/hooks/useColorScheme';
|
||||
import { useVaultMutate } from '@/hooks/useVaultMutate';
|
||||
@@ -29,7 +30,6 @@ export default function IdentityGeneratorSettingsScreen(): React.ReactNode {
|
||||
const [language, setLanguage] = useState<string>('en');
|
||||
const [gender, setGender] = useState<string>('random');
|
||||
const [ageRange, setAgeRange] = useState<string>('random');
|
||||
const [languageOptions] = useState<ILanguageOption[]>(() => getAvailableLanguages());
|
||||
const [ageRangeOptions] = useState<IAgeRangeOption[]>(() => getAvailableAgeRanges());
|
||||
|
||||
// Store pending changes and initial values (language is managed in subview)
|
||||
@@ -119,9 +119,9 @@ export default function IdentityGeneratorSettingsScreen(): React.ReactNode {
|
||||
* Get the display label for the current language
|
||||
*/
|
||||
const getLanguageDisplayLabel = useCallback((): string => {
|
||||
const option = languageOptions.find(opt => opt.value === language);
|
||||
return option ? `${option.flag} ${option.label}` : language;
|
||||
}, [language, languageOptions]);
|
||||
const info = getLanguageInfo(language);
|
||||
return `${info.flag} ${info.label}`;
|
||||
}, [language]);
|
||||
|
||||
/**
|
||||
* Handle gender change
|
||||
|
||||
@@ -395,35 +395,10 @@ declare function getAvailableAgeRanges(): IAgeRangeOption[];
|
||||
declare function convertAgeRangeToBirthdateOptions(ageRange: string): IBirthdateOptions | null;
|
||||
|
||||
/**
|
||||
* Represents a language option for identity generation.
|
||||
* Gets all available language codes for identity generation.
|
||||
* @returns Array of ISO language codes (e.g. ["da", "de", "en", ...]).
|
||||
*/
|
||||
interface ILanguageOption {
|
||||
/**
|
||||
* The language code (e.g., "en", "nl", "de")
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* The display label in the native language (e.g., "English", "Nederlands", "Deutsch")
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The flag emoji for the language (e.g., "🇬🇧", "🇳🇱", "🇩🇪")
|
||||
*/
|
||||
flag: string;
|
||||
/**
|
||||
* Alternative language codes that map to this identity generator language.
|
||||
* Used for matching UI locale codes to identity generator languages.
|
||||
* For example, "en-US", "en-GB", "en-CA" all map to "en"
|
||||
*/
|
||||
alternativeCodes?: string[];
|
||||
}
|
||||
/**
|
||||
* Gets all available languages for identity generation.
|
||||
* Display labels are in the native language, with optional flag emoji that clients can choose to display.
|
||||
* Languages are sorted alphabetically by label for consistent display across all platforms.
|
||||
* @returns Array of language options sorted alphabetically by label
|
||||
*/
|
||||
declare function getAvailableLanguages(): ILanguageOption[];
|
||||
declare function getAvailableLanguages(): string[];
|
||||
/**
|
||||
* Maps a UI language code to an identity generator language code.
|
||||
* If no explicit match is found, returns null to indicate no preference.
|
||||
@@ -435,7 +410,7 @@ declare function getAvailableLanguages(): ILanguageOption[];
|
||||
* mapUiLanguageToIdentityLanguage("en-US") // returns "en"
|
||||
* mapUiLanguageToIdentityLanguage("nl") // returns "nl"
|
||||
* mapUiLanguageToIdentityLanguage("de-CH") // returns "de"
|
||||
* mapUiLanguageToIdentityLanguage("fr") // returns null (no French identity generator)
|
||||
* mapUiLanguageToIdentityLanguage("ja") // returns null (no Japanese identity generator)
|
||||
*/
|
||||
declare function mapUiLanguageToIdentityLanguage(uiLanguageCode: string | null | undefined): string | null;
|
||||
|
||||
@@ -454,4 +429,4 @@ declare const CreateIdentityGenerator: (language: string) => IdentityGenerator;
|
||||
*/
|
||||
declare const CreateUsernameEmailGenerator: () => UsernameEmailGenerator;
|
||||
|
||||
export { CreateIdentityGenerator, CreateUsernameEmailGenerator, Gender, type IAgeRangeOption, type IBirthdateOptions, type IDecadeFirstnames, type IIdentityGenerator, type ILanguageOption, type Identity, IdentityGenerator, IdentityGeneratorDa, IdentityGeneratorDe, IdentityGeneratorEn, IdentityGeneratorEs, IdentityGeneratorFa, IdentityGeneratorFr, IdentityGeneratorIt, IdentityGeneratorNl, IdentityGeneratorRo, IdentityGeneratorSv, IdentityGeneratorUr, IdentityHelperUtils, UsernameEmailGenerator, convertAgeRangeToBirthdateOptions, getAvailableAgeRanges, getAvailableLanguages, mapUiLanguageToIdentityLanguage };
|
||||
export { CreateIdentityGenerator, CreateUsernameEmailGenerator, Gender, type IAgeRangeOption, type IBirthdateOptions, type IDecadeFirstnames, type IIdentityGenerator, type Identity, IdentityGenerator, IdentityGeneratorDa, IdentityGeneratorDe, IdentityGeneratorEn, IdentityGeneratorEs, IdentityGeneratorFa, IdentityGeneratorFr, IdentityGeneratorIt, IdentityGeneratorNl, IdentityGeneratorRo, IdentityGeneratorSv, IdentityGeneratorUr, IdentityHelperUtils, UsernameEmailGenerator, convertAgeRangeToBirthdateOptions, getAvailableAgeRanges, getAvailableLanguages, mapUiLanguageToIdentityLanguage };
|
||||
|
||||
@@ -11763,95 +11763,39 @@ function convertAgeRangeToBirthdateOptions(ageRange) {
|
||||
}
|
||||
|
||||
// src/utils/LanguageProvider.ts
|
||||
var LANGUAGE_DEFS = [
|
||||
{ value: "da", alternativeCodes: ["da-DK"] },
|
||||
{ value: "de", alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"] },
|
||||
{ value: "en", alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"] },
|
||||
{ value: "es", alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"] },
|
||||
{ value: "fr", alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"] },
|
||||
{ value: "it", alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"] },
|
||||
{ value: "nl", alternativeCodes: ["nl-NL", "nl-BE"] },
|
||||
{ value: "ro", alternativeCodes: ["ro-RO", "ro-MD"] },
|
||||
{ value: "sv", alternativeCodes: ["sv-SE", "sv-FI"] },
|
||||
{ value: "ur", alternativeCodes: ["ur-PK", "ur-IN"] },
|
||||
{ value: "fa", alternativeCodes: ["fa-IR", "fa-AF"] }
|
||||
];
|
||||
function getAvailableLanguages() {
|
||||
const languages = [
|
||||
{
|
||||
value: "da",
|
||||
label: "Dansk",
|
||||
flag: "\u{1F1E9}\u{1F1F0}",
|
||||
alternativeCodes: ["da-DK"]
|
||||
},
|
||||
{
|
||||
value: "de",
|
||||
label: "Deutsch",
|
||||
flag: "\u{1F1E9}\u{1F1EA}",
|
||||
alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"]
|
||||
},
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
flag: "\u{1F1EC}\u{1F1E7}",
|
||||
alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"]
|
||||
},
|
||||
{
|
||||
value: "es",
|
||||
label: "Espa\xF1ol",
|
||||
flag: "\u{1F1EA}\u{1F1F8}",
|
||||
alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"]
|
||||
},
|
||||
{
|
||||
value: "fr",
|
||||
label: "Fran\xE7ais",
|
||||
flag: "\u{1F1EB}\u{1F1F7}",
|
||||
alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"]
|
||||
},
|
||||
{
|
||||
value: "it",
|
||||
label: "Italiano",
|
||||
flag: "\u{1F1EE}\u{1F1F9}",
|
||||
alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"]
|
||||
},
|
||||
{
|
||||
value: "nl",
|
||||
label: "Nederlands",
|
||||
flag: "\u{1F1F3}\u{1F1F1}",
|
||||
alternativeCodes: ["nl-NL", "nl-BE"]
|
||||
},
|
||||
{
|
||||
value: "ro",
|
||||
label: "Rom\xE2n\u0103",
|
||||
flag: "\u{1F1F7}\u{1F1F4}",
|
||||
alternativeCodes: ["ro-RO", "ro-MD"]
|
||||
},
|
||||
{
|
||||
value: "sv",
|
||||
label: "Svenska",
|
||||
flag: "\u{1F1F8}\u{1F1EA}",
|
||||
alternativeCodes: ["sv-SE", "sv-FI"]
|
||||
},
|
||||
{
|
||||
value: "ur",
|
||||
label: "\u0627\u0631\u062F\u0648",
|
||||
flag: "\u{1F1F5}\u{1F1F0}",
|
||||
alternativeCodes: ["ur-PK", "ur-IN"]
|
||||
},
|
||||
{
|
||||
value: "fa",
|
||||
label: "\u0641\u0627\u0631\u0633\u06CC",
|
||||
flag: "\u{1F1EE}\u{1F1F7}",
|
||||
alternativeCodes: ["fa-IR", "fa-AF"]
|
||||
}
|
||||
];
|
||||
return languages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return LANGUAGE_DEFS.map((lang) => lang.value);
|
||||
}
|
||||
function mapUiLanguageToIdentityLanguage(uiLanguageCode) {
|
||||
if (!uiLanguageCode) {
|
||||
return null;
|
||||
}
|
||||
const normalizedCode = uiLanguageCode.toLowerCase();
|
||||
const availableLanguages = getAvailableLanguages();
|
||||
const exactMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
const exactMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
if (exactMatch) {
|
||||
return exactMatch.value;
|
||||
}
|
||||
const alternativeMatch = availableLanguages.find(
|
||||
const alternativeMatch = LANGUAGE_DEFS.find(
|
||||
(lang) => lang.alternativeCodes?.some((code) => code.toLowerCase() === normalizedCode)
|
||||
);
|
||||
if (alternativeMatch) {
|
||||
return alternativeMatch.value;
|
||||
}
|
||||
const baseCode = normalizedCode.split("-")[0];
|
||||
const baseMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
const baseMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
if (baseMatch) {
|
||||
return baseMatch.value;
|
||||
}
|
||||
|
||||
@@ -11718,95 +11718,39 @@ function convertAgeRangeToBirthdateOptions(ageRange) {
|
||||
}
|
||||
|
||||
// src/utils/LanguageProvider.ts
|
||||
var LANGUAGE_DEFS = [
|
||||
{ value: "da", alternativeCodes: ["da-DK"] },
|
||||
{ value: "de", alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"] },
|
||||
{ value: "en", alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"] },
|
||||
{ value: "es", alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"] },
|
||||
{ value: "fr", alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"] },
|
||||
{ value: "it", alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"] },
|
||||
{ value: "nl", alternativeCodes: ["nl-NL", "nl-BE"] },
|
||||
{ value: "ro", alternativeCodes: ["ro-RO", "ro-MD"] },
|
||||
{ value: "sv", alternativeCodes: ["sv-SE", "sv-FI"] },
|
||||
{ value: "ur", alternativeCodes: ["ur-PK", "ur-IN"] },
|
||||
{ value: "fa", alternativeCodes: ["fa-IR", "fa-AF"] }
|
||||
];
|
||||
function getAvailableLanguages() {
|
||||
const languages = [
|
||||
{
|
||||
value: "da",
|
||||
label: "Dansk",
|
||||
flag: "\u{1F1E9}\u{1F1F0}",
|
||||
alternativeCodes: ["da-DK"]
|
||||
},
|
||||
{
|
||||
value: "de",
|
||||
label: "Deutsch",
|
||||
flag: "\u{1F1E9}\u{1F1EA}",
|
||||
alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"]
|
||||
},
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
flag: "\u{1F1EC}\u{1F1E7}",
|
||||
alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"]
|
||||
},
|
||||
{
|
||||
value: "es",
|
||||
label: "Espa\xF1ol",
|
||||
flag: "\u{1F1EA}\u{1F1F8}",
|
||||
alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"]
|
||||
},
|
||||
{
|
||||
value: "fr",
|
||||
label: "Fran\xE7ais",
|
||||
flag: "\u{1F1EB}\u{1F1F7}",
|
||||
alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"]
|
||||
},
|
||||
{
|
||||
value: "it",
|
||||
label: "Italiano",
|
||||
flag: "\u{1F1EE}\u{1F1F9}",
|
||||
alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"]
|
||||
},
|
||||
{
|
||||
value: "nl",
|
||||
label: "Nederlands",
|
||||
flag: "\u{1F1F3}\u{1F1F1}",
|
||||
alternativeCodes: ["nl-NL", "nl-BE"]
|
||||
},
|
||||
{
|
||||
value: "ro",
|
||||
label: "Rom\xE2n\u0103",
|
||||
flag: "\u{1F1F7}\u{1F1F4}",
|
||||
alternativeCodes: ["ro-RO", "ro-MD"]
|
||||
},
|
||||
{
|
||||
value: "sv",
|
||||
label: "Svenska",
|
||||
flag: "\u{1F1F8}\u{1F1EA}",
|
||||
alternativeCodes: ["sv-SE", "sv-FI"]
|
||||
},
|
||||
{
|
||||
value: "ur",
|
||||
label: "\u0627\u0631\u062F\u0648",
|
||||
flag: "\u{1F1F5}\u{1F1F0}",
|
||||
alternativeCodes: ["ur-PK", "ur-IN"]
|
||||
},
|
||||
{
|
||||
value: "fa",
|
||||
label: "\u0641\u0627\u0631\u0633\u06CC",
|
||||
flag: "\u{1F1EE}\u{1F1F7}",
|
||||
alternativeCodes: ["fa-IR", "fa-AF"]
|
||||
}
|
||||
];
|
||||
return languages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return LANGUAGE_DEFS.map((lang) => lang.value);
|
||||
}
|
||||
function mapUiLanguageToIdentityLanguage(uiLanguageCode) {
|
||||
if (!uiLanguageCode) {
|
||||
return null;
|
||||
}
|
||||
const normalizedCode = uiLanguageCode.toLowerCase();
|
||||
const availableLanguages = getAvailableLanguages();
|
||||
const exactMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
const exactMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
if (exactMatch) {
|
||||
return exactMatch.value;
|
||||
}
|
||||
const alternativeMatch = availableLanguages.find(
|
||||
const alternativeMatch = LANGUAGE_DEFS.find(
|
||||
(lang) => lang.alternativeCodes?.some((code) => code.toLowerCase() === normalizedCode)
|
||||
);
|
||||
if (alternativeMatch) {
|
||||
return alternativeMatch.value;
|
||||
}
|
||||
const baseCode = normalizedCode.split("-")[0];
|
||||
const baseMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
const baseMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
if (baseMatch) {
|
||||
return baseMatch.value;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var MAX_WORD_COUNT = 10;
|
||||
// src/defaults/Languages.ts
|
||||
var DEFAULT_LANGUAGE_CODE = "en";
|
||||
var LANGUAGES = [
|
||||
{ code: "en", flag: "\u{1F1EC}\u{1F1E7}", label: "English" },
|
||||
{ code: "en", flag: "\u{1F1FA}\u{1F1F8}", label: "English" },
|
||||
{ code: "nl", flag: "\u{1F1F3}\u{1F1F1}", label: "Nederlands" },
|
||||
{ code: "de", flag: "\u{1F1E9}\u{1F1EA}", label: "Deutsch" },
|
||||
{ code: "fr", flag: "\u{1F1EB}\u{1F1F7}", label: "Fran\xE7ais" },
|
||||
@@ -23,12 +23,14 @@ var LANGUAGES = [
|
||||
{ code: "fi", flag: "\u{1F1EB}\u{1F1EE}", label: "Suomi" },
|
||||
{ code: "he", flag: "\u{1F1EE}\u{1F1F1}", label: "\u05E2\u05D1\u05E8\u05D9\u05EA" },
|
||||
{ code: "pl", flag: "\u{1F1F5}\u{1F1F1}", label: "Polski" },
|
||||
{ code: "pt", flag: "\u{1F1E7}\u{1F1F7}", label: "Portugu\xEAs" },
|
||||
{ code: "pt", flag: "\u{1F1E7}\u{1F1F7}", label: "Portugu\xEAs Brasileiro" },
|
||||
{ code: "ro", flag: "\u{1F1F7}\u{1F1F4}", label: "Rom\xE2n\u0103" },
|
||||
{ code: "ru", flag: "\u{1F1F7}\u{1F1FA}", label: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" },
|
||||
{ code: "sv", flag: "\u{1F1F8}\u{1F1EA}", label: "Svenska" },
|
||||
{ code: "uk", flag: "\u{1F1FA}\u{1F1E6}", label: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430" },
|
||||
{ code: "zh", flag: "\u{1F1E8}\u{1F1F3}", label: "\u4E2D\u6587" }
|
||||
{ code: "zh", flag: "\u{1F1E8}\u{1F1F3}", label: "\u7B80\u4F53\u4E2D\u6587" },
|
||||
{ code: "ur", flag: "\u{1F1F5}\u{1F1F0}", label: "\u0627\u0631\u062F\u0648" },
|
||||
{ code: "fa", flag: "\u{1F1EE}\u{1F1F7}", label: "\u0641\u0627\u0631\u0633\u06CC" }
|
||||
];
|
||||
function normalizeLanguageCode(code) {
|
||||
return (code ?? "").slice(0, 2).toLowerCase();
|
||||
|
||||
@@ -23,7 +23,7 @@ public static class Languages
|
||||
/// </summary>
|
||||
private static readonly (string Code, string Flag, string Label)[] Meta =
|
||||
[
|
||||
("en", "🇬🇧", "English"),
|
||||
("en", "🇺🇸", "English"),
|
||||
("nl", "🇳🇱", "Nederlands"),
|
||||
("de", "🇩🇪", "Deutsch"),
|
||||
("fr", "🇫🇷", "Français"),
|
||||
@@ -33,12 +33,14 @@ public static class Languages
|
||||
("fi", "🇫🇮", "Suomi"),
|
||||
("he", "🇮🇱", "עברית"),
|
||||
("pl", "🇵🇱", "Polski"),
|
||||
("pt", "🇧🇷", "Português"),
|
||||
("pt", "🇧🇷", "Português Brasileiro"),
|
||||
("ro", "🇷🇴", "Română"),
|
||||
("ru", "🇷🇺", "Русский"),
|
||||
("sv", "🇸🇪", "Svenska"),
|
||||
("uk", "🇺🇦", "Українська"),
|
||||
("zh", "🇨🇳", "中文"),
|
||||
("zh", "🇨🇳", "简体中文"),
|
||||
("ur", "🇵🇰", "اردو"),
|
||||
("fa", "🇮🇷", "فارسی"),
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
@@ -72,6 +74,45 @@ public static class Languages
|
||||
return $"🌐 {code}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the flag emoji for an ISO language code. Falls back to a globe flag for unknown languages.
|
||||
/// </summary>
|
||||
/// <param name="code">The ISO language code (case-insensitive).</param>
|
||||
/// <returns>The flag emoji, e.g. "🇳🇱".</returns>
|
||||
public static string GetFlag(string code)
|
||||
{
|
||||
var iso = Normalize(code);
|
||||
foreach (var (metaCode, flag, _) in Meta)
|
||||
{
|
||||
if (metaCode == iso)
|
||||
{
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
return "🌐";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the native display label for an ISO language code. Falls back to the raw code for unknown
|
||||
/// languages.
|
||||
/// </summary>
|
||||
/// <param name="code">The ISO language code (case-insensitive).</param>
|
||||
/// <returns>The native label, e.g. "Nederlands".</returns>
|
||||
public static string GetLabel(string code)
|
||||
{
|
||||
var iso = Normalize(code);
|
||||
foreach (var (metaCode, _, label) in Meta)
|
||||
{
|
||||
if (metaCode == iso)
|
||||
{
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve a default language code for an app/UI language, restricted to a set of available
|
||||
/// codes. Returns the app language when available, otherwise the first available code,
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace AliasVault.Client.Services.JsInterop;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using AliasVault.Client.Main.Models;
|
||||
using AliasVault.Client.Services.JsInterop.Models;
|
||||
using AliasVault.Shared.Core;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -436,8 +437,21 @@ public sealed class JsInteropService(IJSRuntime jsRuntime)
|
||||
await InitializeAsync();
|
||||
}
|
||||
|
||||
var result = await _identityGeneratorModule!.InvokeAsync<List<LanguageOption>>("getAvailableLanguages");
|
||||
return result ?? new List<LanguageOption>();
|
||||
var codes = await _identityGeneratorModule!.InvokeAsync<List<string>>("getAvailableLanguages");
|
||||
if (codes == null)
|
||||
{
|
||||
return new List<LanguageOption>();
|
||||
}
|
||||
|
||||
return codes
|
||||
.Select(code => new LanguageOption
|
||||
{
|
||||
Value = code,
|
||||
Flag = Languages.GetFlag(code),
|
||||
Label = Languages.GetLabel(code),
|
||||
})
|
||||
.OrderBy(option => option.Label, StringComparer.CurrentCulture)
|
||||
.ToList();
|
||||
}
|
||||
catch (JSException ex)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace AliasVault.Client.Services;
|
||||
|
||||
using System.Globalization;
|
||||
using AliasVault.Client.Main.Models;
|
||||
using AliasVault.Client.Services.Database;
|
||||
using Blazored.LocalStorage;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
@@ -23,31 +24,13 @@ public class LanguageService(
|
||||
DbService dbService)
|
||||
{
|
||||
/// <summary>
|
||||
/// Language configuration containing all supported languages.
|
||||
/// To add a new language, simply add a new entry to this list.
|
||||
/// The set of UI languages the client has translations for. Native display names and flag emojis
|
||||
/// are sourced from the shared <see cref="Languages"/> reference, so adding a new language only
|
||||
/// requires adding its ISO code here (plus the matching translation resources).
|
||||
/// </summary>
|
||||
private static readonly List<LanguageConfig> SupportedLanguages = new()
|
||||
private static readonly List<string> SupportedLanguageCodes = new()
|
||||
{
|
||||
new LanguageConfig("da", "Dansk", "🇩🇰"),
|
||||
new LanguageConfig("de", "Deutsch", "🇩🇪"),
|
||||
new LanguageConfig("en", "English", "🇺🇸"),
|
||||
new LanguageConfig("es", "Español", "🇪🇸"),
|
||||
new LanguageConfig("fi", "Suomi", "🇫🇮"),
|
||||
new LanguageConfig("fr", "Français", "🇫🇷"),
|
||||
new LanguageConfig("he", "עברית", "🇮🇱"),
|
||||
new LanguageConfig("it", "Italiano", "🇮🇹"),
|
||||
new LanguageConfig("nl", "Nederlands", "🇳🇱"),
|
||||
new LanguageConfig("pl", "Polski", "🇵🇱"),
|
||||
new LanguageConfig("pt", "Português Brasileiro", "🇧🇷"),
|
||||
new LanguageConfig("ro", "Română", "🇷🇴"),
|
||||
new LanguageConfig("ru", "Русский", "🇷🇺"),
|
||||
new LanguageConfig("sv", "Svenska", "🇸🇪"),
|
||||
new LanguageConfig("uk", "Українська", "🇺🇦"),
|
||||
new LanguageConfig("zh", "简体中文", "🇨🇳"),
|
||||
|
||||
// Add new languages here:
|
||||
// new LanguageConfig("fr", "Français", "🇫🇷"),
|
||||
// new LanguageConfig("es", "Español", "🇪🇸"),
|
||||
"da", "de", "en", "es", "fi", "fr", "he", "it", "nl", "pl", "pt", "ro", "ru", "sv", "uk", "zh",
|
||||
};
|
||||
|
||||
private readonly ILocalStorageService _localStorage = localStorage;
|
||||
@@ -66,16 +49,7 @@ public class LanguageService(
|
||||
/// <returns>Dictionary of language codes and display names.</returns>
|
||||
public static Dictionary<string, string> GetSupportedLanguages()
|
||||
{
|
||||
return SupportedLanguages.ToDictionary(lang => lang.Code, lang => lang.DisplayName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of supported languages with flag emojis.
|
||||
/// </summary>
|
||||
/// <returns>Dictionary of language codes and display names with flag emojis.</returns>
|
||||
public static Dictionary<string, string> GetSupportedLanguagesWithFlags()
|
||||
{
|
||||
return SupportedLanguages.ToDictionary(lang => lang.Code, lang => $"{lang.FlagEmoji} {lang.DisplayName}");
|
||||
return SupportedLanguageCodes.ToDictionary(code => code, Languages.GetLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -85,8 +59,7 @@ public class LanguageService(
|
||||
/// <returns>Flag emoji string.</returns>
|
||||
public static string GetLanguageFlag(string languageCode)
|
||||
{
|
||||
var language = SupportedLanguages.FirstOrDefault(lang => lang.Code == languageCode);
|
||||
return language?.FlagEmoji ?? "🌐";
|
||||
return Languages.GetFlag(languageCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,8 +69,7 @@ public class LanguageService(
|
||||
/// <returns>Display name string.</returns>
|
||||
public static string GetLanguageDisplayName(string languageCode)
|
||||
{
|
||||
var language = SupportedLanguages.FirstOrDefault(lang => lang.Code == languageCode);
|
||||
return language?.DisplayName ?? languageCode;
|
||||
return Languages.GetLabel(languageCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -107,7 +79,7 @@ public class LanguageService(
|
||||
/// <returns>True if the language is supported, false otherwise.</returns>
|
||||
public static bool IsLanguageSupported(string languageCode)
|
||||
{
|
||||
return SupportedLanguages.Any(lang => lang.Code == languageCode);
|
||||
return SupportedLanguageCodes.Contains(languageCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,7 +88,7 @@ public class LanguageService(
|
||||
/// <returns>Default language code.</returns>
|
||||
public static string GetDefaultLanguage()
|
||||
{
|
||||
return SupportedLanguages.FirstOrDefault()?.Code ?? "en";
|
||||
return SupportedLanguageCodes.FirstOrDefault() ?? "en";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -333,9 +305,4 @@ public class LanguageService(
|
||||
// Ignore migration errors - user can still change language manually
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for a supported language.
|
||||
/// </summary>
|
||||
private sealed record LanguageConfig(string Code, string DisplayName, string FlagEmoji);
|
||||
}
|
||||
|
||||
@@ -395,35 +395,10 @@ declare function getAvailableAgeRanges(): IAgeRangeOption[];
|
||||
declare function convertAgeRangeToBirthdateOptions(ageRange: string): IBirthdateOptions | null;
|
||||
|
||||
/**
|
||||
* Represents a language option for identity generation.
|
||||
* Gets all available language codes for identity generation.
|
||||
* @returns Array of ISO language codes (e.g. ["da", "de", "en", ...]).
|
||||
*/
|
||||
interface ILanguageOption {
|
||||
/**
|
||||
* The language code (e.g., "en", "nl", "de")
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* The display label in the native language (e.g., "English", "Nederlands", "Deutsch")
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The flag emoji for the language (e.g., "🇬🇧", "🇳🇱", "🇩🇪")
|
||||
*/
|
||||
flag: string;
|
||||
/**
|
||||
* Alternative language codes that map to this identity generator language.
|
||||
* Used for matching UI locale codes to identity generator languages.
|
||||
* For example, "en-US", "en-GB", "en-CA" all map to "en"
|
||||
*/
|
||||
alternativeCodes?: string[];
|
||||
}
|
||||
/**
|
||||
* Gets all available languages for identity generation.
|
||||
* Display labels are in the native language, with optional flag emoji that clients can choose to display.
|
||||
* Languages are sorted alphabetically by label for consistent display across all platforms.
|
||||
* @returns Array of language options sorted alphabetically by label
|
||||
*/
|
||||
declare function getAvailableLanguages(): ILanguageOption[];
|
||||
declare function getAvailableLanguages(): string[];
|
||||
/**
|
||||
* Maps a UI language code to an identity generator language code.
|
||||
* If no explicit match is found, returns null to indicate no preference.
|
||||
@@ -435,7 +410,7 @@ declare function getAvailableLanguages(): ILanguageOption[];
|
||||
* mapUiLanguageToIdentityLanguage("en-US") // returns "en"
|
||||
* mapUiLanguageToIdentityLanguage("nl") // returns "nl"
|
||||
* mapUiLanguageToIdentityLanguage("de-CH") // returns "de"
|
||||
* mapUiLanguageToIdentityLanguage("fr") // returns null (no French identity generator)
|
||||
* mapUiLanguageToIdentityLanguage("ja") // returns null (no Japanese identity generator)
|
||||
*/
|
||||
declare function mapUiLanguageToIdentityLanguage(uiLanguageCode: string | null | undefined): string | null;
|
||||
|
||||
@@ -454,4 +429,4 @@ declare const CreateIdentityGenerator: (language: string) => IdentityGenerator;
|
||||
*/
|
||||
declare const CreateUsernameEmailGenerator: () => UsernameEmailGenerator;
|
||||
|
||||
export { CreateIdentityGenerator, CreateUsernameEmailGenerator, Gender, type IAgeRangeOption, type IBirthdateOptions, type IDecadeFirstnames, type IIdentityGenerator, type ILanguageOption, type Identity, IdentityGenerator, IdentityGeneratorDa, IdentityGeneratorDe, IdentityGeneratorEn, IdentityGeneratorEs, IdentityGeneratorFa, IdentityGeneratorFr, IdentityGeneratorIt, IdentityGeneratorNl, IdentityGeneratorRo, IdentityGeneratorSv, IdentityGeneratorUr, IdentityHelperUtils, UsernameEmailGenerator, convertAgeRangeToBirthdateOptions, getAvailableAgeRanges, getAvailableLanguages, mapUiLanguageToIdentityLanguage };
|
||||
export { CreateIdentityGenerator, CreateUsernameEmailGenerator, Gender, type IAgeRangeOption, type IBirthdateOptions, type IDecadeFirstnames, type IIdentityGenerator, type Identity, IdentityGenerator, IdentityGeneratorDa, IdentityGeneratorDe, IdentityGeneratorEn, IdentityGeneratorEs, IdentityGeneratorFa, IdentityGeneratorFr, IdentityGeneratorIt, IdentityGeneratorNl, IdentityGeneratorRo, IdentityGeneratorSv, IdentityGeneratorUr, IdentityHelperUtils, UsernameEmailGenerator, convertAgeRangeToBirthdateOptions, getAvailableAgeRanges, getAvailableLanguages, mapUiLanguageToIdentityLanguage };
|
||||
|
||||
@@ -11763,95 +11763,39 @@ function convertAgeRangeToBirthdateOptions(ageRange) {
|
||||
}
|
||||
|
||||
// src/utils/LanguageProvider.ts
|
||||
var LANGUAGE_DEFS = [
|
||||
{ value: "da", alternativeCodes: ["da-DK"] },
|
||||
{ value: "de", alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"] },
|
||||
{ value: "en", alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"] },
|
||||
{ value: "es", alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"] },
|
||||
{ value: "fr", alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"] },
|
||||
{ value: "it", alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"] },
|
||||
{ value: "nl", alternativeCodes: ["nl-NL", "nl-BE"] },
|
||||
{ value: "ro", alternativeCodes: ["ro-RO", "ro-MD"] },
|
||||
{ value: "sv", alternativeCodes: ["sv-SE", "sv-FI"] },
|
||||
{ value: "ur", alternativeCodes: ["ur-PK", "ur-IN"] },
|
||||
{ value: "fa", alternativeCodes: ["fa-IR", "fa-AF"] }
|
||||
];
|
||||
function getAvailableLanguages() {
|
||||
const languages = [
|
||||
{
|
||||
value: "da",
|
||||
label: "Dansk",
|
||||
flag: "\u{1F1E9}\u{1F1F0}",
|
||||
alternativeCodes: ["da-DK"]
|
||||
},
|
||||
{
|
||||
value: "de",
|
||||
label: "Deutsch",
|
||||
flag: "\u{1F1E9}\u{1F1EA}",
|
||||
alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"]
|
||||
},
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
flag: "\u{1F1EC}\u{1F1E7}",
|
||||
alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"]
|
||||
},
|
||||
{
|
||||
value: "es",
|
||||
label: "Espa\xF1ol",
|
||||
flag: "\u{1F1EA}\u{1F1F8}",
|
||||
alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"]
|
||||
},
|
||||
{
|
||||
value: "fr",
|
||||
label: "Fran\xE7ais",
|
||||
flag: "\u{1F1EB}\u{1F1F7}",
|
||||
alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"]
|
||||
},
|
||||
{
|
||||
value: "it",
|
||||
label: "Italiano",
|
||||
flag: "\u{1F1EE}\u{1F1F9}",
|
||||
alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"]
|
||||
},
|
||||
{
|
||||
value: "nl",
|
||||
label: "Nederlands",
|
||||
flag: "\u{1F1F3}\u{1F1F1}",
|
||||
alternativeCodes: ["nl-NL", "nl-BE"]
|
||||
},
|
||||
{
|
||||
value: "ro",
|
||||
label: "Rom\xE2n\u0103",
|
||||
flag: "\u{1F1F7}\u{1F1F4}",
|
||||
alternativeCodes: ["ro-RO", "ro-MD"]
|
||||
},
|
||||
{
|
||||
value: "sv",
|
||||
label: "Svenska",
|
||||
flag: "\u{1F1F8}\u{1F1EA}",
|
||||
alternativeCodes: ["sv-SE", "sv-FI"]
|
||||
},
|
||||
{
|
||||
value: "ur",
|
||||
label: "\u0627\u0631\u062F\u0648",
|
||||
flag: "\u{1F1F5}\u{1F1F0}",
|
||||
alternativeCodes: ["ur-PK", "ur-IN"]
|
||||
},
|
||||
{
|
||||
value: "fa",
|
||||
label: "\u0641\u0627\u0631\u0633\u06CC",
|
||||
flag: "\u{1F1EE}\u{1F1F7}",
|
||||
alternativeCodes: ["fa-IR", "fa-AF"]
|
||||
}
|
||||
];
|
||||
return languages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return LANGUAGE_DEFS.map((lang) => lang.value);
|
||||
}
|
||||
function mapUiLanguageToIdentityLanguage(uiLanguageCode) {
|
||||
if (!uiLanguageCode) {
|
||||
return null;
|
||||
}
|
||||
const normalizedCode = uiLanguageCode.toLowerCase();
|
||||
const availableLanguages = getAvailableLanguages();
|
||||
const exactMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
const exactMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
if (exactMatch) {
|
||||
return exactMatch.value;
|
||||
}
|
||||
const alternativeMatch = availableLanguages.find(
|
||||
const alternativeMatch = LANGUAGE_DEFS.find(
|
||||
(lang) => lang.alternativeCodes?.some((code) => code.toLowerCase() === normalizedCode)
|
||||
);
|
||||
if (alternativeMatch) {
|
||||
return alternativeMatch.value;
|
||||
}
|
||||
const baseCode = normalizedCode.split("-")[0];
|
||||
const baseMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
const baseMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
if (baseMatch) {
|
||||
return baseMatch.value;
|
||||
}
|
||||
|
||||
@@ -11718,95 +11718,39 @@ function convertAgeRangeToBirthdateOptions(ageRange) {
|
||||
}
|
||||
|
||||
// src/utils/LanguageProvider.ts
|
||||
var LANGUAGE_DEFS = [
|
||||
{ value: "da", alternativeCodes: ["da-DK"] },
|
||||
{ value: "de", alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"] },
|
||||
{ value: "en", alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"] },
|
||||
{ value: "es", alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"] },
|
||||
{ value: "fr", alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"] },
|
||||
{ value: "it", alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"] },
|
||||
{ value: "nl", alternativeCodes: ["nl-NL", "nl-BE"] },
|
||||
{ value: "ro", alternativeCodes: ["ro-RO", "ro-MD"] },
|
||||
{ value: "sv", alternativeCodes: ["sv-SE", "sv-FI"] },
|
||||
{ value: "ur", alternativeCodes: ["ur-PK", "ur-IN"] },
|
||||
{ value: "fa", alternativeCodes: ["fa-IR", "fa-AF"] }
|
||||
];
|
||||
function getAvailableLanguages() {
|
||||
const languages = [
|
||||
{
|
||||
value: "da",
|
||||
label: "Dansk",
|
||||
flag: "\u{1F1E9}\u{1F1F0}",
|
||||
alternativeCodes: ["da-DK"]
|
||||
},
|
||||
{
|
||||
value: "de",
|
||||
label: "Deutsch",
|
||||
flag: "\u{1F1E9}\u{1F1EA}",
|
||||
alternativeCodes: ["de-DE", "de-AT", "de-CH", "de-LU", "de-LI"]
|
||||
},
|
||||
{
|
||||
value: "en",
|
||||
label: "English",
|
||||
flag: "\u{1F1EC}\u{1F1E7}",
|
||||
alternativeCodes: ["en-US", "en-GB", "en-CA", "en-AU", "en-NZ", "en-IE", "en-ZA", "en-SG", "en-IN"]
|
||||
},
|
||||
{
|
||||
value: "es",
|
||||
label: "Espa\xF1ol",
|
||||
flag: "\u{1F1EA}\u{1F1F8}",
|
||||
alternativeCodes: ["es-ES", "es-MX", "es-AR", "es-CO", "es-CL", "es-PE", "es-VE", "es-EC", "es-GT", "es-CU", "es-BO", "es-DO", "es-HN", "es-PY", "es-SV", "es-NI", "es-CR", "es-PA", "es-UY", "es-PR"]
|
||||
},
|
||||
{
|
||||
value: "fr",
|
||||
label: "Fran\xE7ais",
|
||||
flag: "\u{1F1EB}\u{1F1F7}",
|
||||
alternativeCodes: ["fr-FR", "fr-CA", "fr-BE", "fr-CH", "fr-LU", "fr-MC"]
|
||||
},
|
||||
{
|
||||
value: "it",
|
||||
label: "Italiano",
|
||||
flag: "\u{1F1EE}\u{1F1F9}",
|
||||
alternativeCodes: ["it-IT", "it-CH", "it-SM", "it-VA"]
|
||||
},
|
||||
{
|
||||
value: "nl",
|
||||
label: "Nederlands",
|
||||
flag: "\u{1F1F3}\u{1F1F1}",
|
||||
alternativeCodes: ["nl-NL", "nl-BE"]
|
||||
},
|
||||
{
|
||||
value: "ro",
|
||||
label: "Rom\xE2n\u0103",
|
||||
flag: "\u{1F1F7}\u{1F1F4}",
|
||||
alternativeCodes: ["ro-RO", "ro-MD"]
|
||||
},
|
||||
{
|
||||
value: "sv",
|
||||
label: "Svenska",
|
||||
flag: "\u{1F1F8}\u{1F1EA}",
|
||||
alternativeCodes: ["sv-SE", "sv-FI"]
|
||||
},
|
||||
{
|
||||
value: "ur",
|
||||
label: "\u0627\u0631\u062F\u0648",
|
||||
flag: "\u{1F1F5}\u{1F1F0}",
|
||||
alternativeCodes: ["ur-PK", "ur-IN"]
|
||||
},
|
||||
{
|
||||
value: "fa",
|
||||
label: "\u0641\u0627\u0631\u0633\u06CC",
|
||||
flag: "\u{1F1EE}\u{1F1F7}",
|
||||
alternativeCodes: ["fa-IR", "fa-AF"]
|
||||
}
|
||||
];
|
||||
return languages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return LANGUAGE_DEFS.map((lang) => lang.value);
|
||||
}
|
||||
function mapUiLanguageToIdentityLanguage(uiLanguageCode) {
|
||||
if (!uiLanguageCode) {
|
||||
return null;
|
||||
}
|
||||
const normalizedCode = uiLanguageCode.toLowerCase();
|
||||
const availableLanguages = getAvailableLanguages();
|
||||
const exactMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
const exactMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === normalizedCode);
|
||||
if (exactMatch) {
|
||||
return exactMatch.value;
|
||||
}
|
||||
const alternativeMatch = availableLanguages.find(
|
||||
const alternativeMatch = LANGUAGE_DEFS.find(
|
||||
(lang) => lang.alternativeCodes?.some((code) => code.toLowerCase() === normalizedCode)
|
||||
);
|
||||
if (alternativeMatch) {
|
||||
return alternativeMatch.value;
|
||||
}
|
||||
const baseCode = normalizedCode.split("-")[0];
|
||||
const baseMatch = availableLanguages.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
const baseMatch = LANGUAGE_DEFS.find((lang) => lang.value.toLowerCase() === baseCode);
|
||||
if (baseMatch) {
|
||||
return baseMatch.value;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,45 @@ ${tuples}
|
||||
return $"🌐 {code}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the flag emoji for an ISO language code. Falls back to a globe flag for unknown languages.
|
||||
/// </summary>
|
||||
/// <param name="code">The ISO language code (case-insensitive).</param>
|
||||
/// <returns>The flag emoji, e.g. "🇳🇱".</returns>
|
||||
public static string GetFlag(string code)
|
||||
{
|
||||
var iso = Normalize(code);
|
||||
foreach (var (metaCode, flag, _) in Meta)
|
||||
{
|
||||
if (metaCode == iso)
|
||||
{
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
return "🌐";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the native display label for an ISO language code. Falls back to the raw code for unknown
|
||||
/// languages.
|
||||
/// </summary>
|
||||
/// <param name="code">The ISO language code (case-insensitive).</param>
|
||||
/// <returns>The native label, e.g. "Nederlands".</returns>
|
||||
public static string GetLabel(string code)
|
||||
{
|
||||
var iso = Normalize(code);
|
||||
foreach (var (metaCode, _, label) in Meta)
|
||||
{
|
||||
if (metaCode == iso)
|
||||
{
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve a default language code for an app/UI language, restricted to a set of available
|
||||
/// codes. Returns the app language when available, otherwise the first available code,
|
||||
|
||||
@@ -23,7 +23,7 @@ export const DEFAULT_LANGUAGE_CODE = 'en';
|
||||
* Covers the AliasVault app UI languages so this list can be reused beyond a single feature.
|
||||
*/
|
||||
export const LANGUAGES: ILanguageInfo[] = [
|
||||
{ code: 'en', flag: '🇬🇧', label: 'English' },
|
||||
{ code: 'en', flag: '🇺🇸', label: 'English' },
|
||||
{ code: 'nl', flag: '🇳🇱', label: 'Nederlands' },
|
||||
{ code: 'de', flag: '🇩🇪', label: 'Deutsch' },
|
||||
{ code: 'fr', flag: '🇫🇷', label: 'Français' },
|
||||
@@ -33,12 +33,14 @@ export const LANGUAGES: ILanguageInfo[] = [
|
||||
{ code: 'fi', flag: '🇫🇮', label: 'Suomi' },
|
||||
{ code: 'he', flag: '🇮🇱', label: 'עברית' },
|
||||
{ code: 'pl', flag: '🇵🇱', label: 'Polski' },
|
||||
{ code: 'pt', flag: '🇧🇷', label: 'Português' },
|
||||
{ code: 'pt', flag: '🇧🇷', label: 'Português Brasileiro' },
|
||||
{ code: 'ro', flag: '🇷🇴', label: 'Română' },
|
||||
{ code: 'ru', flag: '🇷🇺', label: 'Русский' },
|
||||
{ code: 'sv', flag: '🇸🇪', label: 'Svenska' },
|
||||
{ code: 'uk', flag: '🇺🇦', label: 'Українська' },
|
||||
{ code: 'zh', flag: '🇨🇳', label: '中文' },
|
||||
{ code: 'zh', flag: '🇨🇳', label: '简体中文' },
|
||||
{ code: 'ur', flag: '🇵🇰', label: 'اردو' },
|
||||
{ code: 'fa', flag: '🇮🇷', label: 'فارسی' },
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
27
core/typescript/identity-generator/package-lock.json
generated
27
core/typescript/identity-generator/package-lock.json
generated
@@ -141,6 +141,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -157,6 +158,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -173,6 +175,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -189,6 +192,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -205,6 +209,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -221,6 +226,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -237,6 +243,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -253,6 +260,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -269,6 +277,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -285,6 +294,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -301,6 +311,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -317,6 +328,7 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -333,6 +345,7 @@
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -349,6 +362,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -365,6 +379,7 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -381,6 +396,7 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -397,6 +413,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -413,6 +430,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -429,6 +447,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -445,6 +464,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -461,6 +481,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -477,6 +498,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -493,6 +515,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -509,6 +532,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -525,6 +549,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2826,7 +2851,7 @@
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
|
||||
"integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -3,14 +3,15 @@ import { getAvailableLanguages, mapUiLanguageToIdentityLanguage } from '../utils
|
||||
|
||||
describe('LanguageProvider', () => {
|
||||
describe('getAvailableLanguages', () => {
|
||||
it('should return a list of available languages', () => {
|
||||
it('should return a list of available language codes', () => {
|
||||
const languages = getAvailableLanguages();
|
||||
|
||||
expect(languages).toBeDefined();
|
||||
expect(languages.length).toBeGreaterThan(0);
|
||||
expect(languages[0]).toHaveProperty('value');
|
||||
expect(languages[0]).toHaveProperty('label');
|
||||
expect(languages[0]).toHaveProperty('flag');
|
||||
// Returns ISO codes only; display metadata (flag/label) is resolved by clients from core/models.
|
||||
expect(languages.every(code => typeof code === 'string')).toBe(true);
|
||||
expect(languages).toContain('en');
|
||||
expect(languages).toContain('nl');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,108 +1,47 @@
|
||||
/**
|
||||
* Represents a language option for identity generation.
|
||||
* Internal definition of an identity-generator language: its ISO code plus the alternative locale
|
||||
* codes that map onto it. Display metadata (flag, native label) is intentionally NOT stored here —
|
||||
* clients look that up from the shared `core/models` language reference so it lives in one place.
|
||||
*/
|
||||
export interface ILanguageOption {
|
||||
interface ILanguageDef {
|
||||
/**
|
||||
* The language code (e.g., "en", "nl", "de")
|
||||
* The language code (e.g., "en", "nl", "de").
|
||||
*/
|
||||
value: string;
|
||||
|
||||
/**
|
||||
* The display label in the native language (e.g., "English", "Nederlands", "Deutsch")
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* The flag emoji for the language (e.g., "🇬🇧", "🇳🇱", "🇩🇪")
|
||||
*/
|
||||
flag: string;
|
||||
|
||||
/**
|
||||
* Alternative language codes that map to this identity generator language.
|
||||
* Used for matching UI locale codes to identity generator languages.
|
||||
* For example, "en-US", "en-GB", "en-CA" all map to "en"
|
||||
* For example, "en-US", "en-GB", "en-CA" all map to "en".
|
||||
*/
|
||||
alternativeCodes?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all available languages for identity generation.
|
||||
* Display labels are in the native language, with optional flag emoji that clients can choose to display.
|
||||
* Languages are sorted alphabetically by label for consistent display across all platforms.
|
||||
* @returns Array of language options sorted alphabetically by label
|
||||
* The languages supported by the identity generator. Only ISO codes and their alternative locale
|
||||
* codes are defined here; flag emojis and native labels come from the shared `core/models` language
|
||||
* reference (`getLanguageInfo`).
|
||||
*/
|
||||
export function getAvailableLanguages(): ILanguageOption[] {
|
||||
const languages: ILanguageOption[] = [
|
||||
{
|
||||
value: 'da',
|
||||
label: 'Dansk',
|
||||
flag: '🇩🇰',
|
||||
alternativeCodes: ['da-DK']
|
||||
},
|
||||
{
|
||||
value: 'de',
|
||||
label: 'Deutsch',
|
||||
flag: '🇩🇪',
|
||||
alternativeCodes: ['de-DE', 'de-AT', 'de-CH', 'de-LU', 'de-LI']
|
||||
},
|
||||
{
|
||||
value: 'en',
|
||||
label: 'English',
|
||||
flag: '🇬🇧',
|
||||
alternativeCodes: ['en-US', 'en-GB', 'en-CA', 'en-AU', 'en-NZ', 'en-IE', 'en-ZA', 'en-SG', 'en-IN']
|
||||
},
|
||||
{
|
||||
value: 'es',
|
||||
label: 'Español',
|
||||
flag: '🇪🇸',
|
||||
alternativeCodes: ['es-ES', 'es-MX', 'es-AR', 'es-CO', 'es-CL', 'es-PE', 'es-VE', 'es-EC', 'es-GT', 'es-CU', 'es-BO', 'es-DO', 'es-HN', 'es-PY', 'es-SV', 'es-NI', 'es-CR', 'es-PA', 'es-UY', 'es-PR']
|
||||
},
|
||||
{
|
||||
value: 'fr',
|
||||
label: 'Français',
|
||||
flag: '🇫🇷',
|
||||
alternativeCodes: ['fr-FR', 'fr-CA', 'fr-BE', 'fr-CH', 'fr-LU', 'fr-MC']
|
||||
},
|
||||
{
|
||||
value: 'it',
|
||||
label: 'Italiano',
|
||||
flag: '🇮🇹',
|
||||
alternativeCodes: ['it-IT', 'it-CH', 'it-SM', 'it-VA']
|
||||
},
|
||||
{
|
||||
value: 'nl',
|
||||
label: 'Nederlands',
|
||||
flag: '🇳🇱',
|
||||
alternativeCodes: ['nl-NL', 'nl-BE']
|
||||
},
|
||||
{
|
||||
value: 'ro',
|
||||
label: 'Română',
|
||||
flag: '🇷🇴',
|
||||
alternativeCodes: ['ro-RO', 'ro-MD']
|
||||
},
|
||||
{
|
||||
value: 'sv',
|
||||
label: 'Svenska',
|
||||
flag: '🇸🇪',
|
||||
alternativeCodes: ['sv-SE', 'sv-FI']
|
||||
},
|
||||
{
|
||||
value: 'ur',
|
||||
label: 'اردو',
|
||||
flag: '🇵🇰',
|
||||
alternativeCodes: ['ur-PK', 'ur-IN']
|
||||
},
|
||||
{
|
||||
value: 'fa',
|
||||
label: 'فارسی',
|
||||
flag: '🇮🇷',
|
||||
alternativeCodes: ['fa-IR', 'fa-AF']
|
||||
}
|
||||
];
|
||||
const LANGUAGE_DEFS: ILanguageDef[] = [
|
||||
{ value: 'da', alternativeCodes: ['da-DK'] },
|
||||
{ value: 'de', alternativeCodes: ['de-DE', 'de-AT', 'de-CH', 'de-LU', 'de-LI'] },
|
||||
{ value: 'en', alternativeCodes: ['en-US', 'en-GB', 'en-CA', 'en-AU', 'en-NZ', 'en-IE', 'en-ZA', 'en-SG', 'en-IN'] },
|
||||
{ value: 'es', alternativeCodes: ['es-ES', 'es-MX', 'es-AR', 'es-CO', 'es-CL', 'es-PE', 'es-VE', 'es-EC', 'es-GT', 'es-CU', 'es-BO', 'es-DO', 'es-HN', 'es-PY', 'es-SV', 'es-NI', 'es-CR', 'es-PA', 'es-UY', 'es-PR'] },
|
||||
{ value: 'fr', alternativeCodes: ['fr-FR', 'fr-CA', 'fr-BE', 'fr-CH', 'fr-LU', 'fr-MC'] },
|
||||
{ value: 'it', alternativeCodes: ['it-IT', 'it-CH', 'it-SM', 'it-VA'] },
|
||||
{ value: 'nl', alternativeCodes: ['nl-NL', 'nl-BE'] },
|
||||
{ value: 'ro', alternativeCodes: ['ro-RO', 'ro-MD'] },
|
||||
{ value: 'sv', alternativeCodes: ['sv-SE', 'sv-FI'] },
|
||||
{ value: 'ur', alternativeCodes: ['ur-PK', 'ur-IN'] },
|
||||
{ value: 'fa', alternativeCodes: ['fa-IR', 'fa-AF'] },
|
||||
];
|
||||
|
||||
// Sort alphabetically by label using locale-aware comparison
|
||||
return languages.sort((a, b) => a.label.localeCompare(b.label));
|
||||
/**
|
||||
* Gets all available language codes for identity generation.
|
||||
* @returns Array of ISO language codes (e.g. ["da", "de", "en", ...]).
|
||||
*/
|
||||
export function getAvailableLanguages(): string[] {
|
||||
return LANGUAGE_DEFS.map(lang => lang.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +55,7 @@ export function getAvailableLanguages(): ILanguageOption[] {
|
||||
* mapUiLanguageToIdentityLanguage("en-US") // returns "en"
|
||||
* mapUiLanguageToIdentityLanguage("nl") // returns "nl"
|
||||
* mapUiLanguageToIdentityLanguage("de-CH") // returns "de"
|
||||
* mapUiLanguageToIdentityLanguage("fr") // returns null (no French identity generator)
|
||||
* mapUiLanguageToIdentityLanguage("ja") // returns null (no Japanese identity generator)
|
||||
*/
|
||||
export function mapUiLanguageToIdentityLanguage(uiLanguageCode: string | null | undefined): string | null {
|
||||
if (!uiLanguageCode) {
|
||||
@@ -124,16 +63,15 @@ export function mapUiLanguageToIdentityLanguage(uiLanguageCode: string | null |
|
||||
}
|
||||
|
||||
const normalizedCode = uiLanguageCode.toLowerCase();
|
||||
const availableLanguages = getAvailableLanguages();
|
||||
|
||||
// First, try exact match with the primary value
|
||||
const exactMatch = availableLanguages.find(lang => lang.value.toLowerCase() === normalizedCode);
|
||||
const exactMatch = LANGUAGE_DEFS.find(lang => lang.value.toLowerCase() === normalizedCode);
|
||||
if (exactMatch) {
|
||||
return exactMatch.value;
|
||||
}
|
||||
|
||||
// Then, try matching with alternative codes
|
||||
const alternativeMatch = availableLanguages.find(lang =>
|
||||
const alternativeMatch = LANGUAGE_DEFS.find(lang =>
|
||||
lang.alternativeCodes?.some(code => code.toLowerCase() === normalizedCode)
|
||||
);
|
||||
if (alternativeMatch) {
|
||||
@@ -142,7 +80,7 @@ export function mapUiLanguageToIdentityLanguage(uiLanguageCode: string | null |
|
||||
|
||||
// Finally, try matching the base language code (e.g., "en" from "en-US")
|
||||
const baseCode = normalizedCode.split('-')[0];
|
||||
const baseMatch = availableLanguages.find(lang => lang.value.toLowerCase() === baseCode);
|
||||
const baseMatch = LANGUAGE_DEFS.find(lang => lang.value.toLowerCase() === baseCode);
|
||||
if (baseMatch) {
|
||||
return baseMatch.value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user