#!/usr/bin/env python3 """Fusiona bloques csv_upload en messages/en.json y messages/es.json y vuelca a src/lib/i18n/csv-upload-messages.*.json. La fuente de verdad del copy CSV es `messages/{en,es}.json` (alineado con sidebar, dashboard, facturas). Si editas solo esos JSON, sincroniza con: node -e "const fs=require('fs'),p=require('path'),r='.../frontend';for(const l of['en','es']){const j=JSON.parse(fs.readFileSync(p.join(r,'messages',l+'.json'),'utf8'));fs.writeFileSync(p.join(r,'src/lib/i18n','csv-upload-messages.'+l+'.json'),JSON.stringify(j.csv_upload,null,'\\t')+'\\n')}" Ejecutar desde frontend/: python scripts/merge_csv_upload_i18n.py """ from __future__ import annotations import json from pathlib import Path ROOT = Path(__file__).resolve().parent.parent MESSAGES = ROOT / "messages" EN_CSV = { "page_title": "CSV import", "intro_help": "Left-click: upload CSV file. Right-click: download template.", "tab_catalogos": "Catalogs", "tab_transportes": "Transportation", "tab_importacion": "Import", "tab_exportacion": "Export", "section_catalogs": "General Catalogs", "section_transport": "Transportation", "section_import": "Import operations", "section_export": "Export operations", "params_header": "Global parameters", "config_prefix": "Settings", "soon": "Coming soon", "drop_here": "Drop the file!", "groups": { "permisos": "Permissions", "impo_temp": "Temporary import", "impo_def": "Definitive import", "cmex": "Mexican purchases", "expo_def": "Definitive export / regime change", "expo_rep": "Export replenishment", "manifest": "Manifest", }, "items": { "customs_brokers": "Customs Brokers", "clients_providers": "Clients and Providers", "exchange_rates": "Exchange Rates", "material_classes": "Classes", "part_numbers": "Parts", "boms": "BOMs", "items": "Lines (permissions)", "headers": "Headers (permissions)", "historical_fractions": "Historical tariff fractions", "pedimentos": "Pedimentos", "transporters": "Carriers", "transports": "Vehicles", "drivers": "Drivers", "trailers": "Trailers", "imp_temp_header": "Header", "imp_temp_details": "Lines", "imp_temp_series": "Serial numbers", "imp_def_header": "Header", "imp_def_details": "Lines", "imp_def_series": "Serial numbers", "comp_mex_header": "Header", "comp_mex_details": "Lines", "comp_mex_series": "Serial numbers", "exp_def_header": "Header", "exp_def_details": "Lines", "exp_def_series": "Serial numbers", "exp_def_nodes": "NODES", "exp_rep_header": "Header", "exp_rep_details": "Lines", "exp_rep_series": "Serial numbers", "manifest_header": "Header", }, "params": { "load_mode": "Load mode", "date_format": "Date format", "weight_unit": "Weight unit", "autonumber_series": "Autonumber lines/series", "load_subpartidas": "Load sub-lines", "recalculate_pedimento_date": "Recalculate pedimento date", "autonumber_remesas": "Autonumber consignments", "recalculate_dates": "Recalculate dates", "invoice_type": "Invoice type", "is_regime_change": "Regime change", }, "options": { "update": "Update", "replace": "Replace", "yes": "Yes", "no": "No", "kgs": "Kilograms (kg)", "lbs": "Pounds (lb)", "date_dd_mm": "DD/MM/YYYY", "date_mm_dd": "MM/DD/YYYY", "date_iso": "YYYY-MM-DD", "afi": "AFIJO", "normal": "NORMAL", }, "progress": { "upload": "Uploading CSV file", "scan": "Validating records on the server", "commit": "Saving records to the database", "upload_known": "Uploading file…", "upload_unknown": "Uploading file (unknown size in browser)…", "in_progress": "In progress…", "resume_hint": "Resuming import saved in this tab…", "rows_file": "File: ~{n} data row(s) — uploading (not yet validated on server)…", "rows_scan": "Records processed: {current} / {total}", "rows_commit": "Records saved: {current} / {total}", "rows_commit_fallback": "Saving to database… ({current} / {total} using last known total)", }, "toast": { "invalid_csv": "Invalid format. Only .csv files are allowed.", "download_loading": "Downloading template…", "download_ok": "Template downloaded.", "download_err": "Could not download the template.", "upload_err": "Could not upload the file.", "upload_err_generic": "Unexpected error uploading the file.", "scan_done": "Scan complete. Review the results.", "import_done": "Import completed. Review the record list.", "import_maybe_done": "Import may have completed. Review the record list.", "stale_job": "This import is no longer available (session expired or job removed). You can start a new upload.", "poll_err": "Could not fetch status", "commit_err": "Could not start import", "scan_alt": "Scan finished. If you do not see the modal, check the record list.", "finished_none": "No records inserted. Review the errors below.", "commit_warning_ok": "{inserted} inserted, {updated} updated. {skipped} rejected.", "commit_warning_none": "No records inserted or updated. {skipped} rejected.", "success_counts": "Import completed: {msg}", "warn_skipped": "{n} records rejected or skipped", "error_processing": "Processing error: {msg}", }, "pending": { "badge": "Pending", "title": "Imports pending confirmation", "description": "Scans ready to save to the database. Expired jobs disappear when you refresh.", "refresh": "Refresh", "empty": "No pending imports for this company.", "checking": "Checking with the server…", "total_rows": "Total rows", "valid_rows": "Valid", "resume": "Resume", "remove": "Remove", "profiles": { "customs_brokers": "Customs Brokers", "clients_providers": "Clients and Providers", "exchange_rates": "Exchange Rates", "pedimentos": "Pedimentos", "material_classes": "Classes", "vehicles": "Vehicles", "drivers": "Drivers", "trailers": "Trailers", "transporters": "Carriers", "part_numbers": "Parts", "boms": "BOMs", "exportacion": "Export operations", "imports": "Import operations", }, }, "modal": { "title_pending": "Import validation", "title_success": "Import successful", "title_warning": "Import with remarks", "desc_pending": "Review the preliminary analysis before confirming.", "desc_done": "The import process has finished.", "total_rows": "Total rows", "valid_rows": "Valid", "invalid_rows": "Invalid", }, } ES_CSV = { "page_title": "Importación CSV", "intro_help": "Clic izquierdo: cargar archivo CSV. Clic derecho: descargar plantilla.", "tab_catalogos": "Catálogos", "tab_transportes": "Transportes", "tab_importacion": "Importación", "tab_exportacion": "Exportación", "section_catalogs": "Catalogos Generales", "section_transport": "Transportes", "section_import": "Operaciones de importación", "section_export": "Operaciones de exportación", "params_header": "Parámetros globales", "config_prefix": "Configuración", "soon": "Próximamente", "drop_here": "¡Suelta el archivo!", "groups": { "permisos": "Permisos", "impo_temp": "Impo. temp.", "impo_def": "Impo. def.", "cmex": "Compras mex.", "expo_def": "Expo. def./Cam. reg.", "expo_rep": "Expo. rep.", "manifest": "Manifiesto", }, "items": { "customs_brokers": "Agentes Aduanales", "clients_providers": "Clientes y Proveedores", "exchange_rates": "Tipos de cambio", "material_classes": "Clases", "part_numbers": "Partes", "boms": "BOMs", "items": "Partidas (permisos)", "headers": "Encabezados (permisos)", "historical_fractions": "Fracciones históricas", "pedimentos": "Pedimentos", "transporters": "Transportistas", "transports": "Vehículos", "drivers": "Conductores", "trailers": "Trailers", "imp_temp_header": "Encabezado", "imp_temp_details": "Partidas", "imp_temp_series": "Series", "imp_def_header": "Encabezado", "imp_def_details": "Partidas", "imp_def_series": "Series", "comp_mex_header": "Encabezado", "comp_mex_details": "Partidas", "comp_mex_series": "Series", "exp_def_header": "Encabezado", "exp_def_details": "Partidas", "exp_def_series": "Series", "exp_def_nodes": "NODES", "exp_rep_header": "Encabezado", "exp_rep_details": "Partidas", "exp_rep_series": "Series", "manifest_header": "Encabezado", }, "params": { "load_mode": "Modo de carga", "date_format": "Formato de fecha", "weight_unit": "Unidad de peso", "autonumber_series": "Autonumerar partidas/series", "load_subpartidas": "Levantar subpartidas", "recalculate_pedimento_date": "Recalcular fecha pedimento", "autonumber_remesas": "Autonumerar remesas", "recalculate_dates": "Recalcular fechas", "invoice_type": "Tipo de factura", "is_regime_change": "Es cambio de régimen", }, "options": { "update": "Actualizar", "replace": "Reemplazar", "yes": "Sí", "no": "No", "kgs": "Kilos (kg)", "lbs": "Libras (lb)", "date_dd_mm": "DD/MM/YYYY", "date_mm_dd": "MM/DD/YYYY", "date_iso": "YYYY-MM-DD", "afi": "AFIJO", "normal": "NORMAL", }, "progress": { "upload": "Subiendo archivo CSV", "scan": "Validando registros en el servidor", "commit": "Grabando registros en base de datos", "upload_known": "Subiendo archivo…", "upload_unknown": "Subiendo archivo (tamaño desconocido en el navegador)…", "in_progress": "En proceso…", "resume_hint": "Reanudando la importación guardada en esta pestaña…", "rows_file": "Archivo: ~{n} fila(s) de datos — subiendo (aún no se validan registros en servidor)…", "rows_scan": "Registros procesados: {current} / {total}", "rows_commit": "Registros grabados: {current} / {total}", "rows_commit_fallback": "Grabando en base de datos… ({current} / {total} según último total conocido)", }, "toast": { "invalid_csv": "Formato inválido. Solo se permiten archivos .csv", "download_loading": "Descargando plantilla…", "download_ok": "Plantilla descargada.", "download_err": "Error al descargar la plantilla", "upload_err": "Error al subir el archivo", "upload_err_generic": "Error inesperado al subir el archivo", "scan_done": "Escaneo completado. Revisa los resultados.", "import_done": "Importación completada. Revisa el listado de registros.", "import_maybe_done": "La importación pudo completarse. Revisa el listado de registros.", "stale_job": "Esta importación ya no está disponible (sesión expirada o trabajo eliminado). Puedes iniciar una nueva carga.", "poll_err": "Error al consultar el estado", "commit_err": "Error al iniciar la importación", "scan_alt": "El escaneo terminó. Si no ves el modal, revisa el listado de registros.", "finished_none": "No se insertaron registros. Revisa los errores a continuación.", "commit_warning_ok": "{inserted} insertados, {updated} actualizados. {skipped} rechazados.", "commit_warning_none": "No se insertaron ni actualizaron registros. {skipped} rechazados.", "success_counts": "Importación completada: {msg}", "warn_skipped": "{n} registros fueron rechazados u omitidos", "error_processing": "Error en el procesamiento: {msg}", }, "pending": { "badge": "Pendientes", "title": "Importaciones pendientes de confirmar", "description": "Escaneos listos para insertar en base de datos. Si el trabajo ya expiró en el servidor, desaparecerán al actualizar.", "refresh": "Actualizar", "empty": "No hay importaciones pendientes para esta empresa.", "checking": "Comprobando con el servidor…", "total_rows": "Total filas", "valid_rows": "Válidas", "resume": "Reanudar", "remove": "Quitar", "profiles": { "customs_brokers": "Agentes Aduanales", "clients_providers": "Clientes y Proveedores", "exchange_rates": "Tipos de cambio", "pedimentos": "Pedimentos", "material_classes": "Clases", "vehicles": "Vehículos", "drivers": "Conductores", "trailers": "Trailers", "transporters": "Transportistas", "part_numbers": "Partes", "boms": "BOMs", "exportacion": "Exportación (operaciones)", "imports": "Importación (operaciones)", }, }, "modal": { "title_pending": "Validación de importación", "title_success": "Importación exitosa", "title_warning": "Importación con observaciones", "desc_pending": "Revise el análisis preliminar antes de confirmar la carga de datos.", "desc_done": "El proceso de importación ha finalizado.", "total_rows": "Total filas", "valid_rows": "Válidos", "invalid_rows": "Inválidos", }, } def merge_locale(filename: str, csv_obj: dict) -> None: path = MESSAGES / filename data = json.loads(path.read_text(encoding="utf-8")) data["csv_upload"] = csv_obj path.write_text(json.dumps(data, ensure_ascii=False, indent="\t") + "\n", encoding="utf-8") def extract_csv_upload_to_lib() -> None: """Copia `csv_upload` a src/lib/i18n/csv-upload-messages.*.json (lo que importa csv-msg.ts).""" dest_dir = ROOT / "src/lib/i18n" for filename, suffix in (("en.json", "en"), ("es.json", "es")): path = MESSAGES / filename data = json.loads(path.read_text(encoding="utf-8")) cu = data.get("csv_upload") if cu is None: raise SystemExit(f"merge_csv_upload_i18n: falta csv_upload en {filename}") out = dest_dir / f"csv-upload-messages.{suffix}.json" out.write_text(json.dumps(cu, ensure_ascii=False, indent="\t") + "\n", encoding="utf-8") print(f"Wrote {out.relative_to(ROOT)}") def main() -> None: merge_locale("en.json", EN_CSV) merge_locale("es.json", ES_CSV) print("Merged csv_upload into en.json and es.json") extract_csv_upload_to_lib() if __name__ == "__main__": main()