feat(mcp): tools de idiomas y traducciones

- Nueva categoria tools/languages: list_web_languages,
  get_record_translations y set_record_translations (registros, config,
  textos_generales y vars de modulo via builder_custom).
- Param lang opcional en list_table_records y get_record
  (options.translates de CocoDB).
- Docs actualizados (03/04/06/09/11b + ACAI_ENDPOINTS) con el modelo de
  cms_traducciones y los workflows de traduccion.
This commit is contained in:
Jordan Diaz
2026-07-16 20:09:47 +00:00
parent d475845c27
commit d46c204ed0
13 changed files with 382 additions and 3 deletions

View File

@@ -203,6 +203,16 @@ Acceso en Twig:
Las variables son **propiedades del objeto iterado**, no variables sueltas.
## Traducir las variables de un módulo
Los valores textuales de las variables de un módulo (títulos, descripciones, wysiwyg, etc.) NO se guardan en la fila de la página, sino en la tabla `builder_custom`. Por eso una traducción de módulo apunta siempre a `builder_custom`, no a `apartados` ni a la tabla de la página.
Para traducir las vars de una instancia de módulo:
1. `get_module_config_vars({ tableName, recordNum, sectionId })` devuelve `varsMeta`: por cada variable, su ubicación física `{ fieldName, recordNum }` en `builder_custom` (y por cada item en las vars multi).
2. `set_record_translations({ tableName: "builder_custom", recordNum: <de varsMeta>, prefix, fields: { <fieldName de varsMeta>: "texto traducido" } })`.
El nombre humano de la variable (p.ej. `titulo`) NO es el nombre de columna real (p.ej. `title2`): usa siempre el `fieldName` que da `varsMeta`. Ver el workflow completo en `09-mcp-tools-reference.md`.
## Layout global vs módulos
`header`, `footer`, `style` global y `javascript` global NO son módulos normales. Viven en `cms/lib/plugins/builder_saas/layout.json` y se editan con tools dedicadas (`get_layout_field` / `set_layout_field`). Ver `08-layout-and-libraries.md`.

View File

@@ -152,6 +152,20 @@ create_or_update_record:
metatag_descripcion: "Descubre nuestros servicios…"
```
## Traducciones (multiidioma)
En sitios multiidioma las traducciones NO viven en la fila del registro: se guardan en la tabla central `cms_traducciones`, indexadas por `(prefix = código de idioma, tableName sin cms_, fieldName, recordNum)`. El valor (`fieldValue`) se almacena en Base64, pero es transparente: las tools y el motor codifican/decodifican por ti, tú siempre trabajas con texto plano.
- El idioma base usa el prefix `www` y sus URLs no llevan prefijo. Los idiomas secundarios (p.ej. `en`) sirven bajo `/en/...`. Consulta los activos con `list_web_languages`.
- Solo se traducen los tipos de campo traducibles: `textfield`, `textbox`, `wysiwyg`, `codigo` y `multitext` (este último se traduce como el JSON serializado completo en una sola fila).
- Un valor vacío (`''`) en `set_record_translations` **borra** la traducción y el runtime cae al idioma base.
- Para leer un registro ya traducido, pasa `lang: "<prefix>"` a `get_record` o `list_table_records`.
- El campo `identificador` de `textos_generales` (los literales del filtro Twig `| translate`) se traduce por su campo `texto`.
### Regla dura: nunca traduzcas `enlace`
El campo `enlace` NUNCA se traduce a mano — lo mantiene el motor CocoEnlace. La action PHP rechaza cualquier intento de traducir `enlace`. No lo incluyas en `set_record_translations`.
## Patrón canónico — Detalle de registro
Para cualquier tabla con campo `enlace` (productos, noticias, vacantes, servicios), **el detalle se resuelve por convención** vía sección general `custom-{tableName}`. Ver `03-modules-and-sections.md` para detalles.

View File

@@ -149,7 +149,7 @@ $datos = CmsApi::get("productos", "", "", "", [
| `uploads` | bool | `true` | Incluir datos de upload fields |
| `relations` | bool/array | `true` | Resolver foreign keys. Array para limitar: `['categoria']` |
| `relationsDepth` | int | 2 | Profundidad de relaciones anidadas |
| `translates` | string | idioma actual | Código de idioma para `| translate` |
| `translates` | string/bool | idioma actual | Código de idioma (prefix, p.ej. `'en'`) para devolver los campos ya traducidos desde `cms_traducciones`. `true` usa el idioma activo de la request |
| `groupBy` | string | null | Cláusula GROUP BY |
| `aggregates` | array | `[]` | Funciones de agregación |
| `onlyFields` | array | null | Seleccionar solo ciertos campos |
@@ -157,6 +157,8 @@ $datos = CmsApi::get("productos", "", "", "", [
| `redis` | bool | null | Forzar cache Redis |
| `redis_expire` | int | 60 | TTL del cache (segundos) |
**Idioma en lectura**: pasar `options['translates'] => '<prefix>'` hace que `CmsApi::get`/`cmsApi` devuelva los valores traducidos de los campos traducibles. Las tools MCP `list_table_records` y `get_record` exponen esto con su parámetro `lang` (que se traduce a `options.translates`). A nivel HTTP, `cms_api` v3 también fuerza el idioma con el header `X-ACAI-ACCEPT-LANGUAGE: <prefix>`, que tiene el mismo efecto que `translates` para toda la request. Ver `09-mcp-tools-reference.md` (sección Idiomas y traducciones) para el flujo de escritura con `set_record_translations`.
### Insert — `CmsApi::insert()`
```php

View File

@@ -153,6 +153,20 @@ Tools del MCP `playwright`. El browser headless es del agente — el usuario NO
|------|--------|
| `refresh_acai_token` | Renueva el JWT cuando expira (errores 403) |
### Idiomas y traducciones
Las webs multiidioma guardan las traducciones en la tabla central `cms_traducciones`. Ver `04-pages-and-records.md` y `11b-rules-cheat-sheet.md`.
| Tool | Acción | Notas |
|------|--------|-------|
| `list_web_languages` | Lista los idiomas activos del sitio (`settings.dat.php [idiomas]`) | Devuelve `prefix`/`urlPrefix`/`isDefault`. `prefix="www"` = idioma base (URLs sin prefijo); otro prefix (p.ej. `en`) = idioma bajo `/en/...` |
| `get_record_translations` | Lee traducciones de uno o varios registros | Por `tableName` (sin `cms_`) + `recordNums`. Opcional `fields` y `prefix`. Devuelve `{recordNum:{prefix:{fieldName:valor}}}` ya decodificado |
| `set_record_translations` | Escribe traducciones de un registro + idioma | `tableName`+`recordNum`+`prefix`+`fields`. `''` borra la traducción. **`enlace` prohibido** (lo mantiene CocoEnlace) |
Además, las tools de lectura `list_table_records` y `get_record` aceptan el parámetro opcional `lang` (prefix, p.ej. `'en'`): devuelven los valores ya traducidos de los campos traducibles (el motor CocoDB aplica la traducción en lectura).
Campos traducibles por tipo: `textfield`, `textbox`, `wysiwyg`, `codigo`, `multitext` (el multitext se traduce como el JSON serializado completo en una sola fila). El valor en la DB va en Base64, pero es transparente: pasas y recibes texto plano.
### Documentación
| Tool | Acción |
@@ -279,6 +293,32 @@ Notas:
- En modo producción todas estas tools sincronizan automáticamente con el servidor real (no solo modifican local).
- Si solo tienes el `recordId` y necesitas saber qué `fieldName` tiene uploads, llama antes a `get_table_schema({ minimal: true })` y filtra los campos `type: "upload"`.
### 12. Traducir un registro a otro idioma
Ejemplo: traducir la vacante num=12 al inglés.
1. `list_web_languages` — obtén el `prefix` del idioma destino (p.ej. `en`). El prefix `www` es el idioma base y NO se traduce por aquí (se edita con los campos normales del registro).
2. (Opcional) `get_record({ tableName: "vacantes", recordNum: 12 })` para leer los textos originales, o `get_record_translations({ tableName: "vacantes", recordNums: [12], prefix: "en" })` para ver qué falta.
3. `set_record_translations({ tableName: "vacantes", recordNum: 12, prefix: "en", fields: { titulo: "...", descripcion: "..." } })`. Solo campos traducibles (`textfield`, `textbox`, `wysiwyg`, `codigo`, `multitext`). **NUNCA** incluyas `enlace`. Un `''` borra esa traducción.
4. Verifica con `get_record({ tableName: "vacantes", recordNum: 12, lang: "en" })` — devuelve los valores ya traducidos.
### 13. Traducir las variables de un módulo
Los textos de un módulo Builder viven en la tabla `builder_custom`, no en la tabla de la página.
1. `list_web_languages` — obtén el `prefix` destino.
2. `get_module_config_vars({ tableName, recordNum, sectionId })` — devuelve `varsMeta`: por cada variable su `{ fieldName, recordNum }` físico en `builder_custom` (y por item en vars multi).
3. Por cada variable a traducir, `set_record_translations({ tableName: "builder_custom", recordNum: <de varsMeta>, prefix, fields: { <fieldName de varsMeta>: "texto traducido" } })`.
4. Para vars multi, repite con el `recordNum`/`fieldName` de cada item que devuelve `varsMeta`.
### 14. Traducir un texto general (literal de plantilla)
Los literales del filtro Twig `| translate` son registros normales de la tabla `textos_generales`; se traduce su campo `texto`.
1. `list_web_languages` — obtén el `prefix` destino.
2. `list_table_records({ tableName: "textos_generales", where: "identificador = '...'", fields: ["num", "identificador", "texto"] })` para localizar el `num`.
3. `set_record_translations({ tableName: "textos_generales", recordNum: <num>, prefix, fields: { texto: "traducción" } })`.
## Reglas globales para todas las tools
1. **`tableName` siempre SIN prefijo `cms_`** (excepto en `queryDB` Twig y en el `middleWare` de `set_hook_middleware`).

View File

@@ -87,6 +87,20 @@ Resumen ejecutable de reglas críticas, tipos de campo, filtros y formatos de da
| `multitext` | String JSON | `"[{\"item\":\"valor\"}]"` |
| `upload` | NO enviar — usar `upload_record_image` después |
## Traducciones (multiidioma)
| Regla | Detalle |
|-------|---------|
| Prefix `www` = idioma base | URLs sin prefijo. Otro prefix (p.ej. `en`) sirve bajo `/en/...` — lista con `list_web_languages` |
| Nunca traducir `enlace` | Lo mantiene CocoEnlace; la action PHP lo rechaza |
| Valor `''` borra la traducción | `set_record_translations` con `''` elimina la fila y cae al idioma base |
| Base64 transparente | El valor se guarda Base64 en `cms_traducciones`; tú siempre pasas/recibes texto plano |
| `multitext` = JSON completo | Se traduce el JSON serializado entero en una sola fila, no item por item |
| Campos traducibles | Solo `textfield`, `textbox`, `wysiwyg`, `codigo`, `multitext` |
| Leer traducido | `get_record`/`list_table_records` con `lang: "<prefix>"` |
| Vars de módulo | Se traducen sobre `builder_custom` usando `varsMeta` de `get_module_config_vars` |
| Textos generales | Traducir el campo `texto` del registro en `textos_generales` (localiza por `identificador`) |
## Variables globales en Twig
| Variable | Descripción |