Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions backend/src/i18n/locales/es-mx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"invoiceTitle": "Factura",
"invoiceNumberLabel": "Número de factura",
"invoiceNumberShortLabel": "Factura núm.",
"invoiceDateLabel": "Fecha de la factura",
"dateLabel": "Fecha",
"dueDateLabel": "Fecha de vencimiento",
"dueShortLabel": "Vence",
"referenceLabel": "Referencia",
"billToHeading": "Facturar a",
"itemsHeading": "Conceptos de la factura",
"itemHeaderDescription": "Descripción",
"itemHeaderQuantity": "Cantidad",
"itemHeaderQuantityShort": "Cant.",
"itemHeaderUnit": "Unidad",
"itemHeaderUnitPrice": "Precio unitario",
"itemHeaderUnitPriceShort": "Precio",
"itemHeaderAmount": "Importe",
"itemHeaderTax": "IVA",
"summaryHeading": "Resumen de la factura",
"subtotalLabel": "Subtotal",
"discountLabel": "Descuento",
"taxLabel": "IVA",
"totalLabel": "Total",
"statusLabel": "Estado",
"taxSummaryHeading": "Resumen de impuestos",
"taxableLabel": "Base gravable",
"taxAmountLabel": "Importe del IVA",
"taxIdLabel": "RFC",
"outstandingBalanceLabel": "Saldo pendiente",
"paymentInformationHeading": "Información de pago",
"paymentMethodsLabel": "Métodos de pago",
"paymentMethodsPrefix": "Métodos:",
"bankAccountLabel": "Cuenta bancaria",
"bankAccountPrefix": "Banco:",
"paymentTermsLabel": "Condiciones de pago",
"notesHeading": "Notas",
"thankYouNote": "Gracias por tu preferencia."
}
2 changes: 2 additions & 0 deletions backend/src/i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import deRaw from "./locales/de.json" with { type: "json" };
import ptBrRaw from "./locales/pt-br.json" with { type: "json" };
import trRaw from "./locales/tr.json" with { type: "json" };
import esCoRaw from "./locales/es-co.json" with { type: "json" };
import esMxRaw from "./locales/es-mx.json" with { type: "json" };
import esArRaw from "./locales/es-ar.json" with { type: "json" };

export type InvoiceLabels = {
Expand Down Expand Up @@ -109,6 +110,7 @@ const catalogs: Record<string, InvoiceLabels> = Object.freeze({
pt: coerceLabels("pt", ptBrRaw), // alias for pt-br
tr: coerceLabels("tr", trRaw),
"es-co": coerceLabels("es-co", esCoRaw),
"es-mx": coerceLabels("es-mx", esMxRaw),
es: coerceLabels("es", esCoRaw), // alias for es-co
"es-ar": coerceLabels("es-ar", esArRaw),
});
Expand Down
29 changes: 29 additions & 0 deletions backend/src/i18n/translations_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { availableInvoiceLocales, getInvoiceLabels } from "./translations.ts";

function assert(
condition: boolean,
message = "Expected condition to be true",
): asserts condition {
if (!condition) throw new Error(message);
}

function assertEquals(actual: unknown, expected: unknown): void {
if (actual !== expected) {
throw new Error(`Expected ${String(expected)}, received ${String(actual)}`);
}
}

Deno.test("es-MX invoice labels use Mexican terminology", () => {
const { locale, labels } = getInvoiceLabels("es-MX");

assertEquals(locale, "es-mx");
assertEquals(labels.invoiceTitle, "Factura");
assertEquals(labels.taxIdLabel, "RFC");
assertEquals(labels.taxLabel, "IVA");
assertEquals(labels.taxableLabel, "Base gravable");
assertEquals(labels.taxAmountLabel, "Importe del IVA");
});

Deno.test("es-MX is an explicit invoice locale", () => {
assert(availableInvoiceLocales().includes("es-mx"));
});
297 changes: 297 additions & 0 deletions frontend/src/lib/i18n/locales/es-mx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
{
"Active": "Activo",
"Paid with": "Pagado con",
"Active (Available in dropdowns)": "Activo (disponible en listas desplegables)",
"Add Category": "Agregar categoría",
"Add Unit": "Agregar unidad",
"Add item": "Agregar concepto",
"Add tax": "Agregar impuesto",
"Address": "Dirección",
"2FA code": "Código 2FA",
"2FA QR code": "Código QR 2FA",
"Adjust the look and feel of the application.": "Ajuste la apariencia de la aplicación.",
"Allow editing and deleting sent/paid invoices": "Permitir editar y eliminar facturas enviadas o pagadas",
"Allowing edits/deletes for sent or paid invoices can violate invoice retention laws. Only enable this if you understand the legal impact.": "Permitir editar o eliminar facturas enviadas o pagadas puede infringir las normas de conservación de documentos fiscales. Habilite esto únicamente si comprende las implicaciones legales.",
"Admin": "Admin",
"Administrator": "Administrador",
"Administrators have full access to all features.": "Los administradores tienen acceso completo a todas las funciones.",
"All": "Todos",
"Appearance": "Apariencia",
"Are you sure you want to delete this customer? This cannot be undone.": "¿Está seguro de que desea eliminar este cliente? Esta acción no se puede deshacer.",
"Confirm disable two-factor authentication": "Confirmar la desactivación de la autenticación en dos pasos",
"Enter your current 2FA code to confirm.": "Ingrese su código 2FA actual para confirmar.",
"Are you sure you want to delete this product?": "¿Está seguro de que desea eliminar este producto?",
"Are you sure you want to delete this template?": "¿Está seguro de que desea eliminar esta plantilla?",
"Are you sure you want to delete this user? This action cannot be undone.": "¿Está seguro de que desea eliminar este usuario? Esta acción no se puede deshacer.",
"Bank Account": "Cuenta bancaria",
"Branding": "Marca",
"Cancel": "Cancelar",
"Category": "Categoría",
"Category Code": "Código de categoría",
"Category Name": "Nombre de la categoría",
"City": "Ciudad",
"Clear All": "Limpiar todo",
"Company Address": "Dirección de la empresa",
"Company Information": "Información de la empresa",
"Company Name": "Razón social",
"Contact Name": "Nombre de contacto",
"Copy link": "Copiar enlace",
"Country Code": "Código de país",
"Country Code (ISO alpha-2)": "Código de país (ISO alfa-2)",
"Country code": "Código de país",
"Country code placeholder": "p. ej., MX, US, CA",
"Create": "Crear",
"Create Customer": "Crear cliente",
"Create Invoice": "Crear factura",
"Create Product": "Crear producto",
"Create User": "Crear usuario",
"Create your first customer": "Cree su primer cliente",
"Create your first invoice": "Cree su primera factura",
"Create your first product": "Cree su primer producto",
"Ctrl/Cmd+Enter to add": "Ctrl/Cmd+Enter para agregar",
"Currency": "Moneda",
"Customer": "Cliente",
"Customer data hidden": "Datos del cliente ocultos",
"Customers": "Clientes",
"Dark": "Oscuro",
"Dashboard": "Panel principal",
"Date Format": "Formato de fecha",
"Default": "Predeterminado",
"Default Notes": "Notas predeterminadas",
"Default Prices Include Tax": "Los precios predeterminados incluyen impuestos",
"Default Tax Rate": "Tasa de impuesto predeterminada",
"Delete": "Eliminar",
"Delete User": "Eliminar usuario",
"Delete this invoice? This cannot be undone.": "¿Desea eliminar esta factura? Esta acción no se puede deshacer.",
"Demo mode warning": "Modo de demostración: la aplicación es totalmente funcional, pero la base de datos se restablece cada 30 minutos; sus cambios no son permanentes.",
"Description": "Descripción",
"Deutsch": "Alemán",
"Disabled users cannot log in.": "Los usuarios desactivados no pueden iniciar sesión.",
"Discount": "Descuento",
"Display Name": "Nombre visible",
"Display name": "Nombre para mostrar",
"Display name placeholder": "p. ej., Impuesto al valor agregado (IVA)",
"Download PDF": "Descargar PDF",
"Download XML": "Descargar XML",
"Draft": "Borrador",
"Due Date": "Fecha de vencimiento",
"Duplicate": "Duplicar",
"Edit": "Editar",
"Edit Customer": "Editar cliente",
"Edit Invoice": "Editar factura",
"Edit Product": "Editar producto",
"Edit User": "Editar usuario",
"Email": "Correo electrónico",
"Embed XML in HTML": "Incrustar XML en HTML",
"Embed XML in PDF": "Incrustar XML en PDF",
"Enable Automatic Invoice Numbering": "Habilitar numeración automática de facturas",
"English": "Inglés",
"Enter 6-digit code": "Ingrese el código de 6 dígitos",
"Enter a manifest URL": "Ingrese la URL del manifiesto",
"Enter your password": "Ingrese su contraseña",
"Enter your username": "Ingrese su nombre de usuario",
"FacturX / ZUGFeRD 2.2": "FacturX / ZUGFeRD 2.2",
"Disable 2FA": "Desactivar 2FA",
"Failed to delete": "Error al eliminar",
"Failed to disable two-factor authentication": "Error al desactivar la autenticación en dos pasos",
"Failed to enable two-factor authentication": "Error al activar la autenticación en dos pasos",
"Failed to generate 2FA setup": "Error al generar la configuración de 2FA",
"Failed to save settings": "Error al guardar la configuración",
"Failed to update": "Error al actualizar",
"File must be a .zip archive": "El archivo debe ser un paquete .zip",
"File too large (max 5MB)": "Archivo demasiado grande (máx. 5MB)",
"Format": "Formato",
"Highlight Color": "Color de resaltado",
"Inactive": "Inactivo",
"Install": "Instalar",
"Install failed": "Error en la instalación",
"Install from URL": "Instalar desde URL",
"Installed Templates": "Plantillas instaladas",
"Installing...": "Instalando...",
"Invalid URL or data format": "URL o formato de datos no válido",
"Invio": "Invio",
"Invoice": "Factura",
"Invoice #": "Factura N.º",
"Invoice No": "N.º de factura",
"Invoice Number": "Número de factura",
"Invoice Number Pattern": "Formato de numeración de facturas",
"Invoice data hidden": "Datos de la factura ocultos",
"Invoice published": "Factura publicada",
"Invoice total": "Total de la factura",
"Invoices": "Facturas",
"Issue Date": "Fecha de emisión",
"Item": "Concepto",
"Items": "Conceptos",
"Language": "Idioma",
"Leave blank to keep current password": "Dejar en blanco para conservar la contraseña actual",
"Light": "Claro",
"Link copied!": "¡Enlace copiado!",
"Localization": "Configuración regional",
"Login": "Iniciar sesión",
"Logo URL or Path": "URL o ruta del logo",
"Logout": "Cerrar sesión",
"Make public and mark as sent": "Hacer público y marcar como enviada",
"Manage invoice HTML templates.": "Administre las plantillas HTML de las facturas.",
"Manifest URL": "URL del manifiesto",
"Complete": "Completado",
"Mark as Complete": "Marcar como completado",
"Mark as Paid": "Marcar como pagada",
"Mark as Sent": "Marcar como enviada",
"Minimum 8 characters": "Mínimo 8 caracteres",
"More": "Más",
"Name": "Nombre",
"Nederlands": "Neerlandés",
"Türkçe": "Turco",
"New Customer": "Nuevo cliente",
"New Password": "Nueva contraseña",
"New Product": "Nuevo producto",
"New Template": "Nueva plantilla",
"New User": "Nuevo usuario",
"No": "No",
"No categories defined": "No hay categorías definidas",
"No custom templates installed.": "No hay plantillas personalizadas instaladas.",
"No customers yet.": "Aún no hay clientes registrados.",
"No default tax": "Sin impuesto predeterminado",
"No description": "Sin descripción",
"No invoices found": "No se encontraron facturas",
"No invoices match this filter": "Ninguna factura coincide con este filtro",
"No products yet.": "Aún no hay productos registrados.",
"No tax definitions yet.": "Aún no hay impuestos definidos.",
"No templates found.": "No se encontraron plantillas.",
"No units defined": "No hay unidades de medida definidas",
"No users found.": "No se encontraron usuarios.",
"Notes": "Notas",
"Number Format": "Formato de número",
"Numbering": "Numeración",
"Open": "Abierta",
"Open Invoices": "Facturas abiertas",
"Open in new tab": "Abrir en una pestaña nueva",
"Optional": "Opcional",
"Optionally record how this invoice was paid.": "Opcionalmente, registre el medio con el que se pagó esta factura.",
"Outstanding": "Pendiente",
"Overdue": "Vencida",
"Paid": "Pagada",
"Password": "Contraseña",
"Payment Method": "Método de pago",
"Payment Methods": "Métodos de pago",
"Payment Terms": "Términos de pago",
"Payments & Texts": "Pagos y textos",
"Per line": "Por línea",
"Permissions": "Permisos",
"Phone": "Teléfono",
"Please select a .zip file": "Por favor seleccione un archivo .zip",
"Please select a valid image file.": "Por favor seleccione un archivo de imagen válido.",
"Postal Code": "Código postal",
"Preview": "Vista previa",
"Price": "Precio",
"Prices include tax": "Los precios incluyen impuesto",
"Prices include tax?": "¿Los precios incluyen impuesto?",
"Product": "Producto",
"Product Categories": "Categorías de producto",
"Product Units": "Unidades de medida",
"Products": "Productos",
"Public link": "Enlace público",
"Publish": "Publicar",
"Qty": "Cant.",
"Quantity": "Cantidad",
"Reactivate": "Reactivar",
"Recent Invoices": "Facturas recientes",
"Recovery code": "Código de recuperación",
"Recovery codes (save these now)": "Códigos de recuperación (guárdelos ahora)",
"Reconfigure 2FA": "Reconfigurar 2FA",
"Remove item": "Eliminar concepto",
"Resource": "Recurso",
"Role": "Rol",
"Round on total": "Redondear en el total",
"Round per line": "Redondear por línea",
"Rounding": "Redondeo",
"Rounding Mode": "Modo de redondeo",
"Rounding mode": "Modo de redondeo",
"SKU": "SKU / Código",
"Save": "Guardar",
"Save Changes": "Guardar cambios",
"Save Settings": "Guardar configuración",
"Scan this QR code with your authenticator app": "Escanee este código QR con su aplicación de autenticación",
"Select .zip archive": "Seleccionar archivo .zip",
"Select All": "Seleccionar todo",
"Select category": "Seleccionar categoría",
"Select customer": "Seleccionar cliente",
"Select unit": "Seleccionar unidad",
"Select which resources and actions this user can access. Admins bypass these checks.": "Seleccione a qué recursos y acciones puede acceder este usuario. Los administradores omiten estas restricciones.",
"Sent": "Enviada",
"Settings": "Configuración",
"Settings updated successfully": "Configuración actualizada con éxito",
"Set up 2FA": "Configurar 2FA",
"Standard UBL XML": "XML UBL estándar",
"Status": "Estado",
"Status History": "Historial de estados",
"Subtotal": "Subtotal",
"Suggested colors from logo:": "Colores sugeridos a partir del logo:",
"Tax": "IVA",
"Tax %": "% de IVA",
"Tax Definition": "Definición de impuesto",
"Tax Definitions": "Impuestos",
"Tax ID": "RFC",
"Tax Label": "Nombre del impuesto",
"Tax Mode": "Modo de impuesto",
"Tax Rate (%)": "Tasa de impuesto (%)",
"Tax Settings": "Configuración de impuestos",
"Tax code": "Código de impuesto",
"Tax code placeholder": "p. ej., IVA, IEPS",
"Tax mode": "Modo de impuesto",
"Tax rate": "Tasa de impuesto",
"Template": "Plantilla",
"Templates": "Plantillas",
"These codes are shown once and can be used if you lose access to your authenticator app.": "Estos códigos se muestran solo una vez y pueden utilizarse si pierde el acceso a su aplicación de autenticación.",
"Toggle light/dark theme": "Alternar tema claro/oscuro",
"Total": "Total",
"Total Billed": "Total facturado",
"Two-factor authentication disabled": "Autenticación en dos pasos desactivada",
"Two-factor authentication enabled": "Autenticación en dos pasos activada",
"Two-factor authentication is enabled": "La autenticación en dos pasos está activada",
"Two-factor authentication is not enabled": "La autenticación en dos pasos no está activada",
"UBL 2.1 (PEPPOL)": "UBL 2.1 (PEPPOL)",
"Unit": "Unidad",
"Unit Code": "Código de unidad",
"Unit Name": "Nombre de la unidad",
"Unit Price": "Precio unitario",
"Unknown Customer": "Cliente desconocido",
"Unnamed": "Sin nombre",
"Unpublish": "Despublicar",
"Update": "Actualizar",
"Updated": "Actualizado",
"Upload": "Subir",
"Upload Template": "Subir plantilla",
"Upload failed": "Error al subir el archivo",
"Uploading...": "Subiendo...",
"User": "Usuario",
"User not found": "Usuario no encontrado",
"User saved successfully.": "Usuario guardado con éxito.",
"Username": "Nombre de usuario",
"Users": "Usuarios",
"Version": "Versión",
"Verify and Enable 2FA": "Verificar y activar 2FA",
"View HTML": "Ver HTML",
"View public link": "Ver enlace público",
"Void Invoice": "Anular factura",
"Warning: you are editing a sent/paid invoice. Ensure this is legally allowed in your jurisdiction.": "Advertencia: está editando una factura enviada o pagada. Asegúrese de que esto esté permitido legalmente según su jurisdicción fiscal.",
"Void this invoice?": "¿Desea anular esta factura?",
"Voided": "Anulada",
"Welcome to Invio": "Bienvenido a Invio",
"XML": "XML",
"XML Data": "Datos XML",
"XML Export": "Exportación XML",
"XML Profile ID": "ID de perfil XML",
"Yes": "Sí",
"Yes, disable": "Sí, desactivar",
"You are about to delete a sent/paid invoice. This may violate invoice retention laws and cannot be undone. Continue?": "Está a punto de eliminar una factura enviada o pagada. Esto puede infringir las normas de retención de facturas y no se puede deshacer. ¿Desea continuar?",
"You are about to edit a sent/paid invoice. Ensure this is legally allowed in your jurisdiction. Continue?": "Está a punto de editar una factura enviada o pagada. Asegúrese de que esto esté permitido por las normas fiscales de su jurisdicción. ¿Desea continuar?",
"You do not have permission to modify settings.": "No tiene permisos para modificar la configuración.",
"You do not have permission to view customers. Contact an administrator to request access.": "No tiene permisos para ver clientes. Póngase en contacto con un administrador para solicitar acceso.",
"You do not have permission to view invoices. Contact an administrator to request access.": "No tiene permisos para ver facturas. Póngase en contacto con un administrador para solicitar acceso.",
"Protect your account with two-factor authentication.": "Proteja su cuenta con autenticación en dos pasos.",
"e.g. Bank Transfer, PayPal": "p. ej., Transferencia bancaria, tarjeta, efectivo",
"e.g. INV-2025-001": "p. ej., FAC-2026-001",
"e.g. US, NL, DE": "p. ej., MX, US, CA",
"item(s)": "concepto(s)"
}
2 changes: 2 additions & 0 deletions frontend/src/lib/i18n/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import deMessages from "./locales/de.json" with { type: "json" };
import ptMessages from "./locales/pt-br.json" with { type: "json" };
import trMessages from "./locales/tr.json" with { type: "json" };
import esMessages from "./locales/es-co.json" with { type: "json" };
import esMxMessages from "./locales/es-mx.json" with { type: "json" };
import esArMessages from "./locales/es-ar.json" with { type: "json" };

export type UiMessages = Record<string, string>;
Expand All @@ -16,6 +17,7 @@ const catalogs: Record<string, UiMessages> = {
pt: ptMessages as UiMessages,
tr: trMessages as UiMessages,
es: esMessages as UiMessages,
"es-mx": esMxMessages as UiMessages,
"es-ar": esArMessages as UiMessages,
};

Expand Down
Loading