Files
Autoparts-DB/pos/static/js/i18n.js
consultoria-as c1d0638b45 feat(pos): add multi-language i18n (#37) and multi-currency USD/MXN (#38)
- i18n.js with 130+ translation keys for es/en, loaded in all 11 templates
- sidebar.js uses t() for all nav labels, adds MX/US language toggle
- app-init.js role labels use i18n
- currency.py service with convert() and format_currency()
- config.py adds DEFAULT_CURRENCY and EXCHANGE_RATE_USD_MXN settings
- config_bp.py adds GET/PUT /pos/api/config/currency endpoints
- config.html adds currency/exchange-rate section (Section 8)
- config.js adds loadCurrency/saveCurrency with localStorage sync
- pos.js fmt() reads pos_currency from localStorage for USD/MXN display

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 08:19:18 +00:00

343 lines
8.7 KiB
JavaScript

/**
* i18n.js — Simple internationalization for Nexus Autoparts POS
* Supports: es (Spanish/Mexico) and en (English/USA)
*/
var I18N = {
es: {
// Sidebar nav
'dashboard': 'Dashboard',
'pos': 'Punto de Venta',
'catalog': 'Catalogo',
'inventory': 'Inventario',
'customers': 'Clientes',
'invoicing': 'Facturacion',
'accounting': 'Contabilidad',
'reports': 'Reportes',
'fleet': 'Flotillas',
'whatsapp': 'WhatsApp',
'config': 'Configuracion',
// Sidebar sections
'nav_main': 'Principal',
'nav_management': 'Gestion',
'nav_system': 'Sistema',
// Common actions
'search': 'Buscar',
'save': 'Guardar',
'cancel': 'Cancelar',
'delete': 'Eliminar',
'edit': 'Editar',
'new': 'Nuevo',
'close': 'Cerrar',
'confirm': 'Confirmar',
'back': 'Regresar',
'next': 'Siguiente',
'print': 'Imprimir',
'export': 'Exportar',
'import': 'Importar',
'refresh': 'Actualizar',
'loading': 'Cargando...',
'no_results': 'Sin resultados',
'error': 'Error',
'success': 'Exito',
'warning': 'Advertencia',
// Financial
'total': 'Total',
'subtotal': 'Subtotal',
'tax': 'IVA',
'price': 'Precio',
'unit_price': 'Precio Unitario',
'cost': 'Costo',
'discount': 'Descuento',
'margin': 'Margen',
'profit': 'Utilidad',
'balance': 'Saldo',
'amount': 'Monto',
// Inventory
'quantity': 'Cantidad',
'stock': 'Existencias',
'min_stock': 'Minimo',
'max_stock': 'Maximo',
'sku': 'SKU',
'barcode': 'Codigo de Barras',
'brand': 'Marca',
'category': 'Categoria',
'description': 'Descripcion',
'location': 'Ubicacion',
// Table headers
'name': 'Nombre',
'date': 'Fecha',
'status': 'Estado',
'actions': 'Acciones',
'id': 'ID',
'type': 'Tipo',
'notes': 'Notas',
// POS
'charge': 'Cobrar',
'quote': 'Cotizacion',
'layaway': 'Apartado',
'credit': 'Credito',
'cash': 'Efectivo',
'transfer': 'Transferencia',
'card': 'Tarjeta',
'mixed': 'Mixto',
'change': 'Cambio',
'customer': 'Cliente',
'general_public': 'Publico General',
'sale': 'Venta',
'sales': 'Ventas',
'ticket': 'Ticket',
'receipt': 'Recibo',
'payment': 'Pago',
'payment_method': 'Metodo de Pago',
'add_to_cart': 'Agregar',
'clear_cart': 'Limpiar Carrito',
'hold_sale': 'Pausar Venta',
'recall_sale': 'Retomar Venta',
'cancel_sale': 'Cancelar Venta',
'confirm_payment': 'Confirmar Pago',
'cash_received': 'Efectivo Recibido',
'amount_due': 'Total a Pagar',
'remaining': 'Faltante',
// Customers
'phone': 'Telefono',
'email': 'Correo',
'address': 'Direccion',
'rfc': 'RFC',
'credit_limit': 'Limite de Credito',
'credit_balance': 'Saldo de Credito',
'price_tier': 'Nivel de Precio',
// Invoicing
'invoice': 'Factura',
'cfdi': 'CFDI',
'stamp': 'Timbrar',
'cancel_invoice': 'Cancelar Factura',
// Config
'appearance': 'Apariencia',
'business_data': 'Datos de la Empresa',
'employees': 'Empleados',
'printers': 'Impresoras',
'branches': 'Sucursales',
'fiscal_params': 'Parametros Fiscales',
'system_prefs': 'Preferencias del Sistema',
'currency_config': 'Moneda',
'language': 'Idioma',
'theme': 'Tema',
'dark_theme': 'Tema oscuro',
'light_theme': 'Tema claro',
'logout': 'Cerrar sesion',
// Currency
'currency': 'Moneda',
'exchange_rate': 'Tipo de Cambio',
'default_currency': 'Moneda Predeterminada',
'mxn': 'Peso Mexicano',
'usd': 'Dolar Estadounidense',
// Roles
'role_owner': 'Dueno',
'role_admin': 'Administrador',
'role_cashier': 'Cajero',
'role_warehouse': 'Almacen',
'role_accountant': 'Contador',
// Reports
'daily_sales': 'Ventas del Dia',
'weekly_sales': 'Ventas de la Semana',
'monthly_sales': 'Ventas del Mes',
'top_products': 'Productos Mas Vendidos',
'low_stock': 'Bajo Stock',
// Fleet
'vehicle': 'Vehiculo',
'plate': 'Placa',
'vin': 'VIN',
'mileage': 'Kilometraje',
// Misc
'yes': 'Si',
'no': 'No',
'all': 'Todos',
'active': 'Activo',
'inactive': 'Inactivo',
'pending': 'Pendiente',
'completed': 'Completado',
'cancelled': 'Cancelado',
},
en: {
// Sidebar nav
'dashboard': 'Dashboard',
'pos': 'Point of Sale',
'catalog': 'Catalog',
'inventory': 'Inventory',
'customers': 'Customers',
'invoicing': 'Invoicing',
'accounting': 'Accounting',
'reports': 'Reports',
'fleet': 'Fleet',
'whatsapp': 'WhatsApp',
'config': 'Settings',
// Sidebar sections
'nav_main': 'Main',
'nav_management': 'Management',
'nav_system': 'System',
// Common actions
'search': 'Search',
'save': 'Save',
'cancel': 'Cancel',
'delete': 'Delete',
'edit': 'Edit',
'new': 'New',
'close': 'Close',
'confirm': 'Confirm',
'back': 'Back',
'next': 'Next',
'print': 'Print',
'export': 'Export',
'import': 'Import',
'refresh': 'Refresh',
'loading': 'Loading...',
'no_results': 'No results',
'error': 'Error',
'success': 'Success',
'warning': 'Warning',
// Financial
'total': 'Total',
'subtotal': 'Subtotal',
'tax': 'Tax',
'price': 'Price',
'unit_price': 'Unit Price',
'cost': 'Cost',
'discount': 'Discount',
'margin': 'Margin',
'profit': 'Profit',
'balance': 'Balance',
'amount': 'Amount',
// Inventory
'quantity': 'Quantity',
'stock': 'Stock',
'min_stock': 'Minimum',
'max_stock': 'Maximum',
'sku': 'SKU',
'barcode': 'Barcode',
'brand': 'Brand',
'category': 'Category',
'description': 'Description',
'location': 'Location',
// Table headers
'name': 'Name',
'date': 'Date',
'status': 'Status',
'actions': 'Actions',
'id': 'ID',
'type': 'Type',
'notes': 'Notes',
// POS
'charge': 'Charge',
'quote': 'Quote',
'layaway': 'Layaway',
'credit': 'Credit',
'cash': 'Cash',
'transfer': 'Transfer',
'card': 'Card',
'mixed': 'Mixed',
'change': 'Change',
'customer': 'Customer',
'general_public': 'Walk-in Customer',
'sale': 'Sale',
'sales': 'Sales',
'ticket': 'Ticket',
'receipt': 'Receipt',
'payment': 'Payment',
'payment_method': 'Payment Method',
'add_to_cart': 'Add',
'clear_cart': 'Clear Cart',
'hold_sale': 'Hold Sale',
'recall_sale': 'Recall Sale',
'cancel_sale': 'Cancel Sale',
'confirm_payment': 'Confirm Payment',
'cash_received': 'Cash Received',
'amount_due': 'Amount Due',
'remaining': 'Remaining',
// Customers
'phone': 'Phone',
'email': 'Email',
'address': 'Address',
'rfc': 'Tax ID (RFC)',
'credit_limit': 'Credit Limit',
'credit_balance': 'Credit Balance',
'price_tier': 'Price Tier',
// Invoicing
'invoice': 'Invoice',
'cfdi': 'CFDI',
'stamp': 'Stamp',
'cancel_invoice': 'Cancel Invoice',
// Config
'appearance': 'Appearance',
'business_data': 'Business Info',
'employees': 'Employees',
'printers': 'Printers',
'branches': 'Branches',
'fiscal_params': 'Tax Settings',
'system_prefs': 'System Preferences',
'currency_config': 'Currency',
'language': 'Language',
'theme': 'Theme',
'dark_theme': 'Dark theme',
'light_theme': 'Light theme',
'logout': 'Log out',
// Currency
'currency': 'Currency',
'exchange_rate': 'Exchange Rate',
'default_currency': 'Default Currency',
'mxn': 'Mexican Peso',
'usd': 'US Dollar',
// Roles
'role_owner': 'Owner',
'role_admin': 'Administrator',
'role_cashier': 'Cashier',
'role_warehouse': 'Warehouse',
'role_accountant': 'Accountant',
// Reports
'daily_sales': 'Daily Sales',
'weekly_sales': 'Weekly Sales',
'monthly_sales': 'Monthly Sales',
'top_products': 'Top Products',
'low_stock': 'Low Stock',
// Fleet
'vehicle': 'Vehicle',
'plate': 'Plate',
'vin': 'VIN',
'mileage': 'Mileage',
// Misc
'yes': 'Yes',
'no': 'No',
'all': 'All',
'active': 'Active',
'inactive': 'Inactive',
'pending': 'Pending',
'completed': 'Completed',
'cancelled': 'Cancelled',
}
};
var currentLang = localStorage.getItem('pos_lang') || 'es';
/**
* Translate a key to the current language.
* Falls back to Spanish, then to the raw key.
*/
window.t = function(key) {
return (I18N[currentLang] && I18N[currentLang][key]) || (I18N['es'] && I18N['es'][key]) || key;
};
/**
* Switch the UI language and reload.
*/
window.setLang = function(lang) {
currentLang = lang;
localStorage.setItem('pos_lang', lang);
location.reload();
};
/**
* Get the current language code.
*/
window.getLang = function() {
return currentLang;
};