From 871852ff596e1ce7614453ea13b002079c30376d Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Fri, 12 Dec 2025 11:29:38 +0100 Subject: [PATCH] Translate field labels based on field key in each client locally (#1404) --- .../Credentials/Details/FieldBlock.tsx | 15 +++++++++----- .../popup/pages/credentials/ItemAddEdit.tsx | 10 +++++----- .../src/i18n/locales/en.json | 18 +++++++++++++++++ .../src/utils/SqliteClient.ts | 12 +++++------ core/models/src/vault/SystemFieldRegistry.ts | 20 +------------------ 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/apps/browser-extension/src/entrypoints/popup/components/Credentials/Details/FieldBlock.tsx b/apps/browser-extension/src/entrypoints/popup/components/Credentials/Details/FieldBlock.tsx index 55fc45c2b..015ba4f62 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/Credentials/Details/FieldBlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/Credentials/Details/FieldBlock.tsx @@ -36,6 +36,11 @@ const FieldBlock: React.FC = ({ field, itemId }) => { const [showHistoryModal, setShowHistoryModal] = useState(false); const [historyCount, setHistoryCount] = useState(0); + /* Get translated label for this field. System fields use fieldLabels.* translations, custom fields use their stored label */ + const label = field.FieldKey.startsWith('custom_') + ? (field.Label || field.FieldKey) + : t(`fieldLabels.${field.FieldKey}`, { defaultValue: field.FieldKey }); + // Check if this field supports history const systemField = !field.FieldKey.startsWith('custom_') ? getSystemField(field.FieldKey) : null; const hasHistoryEnabled = systemField?.EnableHistory === true; @@ -67,7 +72,7 @@ const FieldBlock: React.FC = ({ field, itemId }) => { @@ -99,7 +104,7 @@ const FieldBlock: React.FC = ({ field, itemId }) => { onClose={() => setShowHistoryModal(false)} itemId={itemId} fieldKey={field.FieldKey} - fieldLabel={field.Label} + fieldLabel={label} fieldType={field.FieldType} isHidden={field.IsHidden} /> @@ -113,7 +118,7 @@ const FieldBlock: React.FC = ({ field, itemId }) => { <>
@@ -154,7 +159,7 @@ const FieldBlock: React.FC = ({ field, itemId }) => { return (

= ({ field, itemId }) => { <>

diff --git a/apps/browser-extension/src/entrypoints/popup/pages/credentials/ItemAddEdit.tsx b/apps/browser-extension/src/entrypoints/popup/pages/credentials/ItemAddEdit.tsx index d01471a89..51c135c45 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/credentials/ItemAddEdit.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/credentials/ItemAddEdit.tsx @@ -374,7 +374,7 @@ const ItemAddEdit: React.FC = () => { // Build the fields array from fieldValues const fields: ItemField[] = []; - // Add system fields + /* Add system fields */ applicableSystemFields.forEach(systemField => { const value = fieldValues[systemField.FieldKey]; @@ -382,7 +382,7 @@ const ItemAddEdit: React.FC = () => { if (value && (Array.isArray(value) ? value.length > 0 : value.trim() !== '')) { fields.push({ FieldKey: systemField.FieldKey, - Label: systemField.Label, + Label: systemField.FieldKey, // UI translates via fieldLabels.* FieldType: systemField.FieldType, Value: value, IsHidden: systemField.IsHidden, @@ -798,7 +798,7 @@ const ItemAddEdit: React.FC = () => {
{renderFieldInput( field.FieldKey, - field.Label, + t(`fieldLabels.${field.FieldKey}`, { defaultValue: field.FieldKey }), field.FieldType, field.IsHidden, field.IsMultiValue @@ -821,7 +821,7 @@ const ItemAddEdit: React.FC = () => {
{renderFieldInput( field.FieldKey, - field.Label, + t(`fieldLabels.${field.FieldKey}`, { defaultValue: field.FieldKey }), field.FieldType, field.IsHidden, field.IsMultiValue @@ -877,7 +877,7 @@ const ItemAddEdit: React.FC = () => { > {renderFieldInput( notesField.FieldKey, - notesField.Label, + t(`fieldLabels.${notesField.FieldKey}`, { defaultValue: notesField.FieldKey }), notesField.FieldType, notesField.IsHidden, notesField.IsMultiValue diff --git a/apps/browser-extension/src/i18n/locales/en.json b/apps/browser-extension/src/i18n/locales/en.json index 8a10965c6..39fc31af2 100644 --- a/apps/browser-extension/src/i18n/locales/en.json +++ b/apps/browser-extension/src/i18n/locales/en.json @@ -330,6 +330,24 @@ "description": "Encrypted notes and private information" } }, + "fieldLabels": { + "login.username": "Username", + "login.password": "Password", + "login.url": "Website URL", + "alias.email": "Email", + "alias.first_name": "First Name", + "alias.last_name": "Last Name", + "alias.nickname": "Nickname", + "alias.gender": "Gender", + "alias.birthdate": "Birth Date", + "card.cardholder_name": "Cardholder Name", + "card.number": "Card Number", + "card.expiry_month": "Expiry Month", + "card.expiry_year": "Expiry Year", + "card.cvv": "CVV", + "card.pin": "PIN", + "metadata.notes": "Notes" + }, "totp": { "addCode": "Add 2FA Code", "instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.", diff --git a/apps/browser-extension/src/utils/SqliteClient.ts b/apps/browser-extension/src/utils/SqliteClient.ts index 2a2d413d0..b6ff8fbd7 100644 --- a/apps/browser-extension/src/utils/SqliteClient.ts +++ b/apps/browser-extension/src/utils/SqliteClient.ts @@ -420,7 +420,7 @@ export class SqliteClient { DisplayOrder: number; }>(fieldsQuery, itemIds); - // Process fields - handle system fields vs custom fields + /* Process fields - handle system fields vs custom fields. */ const fields = fieldRows.map(row => { // System field: has FieldKey, get metadata from SystemFieldRegistry if (row.FieldKey) { @@ -428,7 +428,7 @@ export class SqliteClient { return { ItemId: row.ItemId, FieldKey: row.FieldKey, - Label: systemField?.Label || row.FieldKey, + Label: row.FieldKey, // Use FieldKey as label; UI layer translates via fieldLabels.* FieldType: systemField?.FieldType || 'Text', IsHidden: systemField?.IsHidden ? 1 : 0, Value: row.Value, @@ -615,14 +615,14 @@ export class SqliteClient { } fieldValuesByKey[fieldKey].push(row.Value); - // Store field metadata (only once per FieldKey) + /* Store field metadata (only once per FieldKey). */ if (!uniqueFields[fieldKey]) { if (row.FieldKey) { // System field const systemField = getSystemField(row.FieldKey); uniqueFields[fieldKey] = { FieldKey: row.FieldKey, - Label: systemField?.Label || row.FieldKey, + Label: row.FieldKey, // Use FieldKey as label; UI layer translates via fieldLabels.* FieldType: systemField?.FieldType || 'Text', IsHidden: systemField?.IsHidden ? 1 : 0, DisplayOrder: row.DisplayOrder @@ -2825,14 +2825,14 @@ export class SqliteClient { DisplayOrder: number; }>(fieldsQuery, itemIds); - // Process fields + /* Process fields. System fields use FieldKey for Label (translation happens in UI layer) */ const fields = fieldRows.map(row => { if (row.FieldKey) { const systemField = getSystemField(row.FieldKey); return { ItemId: row.ItemId, FieldKey: row.FieldKey, - Label: systemField?.Label || row.FieldKey, + Label: row.FieldKey, // UI layer translates via fieldLabels.* FieldType: systemField?.FieldType || 'Text', IsHidden: systemField?.IsHidden ? 1 : 0, Value: row.Value, diff --git a/core/models/src/vault/SystemFieldRegistry.ts b/core/models/src/vault/SystemFieldRegistry.ts index f596ec375..02576ec40 100644 --- a/core/models/src/vault/SystemFieldRegistry.ts +++ b/core/models/src/vault/SystemFieldRegistry.ts @@ -29,13 +29,11 @@ export type FieldCategory = typeof FieldCategories[keyof typeof FieldCategories] /** * System field definition with metadata. * System fields are predefined fields with immutable keys like 'login.username'. - * Their metadata (label, type, etc.) is defined here in code, not in the database. + * Their metadata (type, etc.) is defined here in code, not in the database. */ export type SystemFieldDefinition = { /** Unique system field key (e.g., 'login.username') */ FieldKey: string; - /** Display label for the field */ - Label: string; /** Field type for rendering/validation */ FieldType: FieldType; /** Whether field is hidden/masked by default */ @@ -69,7 +67,6 @@ export const SystemFieldRegistry: Record = { /* =================== LOGIN FIELDS =================== */ 'login.username': { FieldKey: 'login.username', - Label: 'Username', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -83,7 +80,6 @@ export const SystemFieldRegistry: Record = { }, 'login.password': { FieldKey: 'login.password', - Label: 'Password', FieldType: 'Password', IsHidden: true, IsMultiValue: false, @@ -97,7 +93,6 @@ export const SystemFieldRegistry: Record = { }, 'login.url': { FieldKey: 'login.url', - Label: 'URL', FieldType: 'URL', IsHidden: false, IsMultiValue: true, @@ -113,7 +108,6 @@ export const SystemFieldRegistry: Record = { /* =================== ALIAS FIELDS =================== */ 'alias.email': { FieldKey: 'alias.email', - Label: 'Email', FieldType: 'Email', IsHidden: false, IsMultiValue: false, @@ -126,7 +120,6 @@ export const SystemFieldRegistry: Record = { }, 'alias.first_name': { FieldKey: 'alias.first_name', - Label: 'First Name', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -139,7 +132,6 @@ export const SystemFieldRegistry: Record = { }, 'alias.last_name': { FieldKey: 'alias.last_name', - Label: 'Last Name', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -152,7 +144,6 @@ export const SystemFieldRegistry: Record = { }, 'alias.nickname': { FieldKey: 'alias.nickname', - Label: 'Nickname', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -165,7 +156,6 @@ export const SystemFieldRegistry: Record = { }, 'alias.gender': { FieldKey: 'alias.gender', - Label: 'Gender', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -178,7 +168,6 @@ export const SystemFieldRegistry: Record = { }, 'alias.birthdate': { FieldKey: 'alias.birthdate', - Label: 'Birth Date', FieldType: 'Date', IsHidden: false, IsMultiValue: false, @@ -193,7 +182,6 @@ export const SystemFieldRegistry: Record = { /* =================== CREDIT CARD FIELDS =================== */ 'card.cardholder_name': { FieldKey: 'card.cardholder_name', - Label: 'Cardholder Name', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -206,7 +194,6 @@ export const SystemFieldRegistry: Record = { }, 'card.number': { FieldKey: 'card.number', - Label: 'Card Number', FieldType: 'Hidden', IsHidden: true, IsMultiValue: false, @@ -219,7 +206,6 @@ export const SystemFieldRegistry: Record = { }, 'card.expiry_month': { FieldKey: 'card.expiry_month', - Label: 'Expiry Month', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -232,7 +218,6 @@ export const SystemFieldRegistry: Record = { }, 'card.expiry_year': { FieldKey: 'card.expiry_year', - Label: 'Expiry Year', FieldType: 'Text', IsHidden: false, IsMultiValue: false, @@ -245,7 +230,6 @@ export const SystemFieldRegistry: Record = { }, 'card.cvv': { FieldKey: 'card.cvv', - Label: 'CVV', FieldType: 'Hidden', IsHidden: true, IsMultiValue: false, @@ -258,7 +242,6 @@ export const SystemFieldRegistry: Record = { }, 'card.pin': { FieldKey: 'card.pin', - Label: 'PIN', FieldType: 'Hidden', IsHidden: true, IsMultiValue: false, @@ -273,7 +256,6 @@ export const SystemFieldRegistry: Record = { /* =================== METADATA FIELDS =================== */ 'metadata.notes': { FieldKey: 'metadata.notes', - Label: 'Notes', FieldType: 'TextArea', IsHidden: false, IsMultiValue: false,