Guías
Trabajar con traductores
Tus catálogos son JSON normal, sin nada propietario que convertir. Eso te da tres formas de traducir: pasarle los ficheros a un TMS, ir y volver con ficheros de traductor usando export/import, o traducir a máquina con translate.
JSON plano, sin lock-in
La mayoría de los TMS (Crowdin, Lokalise, Phrase y compañía) leen catálogos JSON de forma nativa. Apuntas la plataforma a locales/ y ya está: sin paso de exportación y sin formato propio. Las traducciones vuelven en los mismos ficheros, y cada cambio se revisa como un diff normal.
Export para humanos
Cuando no hay TMS (un traductor freelance, una agencia, un compañero con una hoja de cálculo), verbaly export escribe un fichero listo para traducir por idioma, con el texto original al lado de cada traducción. Si guardas tu catálogo por grupos, exporta una fila por mensaje, nombrada con su ruta completa, así que el traductor ve texto y nunca un grupo.
XLIFF 2.0 es el formato de intercambio de la industria, así que los TMS y las herramientas CAT lo abren directamente. Tus huecos y tus etiquetas viajan ahí como fichas protegidas con nombres con sentido, así que un traductor no las puede romper sin querer; las palabras de plural y de select se quedan editables, porque esas sí hay que traducirlas. Gettext PO funciona con Poedit y con cualquier herramienta de PO, y CSV es para todos los demás, editable en cualquier sitio. Todos los formatos dicen dónde vive cada texto en tu fuente, así que el traductor ve el contexto en vez de adivinarlo.
Antes de exportar, verbaly status muestra cuánto falta por idioma: es: 45/48 translated (94%).
Import de vuelta, validado
verbaly import lee los archivos traducidos (XLIFF 2.0 o 1.2, CSV o gettext PO) y rellena tus catálogos. Cada entrada pasa la misma validación estructural que la traducción a máquina: placeholders, variantes de plural y tags deben sobrevivir intactos. Lo demás se rechaza y se reporta, nunca se escribe.
Las traducciones existentes se conservan salvo que pases --overwrite; las keys desconocidas se ignoran y se reportan. Las entradas que una herramienta PO marca como fuzzy cuentan como sin traducir. El idioma destino sale del propio archivo (o del nombre del archivo), y --locale gana sobre ambos. Se lee como uno de tus idiomas, así que el pt_BR de gettext llena tu pt-BR. Un idioma nuevo empieza su catálogo con el código bien escrito, salvo que tu config liste sus locales: entonces un idioma fuera de esa lista se salta con un aviso y código de salida 1, porque tu app nunca lo cargaría. Pasa --draft cuando nadie ha leído aún el archivo: lo que trae queda marcado como borrador hasta que alguien lo apruebe.
La red de seguridad, en palabras simples
Editar locales/es.json a mano es una forma perfectamente válida de traducir, y recibe la misma protección que un archivo importado. verbaly check lee cada traducción al lado de su original y falla cuando la traducción no puede decir lo mismo, y cada build la lista. No hace falta que recuerdes estas reglas: si rompes una, el error te dice cuál y dónde.
- Conserva cada
{placeholder}, escrito igual. Es el hueco donde entra un nombre, una cantidad o una fecha. Traduce las palabras de alrededor, nunca la de adentro. - Conserva los tags en pares.
<em>…</em>y compañía marcan énfasis o un enlace. Muévelos a donde la frase los necesite, pero no dejes una mitad afuera. Las palabras que solo parecen un tag son tuyas para traducir: enPress <Enter> to continuenada cierra ese paréntesis angular, así que nada lo está revisando. - Mantén el bloque de plural como bloque de plural, y conserva siempre su caso
other. Ese caso es el comodín: sin él, cualquier cantidad que no listaste no muestra nada. - Agrega las formas que tu idioma necesita. El inglés se arregla con dos, el polaco y el árabe necesitan más. Verbaly te avisa cuáles faltan, y ese aviso es un consejo, no un fallo.
Cuando alguien cambia un texto fuente, sus traducciones no se quedan como si nada hubiera pasado. Verbaly registra para qué texto fuente se escribió una traducción en el momento en que se escribe, ya sea un proveedor, un agente, Studio o un archivo importado, y la marca como desactualizada en cuanto ese texto cambia, hasta que se actualiza, o se mantiene con verbaly review --approve cuando sigue diciendo lo correcto. El archivo de un traductor trae el texto fuente del que partió, así que un archivo traducido de una versión anterior queda desactualizado en cuanto llega. Editar una traducción quita la marca sola.
npx verbaly status es el vistazo rápido: cuánto está traducido por idioma, cuánto espera revisión, cuánto está desactualizado y cuánto está roto.
Exportar para apps móviles
Los mismos catálogos pueden viajar a una app móvil hermana como recursos nativos: android-xml escribe carpetas strings.xml que puedes soltar en res/, y ios-strings escribe carpetas .lproj para Xcode.
Tu idioma fuente se vuelve el default de la plataforma (values/strings.xml, en.lproj), y las claves sin traducir se omiten para que la app haga fallback a él en vez de mostrar texto vacío. Las keys se adaptan a nombres de recurso válidos de Android, los valores conservan su sintaxis de parámetros intacta, y el flujo es de ida: las traducciones van de tus catálogos a la app.
¿Aún sin humanos disponibles?
La traducción a máquina llena los huecos con las mismas garantías estructurales, y todo lo que un provider o una persona haga mal lo atrapa verbaly check en CI.
La salida de la máquina llega marcada como borrador: translate registra lo que escribió en verbaly.lock, junto a tu config. Como el lockfile de un gestor de paquetes, Verbaly lo escribe, tú lo committeas con tus catálogos y nadie lo edita a mano, así nadie confunde la salida de la máquina con trabajo revisado. Si un merge lo deja con un conflicto, Verbaly conserva los borradores de los dos lados y el siguiente extract lo escribe entero: inclúyelo en el commit del merge. Borrarlo marcaría todos los borradores como revisados.
Antes de la primera corrida, escribe lo que no se traduce. Los nombres de tu producto, los de sus funciones y cualquier forma que ya hayas decidido van en translate.glossary, y cómo tratas al lector va en translate.instructions. Sale más barato que corregir la misma palabra a mano después de cada corrida.
Una corrida larga sobrevive a una mala conexión. Las peticiones que fallan por algo pasajero se reintentan, y si aun así una no vuelve, todo lo demás queda guardado y el informe nombra los mensajes que no pudo llenar. Volver a correr translate pide solo esos, así que una corrida cortada nunca te cuesta todo dos veces. Tampoco le cuesta a nadie su trabajo: un mensaje que alguien guardó mientras la corrida estaba fuera conserva su texto, y el informe dice cuántos respetó.
verbaly review lista lo que espera, borradores de la máquina y traducciones escritas para un texto fuente anterior, cada uno con el texto que se publica al lado de su traducción. verbaly review --approve lo acepta después de leerlo, seguido de claves cuando quieres aceptar solo esas, y un archivo de traductor traído con import cuenta como revisión por sí solo. En CI, verbaly check --drafts sigue fallando mientras queden borradores sin revisar, y --outdated mientras quede una traducción desactualizada.
Un agente de código puede correr este mismo ciclo a través del servidor MCP: lo que traduzca sigue llegando como borrador detrás de la misma puerta de revisión, también las traducciones que escriba él mismo.