diff --git a/apps/backoffice-tokenization/messages/en.json b/apps/backoffice-tokenization/messages/en.json
new file mode 100644
index 0000000..f181ce4
--- /dev/null
+++ b/apps/backoffice-tokenization/messages/en.json
@@ -0,0 +1,266 @@
+{
+ "common": {
+ "loading": "Loading...",
+ "error": "Error",
+ "cancel": "Cancel",
+ "retry": "Retry",
+ "continue": "Continue",
+ "next": "Next →",
+ "previous": "Previous",
+ "save": "Save",
+ "create": "Create",
+ "update": "Update",
+ "delete": "Delete",
+ "close": "Close",
+ "viewTransaction": "View Transaction",
+ "unknownError": "Unknown error",
+ "connectWallet": "Connect your wallet to create a campaign"
+ },
+ "metadata": {
+ "title": "Backoffice Tokenization",
+ "description": "Backoffice Tokenization"
+ },
+ "nav": {
+ "campaigns": "Campaigns",
+ "roi": "Return on Investment"
+ },
+ "home": {
+ "heroTitle": "Entrepreneur Support",
+ "heroDescription": "We accompany entrepreneurs and micro-business owners with solutions that drive the growth of their businesses.",
+ "heroTagline": "Growth with impact.",
+ "openApp": "Open backoffice",
+ "aboutUs": "About us",
+ "cardFinancing": "Financing",
+ "cardFinancingDesc": "Credit solutions designed to boost the growth of entrepreneurs and micro-businesses.",
+ "cardSupport": "Support",
+ "cardSupportDesc": "Guidance and support to strengthen capabilities, make decisions, and move forward with confidence.",
+ "cardStrengthening": "Business Strengthening",
+ "cardStrengtheningDesc": "Tools and opportunities to build sustainable businesses with impact.",
+ "cardOpportunities": "Opportunities",
+ "cardOpportunitiesDesc": "We connect entrepreneurs and micro-businesses with solutions that pave the way for growth and business consolidation.",
+ "heroHighlight": "Oversight",
+ "bentoDeployTitle": "Deploy Core Contracts",
+ "bentoDeployDesc": "Set up the Escrow, Token Sale, and Participation Token that power the project's funding flow.",
+ "bentoApprovalsTitle": "Milestone Approvals",
+ "bentoApprovalsDesc": "Review and approve project milestones to authorize on-chain fund releases.",
+ "bentoReleasesTitle": "Escrow Releases",
+ "bentoReleasesDesc": "Trigger controlled USDC releases as each milestone is completed and validated.",
+ "bentoDisputeTitle": "Dispute Management",
+ "bentoDisputeDesc": "Pause releases, review evidence, and resolve issues when project conditions are not met.",
+ "bentoMonitorTitle": "Project & Contract Monitoring",
+ "bentoMonitorDesc": "View contract addresses, escrow balances, sale status, and real-time project activity.",
+ "bentoSetup": "Setup",
+ "bentoApproval": "Approval",
+ "bentoRelease": "Release",
+ "bentoDeployCards": "Deploy core contracts for each project",
+ "bentoApproveCards": "Review and approve project milestones",
+ "bentoReleaseCards": "Release escrow funds as milestones are completed",
+ "bentoManageCards": "Manage deployments, configure milestones, and run on-chain releases.",
+ "bentoOperations": "Operations"
+ },
+ "campaigns": {
+ "title": "Campaigns",
+ "description": "Browse and manage active investment campaigns.",
+ "newCampaign": "New Campaign",
+ "loading": "Loading campaigns...",
+ "loadError": "Failed to load campaigns.",
+ "empty": "No campaigns available.",
+ "manageLoans": "Manage Loans",
+ "poolSize": "Pool Size",
+ "loansCompleted": "Loans Completed",
+ "loans": "Loans",
+ "noLoansAvailable": "No loans available.",
+ "loan": "Loan {index}",
+ "filterAll": "All",
+ "filterFundraising": "Fundraising",
+ "filterActive": "Active",
+ "filterClosed": "Closed",
+ "searchPlaceholder": "Search campaigns...",
+ "status": {
+ "DRAFT": "Draft",
+ "FUNDRAISING": "Fundraising",
+ "ACTIVE": "Active",
+ "REPAYMENT": "Repayment",
+ "CLAIMABLE": "Claimable",
+ "CLOSED": "Closed",
+ "PAUSED": "Paused"
+ }
+ },
+ "createCampaign": {
+ "title": "New Campaign",
+ "description": "Complete the steps to set up your micro-loan program.",
+ "step1": "Campaign Basics",
+ "step2": "Initialize Escrow",
+ "step3": "Deploy & Create",
+ "nameLabel": "Campaign Name",
+ "namePlaceholder": "e.g. Micro-Loans for Women Entrepreneurs",
+ "descriptionLabel": "Description",
+ "descriptionPlaceholder": "Briefly describe the purpose of this fund...",
+ "tokenNameLabel": "Token Name",
+ "tokenNamePlaceholder": "e.g. AgriGrowth Bond",
+ "poolSizeLabel": "Pool Size (USD)",
+ "loanSizeLabel": "Loan Size (USD)",
+ "loanDurationLabel": "Loan Duration (months)",
+ "expectedReturnLabel": "Expected Return (%)",
+ "initializingEscrow": "Initializing escrow...",
+ "escrowInitError": "Error initializing escrow",
+ "escrowInitSuccess": "Escrow initialized successfully",
+ "deployingTitle": "Deploying contracts and creating campaign",
+ "deployPhase1": "Creating participation token and tokenizing",
+ "deployPhase2": "Final steps...",
+ "validation": {
+ "nameRequired": "Name is required",
+ "descriptionMin": "Description must be at least 10 characters",
+ "mustBePositive": "Must be greater than 0",
+ "tokenNameRequired": "Token name is required"
+ }
+ },
+ "loans": {
+ "title": "Manage Loans",
+ "description": "Manage milestones and loans for the campaign.",
+ "beneficiaries": "Beneficiaries",
+ "noMilestones": "No milestones registered.",
+ "status": "Status",
+ "disbursed": "Disbursed",
+ "disburse": "Disburse",
+ "approve": "Approve",
+ "insufficientFunds": "Insufficient funds in escrow",
+ "changeLoanStatus": "Change loan status",
+ "statusLabel": "Status",
+ "statusPlaceholder": "e.g. completed",
+ "evidenceLabel": "Evidence",
+ "evidencePlaceholder": "Evidence (optional)",
+ "addNewBeneficiary": "Add New Beneficiary",
+ "loanDescription": "Loan Description",
+ "loanDescPlaceholder": "Describe the purpose of this milestone",
+ "ngoAddress": "NGO Address",
+ "noWalletConnected": "No wallet connected",
+ "amountUsdc": "Amount (USDC)",
+ "createNewMilestone": "Create New Milestone",
+ "escrowNotFound": "Escrow not found",
+ "loanApproved": "Loan {index} approved",
+ "fundsReleased": "Loan {index} funds released",
+ "loanAddedSuccess": "Loan added successfully",
+ "validation": {
+ "descriptionRequired": "Description is required",
+ "amountPositive": "Must be greater than 0"
+ }
+ },
+ "roi": {
+ "title": "Returns",
+ "description": "Manage and monitor your active ROI programs in real time.",
+ "activeCampaigns": "Active ROI Campaigns",
+ "projectName": "Project Name",
+ "invested": "Invested",
+ "statusHeader": "Status",
+ "actions": "Actions",
+ "manageLoans": "Manage Loans",
+ "uploadFunds": "Upload Funds",
+ "updateRoi": "Update ROI",
+ "loadMore": "Load More",
+ "fundRoi": {
+ "title": "Fund ROI — {name}",
+ "description": "Transfer USDC to the vault so investors can claim their returns.",
+ "amountLabel": "Amount (USDC)",
+ "amountPlaceholder": "e.g. 1000",
+ "vaultLabel": "Vault",
+ "funding": "Funding Vault...",
+ "submit": "Fund Vault",
+ "success": "Vault funded successfully"
+ },
+ "createRoi": {
+ "title": "Create ROI",
+ "description": "Set the multiplier for tracking the performance of your assets.",
+ "priceLabel": "Price (%)",
+ "howItWorks": "How it works",
+ "howItWorksDesc": "The multiplier adjusts real-time tracking based on the price percentage set to optimize the performance of your assets in the Vault.",
+ "readMore": "Read more →",
+ "createVault": "Create Vault",
+ "validation": {
+ "priceRequired": "Price is required",
+ "minPrice": "Must be greater than or equal to 0",
+ "maxPrice": "Cannot exceed 100%"
+ }
+ },
+ "toggleVault": {
+ "enable": "Enable",
+ "disable": "Disable",
+ "enabled": "Vault enabled",
+ "disabled": "Vault disabled"
+ }
+ },
+ "tokens": {
+ "tokenizeEscrow": "Tokenize Escrow",
+ "title": "Tokenize Escrow",
+ "escrowIdLabel": "Escrow ID",
+ "escrowIdPlaceholder": "Enter escrow contract ID",
+ "tokenNameLabel": "Token Name",
+ "tokenNamePlaceholder": "e.g., Trustless Work Token",
+ "tokenSymbolLabel": "Token Symbol/Ticker",
+ "tokenSymbolPlaceholder": "e.g., TRUST",
+ "tokenSymbolDesc": "Maximum 12 characters. Uppercase letters and numbers only.",
+ "tokenNote": "This token represents Escrow {escrowId} and can only be minted by its Token Sale contract.",
+ "deploying": "Deploying...",
+ "deployToken": "Deploy Token",
+ "deploySuccess": "Token Deployment Successful",
+ "tokenFactoryAddress": "Token Factory Address",
+ "tokenSaleAddress": "Token Sale Address",
+ "copied": "Copied!",
+ "copy": "Copy",
+ "validation": {
+ "escrowIdRequired": "Escrow ID is required",
+ "tokenNameRequired": "Token name is required",
+ "tokenSymbolRequired": "Token symbol is required",
+ "symbolMaxLength": "Symbol must be 12 characters or less",
+ "symbolPattern": "Symbol must contain only uppercase letters and numbers"
+ }
+ },
+ "vaults": {
+ "createVault": "Create Vault",
+ "title": "Create Vault",
+ "priceLabel": "Price",
+ "priceDesc": "The percentage you enter becomes a multiplier on the base price. 6% → 1.06, 20% → 1.20. That multiplier is the final price per token.",
+ "pricePlaceholder": "Enter price",
+ "multiplier": "Multiplier",
+ "pricePerToken": "Price per token (base 1)",
+ "factoryAddressLabel": "Factory Address",
+ "factoryAddressDesc": "The factory address of the token you want to deploy the vault for.",
+ "factoryAddressPlaceholder": "Enter factory address",
+ "creating": "Creating...",
+ "enableVault": "Enable Vault",
+ "vaultContractAddressLabel": "Vault Contract Address",
+ "vaultContractAddressPlaceholder": "Enter vault contract ID (C...)",
+ "enabling": "Enabling...",
+ "enable": "Enable",
+ "deploySuccess": "Vault Deployment Successful",
+ "vaultContractAddress": "Vault Contract Address",
+ "copied": "Copied!",
+ "copy": "Copy",
+ "validation": {
+ "priceRequired": "Price is required",
+ "maxPrice": "Cannot exceed 100%",
+ "factoryRequired": "Factory address is required",
+ "vaultAddressRequired": "Vault contract address is required"
+ }
+ },
+ "contractErrors": {
+ "title": "Contract Error",
+ "unknownCode": "Contract error code {code}",
+ "vault": {
+ "1": "Admin not found",
+ "2": "Only admin can change availability",
+ "3": "Exchange is currently disabled",
+ "4": "Beneficiary has no tokens to claim",
+ "5": "Vault does not have enough USDC"
+ },
+ "tokenSale": {
+ "1": "Escrow contract not found",
+ "2": "Participation token not found",
+ "3": "Admin not found",
+ "4": "Only admin can set token",
+ "5": "Hard cap exceeded – the campaign is fully funded",
+ "6": "Investor cap exceeded – you have reached the maximum investment",
+ "7": "Amount must be positive"
+ }
+ }
+}
diff --git a/apps/backoffice-tokenization/messages/es.json b/apps/backoffice-tokenization/messages/es.json
new file mode 100644
index 0000000..f190cff
--- /dev/null
+++ b/apps/backoffice-tokenization/messages/es.json
@@ -0,0 +1,266 @@
+{
+ "common": {
+ "loading": "Cargando...",
+ "error": "Error",
+ "cancel": "Cancelar",
+ "retry": "Reintentar",
+ "continue": "Continuar",
+ "next": "Siguiente →",
+ "previous": "Anterior",
+ "save": "Guardar",
+ "create": "Crear",
+ "update": "Actualizar",
+ "delete": "Eliminar",
+ "close": "Cerrar",
+ "viewTransaction": "Ver Transacción",
+ "unknownError": "Error desconocido",
+ "connectWallet": "Conecta tu wallet para crear una campaña"
+ },
+ "metadata": {
+ "title": "Backoffice Tokenización",
+ "description": "Backoffice Tokenización"
+ },
+ "nav": {
+ "campaigns": "Campañas",
+ "roi": "Retorno de Inversión"
+ },
+ "home": {
+ "heroTitle": "Apoyo al emprendedor",
+ "heroDescription": "Acompañamos a emprendedores y microempresarios con soluciones que impulsan el crecimiento de sus negocios.",
+ "heroTagline": "Crecimiento con impacto.",
+ "openApp": "Abrir backoffice",
+ "aboutUs": "Sobre nosotros",
+ "cardFinancing": "Financiación",
+ "cardFinancingDesc": "Soluciones de crédito pensadas para impulsar el crecimiento de emprendedores y microempresas.",
+ "cardSupport": "Acompañamiento",
+ "cardSupportDesc": "Orientación y apoyo para fortalecer capacidades, tomar decisiones y avanzar con confianza.",
+ "cardStrengthening": "Fortalecimiento Empresarial",
+ "cardStrengtheningDesc": "Herramientas y oportunidades para consolidar negocios sostenibles y con impacto.",
+ "cardOpportunities": "Oportunidades",
+ "cardOpportunitiesDesc": "Conectamos a emprendedores y microempresas con soluciones que abren camino al crecimiento y la consolidación de sus negocios.",
+ "heroHighlight": "Oversight",
+ "bentoDeployTitle": "Desplegar Contratos Base",
+ "bentoDeployDesc": "Configura el Escrow, Token Sale y Participation Token que impulsan el flujo de financiamiento del proyecto.",
+ "bentoApprovalsTitle": "Aprobación de Hitos",
+ "bentoApprovalsDesc": "Revisa y aprueba los hitos del proyecto para autorizar la liberación de fondos on-chain.",
+ "bentoReleasesTitle": "Liberaciones de Escrow",
+ "bentoReleasesDesc": "Ejecuta liberaciones controladas de USDC a medida que cada hito se completa y valida.",
+ "bentoDisputeTitle": "Gestión de Disputas",
+ "bentoDisputeDesc": "Pausa liberaciones, revisa evidencia y resuelve problemas cuando las condiciones del proyecto no se cumplen.",
+ "bentoMonitorTitle": "Monitoreo de Proyectos y Contratos",
+ "bentoMonitorDesc": "Visualiza direcciones de contratos, balances de escrow, estado de venta y actividad del proyecto en tiempo real.",
+ "bentoSetup": "Configuración",
+ "bentoApproval": "Aprobación",
+ "bentoRelease": "Liberación",
+ "bentoDeployCards": "Despliega los contratos base para cada proyecto",
+ "bentoApproveCards": "Revisa y aprueba los hitos del proyecto",
+ "bentoReleaseCards": "Libera fondos de escrow a medida que se completan los hitos",
+ "bentoManageCards": "Gestiona despliegues, configura hitos y ejecuta liberaciones on-chain.",
+ "bentoOperations": "Operaciones"
+ },
+ "campaigns": {
+ "title": "Campañas",
+ "description": "Consulta y gestiona las campañas de inversión activas.",
+ "newCampaign": "Nueva Campaña",
+ "loading": "Cargando campañas...",
+ "loadError": "No se pudieron cargar las campañas.",
+ "empty": "No hay campañas disponibles.",
+ "manageLoans": "Manejar Préstamos",
+ "poolSize": "Tamaño del Fondo",
+ "loansCompleted": "Préstamos Completados",
+ "loans": "Préstamos",
+ "noLoansAvailable": "No hay préstamos disponibles.",
+ "loan": "Préstamo {index}",
+ "filterAll": "Todas",
+ "filterFundraising": "Recaudando",
+ "filterActive": "Activa",
+ "filterClosed": "Cerrada",
+ "searchPlaceholder": "Buscar campañas...",
+ "status": {
+ "DRAFT": "Borrador",
+ "FUNDRAISING": "Recaudando",
+ "ACTIVE": "Activa",
+ "REPAYMENT": "En Pago",
+ "CLAIMABLE": "Reclamable",
+ "CLOSED": "Cerrada",
+ "PAUSED": "Pausada"
+ }
+ },
+ "createCampaign": {
+ "title": "Nueva Campaña",
+ "description": "Completa los pasos para configurar tu programa de micro-préstamos.",
+ "step1": "Campaña Básica",
+ "step2": "Inicializar Escrow",
+ "step3": "Desplegar y Crear",
+ "nameLabel": "Nombre de la Campaña",
+ "namePlaceholder": "ej. Micro-Préstamos para Mujeres Emprendedoras",
+ "descriptionLabel": "Descripción",
+ "descriptionPlaceholder": "Describe brevemente el propósito de este fondo...",
+ "tokenNameLabel": "Nombre del Token",
+ "tokenNamePlaceholder": "ej. AgriGrowth Bond",
+ "poolSizeLabel": "Tamaño del Fondo (USD)",
+ "loanSizeLabel": "Tamaño del Préstamo (USD)",
+ "loanDurationLabel": "Duración del Préstamo (meses)",
+ "expectedReturnLabel": "Retorno Esperado (%)",
+ "initializingEscrow": "Inicializando escrow...",
+ "escrowInitError": "Error al inicializar escrow",
+ "escrowInitSuccess": "Escrow inicializado exitosamente",
+ "deployingTitle": "Desplegando contratos y creando campaña",
+ "deployPhase1": "Creando token de participación y tokenizando",
+ "deployPhase2": "Últimos pasos...",
+ "validation": {
+ "nameRequired": "El nombre es requerido",
+ "descriptionMin": "La descripción debe tener al menos 10 caracteres",
+ "mustBePositive": "Debe ser mayor a 0",
+ "tokenNameRequired": "El nombre del token es requerido"
+ }
+ },
+ "loans": {
+ "title": "Manejar Préstamos",
+ "description": "Administra los hitos y préstamos de la campaña.",
+ "beneficiaries": "Beneficiarios",
+ "noMilestones": "No hay hitos registrados.",
+ "status": "Estado",
+ "disbursed": "Desembolsado",
+ "disburse": "Desembolsar",
+ "approve": "Aprobar",
+ "insufficientFunds": "Fondos insuficientes en el escrow",
+ "changeLoanStatus": "Cambiar estado del préstamo",
+ "statusLabel": "Estado",
+ "statusPlaceholder": "Ej: completed",
+ "evidenceLabel": "Evidencia",
+ "evidencePlaceholder": "Evidencia (opcional)",
+ "addNewBeneficiary": "Agregar Nuevo Beneficiario",
+ "loanDescription": "Descripción del Préstamo",
+ "loanDescPlaceholder": "Describe el propósito de este hito",
+ "ngoAddress": "Dirección ONG",
+ "noWalletConnected": "Sin wallet conectada",
+ "amountUsdc": "Monto (USDC)",
+ "createNewMilestone": "Crear Nuevo Hito",
+ "escrowNotFound": "Escrow no encontrado",
+ "loanApproved": "Préstamo {index} aprobado",
+ "fundsReleased": "Fondos del préstamo {index} liberados",
+ "loanAddedSuccess": "Préstamo agregado exitosamente",
+ "validation": {
+ "descriptionRequired": "La descripción es obligatoria",
+ "amountPositive": "Debe ser mayor a 0"
+ }
+ },
+ "roi": {
+ "title": "Retornos",
+ "description": "Gestione y monitoree sus programas de ROI activos en tiempo real.",
+ "activeCampaigns": "Campaña de ROI Activas",
+ "projectName": "Nombre del Proyecto",
+ "invested": "Invertido",
+ "statusHeader": "Estado",
+ "actions": "Acciones",
+ "manageLoans": "Gestionar Préstamos",
+ "uploadFunds": "Subir Fondos",
+ "updateRoi": "Actualizar ROI",
+ "loadMore": "Cargar Más",
+ "fundRoi": {
+ "title": "Fund ROI — {name}",
+ "description": "Transfiere USDC al vault para que los inversores puedan reclamar sus retornos.",
+ "amountLabel": "Monto (USDC)",
+ "amountPlaceholder": "ej. 1000",
+ "vaultLabel": "Vault",
+ "funding": "Fondeando Vault...",
+ "submit": "Fondear Vault",
+ "success": "Vault fondeado exitosamente"
+ },
+ "createRoi": {
+ "title": "Crear ROI",
+ "description": "Establezca el multiplicador para el seguimiento del rendimiento de sus activos.",
+ "priceLabel": "Precio (%)",
+ "howItWorks": "Cómo funciona",
+ "howItWorksDesc": "El multiplicador ajusta el seguimiento en tiempo real basándose en el porcentaje de precio establecido para optimizar el rendimiento de sus activos en el Vault.",
+ "readMore": "Leer más →",
+ "createVault": "Crear Vault",
+ "validation": {
+ "priceRequired": "El precio es obligatorio",
+ "minPrice": "Debe ser mayor o igual a 0",
+ "maxPrice": "No puede ser mayor a 100%"
+ }
+ },
+ "toggleVault": {
+ "enable": "Habilitar",
+ "disable": "Deshabilitar",
+ "enabled": "Vault habilitado",
+ "disabled": "Vault deshabilitado"
+ }
+ },
+ "tokens": {
+ "tokenizeEscrow": "Tokenizar Escrow",
+ "title": "Tokenizar Escrow",
+ "escrowIdLabel": "Escrow ID",
+ "escrowIdPlaceholder": "Ingresa el ID del contrato escrow",
+ "tokenNameLabel": "Nombre del Token",
+ "tokenNamePlaceholder": "ej. Trustless Work Token",
+ "tokenSymbolLabel": "Símbolo/Ticker del Token",
+ "tokenSymbolPlaceholder": "ej. TRUST",
+ "tokenSymbolDesc": "Máximo 12 caracteres. Solo letras mayúsculas y números.",
+ "tokenNote": "Este token representa el Escrow {escrowId} y solo puede ser acuñado por su contrato Token Sale.",
+ "deploying": "Desplegando...",
+ "deployToken": "Desplegar Token",
+ "deploySuccess": "Token desplegado exitosamente",
+ "tokenFactoryAddress": "Dirección del Token Factory",
+ "tokenSaleAddress": "Dirección del Token Sale",
+ "copied": "¡Copiado!",
+ "copy": "Copiar",
+ "validation": {
+ "escrowIdRequired": "El Escrow ID es requerido",
+ "tokenNameRequired": "El nombre del token es requerido",
+ "tokenSymbolRequired": "El símbolo del token es requerido",
+ "symbolMaxLength": "El símbolo debe tener 12 caracteres o menos",
+ "symbolPattern": "El símbolo solo debe contener letras mayúsculas y números"
+ }
+ },
+ "vaults": {
+ "createVault": "Crear Vault",
+ "title": "Crear Vault",
+ "priceLabel": "Precio",
+ "priceDesc": "El porcentaje que ingreses se convierte en un multiplicador sobre el precio base. 6% → 1.06, 20% → 1.20. Ese multiplicador es el precio final por token.",
+ "pricePlaceholder": "Ingresa el precio",
+ "multiplier": "Multiplicador",
+ "pricePerToken": "Precio por token (base 1)",
+ "factoryAddressLabel": "Dirección de Fábrica",
+ "factoryAddressDesc": "La dirección de fábrica del token para el cual deseas desplegar el vault.",
+ "factoryAddressPlaceholder": "Ingresa la dirección de fábrica",
+ "creating": "Creando...",
+ "enableVault": "Habilitar Vault",
+ "vaultContractAddressLabel": "Dirección del Contrato Vault",
+ "vaultContractAddressPlaceholder": "Ingresa el ID del contrato vault (C...)",
+ "enabling": "Habilitando...",
+ "enable": "Habilitar",
+ "deploySuccess": "Vault desplegado exitosamente",
+ "vaultContractAddress": "Dirección del Contrato Vault",
+ "copied": "¡Copiado!",
+ "copy": "Copiar",
+ "validation": {
+ "priceRequired": "El precio es requerido",
+ "maxPrice": "No puede exceder 100%",
+ "factoryRequired": "La dirección de fábrica es requerida",
+ "vaultAddressRequired": "La dirección del contrato vault es requerida"
+ }
+ },
+ "contractErrors": {
+ "title": "Error de Contrato",
+ "unknownCode": "Código de error de contrato {code}",
+ "vault": {
+ "1": "Administrador no encontrado",
+ "2": "Solo el administrador puede cambiar la disponibilidad",
+ "3": "El intercambio está actualmente deshabilitado",
+ "4": "El beneficiario no tiene tokens para reclamar",
+ "5": "El vault no tiene suficiente USDC"
+ },
+ "tokenSale": {
+ "1": "Contrato escrow no encontrado",
+ "2": "Token de participación no encontrado",
+ "3": "Administrador no encontrado",
+ "4": "Solo el administrador puede establecer el token",
+ "5": "Tope máximo excedido — la campaña está completamente financiada",
+ "6": "Tope de inversor excedido — has alcanzado la inversión máxima",
+ "7": "El monto debe ser positivo"
+ }
+ }
+}
diff --git a/apps/backoffice-tokenization/next.config.ts b/apps/backoffice-tokenization/next.config.ts
index 763a4eb..e3fcf88 100644
--- a/apps/backoffice-tokenization/next.config.ts
+++ b/apps/backoffice-tokenization/next.config.ts
@@ -1,4 +1,7 @@
import type { NextConfig } from "next";
+import createNextIntlPlugin from "next-intl/plugin";
+
+const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
const nextConfig: NextConfig = {
reactCompiler: true,
@@ -18,4 +21,4 @@ const nextConfig: NextConfig = {
},
};
-export default nextConfig;
+export default withNextIntl(nextConfig);
diff --git a/apps/backoffice-tokenization/src/app/(dashboard)/campaigns/loans/[id]/page.tsx b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/loans/[id]/page.tsx
similarity index 100%
rename from apps/backoffice-tokenization/src/app/(dashboard)/campaigns/loans/[id]/page.tsx
rename to apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/loans/[id]/page.tsx
diff --git a/apps/backoffice-tokenization/src/app/(dashboard)/campaigns/new/page.tsx b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/new/page.tsx
similarity index 66%
rename from apps/backoffice-tokenization/src/app/(dashboard)/campaigns/new/page.tsx
rename to apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/new/page.tsx
index 218475a..fd5f50f 100644
--- a/apps/backoffice-tokenization/src/app/(dashboard)/campaigns/new/page.tsx
+++ b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/new/page.tsx
@@ -1,12 +1,17 @@
+"use client";
+
import { SectionTitle } from "@/components/shared/section-title";
import { CreateCampaignStepper } from "@/features/campaigns/components/create/create-campaign-stepper";
+import { useTranslations } from "next-intl";
export default function NewCampaignPage() {
+ const t = useTranslations("createCampaign");
+
return (
diff --git a/apps/backoffice-tokenization/src/app/(dashboard)/campaigns/page.tsx b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/page.tsx
similarity index 73%
rename from apps/backoffice-tokenization/src/app/(dashboard)/campaigns/page.tsx
rename to apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/page.tsx
index 3f5ea67..9bc8973 100644
--- a/apps/backoffice-tokenization/src/app/(dashboard)/campaigns/page.tsx
+++ b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/campaigns/page.tsx
@@ -1,23 +1,28 @@
-import Link from "next/link";
+"use client";
+
+import { Link } from "@/i18n/navigation";
import { SectionTitle } from "@/components/shared/section-title";
import { CampaignsView } from "@/features/campaigns/components/campaigns-view";
import { Button } from "@tokenization/ui/button";
import { Plus } from "lucide-react";
+import { useTranslations } from "next-intl";
export default function CampaignsPage() {
+ const t = useTranslations("campaigns");
+
return (
diff --git a/apps/backoffice-tokenization/src/app/(dashboard)/layout.tsx b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/layout.tsx
similarity index 100%
rename from apps/backoffice-tokenization/src/app/(dashboard)/layout.tsx
rename to apps/backoffice-tokenization/src/app/[locale]/(dashboard)/layout.tsx
diff --git a/apps/backoffice-tokenization/src/app/(dashboard)/roi/page.tsx b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/roi/page.tsx
similarity index 64%
rename from apps/backoffice-tokenization/src/app/(dashboard)/roi/page.tsx
rename to apps/backoffice-tokenization/src/app/[locale]/(dashboard)/roi/page.tsx
index eaa771e..da12dcf 100644
--- a/apps/backoffice-tokenization/src/app/(dashboard)/roi/page.tsx
+++ b/apps/backoffice-tokenization/src/app/[locale]/(dashboard)/roi/page.tsx
@@ -1,12 +1,17 @@
+"use client";
+
import { SectionTitle } from "@/components/shared/section-title";
import { RoiView } from "@/features/campaigns/components/roi/roi-view";
+import { useTranslations } from "next-intl";
export default function RoiPage() {
+ const t = useTranslations("roi");
+
return (
diff --git a/apps/backoffice-tokenization/src/app/[locale]/layout.tsx b/apps/backoffice-tokenization/src/app/[locale]/layout.tsx
new file mode 100644
index 0000000..ee01c67
--- /dev/null
+++ b/apps/backoffice-tokenization/src/app/[locale]/layout.tsx
@@ -0,0 +1,71 @@
+import type { Metadata } from "next";
+import "../globals.css";
+import { ReactQueryClientProvider } from "@tokenization/tw-blocks-shared/src/providers/ReactQueryClientProvider";
+import { TrustlessWorkProvider } from "@tokenization/tw-blocks-shared/src/providers/TrustlessWork";
+import { EscrowProvider } from "@tokenization/tw-blocks-shared/src/providers/EscrowProvider";
+import { EscrowDialogsProvider } from "@tokenization/tw-blocks-shared/src/providers/EscrowDialogsProvider";
+import { EscrowAmountProvider } from "@tokenization/tw-blocks-shared/src/providers/EscrowAmountProvider";
+import { Toaster } from "@tokenization/ui/sonner";
+import { WalletProvider } from "@tokenization/tw-blocks-shared/src/wallet-kit/WalletProvider";
+import type { ReactNode } from "react";
+import { Inter } from "next/font/google";
+import { cn } from "@/lib/utils";
+import { NextIntlClientProvider } from "next-intl";
+import { SharedTranslationProvider } from "@tokenization/tw-blocks-shared/src/i18n/TranslationProvider";
+import sharedEn from "@tokenization/tw-blocks-shared/src/i18n/messages/en.json";
+import sharedEs from "@tokenization/tw-blocks-shared/src/i18n/messages/es.json";
+import appEn from "../../../messages/en.json";
+import appEs from "../../../messages/es.json";
+
+const sharedMessages: Record> = { en: sharedEn, es: sharedEs };
+const appMessages: Record> = { en: appEn, es: appEs };
+
+const inter = Inter({
+ subsets: ["latin", "latin-ext"],
+ display: "swap",
+ variable: "--font-inter",
+});
+
+export const metadata: Metadata = {
+ title: "Backoffice Tokenization",
+ description: "Backoffice Tokenization",
+};
+
+export default async function LocaleLayout({
+ children,
+ params,
+}: {
+ children: ReactNode;
+ params: Promise<{ locale: string }>;
+}) {
+ const { locale } = await params;
+ const messages = appMessages[locale] ?? appMessages.es;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/backoffice-tokenization/src/app/page.tsx b/apps/backoffice-tokenization/src/app/[locale]/page.tsx
similarity index 100%
rename from apps/backoffice-tokenization/src/app/page.tsx
rename to apps/backoffice-tokenization/src/app/[locale]/page.tsx
diff --git a/apps/backoffice-tokenization/src/app/layout.tsx b/apps/backoffice-tokenization/src/app/layout.tsx
index 59f9736..f0ad499 100644
--- a/apps/backoffice-tokenization/src/app/layout.tsx
+++ b/apps/backoffice-tokenization/src/app/layout.tsx
@@ -1,50 +1,5 @@
-import type { Metadata } from "next";
-import "./globals.css";
-import { ReactQueryClientProvider } from "@tokenization/tw-blocks-shared/src/providers/ReactQueryClientProvider";
-import { TrustlessWorkProvider } from "@tokenization/tw-blocks-shared/src/providers/TrustlessWork";
-import { EscrowProvider } from "@tokenization/tw-blocks-shared/src/providers/EscrowProvider";
-import { EscrowDialogsProvider } from "@tokenization/tw-blocks-shared/src/providers/EscrowDialogsProvider";
-import { EscrowAmountProvider } from "@tokenization/tw-blocks-shared/src/providers/EscrowAmountProvider";
-import { Toaster } from "@tokenization/ui/sonner";
-import { WalletProvider } from "@tokenization/tw-blocks-shared/src/wallet-kit/WalletProvider";
import type { ReactNode } from "react";
-import { Inter } from "next/font/google";
-import { cn } from "@/lib/utils";
-const inter = Inter({
- subsets: ["latin"],
- display: "swap",
- variable: "--font-inter",
-});
-
-export const metadata: Metadata = {
- title: "Backoffice Tokenization",
- description: "Backoffice Tokenization",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: ReactNode;
-}>) {
- return (
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
-
-
- );
+export default function RootLayout({ children }: { children: ReactNode }) {
+ return children;
}
diff --git a/apps/backoffice-tokenization/src/components/layout/app-header.tsx b/apps/backoffice-tokenization/src/components/layout/app-header.tsx
index e744662..b7ae838 100644
--- a/apps/backoffice-tokenization/src/components/layout/app-header.tsx
+++ b/apps/backoffice-tokenization/src/components/layout/app-header.tsx
@@ -1,11 +1,15 @@
"use client";
import { SidebarTrigger } from "@tokenization/ui/sidebar";
+import { LanguageSwitcher } from "@/components/shared/language-switcher";
export function AppHeader() {
return (
-
-
+
+
+