mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-18 13:28:12 -04:00
Translate field labels based on field key in each client locally (#1404)
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user