Generated by Spark: Add translations to remaining system dialogs and modals

This commit is contained in:
2026-02-05 19:09:14 +00:00
committed by GitHub
parent 2e56ff5a17
commit b1da351caf
5 changed files with 417 additions and 100 deletions

View File

@@ -7,110 +7,114 @@ import {
} from '@/components/ui/dialog'
import { Badge } from '@/components/ui/badge'
import { Keyboard } from '@phosphor-icons/react'
import { useTranslation } from '@/hooks/use-translation'
type KeyboardShortcut = {
keys: string[]
description: string
descriptionKey: string
category: string
}
const shortcuts: KeyboardShortcut[] = [
{
keys: ['Ctrl', 'K'],
description: 'Open quick search',
category: 'Navigation'
},
{
keys: ['Ctrl', '?'],
description: 'Show keyboard shortcuts',
category: 'Navigation'
},
{
keys: ['Escape'],
description: 'Close dialogs or modals',
category: 'Navigation'
},
{
keys: ['Alt', '1'],
description: 'Go to Dashboard',
category: 'Navigation'
},
{
keys: ['Alt', '2'],
description: 'Go to Timesheets',
category: 'Navigation'
},
{
keys: ['Alt', '3'],
description: 'Go to Billing',
category: 'Navigation'
},
{
keys: ['Alt', '4'],
description: 'Go to Payroll',
category: 'Navigation'
},
{
keys: ['Alt', '5'],
description: 'Go to Compliance',
category: 'Navigation'
},
{
keys: ['Alt', 'N'],
description: 'Open notifications',
category: 'Navigation'
},
{
keys: ['Tab'],
description: 'Move focus forward',
category: 'General'
},
{
keys: ['Shift', 'Tab'],
description: 'Move focus backward',
category: 'General'
},
{
keys: ['Enter'],
description: 'Activate button or link',
category: 'General'
},
{
keys: ['Space'],
description: 'Toggle checkbox or select',
category: 'General'
},
{
keys: ['↑', '↓', '←', '→'],
description: 'Navigate table cells',
category: 'Tables'
},
{
keys: ['Enter'],
description: 'Open row details',
category: 'Tables'
},
{
keys: ['Ctrl', 'A'],
description: 'Select all rows',
category: 'Tables'
},
]
const groupedShortcuts = shortcuts.reduce((acc, shortcut) => {
if (!acc[shortcut.category]) {
acc[shortcut.category] = []
}
acc[shortcut.category].push(shortcut)
return acc
}, {} as Record<string, KeyboardShortcut[]>)
interface KeyboardShortcutsDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
}
export function KeyboardShortcutsDialog({ open, onOpenChange }: KeyboardShortcutsDialogProps) {
const { t } = useTranslation()
const shortcuts: KeyboardShortcut[] = [
{
keys: ['Ctrl', 'K'],
descriptionKey: 'keyboardShortcuts.shortcuts.openQuickSearch',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Ctrl', '?'],
descriptionKey: 'keyboardShortcuts.shortcuts.showKeyboardShortcuts',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Escape'],
descriptionKey: 'keyboardShortcuts.shortcuts.closeDialogs',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Alt', '1'],
descriptionKey: 'keyboardShortcuts.shortcuts.goToDashboard',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Alt', '2'],
descriptionKey: 'keyboardShortcuts.shortcuts.goToTimesheets',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Alt', '3'],
descriptionKey: 'keyboardShortcuts.shortcuts.goToBilling',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Alt', '4'],
descriptionKey: 'keyboardShortcuts.shortcuts.goToPayroll',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Alt', '5'],
descriptionKey: 'keyboardShortcuts.shortcuts.goToCompliance',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Alt', 'N'],
descriptionKey: 'keyboardShortcuts.shortcuts.openNotifications',
category: 'keyboardShortcuts.categories.navigation'
},
{
keys: ['Tab'],
descriptionKey: 'keyboardShortcuts.shortcuts.moveFocusForward',
category: 'keyboardShortcuts.categories.general'
},
{
keys: ['Shift', 'Tab'],
descriptionKey: 'keyboardShortcuts.shortcuts.moveFocusBackward',
category: 'keyboardShortcuts.categories.general'
},
{
keys: ['Enter'],
descriptionKey: 'keyboardShortcuts.shortcuts.activateButton',
category: 'keyboardShortcuts.categories.general'
},
{
keys: ['Space'],
descriptionKey: 'keyboardShortcuts.shortcuts.toggleCheckbox',
category: 'keyboardShortcuts.categories.general'
},
{
keys: ['↑', '↓', '←', '→'],
descriptionKey: 'keyboardShortcuts.shortcuts.navigateTableCells',
category: 'keyboardShortcuts.categories.tables'
},
{
keys: ['Enter'],
descriptionKey: 'keyboardShortcuts.shortcuts.openRowDetails',
category: 'keyboardShortcuts.categories.tables'
},
{
keys: ['Ctrl', 'A'],
descriptionKey: 'keyboardShortcuts.shortcuts.selectAllRows',
category: 'keyboardShortcuts.categories.tables'
},
]
const groupedShortcuts = shortcuts.reduce((acc, shortcut) => {
const category = t(shortcut.category)
if (!acc[category]) {
acc[category] = []
}
acc[category].push(shortcut)
return acc
}, {} as Record<string, KeyboardShortcut[]>)
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
@@ -121,10 +125,10 @@ export function KeyboardShortcutsDialog({ open, onOpenChange }: KeyboardShortcut
<DialogHeader>
<DialogTitle id="keyboard-shortcuts-title" className="flex items-center gap-2">
<Keyboard size={24} />
Keyboard Shortcuts
{t('keyboardShortcuts.title')}
</DialogTitle>
<DialogDescription id="keyboard-shortcuts-description">
Use these keyboard shortcuts to navigate and interact with WorkForce Pro more efficiently.
{t('keyboardShortcuts.description')}
</DialogDescription>
</DialogHeader>
@@ -138,7 +142,7 @@ export function KeyboardShortcutsDialog({ open, onOpenChange }: KeyboardShortcut
key={`${category}-${index}`}
className="flex items-center justify-between py-2 px-3 rounded-md hover:bg-muted/50"
>
<span className="text-sm">{shortcut.description}</span>
<span className="text-sm">{t(shortcut.descriptionKey)}</span>
<div className="flex items-center gap-1">
{shortcut.keys.map((key, keyIndex) => (
<span key={keyIndex} className="flex items-center gap-1">
@@ -163,7 +167,10 @@ export function KeyboardShortcutsDialog({ open, onOpenChange }: KeyboardShortcut
<div className="pt-4 border-t border-border">
<p className="text-xs text-muted-foreground">
<strong>Note:</strong> On Mac, use <Badge variant="outline" className="font-mono text-xs"> Cmd</Badge> instead of <Badge variant="outline" className="font-mono text-xs">Ctrl</Badge>
<strong>{t('keyboardShortcuts.note')}</strong> {t('keyboardShortcuts.macNote', {
cmd: '⌘ Cmd',
ctrl: 'Ctrl'
})}
</p>
</div>
</DialogContent>

View File

@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { ScrollArea } from '@/components/ui/scroll-area'
import { cn } from '@/lib/utils'
import { useTranslation } from '@/hooks/use-translation'
import type { Notification } from '@/lib/types'
interface NotificationCenterProps {
@@ -20,10 +21,12 @@ export function NotificationCenter({
onMarkAllAsRead,
onDelete
}: NotificationCenterProps) {
const { t } = useTranslation()
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" size="sm" className="relative">
<Button variant="outline" size="sm" className="relative" aria-label={t('notificationCenter.title')}>
<Bell size={18} />
{unreadCount > 0 && (
<span className="absolute -top-1 -right-1 bg-destructive text-destructive-foreground text-xs rounded-full w-5 h-5 flex items-center justify-center font-medium">
@@ -34,10 +37,10 @@ export function NotificationCenter({
</PopoverTrigger>
<PopoverContent className="w-96 p-0" align="end">
<div className="flex items-center justify-between p-4 border-b border-border">
<h3 className="font-semibold">Notifications</h3>
<h3 className="font-semibold">{t('notificationCenter.title')}</h3>
{unreadCount > 0 && (
<Button variant="ghost" size="sm" onClick={onMarkAllAsRead}>
Mark all read
{t('notificationCenter.markAllRead')}
</Button>
)}
</div>
@@ -45,7 +48,7 @@ export function NotificationCenter({
{notifications.length === 0 ? (
<div className="p-8 text-center text-muted-foreground">
<Bell size={32} className="mx-auto mb-2 opacity-50" />
<p className="text-sm">No notifications</p>
<p className="text-sm">{t('notificationCenter.noNotifications')}</p>
</div>
) : (
<div className="p-2">
@@ -80,6 +83,7 @@ export function NotificationCenter({
e.stopPropagation()
onDelete(notif.id)
}}
aria-label={t('notifications.delete')}
>
<X size={14} />
</Button>

View File

@@ -2108,5 +2108,107 @@
"marginDescription": "Calculate margins, markup, and profitability metrics",
"complianceTitle": "Compliance Tracking Demo",
"complianceDescription": "Monitor document expiry and compliance status"
},
"keyboardShortcuts": {
"title": "Keyboard Shortcuts",
"description": "Use these keyboard shortcuts to navigate and interact with WorkForce Pro more efficiently.",
"categories": {
"navigation": "Navigation",
"general": "General",
"tables": "Tables"
},
"shortcuts": {
"openQuickSearch": "Open quick search",
"showKeyboardShortcuts": "Show keyboard shortcuts",
"closeDialogs": "Close dialogs or modals",
"goToDashboard": "Go to Dashboard",
"goToTimesheets": "Go to Timesheets",
"goToBilling": "Go to Billing",
"goToPayroll": "Go to Payroll",
"goToCompliance": "Go to Compliance",
"openNotifications": "Open notifications",
"moveFocusForward": "Move focus forward",
"moveFocusBackward": "Move focus backward",
"activateButton": "Activate button or link",
"toggleCheckbox": "Toggle checkbox or select",
"navigateTableCells": "Navigate table cells",
"openRowDetails": "Open row details",
"selectAllRows": "Select all rows"
},
"note": "Note:",
"macNote": "On Mac, use {{cmd}} instead of {{ctrl}}"
},
"notificationCenter": {
"title": "Notifications",
"markAllRead": "Mark all read",
"noNotifications": "No notifications",
"noNotificationsDescription": "You're all caught up!"
},
"createInvoiceDialog": {
"title": "Create Invoice",
"description": "Generate a new invoice for billing",
"invoiceNumber": "Invoice Number",
"invoiceNumberPlaceholder": "INV-2024-001",
"clientName": "Client Name",
"clientNamePlaceholder": "Enter client name",
"issueDate": "Issue Date",
"dueDate": "Due Date",
"amount": "Amount",
"amountPlaceholder": "0.00",
"currency": "Currency",
"type": "Invoice Type",
"paymentTerms": "Payment Terms",
"paymentTermsPlaceholder": "e.g., Net 30",
"notes": "Notes",
"notesPlaceholder": "Additional notes",
"cancel": "Cancel",
"create": "Create Invoice",
"invoiceTypes": {
"timesheet": "Timesheet",
"permanentPlacement": "Permanent Placement",
"creditNote": "Credit Note",
"adhoc": "Ad-hoc"
},
"validation": {
"invoiceNumberRequired": "Invoice number is required",
"clientNameRequired": "Client name is required",
"issueDateRequired": "Issue date is required",
"dueDateRequired": "Due date is required",
"amountRequired": "Amount is required",
"amountPositive": "Amount must be a positive number",
"currencyRequired": "Currency is required"
},
"creating": "Creating invoice...",
"success": "Invoice created successfully",
"error": "Failed to create invoice"
},
"createPayrollDialog": {
"title": "Create Payroll Run",
"description": "Process payroll for approved timesheets",
"periodEnding": "Period Ending",
"processingType": "Processing Type",
"processingTypes": {
"allApproved": "All Approved Timesheets",
"selectedWorkers": "Selected Workers",
"oneClick": "One-Click (Auto-select)"
},
"selectedWorkers": "Selected Workers",
"selectWorkers": "Select workers to include in this payroll run",
"availableTimesheets": "Available Timesheets",
"timesheetsFound": "{{count}} approved timesheets found",
"noTimesheets": "No approved timesheets available",
"totalHours": "Total Hours",
"totalAmount": "Total Amount",
"workerCount": "Worker Count",
"summary": "Payroll Summary",
"cancel": "Cancel",
"create": "Create Payroll",
"validation": {
"periodEndingRequired": "Period ending date is required",
"noTimesheetsSelected": "No timesheets available for payroll"
},
"creating": "Creating payroll run...",
"success": "Payroll run created successfully",
"error": "Failed to create payroll run"
}
}

View File

@@ -3644,5 +3644,107 @@
"workflowCompleted": "Flujo de trabajo completado",
"noWorkflows": "Sin flujos de trabajo activos",
"noWorkflowsDescription": "Crea un flujo de trabajo para probar aprobaciones paralelas"
},
"keyboardShortcuts": {
"title": "Atajos de Teclado",
"description": "Usa estos atajos de teclado para navegar e interactuar con WorkForce Pro de manera más eficiente.",
"categories": {
"navigation": "Navegación",
"general": "General",
"tables": "Tablas"
},
"shortcuts": {
"openQuickSearch": "Abrir búsqueda rápida",
"showKeyboardShortcuts": "Mostrar atajos de teclado",
"closeDialogs": "Cerrar diálogos o modales",
"goToDashboard": "Ir al Panel de Control",
"goToTimesheets": "Ir a Hojas de Tiempo",
"goToBilling": "Ir a Facturación",
"goToPayroll": "Ir a Nómina",
"goToCompliance": "Ir a Cumplimiento",
"openNotifications": "Abrir notificaciones",
"moveFocusForward": "Mover foco hacia adelante",
"moveFocusBackward": "Mover foco hacia atrás",
"activateButton": "Activar botón o enlace",
"toggleCheckbox": "Alternar casilla o selección",
"navigateTableCells": "Navegar celdas de tabla",
"openRowDetails": "Abrir detalles de fila",
"selectAllRows": "Seleccionar todas las filas"
},
"note": "Nota:",
"macNote": "En Mac, usa {{cmd}} en lugar de {{ctrl}}"
},
"notificationCenter": {
"title": "Notificaciones",
"markAllRead": "Marcar todo como leído",
"noNotifications": "Sin notificaciones",
"noNotificationsDescription": "¡Estás al día!"
},
"createInvoiceDialog": {
"title": "Crear Factura",
"description": "Generar una nueva factura para facturación",
"invoiceNumber": "Número de Factura",
"invoiceNumberPlaceholder": "FAC-2024-001",
"clientName": "Nombre del Cliente",
"clientNamePlaceholder": "Ingrese el nombre del cliente",
"issueDate": "Fecha de Emisión",
"dueDate": "Fecha de Vencimiento",
"amount": "Monto",
"amountPlaceholder": "0,00",
"currency": "Moneda",
"type": "Tipo de Factura",
"paymentTerms": "Términos de Pago",
"paymentTermsPlaceholder": "ej: Neto 30",
"notes": "Notas",
"notesPlaceholder": "Notas adicionales",
"cancel": "Cancelar",
"create": "Crear Factura",
"invoiceTypes": {
"timesheet": "Hoja de Tiempo",
"permanentPlacement": "Colocación Permanente",
"creditNote": "Nota de Crédito",
"adhoc": "Ad-hoc"
},
"validation": {
"invoiceNumberRequired": "El número de factura es requerido",
"clientNameRequired": "El nombre del cliente es requerido",
"issueDateRequired": "La fecha de emisión es requerida",
"dueDateRequired": "La fecha de vencimiento es requerida",
"amountRequired": "El monto es requerido",
"amountPositive": "El monto debe ser un número positivo",
"currencyRequired": "La moneda es requerida"
},
"creating": "Creando factura...",
"success": "Factura creada con éxito",
"error": "Error al crear la factura"
},
"createPayrollDialog": {
"title": "Crear Ejecución de Nómina",
"description": "Procesar nómina para hojas de tiempo aprobadas",
"periodEnding": "Fin de Período",
"processingType": "Tipo de Procesamiento",
"processingTypes": {
"allApproved": "Todas las Hojas de Tiempo Aprobadas",
"selectedWorkers": "Trabajadores Seleccionados",
"oneClick": "Un Clic (Selección Automática)"
},
"selectedWorkers": "Trabajadores Seleccionados",
"selectWorkers": "Seleccione los trabajadores a incluir en esta ejecución de nómina",
"availableTimesheets": "Hojas de Tiempo Disponibles",
"timesheetsFound": "{{count}} hojas de tiempo aprobadas encontradas",
"noTimesheets": "No hay hojas de tiempo aprobadas disponibles",
"totalHours": "Horas Totales",
"totalAmount": "Monto Total",
"workerCount": "Cantidad de Trabajadores",
"summary": "Resumen de Nómina",
"cancel": "Cancelar",
"create": "Crear Nómina",
"validation": {
"periodEndingRequired": "La fecha de fin de período es requerida",
"noTimesheetsSelected": "No hay hojas de tiempo disponibles para nómina"
},
"creating": "Creando ejecución de nómina...",
"success": "Ejecución de nómina creada con éxito",
"error": "Error al crear la ejecución de nómina"
}
}

View File

@@ -1891,5 +1891,107 @@
"marginDescription": "Calcule les marges, le markup et les métriques de rentabilité",
"complianceTitle": "Démo de Suivi de la Conformité",
"complianceDescription": "Surveille l'expiration des documents et l'état de conformité"
},
"keyboardShortcuts": {
"title": "Raccourcis Clavier",
"description": "Utilisez ces raccourcis clavier pour naviguer et interagir avec WorkForce Pro plus efficacement.",
"categories": {
"navigation": "Navigation",
"general": "Général",
"tables": "Tableaux"
},
"shortcuts": {
"openQuickSearch": "Ouvrir la recherche rapide",
"showKeyboardShortcuts": "Afficher les raccourcis clavier",
"closeDialogs": "Fermer les dialogues ou modales",
"goToDashboard": "Aller au Tableau de Bord",
"goToTimesheets": "Aller aux Feuilles de Temps",
"goToBilling": "Aller à la Facturation",
"goToPayroll": "Aller à la Paie",
"goToCompliance": "Aller à la Conformité",
"openNotifications": "Ouvrir les notifications",
"moveFocusForward": "Déplacer le focus vers l'avant",
"moveFocusBackward": "Déplacer le focus vers l'arrière",
"activateButton": "Activer le bouton ou le lien",
"toggleCheckbox": "Basculer la case à cocher ou la sélection",
"navigateTableCells": "Naviguer dans les cellules du tableau",
"openRowDetails": "Ouvrir les détails de la ligne",
"selectAllRows": "Sélectionner toutes les lignes"
},
"note": "Note :",
"macNote": "Sur Mac, utilisez {{cmd}} au lieu de {{ctrl}}"
},
"notificationCenter": {
"title": "Notifications",
"markAllRead": "Tout marquer comme lu",
"noNotifications": "Aucune notification",
"noNotificationsDescription": "Vous êtes à jour !"
},
"createInvoiceDialog": {
"title": "Créer une Facture",
"description": "Générer une nouvelle facture pour la facturation",
"invoiceNumber": "Numéro de Facture",
"invoiceNumberPlaceholder": "FAC-2024-001",
"clientName": "Nom du Client",
"clientNamePlaceholder": "Entrez le nom du client",
"issueDate": "Date d'Émission",
"dueDate": "Date d'Échéance",
"amount": "Montant",
"amountPlaceholder": "0,00",
"currency": "Devise",
"type": "Type de Facture",
"paymentTerms": "Conditions de Paiement",
"paymentTermsPlaceholder": "ex : Net 30",
"notes": "Notes",
"notesPlaceholder": "Notes supplémentaires",
"cancel": "Annuler",
"create": "Créer la Facture",
"invoiceTypes": {
"timesheet": "Feuille de Temps",
"permanentPlacement": "Placement Permanent",
"creditNote": "Note de Crédit",
"adhoc": "Ad-hoc"
},
"validation": {
"invoiceNumberRequired": "Le numéro de facture est requis",
"clientNameRequired": "Le nom du client est requis",
"issueDateRequired": "La date d'émission est requise",
"dueDateRequired": "La date d'échéance est requise",
"amountRequired": "Le montant est requis",
"amountPositive": "Le montant doit être un nombre positif",
"currencyRequired": "La devise est requise"
},
"creating": "Création de la facture...",
"success": "Facture créée avec succès",
"error": "Échec de la création de la facture"
},
"createPayrollDialog": {
"title": "Créer un Traitement de Paie",
"description": "Traiter la paie pour les feuilles de temps approuvées",
"periodEnding": "Fin de Période",
"processingType": "Type de Traitement",
"processingTypes": {
"allApproved": "Toutes les Feuilles de Temps Approuvées",
"selectedWorkers": "Travailleurs Sélectionnés",
"oneClick": "Un Clic (Sélection Auto)"
},
"selectedWorkers": "Travailleurs Sélectionnés",
"selectWorkers": "Sélectionnez les travailleurs à inclure dans ce traitement de paie",
"availableTimesheets": "Feuilles de Temps Disponibles",
"timesheetsFound": "{{count}} feuilles de temps approuvées trouvées",
"noTimesheets": "Aucune feuille de temps approuvée disponible",
"totalHours": "Heures Totales",
"totalAmount": "Montant Total",
"workerCount": "Nombre de Travailleurs",
"summary": "Résumé de la Paie",
"cancel": "Annuler",
"create": "Créer la Paie",
"validation": {
"periodEndingRequired": "La date de fin de période est requise",
"noTimesheetsSelected": "Aucune feuille de temps disponible pour la paie"
},
"creating": "Création du traitement de paie...",
"success": "Traitement de paie créé avec succès",
"error": "Échec de la création du traitement de paie"
}
}