Hoy la herramienta de evaluación de madurez en gobierno de datos que publiqué hace dos días pasó de hablar un solo idioma a hablar dos.
No es un cambio cosmético. Es una decisión de arquitectura disfrazada de feature de UX.
El problema real
La herramienta en olaveruiz.cl/tools/gdd-review tiene 350+ strings de cara al usuario: 40 preguntas, 40 textos de guía para responder, 200 recomendaciones (5 por pregunta), 8 nombres de dimensión, y ~60 elementos de UI. Todo eso estaba hardcodeado en español.
Agregar inglés sin un sistema de i18n hubiera significado duplicar archivos, mantener dos versiones del formulario en paralelo, y eventualmente perder la sincronía entre ambas. Ese es exactamente el tipo de deuda que se acumula sin hacer ruido y explota cuando menos la esperas.
La arquitectura que elegí
Un solo archivo de datos (gdd-review-data.js) con dos estructuras:
Primero, un objeto TRANSLATIONS plano con ~60 claves por idioma — para todos los textos de UI: labels, placeholders, botones, mensajes de error, toasts de Notion.
Segundo, el array dimensions convertido a objetos bilingües. Cada pregunta pasó de esto:
{ q: "¿Existe un sponsor ejecutivo...?", guidance: "Evalúa si...", recs: { 1: "Tu empresa necesita..." } }
A esto:
{
q: { es: "¿Existe un sponsor ejecutivo...?", en: "Is there a designated executive sponsor...?" },
guidance: { es: "Evalúa si...", en: "Assess whether..." },
recs: { 1: { es: "Tu empresa necesita...", en: "Your organization urgently needs..." } }
}
Una función tObj(obj) de 3 líneas resuelve el idioma activo en runtime. Simple, sin librerías externas, sin archivos adicionales.
El detalle que me costó más pensar
La pantalla de resultados guarda automáticamente en Notion. Cuando el usuario alterna el idioma en esa pantalla, la herramienta tiene que re-renderizar los resultados en el nuevo idioma — pero sin volver a hacer el POST a Notion. Un segundo guardado sería un duplicado en la base de datos.
La solución: un flag resultsSaved y un parámetro fromLangToggle en la función showResults(). Si es un toggle de idioma y los datos ya están cacheados, renderiza con los datos existentes. Si es la primera vez, calcula, guarda en Notion, y cachea. El radar chart también re-dibuja sus etiquetas en el nuevo idioma, porque usa tObj(dim.short) al momento de renderizar.
Otro detalle no obvio: el select de Gerencia/Área tiene un value en español que mapea al campo de Notion. Ese value no cambia — solo cambia el texto visible. El dato que llega a la base de datos siempre es en español, independiente del idioma de la interfaz. Sin eso, el mapping de Notion se rompía.
Pero no solo fue el i18n
Linus — el agente de Git de la corporación — hizo un audit del repo mientras trabajaba. Encontró un working tree con 6 archivos sin commitear, dos ramas remotas huérfanas que nunca habían sido mergeadas a main, y el repo de LabelLoop sin ninguno de los archivos de governance que tiene ORC.
El resultado: 4 commits atómicos para resolver la deuda de staging, merge de las dos ramas huérfanas (una tenía el feature de savings view del módulo Wealth; la otra tenía el pipeline completo del proyecto Scope — 11 commits que llevaban semanas flotando en una rama sin merge), y 5 archivos de governance pusheados a jolaveruiz/LabelLoop: PR template con checklist de seguridad, dos issue templates con prefijo JOL-, CONTRIBUTING.md con Gitflow completo, y un hook de commit que valida Conventional Commits localmente.
El health score del repo pasó de 74 a ~92/100.
Lo que viene
La herramienta de gobierno de datos ahora está disponible para equipos que trabajan en inglés. El siguiente paso real es usarla con personas reales y ver qué fricción produce el formulario en condiciones de uso genuino — no de prueba.
En paralelo, el Taste Engine sigue pendiente para tres landings (Ledger, LabelLoop, PAIP). Y el gate_testing de PAIP sigue abierto.
Días como hoy — sin una feature grande, sin un lanzamiento espectacular — son los que más aprecia el proyecto en retrospectiva. La diferencia entre un codebase que escala y uno que se pudre está exactamente aquí: en los commits que nadie nota.