Ask Metnos with a request like this example: “Which language is set for my account, and where can I change it?”
The instance administrator can change it in the web chat by opening
Settings > System > Users > user name >
Preferences, then selecting the lang field. A user's
detail page is served at /admin/users/<user-id>. If the
question is asked from Telegram, the path still belongs to the Metnos web
chat; it is not a path inside Telegram.
Language is a preference of each user and is applied to one request at a time. Two users can use different languages concurrently without changing the instance-wide configuration or affecting one another.
Metnos must keep four kinds of content aligned: model instructions, tool descriptions, text shown to people, and the lexicon used to understand requests. Translating the interface alone is insufficient: a button may be in French while the planner still reads English instructions, or a response may be correctly localized while recognition of a natural French phrase still fails.
Italian and English are the languages currently registered as fully supported in code. The stores may contain other languages while translation is underway; their presence alone does not prove that the entire product is ready in those languages.
The operational contract is:
In the web chat, the server identifies the authenticated user and reads that
user's lang preference. Telegram resolves the same value through
the channel-to-user binding. Recurring work performed on behalf of a person
also applies its owner's language.
Metnos places the value in a context local to that request. The context remains isolated while several turns run in parallel: setting English for one user changes neither another user's language nor the process-wide default.
| Source | Value used | Scope |
|---|---|---|
lang preference | Language of the authenticated user or the user bound to the channel. | One request or turn. |
METNOS_LANG | Instance fallback when the user has no valid preference. | Process; cached on first use. |
| Built-in default | it, when METNOS_LANG is also absent. | Instance. |
The values offered by the lang field do not come from a list
written into the page. They are derived from the languages present in
i18n.sqlite. Consequently, bootstrapping a new language can make
it selectable before every translation is complete. Fallbacks keep the
service usable, but the administrator should not offer that language to
users before completing the checks described below.
| Area | Content | Runtime source | Fallback |
|---|---|---|---|
| Model prompts | Instructions for planning, judging, describing, Tutor composition, and final-answer generation. | runtime/prompts/<language>/ |
Approved target file; target candidate; approved English file; English candidate. |
| Executor manifests | The executor description and argument descriptions read by the planner. | Language tables in the corresponding manifest.toml. |
Request language; English; first available language in deterministic order. |
| User-facing text | Deterministic messages, errors, confirmations, labels, and notifications. | i18n.sqlite |
Request language; English; Italian; finally <missing:KEY>. |
| Input lexicon | Natural forms and mappings used to recognize intents and parameters in a request. | detection.sqlite, initialized from the runtime registry. |
Union of the current language with Italian and English; gaps are reported and queued. |
Every caller passes an explicit language code to the prompt loader. When an
approved file is absent, the loader can use a candidate from
_pending; when that candidate is also absent, it falls back to
English. A candidate does not replace an approved file that already exists:
for an existing language, the candidate must be reviewed and promoted.
The planner consists of a core, relevant sections, and a footer. If the requested language lacks the core, the whole planner falls back to English. If the core exists but one section is missing, that section can fall back to its English counterpart.
[description]
it = "Cerca file per nome, percorso e intervallo temporale."
en = "Find files by name, path, and time window."
[args.properties.patterns.description]
it = "Nomi o espressioni da cercare."
en = "Names or patterns to find."
affinity = ["cerca", "trova", "find", "search", "files"]
The loader reads these tables directly from the manifest. It does not import
descriptions into the message database. affinity is not a table
per language either: it is one mixed list of signals, because it supports
semantic routing and is not displayed to the user. The
manifest.lang_state.json file stores the hashes needed for
alignment; it does not replace the manifest's content.
Deterministic text is looked up by key in the i18n database. The
needs_translation field describes work still owed by the
translator; if a row already contains non-empty text, that text remains
usable.
The input lexicon is separate from visible text. For a new language, common phrases and mappings can be translated with assistance; regular expressions must still be authored and checked manually. When native forms are absent, Metnos continues to recognize Italian and English forms, while explicitly reporting that the target language is not fully covered.
A model tends to follow the language of its instructions, but this is not a guarantee. Response language must therefore not depend on prompt language alone.
Metnos passes both the turn's language code and its readable name to prompt templates. Prompts that produce visible prose, including the final assembler, describers, and Tutor, explicitly request output in that language. An English fallback prompt can therefore still request a French answer. If the code has no registered readable name, the model receives the code itself: the turn may work, but behavior is less reliable and the language is not ready for release.
If a prompt is written in the wrong language and does not state the output language, the model may indeed answer in the prompt's language. Checks must therefore cover both conditions: the correct template was selected, and the target response language was stated explicitly. Deterministic messages do not depend on model behavior; they always follow the i18n database's fallback chain.
Prompts, manifest descriptions, and messages record a fingerprint of the current text and of the source version from which each translation was produced. When one language is edited, that version becomes the source for realigning the others.
| Resource | How a change is detected | Result |
|---|---|---|
| Prompt | Content-hash comparison; file time is used only to resolve concurrent edits. | A new candidate is written to the target language's _pending directory. |
| Manifest description | Hash comparison for every field and language. | The manifest table is updated and the manifest is signed again. |
| i18n message | Text version and row update time. | Other languages that are no longer aligned are queued for translation. |
Comparison and selection of resources are deterministic; the model is used only to produce candidate wording. A translation remains generated content that requires review, not automatic evidence of linguistic correctness.
Edit one language of a resource at a time before running alignment. For prompts, concurrent edits are resolved by file time. In a manifest, all languages share one file, so a conflict is resolved alphabetically. Do not rely on either rule to reconcile two divergent corrections.
The input lexicon follows a separate cycle: it is queued by language and verified through a coverage check. It does not participate in source selection for the other three areas.
The following command bootstraps a language; it does not declare that
language supported. Run it from the runtime directory with the
Metnos installation's Python environment:
cd <installation-directory>/runtime
../.venv/bin/python -m admin.prompts_cli add-language fr --source-lang=it
The command performs three immediate operations:
runtime/prompts/fr/ and its _pending directory;It does not immediately translate manifests, complete the input lexicon, add the code to the supported-language registries, or change the instance default.
vocab.LANGS and add the language's
readable name to the registries used by the prompt loader and translators.
Check the vocabulary sections that expose their own language-specific
forms as well.
../deploy/run_prompts_translator.sh
../.venv/bin/python cli/detection_cli.py enqueue fr
../.venv/bin/python cli/detection_cli.py translate
Repeat translation until the actionable queue is empty; author and verify
any pending regular expressions manually.
../.venv/bin/python -m admin.prompts_cli sync-status
../.venv/bin/python -m admin.prompts_cli review <role> --lang=fr
../.venv/bin/python -m admin.prompts_cli mark-synced <role> --lang=fr
lang field derives
its values from the i18n catalog automatically; the page has no separate
allow-list to edit. Setting METNOS_LANG is optional and changes
only the instance fallback, not individual preferences.
Run the following commands from
<installation-directory>/runtime with
../.venv/bin/python.
| Command | What it checks |
|---|---|
../.venv/bin/python -m admin.prompts_cli validate | Template syntax and loading invariants. |
../.venv/bin/python -m admin.prompts_cli lint --strict | Prompt structure, metadata, and symmetry. |
../.venv/bin/python -m admin.prompts_cli validate-cross-lang | Placeholders, syntax, and proportions across language versions. |
../.venv/bin/python -m admin.i18n_cli stats | Row counts and translations still pending. |
../.venv/bin/python -m admin.i18n_cli pending | Catalog rows that still require translation. |
../.venv/bin/python -m admin.i18n_cli validate --verbose | Completeness for languages registered in vocab.LANGS. |
../.venv/bin/python cli/detection_cli.py coverage fr | Native coverage of the input lexicon. |
Every visible string in the chat, dialogs, approval requests, and Settings pages must come from the i18n catalog. A sentence written directly in a template or JavaScript violates this contract even when it happens to match the installation's default language.