Skip to main content

Internationalization

Hologram agents are multi-language by default. The Hologram app sends each user's locale on connection (in the DIDComm Profile message); your agent uses that to pick a greeting, a system prompt, and a set of UI strings.

Internationalization in the Agent Pack is split between declarative strings (welcome message, menu labels, fixed prompts) and prompted behaviour (the LLM is asked to "answer in the user's language"). Both come together in the languages block.

The languages block

Top-level keys are language codes (ISO 639-1: en, es, fr, pt, de, ja, …). Each language has up to four sub-fields:

metadata:
defaultLanguage: en

languages:
en:
greetingMessage: "Hi {userName}, what can I help you with?"
systemPrompt: "You are a polite, concise assistant."
strings:
CREDENTIAL: "Authenticate"
LOGOUT: "Logout"
WELCOME: "Welcome"
es:
greetingMessage: "Hola {userName}, ¿en qué puedo ayudarte?"
systemPrompt: "Eres un asistente cortés y conciso."
strings:
CREDENTIAL: "Autenticar"
LOGOUT: "Cerrar sesión"
WELCOME: "Bienvenido"
FieldUsed for
greetingMessageThe welcome message sent on connection (when flows.welcome.templateKey: greetingMessage). Supports {userName} placeholder.
welcomeMessageDeprecated alias for greetingMessage. Don't use in new packs.
systemPromptLLM persona for users in this language. Per-language prompts are useful for tone/formality differences (e.g. tu vs usted in Spanish).
stringsMap of label keys → localized string. Used by menu items, auth flow, MCP config flow, and approvals.

Language resolution

When a user connects, the runtime picks a language by this rule:

  1. The user's DIDComm Profile message includes a language field (set by Hologram from the device locale).
  2. If languages[<that-code>] exists, use it.
  3. Otherwise, fall back to metadata.defaultLanguage.
  4. If the fallback isn't defined either, use en.

Within a single conversation the language is fixed once chosen — even if the user types in a different language, the menu labels and system messages stay in the originally-resolved language. The LLM, however, is free to answer in the user's language (typically driven by the agentPrompt containing "Answer in the user's language; fallback English.").

String keys

The runtime looks up these keys whenever it sends a localized message. Define every key in every language you support — the runtime falls back to the default language for missing keys, but if the default is missing too, an empty string is sent.

Auth flow

KeyUsed when
CREDENTIALAuth menu item label.
LOGOUTLogout menu item label.
WELCOMEGeneric welcome message (rarely used directly).
AUTH_REQUIREDSent to unauthenticated users when flows.authentication.required: true.
AUTH_SUCCESSAfter successful authentication, no name available.
AUTH_SUCCESS_NAMEAfter successful authentication. Placeholders: {name}.
WAITING_CREDENTIALWhile waiting for the user to respond to the credential request.
AUTH_PROCESS_STARTEDAfter the credential request is sent.
LOGOUT_CONFIRMATIONAfter logout.

MCP config flow

KeyUsed when
MCP_CONFIG_MENUMenu item label that opens the per-user MCP config.
MCP_CONFIG_ABORTMenu item label to abort an in-progress config.
MCP_CONFIG_SELECT_SERVERPrompt before listing user-controlled servers.
MCP_CONFIG_SAVEDSuccess message. Placeholders: {server}.
MCP_CONFIG_INVALIDConnection-test failure. Placeholders: {server}.
MCP_CONFIG_ERRORSave error.
MCP_CONFIG_ABORTEDConfirmation that the flow was cancelled.

Plus per-field labels declared inside mcp.servers[].userConfig.fields[].label — see MCP.

Approvals

KeyUsed when
MY_APPROVAL_REQUESTSMenu item for the requester's pending requests.
PENDING_APPROVALSMenu item for an approver's pending queue.

Generic

KeyUsed when
ROOT_TITLEThe agent's title, displayed at the top of the chat (some Hologram clients).
STATS_ERRORWhen the statistics tool fails.
ERROR_MESSAGESGeneric error fallback.

You can also define your own keys and reference them from custom prompts — there's no fixed schema, only the keys the built-in flows look up.

Per-language MCP user-config labels

The MCP config flow's per-field prompt is itself a localized string, declared inside the field definition rather than the strings block:

mcp:
servers:
- name: github
transport: streamable-http
url: https://api.githubcopilot.com/mcp/
accessMode: user-controlled
userConfig:
fields:
- name: token
type: secret
label:
en: "Please enter your GitHub Personal Access Token:"
es: "Por favor, ingresa tu Token de Acceso Personal de GitHub:"
fr: "Veuillez entrer votre jeton d'accès personnel GitHub :"
pt: "Por favor, insira seu Token Pessoal do GitHub:"
headerTemplate: "Bearer {value}"

If the user's resolved language has no entry, the runtime falls back to English (or the first defined language).

Telling the LLM to answer in the user's language

The system prompt block (systemPrompt per language) is one lever. The bigger lever is the agentPrompt in llm:

llm:
provider: openai
model: gpt-4o-mini
agentPrompt: |
You are the Hologram + Verana ecosystem assistant.
- Language: answer in the user's language. Fallback: English.
- Style: concise, friendly. Don't reveal you are an AI.
- Tools: prefer tools over guessing.

This pattern (one agentPrompt for all users, instructing the model to follow the user's language) is what every reference pack does. Per-language systemPrompt is layered on top for finer control — useful when you want different wording or tone per language.

Worked example — five languages

metadata:
id: hologram-welcome
defaultLanguage: en

languages:
en:
greetingMessage: "Hi {userName}! 👋 I'm the Hologram + Verana assistant."
systemPrompt: "You are friendly, helpful, and concise."
strings:
ROOT_TITLE: "Hologram Welcome"
CREDENTIAL: "Authenticate"
LOGOUT: "Logout"

es:
greetingMessage: "¡Hola {userName}! 👋 Soy el asistente de Hologram + Verana."
systemPrompt: "Eres amable, servicial y conciso."
strings:
ROOT_TITLE: "Bienvenida a Hologram"
CREDENTIAL: "Autenticar"
LOGOUT: "Cerrar sesión"

fr:
greetingMessage: "Bonjour {userName} ! 👋 Je suis l'assistant Hologram + Verana."
systemPrompt: "Vous êtes aimable, serviable et concis."
strings:
ROOT_TITLE: "Bienvenue Hologram"
CREDENTIAL: "Authentifier"
LOGOUT: "Déconnexion"

pt:
greetingMessage: "Olá {userName}! 👋 Sou o assistente Hologram + Verana."
systemPrompt: "Você é amigável, prestativo e conciso."
strings:
ROOT_TITLE: "Boas-vindas Hologram"
CREDENTIAL: "Autenticar"
LOGOUT: "Sair"

de:
greetingMessage: "Hallo {userName}! 👋 Ich bin der Hologram + Verana Assistent."
systemPrompt: "Sei freundlich, hilfsbereit und prägnant."
strings:
ROOT_TITLE: "Hologram Willkommen"
CREDENTIAL: "Authentifizieren"
LOGOUT: "Abmelden"

llm:
provider: openai
model: gpt-4o-mini
agentPrompt: |
You are the Hologram + Verana ecosystem assistant.
Always answer in the user's language. Fallback: English.

A user with German locale connects → gets Hallo {userName}! 👋 …, sees menu labels in German, and the LLM replies in German.

Operating notes

  • Translation is your job. There's no auto-translation pipeline. Keep a glossary in your repo if you support more than three languages.
  • Don't translate keys. CREDENTIAL, LOGOUT, etc. are stable identifiers; only their values vary by language.
  • {userName}, {name}, {server}. These are the only placeholders the runtime substitutes. Don't introduce new ones — they won't be replaced.
  • Right-to-left. Hologram clients render RTL based on locale; you don't need to do anything special in the pack.

Next