Use current UI language as default identity generator language (#1383)

This commit is contained in:
Leendert de Borst
2025-11-22 06:43:45 +01:00
committed by Leendert de Borst
parent 51dc4d2844
commit 85e33a9fcd
10 changed files with 150 additions and 11 deletions

View File

@@ -334,12 +334,13 @@ export function handleGetDefaultEmailDomain(): Promise<stringResponse> {
/**
* Get the default identity settings.
* Returns the effective language (with smart UI language matching if no explicit override is set).
*/
export async function handleGetDefaultIdentitySettings(
) : Promise<IdentitySettingsResponse> {
try {
const sqliteClient = await createVaultSqliteClient();
const language = sqliteClient.getDefaultIdentityLanguage();
const language = await sqliteClient.getEffectiveIdentityLanguage();
const gender = sqliteClient.getDefaultIdentityGender();
return {

View File

@@ -370,8 +370,8 @@ const CredentialAddEdit: React.FC = () => {
* Initialize the identity and password generators with settings from user's vault.
*/
const initializeGenerators = useCallback(async () => {
// Get default identity language from database
const identityLanguage = dbContext.sqliteClient!.getDefaultIdentityLanguage();
// Get effective identity language (smart default based on UI language if no explicit override)
const identityLanguage = await dbContext.sqliteClient!.getEffectiveIdentityLanguage();
// Initialize identity generator based on language
const identityGenerator = CreateIdentityGenerator(identityLanguage);

View File

@@ -427,9 +427,36 @@ export class SqliteClient {
/**
* Get the default identity language from the database.
* Returns the stored override value if set, otherwise returns empty string to indicate no explicit preference.
* Use getEffectiveIdentityLanguage() to get the language with smart defaults based on UI language.
*/
public getDefaultIdentityLanguage(): string {
return this.getSetting('DefaultIdentityLanguage', 'en');
return this.getSetting('DefaultIdentityLanguage');
}
/**
* Get the effective identity generator language to use.
* If user has explicitly set a language preference, use that.
* Otherwise, intelligently match the UI language to an available identity generator language.
* Falls back to "en" if no match is found.
*/
public async getEffectiveIdentityLanguage(): Promise<string> {
const explicitLanguage = this.getDefaultIdentityLanguage();
// If user has explicitly set a language preference, use it
if (explicitLanguage) {
return explicitLanguage;
}
// Otherwise, try to match UI language to an identity generator language
const { mapUiLanguageToIdentityLanguage } = await import('@/utils/dist/shared/identity-generator');
const { default: i18n } = await import('@/i18n/i18n');
const uiLanguage = i18n.language;
const mappedLanguage = mapUiLanguageToIdentityLanguage(uiLanguage);
// Return the mapped language, or fall back to "en" if no match found
return mappedLanguage ?? 'en';
}
/**

View File

@@ -96,7 +96,7 @@ export default function AddEditCredentialScreen() : React.ReactNode {
* Generate a random identity.
*/
const generateRandomIdentity = useCallback(async () : Promise<Identity> => {
const identityLanguage = await dbContext.sqliteClient!.getDefaultIdentityLanguage();
const identityLanguage = await dbContext.sqliteClient!.getEffectiveIdentityLanguage();
const identityGenerator = CreateIdentityGenerator(identityLanguage);
const genderPreference = await dbContext.sqliteClient!.getDefaultIdentityGender();

View File

@@ -55,7 +55,7 @@ export default function IdentityGeneratorSettingsScreen(): React.ReactNode {
const loadSettings = async (): Promise<void> => {
try {
const [currentLanguage, currentGender, currentAgeRange] = await Promise.all([
dbContext.sqliteClient!.getDefaultIdentityLanguage(),
dbContext.sqliteClient!.getEffectiveIdentityLanguage(),
dbContext.sqliteClient!.getDefaultIdentityGender(),
dbContext.sqliteClient!.getDefaultIdentityAgeRange()
]);

View File

@@ -464,9 +464,36 @@ class SqliteClient {
/**
* Get the default identity language from the database.
* Returns the stored override value if set, otherwise returns empty string to indicate no explicit preference.
* Use getEffectiveIdentityLanguage() to get the language with smart defaults based on UI language.
*/
public async getDefaultIdentityLanguage(): Promise<string> {
return this.getSetting('DefaultIdentityLanguage', 'en');
return this.getSetting('DefaultIdentityLanguage');
}
/**
* Get the effective identity generator language to use.
* If user has explicitly set a language preference, use that.
* Otherwise, intelligently match the UI language to an available identity generator language.
* Falls back to "en" if no match is found.
*/
public async getEffectiveIdentityLanguage(): Promise<string> {
const explicitLanguage = await this.getDefaultIdentityLanguage();
// If user has explicitly set a language preference, use it
if (explicitLanguage) {
return explicitLanguage;
}
// Otherwise, try to match UI language to an identity generator language
const { mapUiLanguageToIdentityLanguage } = await import('@/utils/dist/shared/identity-generator');
const { default: i18n } = await import('@/i18n');
const uiLanguage = i18n.language;
const mappedLanguage = mapUiLanguageToIdentityLanguage(uiLanguage);
// Return the mapped language, or fall back to "en" if no match found
return mappedLanguage ?? 'en';
}
/**

View File

@@ -181,13 +181,33 @@
}
}
AutoEmailRefresh = DbService.Settings.AutoEmailRefresh;
DefaultIdentityLanguage = DbService.Settings.DefaultIdentityLanguage;
await SetDefaultIdentityLanguageAsync();
DefaultIdentityGender = DbService.Settings.DefaultIdentityGender;
DefaultIdentityAgeRange = DbService.Settings.DefaultIdentityAgeRange;
AppLanguage = DbService.Settings.AppLanguage;
ClipboardClearSeconds = DbService.Settings.ClipboardClearSeconds;
}
/// <summary>
/// Sets the default identity language.
/// </summary>
private async Task SetDefaultIdentityLanguageAsync()
{
var explicitLanguage = DbService.Settings.DefaultIdentityLanguage;
if (!string.IsNullOrWhiteSpace(explicitLanguage))
{
// User has explicitly set a language, use it
DefaultIdentityLanguage = explicitLanguage;
}
else
{
// No explicit language set, try to match UI language to identity generator language
var mappedLanguage = await JsInteropService.MapUiLanguageToIdentityLanguageAsync(DbService.Settings.AppLanguage);
DefaultIdentityLanguage = mappedLanguage ?? "en";
}
}
/// <summary>
/// Updates the default email domain.
/// </summary>
@@ -235,10 +255,14 @@
/// <summary>
/// Updates the app language setting.
/// Also refreshes the identity generator language to reflect the new UI language if no explicit override is set.
/// </summary>
private async Task UpdateAppLanguage()
{
await LanguageService.SetLanguageAsync(AppLanguage);
// After changing UI language, refresh identity generator language if user hasn't set an explicit override
await SetDefaultIdentityLanguageAsync();
StateHasChanged();
}

View File

@@ -61,8 +61,11 @@ public sealed class CredentialService(HttpClient httpClient, DbService dbService
// Convert age range to birthdate options using shared JS utility
var birthdateOptions = await jsInteropService.ConvertAgeRangeToBirthdateOptionsAsync(dbService.Settings.DefaultIdentityAgeRange);
// Get the effective identity language (smart default based on UI language if no explicit override is set)
var identityLanguage = await GetEffectiveIdentityLanguageAsync();
// Generate a random identity using the TypeScript library
var identity = await jsInteropService.GenerateRandomIdentityAsync(dbService.Settings.DefaultIdentityLanguage, dbService.Settings.DefaultIdentityGender, birthdateOptions);
var identity = await jsInteropService.GenerateRandomIdentityAsync(identityLanguage, dbService.Settings.DefaultIdentityGender, birthdateOptions);
// Generate random values for the Identity properties
credential.Username = identity.NickName;
@@ -576,4 +579,29 @@ public sealed class CredentialService(HttpClient httpClient, DbService dbService
}
}
}
/// <summary>
/// Gets the effective identity generator language to use.
/// If user has explicitly set a language preference, use that.
/// Otherwise, intelligently match the UI language to an available identity generator language.
/// Falls back to "en" if no match is found.
/// </summary>
/// <returns>The identity generator language code to use.</returns>
private async Task<string> GetEffectiveIdentityLanguageAsync()
{
var explicitLanguage = dbService.Settings.DefaultIdentityLanguage;
// If user has explicitly set a language preference, use it
if (!string.IsNullOrWhiteSpace(explicitLanguage))
{
return explicitLanguage;
}
// Otherwise, try to match UI language to an identity generator language
var uiLanguage = dbService.Settings.AppLanguage;
var mappedLanguage = await jsInteropService.MapUiLanguageToIdentityLanguageAsync(uiLanguage);
// Return the mapped language, or fall back to "en" if no match found
return mappedLanguage ?? "en";
}
}

View File

@@ -406,6 +406,36 @@ public sealed class JsInteropService(IJSRuntime jsRuntime)
}
}
/// <summary>
/// Maps a UI language code to an identity generator language code.
/// If no explicit match is found, returns null to indicate no preference.
/// </summary>
/// <param name="uiLanguageCode">The UI language code (e.g., "en", "en-US", "nl-NL", "de-DE", "fr").</param>
/// <returns>The matching identity generator language code or null if no match.</returns>
public async Task<string?> MapUiLanguageToIdentityLanguageAsync(string? uiLanguageCode)
{
try
{
if (string.IsNullOrEmpty(uiLanguageCode))
{
return null;
}
if (_identityGeneratorModule == null)
{
await InitializeAsync();
}
var result = await _identityGeneratorModule!.InvokeAsync<string?>("mapUiLanguageToIdentityLanguage", uiLanguageCode);
return result;
}
catch (JSException ex)
{
await Console.Error.WriteLineAsync($"JavaScript error mapping UI language to identity language: {ex.Message}");
return null;
}
}
/// <summary>
/// Converts an age range string to birthdate options using the shared JavaScript utility.
/// </summary>

View File

@@ -43,9 +43,11 @@ public sealed class SettingsService
/// <summary>
/// Gets the DefaultIdentityLanguage setting.
/// Returns the stored override value if set, otherwise returns empty string to indicate no explicit preference.
/// Use GetEffectiveIdentityLanguageAsync() to get the language with smart defaults based on UI language.
/// </summary>
/// <returns>Default identity language as two-letter code.</returns>
public string DefaultIdentityLanguage => GetSetting("DefaultIdentityLanguage", "en")!;
/// <returns>Default identity language as two-letter code, or empty string if not set.</returns>
public string DefaultIdentityLanguage => GetSetting("DefaultIdentityLanguage");
/// <summary>
/// Gets the DefaultIdentityGender setting.