Translate field labels based on field key in each client locally (#1404)

This commit is contained in:
Leendert de Borst
2025-12-12 11:29:38 +01:00
parent e36cf3ee34
commit 871852ff59
5 changed files with 40 additions and 35 deletions

View File

@@ -36,6 +36,11 @@ const FieldBlock: React.FC<FieldBlockProps> = ({ field, itemId }) => {
const [showHistoryModal, setShowHistoryModal] = useState(false);
const [historyCount, setHistoryCount] = useState<number>(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<FieldBlockProps> = ({ field, itemId }) => {
<FormInputCopyToClipboard
key={`${field.FieldKey}-${idx}`}
id={`${field.FieldKey}-${idx}`}
label={idx === 0 ? field.Label : `${field.Label} ${idx + 1}`}
label={idx === 0 ? label : `${label} ${idx + 1}`}
value={value}
type={field.FieldType === 'Password' ? 'password' : 'text'}
/>
@@ -99,7 +104,7 @@ const FieldBlock: React.FC<FieldBlockProps> = ({ 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<FieldBlockProps> = ({ field, itemId }) => {
<>
<div>
<label htmlFor={field.FieldKey} className="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">
{field.Label}
{label}
{HistoryButton}
</label>
<div className="relative">
@@ -154,7 +159,7 @@ const FieldBlock: React.FC<FieldBlockProps> = ({ field, itemId }) => {
return (
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{field.Label}
{label}
</label>
<div className="p-4 bg-gray-50 rounded-lg dark:bg-gray-700">
<p
@@ -176,7 +181,7 @@ const FieldBlock: React.FC<FieldBlockProps> = ({ field, itemId }) => {
<>
<div>
<label htmlFor={field.FieldKey} className="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">
{field.Label}
{label}
{HistoryButton}
</label>
<div className="relative">

View File

@@ -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 = () => {
<div key={field.FieldKey}>
{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 = () => {
<div key={field.FieldKey}>
{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

View File

@@ -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.",

View File

@@ -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,

View File

@@ -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<string, SystemFieldDefinition> = {
/* =================== LOGIN FIELDS =================== */
'login.username': {
FieldKey: 'login.username',
Label: 'Username',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
@@ -83,7 +80,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'login.password': {
FieldKey: 'login.password',
Label: 'Password',
FieldType: 'Password',
IsHidden: true,
IsMultiValue: false,
@@ -97,7 +93,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'login.url': {
FieldKey: 'login.url',
Label: 'URL',
FieldType: 'URL',
IsHidden: false,
IsMultiValue: true,
@@ -113,7 +108,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
/* =================== ALIAS FIELDS =================== */
'alias.email': {
FieldKey: 'alias.email',
Label: 'Email',
FieldType: 'Email',
IsHidden: false,
IsMultiValue: false,
@@ -126,7 +120,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'alias.first_name': {
FieldKey: 'alias.first_name',
Label: 'First Name',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
@@ -139,7 +132,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'alias.last_name': {
FieldKey: 'alias.last_name',
Label: 'Last Name',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
@@ -152,7 +144,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'alias.nickname': {
FieldKey: 'alias.nickname',
Label: 'Nickname',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
@@ -165,7 +156,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'alias.gender': {
FieldKey: 'alias.gender',
Label: 'Gender',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
@@ -178,7 +168,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'alias.birthdate': {
FieldKey: 'alias.birthdate',
Label: 'Birth Date',
FieldType: 'Date',
IsHidden: false,
IsMultiValue: false,
@@ -193,7 +182,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
/* =================== 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<string, SystemFieldDefinition> = {
},
'card.number': {
FieldKey: 'card.number',
Label: 'Card Number',
FieldType: 'Hidden',
IsHidden: true,
IsMultiValue: false,
@@ -219,7 +206,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'card.expiry_month': {
FieldKey: 'card.expiry_month',
Label: 'Expiry Month',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
@@ -232,7 +218,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'card.expiry_year': {
FieldKey: 'card.expiry_year',
Label: 'Expiry Year',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
@@ -245,7 +230,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'card.cvv': {
FieldKey: 'card.cvv',
Label: 'CVV',
FieldType: 'Hidden',
IsHidden: true,
IsMultiValue: false,
@@ -258,7 +242,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
},
'card.pin': {
FieldKey: 'card.pin',
Label: 'PIN',
FieldType: 'Hidden',
IsHidden: true,
IsMultiValue: false,
@@ -273,7 +256,6 @@ export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
/* =================== METADATA FIELDS =================== */
'metadata.notes': {
FieldKey: 'metadata.notes',
Label: 'Notes',
FieldType: 'TextArea',
IsHidden: false,
IsMultiValue: false,