ESTADO: Aprobado
FECHA: 2026-10-07
AUTOR: Carlos Alberto Torres Camargo (arquitecto)
ALCANCE: literales i18n de backend y frontend de Nebula ERP que se cargan en simappe_admin (DEV y QA)
RELACIONADOS: ADR-012 (i18n del menú servida por SimappeAdmin), reglas SAR del frente i18n (ASR-PC-I18N-01..03, ASR-PF-I18N-01..04, ASR-DB-I18N-01..02, ASR-PC-VAL-02)
IMPLEMENTACIÓN DE REFERENCIA: hoja de cálculo «i18n» (pestañas i18n - Frontend, i18n - Backend, Listas) y su formulario publicado como aplicación web de Apps Script
Los literales i18n del ERP viven en simappe_admin (esquema admin, tablas translation_keys y translations, idiomas ES=2 y EN=1). Los desarrolladores de backend y frontend los necesitan a diario y no tienen acceso a esa base: los solicitan a arquitectura, que los carga en DEV y QA a la vez con un script versionado.
Hasta el 7 de octubre de 2026 la solicitud se hacía escribiendo directamente en una hoja de cálculo compartida, con un panel lateral de ayuda. Que el equipo tuviera permiso de edición sobre la hoja produjo estos incidentes, todos verificados contra la hoja y las bases:
| Fecha | Incidente | Efecto |
|---|---|---|
| 23-sep | El estado inicial se escribió con errata (Solitado) |
Quedó propagado a más de 60 filas |
| 2-oct | Literales con errores de escritura en el nombre (UDGET.…, COMMOM.…) |
No se pudieron cargar hasta corregirse |
| 2-oct | Filas marcadas Ejecutado a mano sin haberse cargado |
La hoja afirmaba algo falso: la pantalla mostraba la clave cruda |
| 2-oct | Filas escritas a mano, sin hora de registro | Se perdió la trazabilidad de quién y cuándo |
| 23-sep a 7-oct | Inglés de traducción automática sin revisar («Earrings» por Pendientes, «Asset» por Activo, «font» por fuente) | 38 textos corregidos después de cargados |
| 7-oct | Se renombró a mano el encabezado de las columnas A y B | El formulario dejó de funcionar para todo el equipo |
Además, los literales de backend (mensajes de error del servidor) y los de frontend (etiquetas de pantalla) estaban mezclados en una sola lista, aunque siguen convenciones distintas y las valida SAR con reglas distintas.
Los literales se solicitan únicamente por el formulario de registro, uno a uno o por archivo (Excel o CSV, sección 4.3). La hoja es el registro de lo solicitado y de su estado, no un lugar donde escribir.

Quien registra elige el tipo del literal: Frontend o Backend. Cada tipo se guarda en su propia pestaña y se rige por sus propias reglas (sección 5). El formulario impide registrar como Frontend un literal que ya existe como Backend, y viceversa.
Si una traducción quedó mal, se registra de nuevo el mismo literal con el texto correcto. El formulario detecta que ya existe, precarga su español, inglés y módulo vigentes para editarlos, y al guardar:
| Estado | Quién lo pone | Significado |
|---|---|---|
| Solicitado | El formulario, al registrar | Pendiente de carga |
| Ejecutado | Arquitectura, tras cargar y verificar | Está en DEV y QA, en español e inglés, y el bundle lo sirve. Lleva fecha de ejecución |
| Reemplazado | El formulario, al registrar una corrección | Hubo un texto más nuevo para el mismo literal |
| Descartado | Arquitectura | No se carga (duplicado de una clave existente, solicitud retirada) |
Nadie del equipo cambia un estado. Una fila en Ejecutado sin fecha de ejecución es un error del registro.
Cada corrida de carga:
VNNN__i18n_registro_formulario_<tema>.sql en nebula-erp/docs/i18n. Usa el siguiente número libre entre todas las ramas.POST /simappe-admin/api/v1/translation/bundle/invalidate/{es|en}) y verifica con GET …/bundle/{lang} y GET …/bundle/{lang}/validate (content: []).Es un script de sincronización de datos: no lleva MR ni despliegue. Queda commiteado en su rama para integrarlo cuando corresponda.
Se abre con la cuenta de Google autorizada (la que usas para registrar literales). Si tu correo no está autorizado, pídele acceso a arquitectura. Cada campo del formulario trae su propia descripción con las reglas y ejemplos de esta sección y de la siguiente.
La primera vez, Google pide autorizar el formulario con tu cuenta, porque se ejecuta con ella:
Esos permisos sirven para leer la hoja (consultar literales y la lista de autorizados), sugerir la traducción y enviarle el registro al escritor. El formulario no escribe nada con tu cuenta: la hoja sigue siendo solo de lectura para ti.


Para registrar muchos literales de una vez (por ejemplo, la lista que genera el código de una funcionalidad):
Tipo: marca Frontend o Backend. Aplica a todas las filas del archivo; si tienes de los dos tipos, súbelos en dos archivos.
Abre la pestaña Archivo (Excel o CSV) y descarga la plantilla, o arma el archivo con estas 4 columnas y sus encabezados en la primera fila:
| Literal | Módulo | Español | Inglés |
|---|---|---|---|
BUDGET.ITEM_CATALOG.ACTIONS.DELETE |
budget | Eliminar | Delete |
budget.rubro.error.codigoduplicado |
budget | Ya existe un rubro con el código | A budget item with code {0} already exists |
.xlsx (primera hoja) o .csv separado por ;, , o tabulador, en UTF-8 o en la codificación de Excel en Windows.Sube el archivo. El formulario muestra la vista previa: cada fila con su estado y el motivo.
Revisa el inglés en la tabla (se puede editar) y desmarca las filas que no quieras enviar. Validar de nuevo repite la revisión con tus cambios.
Pulsa Registrar N literales. Las filas rechazadas no impiden registrar las demás; al terminar, la tabla muestra el resultado de cada fila.
El inglés sugerido sale de la primera fuente que tenga respuesta, en este orden, y el formulario indica cuál usó:
common), se usa su inglés. Respeta la puntuación final: «Estado:» → «Status:».{{x}} y {0} se apartan antes de traducir y se reponen después; nunca se traducen.ACTIONS y BUTTONS, verbo sin «To».El glosario vive en la pestaña Listas (columnas Glosario módulo, Glosario español y Glosario inglés) y lo mantiene arquitectura. Si una traducción del ERP se corrige más de una vez, su término va al glosario.
El catálogo de simappe_admin es uno solo para backend y frontend, pero cada frente usa su propia convención y SAR valida cada una por separado.
| Regla | Detalle | Regla SAR |
|---|---|---|
| Patrón | servicio.entidad.tipo.detalle, todo en minúsculas, segmentos de letras y números sin guion bajo. Ej.: treasury.embargo.error.tercero |
ASR-PC-I18N-03 (FAIL para clave nueva; WARN si ya existía en BD) |
Vocabulario del tipo |
error, validation, msg, label, info, solution, solucion, message, detail, success (semilla vigente; SAR lo amplía en su tabla i18n_key_types) |
ASR-PC-I18N-03 |
| Mensaje con solución | La base es un namespace: <base>.message y <base>.solucion son hojas hermanas. Nunca x.y con texto y x.y.solucion a la vez |
ASR-DB-I18N-02 (FAIL) |
| Uso en código | Solo vía constante y <Modulo>Messages.get(...) o SimappeException. Nada de prosa en el código ni defaults literales en getOrDefault |
ASR-PC-I18N-01 (FAIL) |
| Parámetros | Placeholders {0}, {1}… idénticos en español e inglés y en la misma cantidad que los argumentos del llamado |
ASR-PC-I18N-02 (FAIL) |
| Sin archivos locales | El runtime resuelve siempre por SimappeAdmin; no se agregan claves a messages_*.properties |
ASR-PC-VAL-02 |
Claves ya registradas que no cumplen el patrón y que SAR reporta: budget.tiporecurso.categoria.NACIONAL (categoria no es un tipo y el detalle va en mayúsculas), treasury.pago.operacion.sobregiro y budget.error.formato.no.soportado (su tercer segmento no es un tipo del vocabulario). No se registran claves nuevas con esas formas.
| Regla | Detalle | Regla SAR |
|---|---|---|
| Patrón de feature | NAMESPACE.SEGMENTO[.SEGMENTO…] en MAYÚSCULAS, con guion bajo permitido, mínimo dos segmentos. Ej.: BUDGET.FUNDING_SOURCE.TITLE.LIST |
ASR-PF-I18N-02 |
| Menú y breadcrumb | Clave de un solo segmento: OPTIONS_*, SUB_MODULES_*, MENU. Solo en posición de etiqueta (title:, label:, breadcrumb:) |
ASR-PF-I18N-02 |
| Sin prosa en templates | Todo texto de usuario del .html sale de una clave con \| translate |
ASR-PF-I18N-01 |
| Sin prosa en constantes | Las etiquetas declaradas en TypeScript (columnas, formularios, títulos) son claves resueltas con t() |
ASR-PF-I18N-03 (FAIL) |
| Clave sembrada | Toda clave que la feature usa está en un script versionado de docs/i18n. El script de la carga de este proceso cumple esta regla |
ASR-PF-I18N-04 (FAIL) |
| Regla | Detalle | Regla SAR |
|---|---|---|
| Español e inglés | Toda clave se carga con los dos idiomas, nunca uno solo | ASR-DB-I18N-01, ASR-PC-I18N-02 |
| Inglés distinto del español | Un inglés igual al español es un pendiente de traducción (WARN) | ASR-PC-I18N-02 |
| Hoja o namespace, no ambos | Ninguna clave puede ser prefijo de otra separada por punto: una sola colisión tumba todo el i18n del admin | ASR-DB-I18N-02 |
| Buscar antes de crear | Antes de pedir una clave nueva, verifica si el texto ya existe con otra clave | — |
| Texto completo | Sin espacios ni saltos de línea en los extremos, sin el inglés pegado dentro del español, sin números faltantes («Máximo caracteres») | — |
Qué se valida, dónde y con qué efecto:
| Control | Formulario (al registrar) | Arquitectura (en la carga) |
|---|---|---|
| Tipo elegido | Obligatorio | — |
| Formato del literal (segmentos separados por punto, letras, números y guion bajo) | Rechaza | — |
| Convención del frente (sección 5) | — | Se reporta al solicitante antes de cargar |
| Español e inglés presentes | Rechaza | Rechaza |
| Módulo del ERP | Obligatorio (accounting, treasury, budget, invoicing, billing, masters, common) |
— |
| Literal registrado en el otro tipo | Rechaza | — |
| Choque hoja/namespace con otro literal de la hoja | Rechaza | Se detiene si choca con una clave de DEV o QA |
| Clave ya existente en DEV o QA | — | Se actualiza su texto (corrección) o se descarta si es idéntica |
Marcadores ({{x}}, {0}) iguales en español e inglés |
Rechaza | Se revisa antes de cargar |
| Literal repetido dentro del mismo archivo | Rechaza (desde la segunda aparición) | — |
| Choque hoja/namespace entre filas del mismo archivo | Rechaza | — |
| Término del glosario mal traducido | Aviso | Se revisa antes de cargar |
| Calidad del inglés | — | Se señalan las traducciones automáticas erróneas; la corrección pasa por el formulario |
| Carga dual y bundle sano | — | DEV y QA en la misma corrida; validate en content: [] |
simappe_admin (DEV ni QA). El texto que manda es el que pasó por el formulario; un cambio hecho a mano en la base se pierde o diverge entre ambientes.| Alternativa | Por qué no |
|---|---|
| Hoja editable por el equipo (lo que había) | Es el origen de todos los incidentes de la sección 2: dos puntos de entrada y ningún control |
| Google Forms vinculado | Escribe en una hoja de respuestas aparte y no puede validar contra lo ya registrado (duplicados, choques, correcciones) |
| Panel lateral dentro de la hoja | Se ejecuta con la cuenta de quien lo usa: exige permiso de edición sobre la hoja, y con él todo lo demás |
| Que cada equipo cargue sus literales en la base | Rompe la carga dual DEV+QA y deja claves sin script versionado: es la divergencia entre ambientes que ya se midió |
Positivas
Limitaciones conocidas
El código vive en el proyecto Apps Script «Centrica - Fabrica de Software - Registro i18n», ligado a la hoja (Código.gs y Panel.html).
El proyecto tiene dos implementaciones activas:
| Implementación | Ejecutar como | Acceso | Uso |
|---|---|---|---|
| Formulario i18n | Usuario que accede a la app web | Cualquier persona con cuenta de Google (y solo registran los autorizados) | El enlace de la sección 4 |
| Escritor i18n (no compartir) | Arquitectura | Cualquiera, protegido con la clave interna CLAVE_ESCRITOR de las propiedades del script |
Lo llama el formulario; su enlace va en la constante URL_ESCRITOR de Código.gs |
Para publicar un cambio: Implementar → Administrar implementaciones → editar → Nueva versión. Así el enlace no cambia. Crear una implementación nueva genera otro enlace y obliga a recomunicarlo. Si cambia el código de doPost o de la escritura, la nueva versión se publica también en el escritor.
La función instalarSeguridad() se ejecutó una vez el 7-oct-2026: creó la clave interna del escritor y la columna «Usuarios autorizados» en Listas, con los 6 correos que ya habían registrado literales y la cuenta de arquitectura. Volver a ejecutarla no cambia la clave ni duplica correos. probarEscritor() comprueba la conexión con el escritor sin escribir nada (debe responder {"ok":true}).
La función instalarGlosario() se ejecutó una vez el 9-oct-2026: creó en Listas las columnas del glosario con 44 términos de partida (comunes, presupuesto, contabilidad y tesorería), tomados de las correcciones de inglés ya hechas. Volver a ejecutarla no duplica términos. Después el glosario se mantiene a mano en Listas. probarSugerencias() muestra en el registro de ejecución qué sugiere el formulario para casos conocidos.
La plantilla del archivo la sirve el mismo formulario: su enlace con ?plantilla=1 descarga plantilla_literales_i18n.csv.
La función instalarEstructura() se ejecutó una vez el 7-oct-2026: creó i18n - Backend, movió allí los 56 literales de backend, corrigió Solitado → Solicitado y protegió las tres pestañas. Volver a ejecutarla no duplica la pestaña ni mueve filas; reaplica la corrección del estado y la protección.
| Versión | Fecha | Autor | Descripción |
|---|---|---|---|
| 1.2.0 | 2026-10-09 | Carlos Torres | Carga por archivo (Excel o CSV de 4 columnas, hasta 200 filas) con vista previa por fila y registro en bloque; inglés sugerido con contexto del ERP (glosario por módulo en Listas, textos ya aprobados y traductor con marcadores protegidos y reglas por tipo de clave); el formulario rechaza marcadores distintos entre español e inglés; si se pierde la respuesta del escritor, el registro se confirma leyendo la hoja. Formulario y escritor en la versión 7, mismos enlaces |
| 1.1.1 | 2026-10-07 | Carlos Torres | El proyecto Apps Script pasa de «Proyecto sin título» a «Centrica - Fabrica de Software - Registro i18n» (nombre que muestra Google al autorizar el formulario); las URL no cambian |
| 1.1.0 | 2026-10-07 | Carlos Torres | Acceso por correo verificado: el formulario (versión 4, mismo enlace) se ejecuta con la cuenta de quien lo abre y solo registran los «Usuarios autorizados»; la escritura pasa a una implementación escritora protegida con clave interna; el archivo pasa de «Cualquier persona con el enlace» a acceso restringido, con Lector solo para los correos autorizados |
| 1.0.1 | 2026-10-07 | Carlos Torres | El acceso general del archivo pasa de Editor a Lector; el formulario (versión 2 de la implementación, mismo enlace) incorpora descripciones explicativas por campo |
| 1.0.0 | 2026-10-07 | Carlos Torres | Versión inicial: formulario como único punto de entrada, pestañas Backend/Frontend protegidas, correcciones por re-registro con estado Reemplazado, reglas de nombrado apoyadas en SAR y reglas de control |