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"
| Field | Used for |
|---|---|
greetingMessage | The welcome message sent on connection (when flows.welcome.templateKey: greetingMessage). Supports {userName} placeholder. |
welcomeMessage | Deprecated alias for greetingMessage. Don't use in new packs. |
systemPrompt | LLM persona for users in this language. Per-language prompts are useful for tone/formality differences (e.g. tu vs usted in Spanish). |
strings | Map 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:
- The user's DIDComm Profile message includes a
languagefield (set by Hologram from the device locale). - If
languages[<that-code>]exists, use it. - Otherwise, fall back to
metadata.defaultLanguage. - 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
| Key | Used when |
|---|---|
CREDENTIAL | Auth menu item label. |
LOGOUT | Logout menu item label. |
WELCOME | Generic welcome message (rarely used directly). |
AUTH_REQUIRED | Sent to unauthenticated users when flows.authentication.required: true. |
AUTH_SUCCESS | After successful authentication, no name available. |
AUTH_SUCCESS_NAME | After successful authentication. Placeholders: {name}. |
WAITING_CREDENTIAL | While waiting for the user to respond to the credential request. |
AUTH_PROCESS_STARTED | After the credential request is sent. |
LOGOUT_CONFIRMATION | After logout. |
MCP config flow
| Key | Used when |
|---|---|
MCP_CONFIG_MENU | Menu item label that opens the per-user MCP config. |
MCP_CONFIG_ABORT | Menu item label to abort an in-progress config. |
MCP_CONFIG_SELECT_SERVER | Prompt before listing user-controlled servers. |
MCP_CONFIG_SAVED | Success message. Placeholders: {server}. |
MCP_CONFIG_INVALID | Connection-test failure. Placeholders: {server}. |
MCP_CONFIG_ERROR | Save error. |
MCP_CONFIG_ABORTED | Confirmation that the flow was cancelled. |
Plus per-field labels declared inside mcp.servers[].userConfig.fields[].label — see MCP.
Approvals
| Key | Used when |
|---|---|
MY_APPROVAL_REQUESTS | Menu item for the requester's pending requests. |
PENDING_APPROVALS | Menu item for an approver's pending queue. |
Generic
| Key | Used when |
|---|---|
ROOT_TITLE | The agent's title, displayed at the top of the chat (some Hologram clients). |
STATS_ERROR | When the statistics tool fails. |
ERROR_MESSAGES | Generic 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
- Flows — where strings get used.
- MCP — per-field labels.
- Authentication — the auth-flow string keys.