Mensajes
- Las claves se anidan (
ctx.t('menu.title')); una clave también puede llevar puntos literales ({ 'menu.title': '…' }). Registrar otra vez el mismo idioma hace merge profundo. - Los placeholders
{param}se interpolan; los desconocidos se dejan tal cual. - Una hoja
{ one, other, zero?, two?, few?, many? }es una forma plural que se elige conparams.counteIntl.PluralRules.zerotambién aplica a uncount === 0exacto en idiomas sin categoría cero. - Una clave que falta cae al idioma base (
pt-BR→pt), luego al idioma por defecto y por último a la propia clave (con un log de nivel debug).
Cómo se resuelve el idioma
Dentro de la cola por chat, antes de los hooksbefore('message') (ver
el pipeline de entrada), ctx.lang
toma el primer candidato que sirva un idioma registrado:
- La preferencia guardada del usuario: la clave
'lang'dectx.user(la escribectx.setLang(); cambia la clave conuserKey). - El idioma que el provider informa del remitente:
SenderInfo.languageoplatformData.languageCode/language/locale(p. ej. ellanguage_codede Telegram). Se desactiva conproviderLanguage: false. Una actualización que no informa idioma (ellanguage_codede Telegram es opcional) reutiliza el último informado para el mismo remitente, guardado en memoria, para que el chat no salte al idioma por defecto entre turnos. - Tu función
detect(ctx), si la configuras. defaultLocale(por defecto'en', oBotConfig.lang).
ctx.langSource dice qué paso ganó ('user', 'provider', 'detector'
o 'default'; undefined con el i18n apagado). Si un hook
before('message') reescribe ctx.body (por ejemplo, la transcripción de
una nota de voz de @botmesh/voice), hay un detect configurado y el idioma
salió de él o del valor por defecto, se vuelve a resolver después de los hooks para que
detect vea el texto hablado. Se respeta un ctx.lang que el hook haya
puesto a mano.
ctx.defaultLang es el defaultLocale del bot, sea cual sea el ctx.lang
resuelto: los plugins que traen textos solo para algunos idiomas (como
@botmesh/ai) lo usan de reserva para un idioma que no cubren.
La resolución se activa con cualquier llamada a bot.locale() o con
BotConfig.i18n. Sin ninguna de las dos, el bot responde en su idioma por
defecto (mensajes internos en inglés, o en el idioma de lang) y no lee nada
del store.
Un bot en un solo idioma: lang
Los textos internos están en inglés por defecto. lang elige otro idioma
incluido para ellos (y para ctx.lang) sin activar la resolución: no lee
ctx.user, no usa el idioma del provider ni un detector. Es todo lo que
necesita un bot escrito en un solo idioma:
lang e i18n.defaultLocale a la vez, deben coincidir: la misma
etiqueta, o el mismo idioma con una etiqueta más específica ('es' y 'es-MX',
y la más específica queda como predeterminada). Idiomas o regiones distintos
lanzan ConfigValidationError. Usa i18n cuando el bot atienda varios
idiomas.
Fijar el idioma del bot
El idioma que informa el provider es el del cliente del usuario, no necesariamente el de tu bot. Si tu contenido (persona, flujos, títulos de botones) existe en un solo idioma, un cliente en otro idioma recibiría los textos internos (“Voy a: … ¿Confirmas?”, respuestas de los comandos slash, timeouts) en su idioma alrededor de contenido en el tuyo, y un usuario con dos clientes en idiomas distintos podría verlos cambiar de un turno a otro. Fija los idiomas que sirve el bot conlocales:
locales se ignora y la resolución cae a defaultLocale (que siempre se
sirve); ctx.setLang() lo rechaza. Las variantes regionales siguen a su base
('es' sirve 'es-MX'). En resumen, la precedencia: los idiomas que sirve
el bot (locales) acotan todo; dentro de ellos, la elección explícita del
usuario → el idioma de su cliente → detect → defaultLocale. Sin
locales se sirve cualquier idioma registrado (incluidos los es y en
internos). Para un bot bilingüe, lista ambos (locales: ['en', 'es']) y
traduce también tu contenido, por ejemplo construyendo la persona según
ctx.lang.
Cambiar de idioma
ctx.setLang(code) guarda el idioma registrado que sirve a code
('en-GB' → 'en' si solo existe en). Rechaza con TypeError si el
código está vacío o ningún idioma lo sirve, y con Error si el i18n está
desactivado.
Conversaciones
c.t() y c.lang siguen el último mensaje de la conversación: un cambio
de idioma a mitad de conversación se aplica al siguiente paso y también al
destino de c.goto():
Fuera de los handlers
Mensajes internos
Los mensajes propios del core (timeout, reintentos y error de conversación, validación de esperas —incluido el rechazo de unschema sin mensaje
propio—, respuestas de slashCommandsPlugin y las preguntas, pistas,
palabra para saltar y errores de validación de flow().form() bajo
core.form.*) viven bajo core.* con valores por defecto en es y en. Sus claves se exportan como
CORE_MESSAGES. Para cambiarlos, o traducirlos a un idioma nuevo, usa
bot.locale():
conversation.timeoutMessage / retriesMessage / errorMessage
configurado en BotConfig sigue ganando al idioma.
@botmesh/ai hace lo mismo bajo ai.* (claves exportadas como AI_MESSAGES,
valores por defecto es y en en AI_LOCALES, ambos congelados): las respuestas de reserva del
autoresponder cuando el proveedor falla o no contesta nada (ai.error) o se niega
(ai.refusal), salvo que fijes errorMessage / refusalMessage, la pregunta de confirmación
de herramientas (ai.tools.confirm, {tool} / {args}; ai.tools.confirmButtons
y las etiquetas ai.tools.button.yes / ai.tools.button.no en canales con
botones, y los avisos ai.tools.confirmAnswered / ai.tools.confirmExpired
donde los botones no se pueden quitar) y sus piezas
(ai.tools.confirmRepeat, ai.tools.ago.*, ai.tools.value.*,
ai.tools.more), las notas del prompt de sistema (ai.tools.confirmNote,
ai.tools.doneNote), la respuesta al
agotar maxSteps (ai.tools.maxSteps), la respuesta cuando las tool calls
del último paso se cortaron por el tope de tokens (ai.tools.truncated) y la línea de appendSources
(ai.rag.sources, {sources}). Una clave que tus idiomas no definen cae al
texto interno en ctx.lang; para un idioma sin textos internos (cualquiera
salvo es y en), en el idioma por defecto del bot (ctx.defaultLang) si los
tiene, y si no, en inglés:
Siguientes pasos
- Referencia de configuración:
lang,i18ny las demás opciones denew Bot() - Datos por usuario: dónde guarda
ctx.setLang()la elección - Plugin de IA: respuestas en el idioma del usuario