Skip to content

Tutorial 5 Localization es

James Morris edited this page Jul 29, 2026 · 1 revision

Tutorial 5 · Localización (i18n)

Meta: entender cómo LockedIn CLI habla 33 idiomas — y practicar dirigir a un agente para añadir otro más. La localización es una tarea excelente para un agente: es lo bastante mecánica para delegarla, pero tiene restricciones reales (una barrera de pruebas, reglas de diseño y revisión gramatical) que te enseñan a revisar.

← Anterior: Tutorial 4 Prompting y revisión · Volver a Inicio


Qué significa aquí “localizado”

Ejecuta la CLI en español, hindi, japonés, chino simplificado o cualquier idioma incluido y todo cambia — el splash, la tabla de ayuda, la salida de cada comando, la sesión de chat, incluso la letra chica legal. No solo los chistes: toda la superficie visible.

lockedin --lang es post
LOCKEDIN_LANG=hi lockedin
lockedin --lang zh aura

El idioma se autodetecta al arrancar, resuelto por orden de prioridad:

  1. la opción --lang (--lang es, --lang=fr, -l ja)
  2. la variable de entorno LOCKEDIN_LANG
  3. tu locale (LC_ALL / LC_MESSAGES / LANG, luego el locale del sistema)
  4. inglés, como respaldo

normalizeLang() normalmente toma el subtag primario del locale. Así de-DE selecciona de, pero tlh no se recorta por accidente a tl; los alias reales fil y tgl apuntan a Tagalog (tl), el noruego nb y nn apuntan a no, el indonesio histórico in apunta a id, y el código hebreo histórico iw apunta a he. Dos códigos regionales se conservan tal cual en vez de plegarse a su subtag primario: pt-BR/pt_BR seleccionan el código regional canónico mientras que el pt genérico sigue siendo el paquete de portugués de Brasil compatible con versiones anteriores (ambos son portugués de Brasil y comparten los mismos pools/UI), y en-SG/en_SG mantienen el singlish (el en genérico sigue siendo inglés). El chino tradicional de Hong Kong es la misma clase de excepción: zh-HK, zh_HK.UTF-8 y zh-Hant-HK seleccionan zh-HK; zh y las etiquetas continentales seleccionan chino simplificado (zh).

Cambiar sobre la marcha — el panel /language. La CLI siempre pudo arrancar en otro idioma (--lang, LOCKEDIN_LANG); ahora puedes cambiar a mitad de sesión. Escribe /language (alias /lang o /languages) para listar los 33 idiomas por código, cada uno en su propia escritura; /language el cambia por el resto de la sesión. La gracia es la salida de emergencia: tras el cambio se redibuja en el nuevo idioma y luego, en el idioma que acabas de dejar, imprime el camino exacto de vuelta — /language en ahora, lockedin --lang en la próxima vez —, así que aterrizar por accidente en 日本語 o ಕನ್ನಡ nunca te deja varado. (Cambia dos veces y también se ofrece el idioma que nombra tu LOCKEDIN_LANG.) --lang y LOCKEDIN_LANG no cambian. Como /a11y, es una utilidad sincera, no forma parte de la sátira.

La idea: paquetes de idioma

Todo el texto traducible vive en paquetes, uno por idioma, cada uno con esta forma:

{ meta: { lang: 'es', name: 'Español', dir: 'ltr' },
  pools: { HOOKS: [ /* ~25 */ ], LESSONS: [ /* ... */ ], /* ... */ },
  ui:    { buzzwordDensity: 'Densidad de palabrería: ', /* etiquetas, títulos */ } }
  • pools son los arreglos de contenido (los chistes) que viste en el Cap. 2.
  • ui son los textos de interfaz: etiquetas, títulos y pequeñas plantillas.

El inglés es el paquete de referencia dentro de src/lockedin.js; los otros 32 módulos viven en src/content/*.js: ar, bn, bo, de, el, en-SG, es, eu, fa, fi, fr, he, hi, id, is, it, ja, kn, ms, nl, no, pl, pt, pt-BR, ru, sv, tl, tr, uk, ur, zh y zh-HK (pt-BR reutiliza los pools/UI de pt pero se registra por separado). Cada uno se registra en BUNDLES; SUPPORTED_LANGS sale de esas claves, y renderHelp() imprime esa lista generada. Ningún paquete ui mantiene una lista de códigos a mano.

setLang('fr');       // apunta el idioma activo al paquete francés
// L = pools activos, U = ui activo
pick(L.HOOKS)        // un gancho en francés
U.buzzwordDensity    // "Densité de jargon : "

Como cada renderizador lee L y U (nunca un string incrustado), setLang por sí solo cambia toda la experiencia. Ese es todo el truco.

La red de seguridad: paridad de claves

Esta es la invariante que hace seguro añadir un idioma:

Cada paquete debe exponer exactamente las mismas claves pools y ui que el inglés.

Una prueba lo impone en los 33 paquetes. Si añades un string de UI nuevo en inglés y olvidas traducirlo en ucraniano, npm test se pone en rojo y te dice qué clave falta. No puedes publicar en silencio un idioma a medio traducir.

La parte difícil: diseño en terminal

Cada escritura estresa la terminal de una manera distinta:

  • Japonés, chino simplificado y chino tradicional de Hong Kong usan caracteres East Asian Wide / Fullwidth. vw() los cuenta como dos columnas, y wrap() corta tokens largos sin espacios para que el texto CJK no rompa tarjetas ni cajas.
  • Hindi y kannada usan marcas combinantes no espaciadas o envolventes (Mn / Me), como matras y viramas. vw() las cuenta como cero columnas para no inflar el ancho medido.
  • box() envuelve cada línea antes de rellenarla, así que un banner traducido largo ya no atraviesa el borde.
  • Cada paquete define sentenceEnd y listSep (por ejemplo . / , , / ) para que las frases compuestas por generadores suenen naturales.

Al añadir un idioma, los strings de encabezado de tarjeta (cardSubtitle, cardMeta, cardFooter) deben caber en ≤ 60 columnas visibles. Árabe, persa, hebreo y urdu usan meta.dir: 'rtl'. Por defecto, la salida no incluye controles bidi, porque algunos terminales los muestran como etiquetas en recuadros. LOCKEDIN_BIDI=on activa explícitamente aislados equilibrados después del ajuste solo en terminales que se sabe que los admiten, preservando ANSI, comandos ASCII y el orden lógico al copiar. La salida accesible siempre los elimina. Sin esa opción, el orden RTL/LTR mixto puede ser menos sofisticado; no se sondea ni se infiere compatibilidad.

La parte sigilosa: gramática alrededor del texto del usuario

Algunas plantillas insertan cláusulas crudas del usuario con placeholders como {cap}. No las traduzcas casilla por casilla: la oración debe seguir funcionando cuando el placeholder sea una frase que escribió una persona, no un sustantivo bonito.

Bug real como advertencia: en japonés, poner justo después de {cap} puede sonar mal si {cap} es una cláusula completa. La solución no es “traducir con más ganas”, sino reestructurar la plantilla (por ejemplo, añadir un nominalizador o mover el placeholder) para que acepte entradas arbitrarias.

✅ Pruébalo con tu agente — añade un idioma

El ejercicio sigue funcionando igual. Elige un idioma que puedas verificar (o pídele al agente que lo haga) y llévalo de principio a fin. Escribe la especificación primero:

Añade danés (da). Crea src/content/da.js como un paquete { meta, pools, ui } con las mismas claves que el inglés, traduciendo cada entrada (pools de contenido a ~25 cada uno, todos los strings de UI). Registra da en BUNDLES en src/lockedin.js. --lang da y un locale da-* deben seleccionarlo. Mantén los encabezados de tarjeta dentro del límite de ancho. npm test debe seguir en verde, y añade pruebas de invariantes y detección en danés como las localizadas existentes.

Luego corre el ciclo de los Capítulos 3–4:

  1. Plan primero. “Antes de escribir código, dime qué archivos cambiarás y cómo mantendrás la paridad de claves con el inglés.”
  2. Pruebas primero. “Añade pruebas que fallen: detección de da, paridad de claves para da, y una invariante de reflect/connect en danés. Aún no crees el paquete.”
  3. Implementa. “Ahora crea src/content/da.js traduciendo un paquete existente clave por clave, regístralo y haz que las pruebas pasen. Solo pick/shuffle para la aleatoriedad.”
  4. Barrera + revisión. npm test, luego lockedin --lang da post — y lee el diff: ¿se tradujo cada clave? ¿Siguen alineados los bordes? ¿Las plantillas con {cap} sobreviven a cláusulas crudas del usuario?

Ejercicios de calentamiento más pequeños si un idioma completo es demasiado:

  • “Añade un TAGLINE más a los 33 paquetes de idioma, manteniendo iguales los conteos.”
  • “Comprueba que el cardFooter de kannada sea ≤ 60 columnas visibles y explica cómo mediste las marcas combinantes.”
  • “Muéstrame la prueba que fallaría si borrara una clave ui de ja.js.”

A dónde ir después

  • Ojea src/content/es.js — sigue siendo una plantilla amable para un paquete nuevo.
  • Relee docs/HANDOFF.md → “Adding a language”.
  • Disfruta los chistes multilingües en la Referencia de comandos.

Ese es el tutorial completo. Ya puedes dirigir a un agente de IA para construir funciones y localizarlas detrás de una barrera de pruebas — en 33 idiomas y con espacio para más. ¿De acuerdo? 👇

📘 LockedIn CLI wiki

Tutorial

Reference


Satire · Sátira · 風刺. Not affiliated with LinkedIn. GPL-3.0-or-later.

Clone this wiki locally