Publish language packs
Blah can serve Telegram-compatible language packs from a public git repository served over HTTPS. Translators publish versions with Git tags; operators configure each DC to poll that repository. Clients fetch translations from the DC, so the repository is never on a client request path. This does not replace the web client’s own bundled strings.
Prepare the repository
Use one folder per client platform and language, optionally under a common prefix:
translations/ ios/ en-gb/ info.json Welcome/value UnreadMessages/one UnreadMessages/otherThe platform folder must match the client’s lang_pack, such as ios, android,
tdesktop, macos, android_x or webk. It accepts lowercase letters, digits and
underscores, with no hyphens. Language folders use lowercase letters or digits separated
by single hyphens, such as en, pt-br or zh-hans.
Each string key is a directory name of 1–255 UTF-8 bytes that does not start with a dot.
A plain string contains only value. A plural string contains any of zero, one, two,
few, many, and the required other. Do not mix value and plural files or put other
files in a string directory: the entire string is skipped with a warning. Deeper nesting
is ignored, as are files other than info.json directly inside a language folder.
Write UTF-8 text. The importer strips exactly one final newline (\n or \r\n); other
whitespace is preserved. Invalid UTF-8 makes that string invalid.
Each language needs info.json:
{ "name": "English (United Kingdom)", "native_name": "English (UK)", "plural_code": "en", "lang_code": "en-gb", "base_lang_code": "en", "official": false, "rtl": false, "beta": true}name, native_name, and plural_code are required. official, rtl, and beta default
to false. Optional lang_code must equal the folder name. base_lang_code names a language
clients fetch separately; publish that base as its own language. The server does not merge base strings. Optional strings_count
and translated_count default to the valid string count. Optional translations_url
defaults to the configured repository URL. Unknown metadata keys are ignored.
Missing or invalid metadata fails that language’s update and keeps its previous version.
Keep each variant file and info.json at or below 64 KiB, each language at or below
50,000 strings, and fetched text at or below 32 MiB per language per synchronization pass.
Exceeding a limit fails that language’s update.
Publish a version
Commit the language and tag that commit with <platform>-<language>-v<N>:
git tag ios-en-gb-v1git push origin ios-en-gb-v1Versions are positive decimal integers up to 2,147,483,647, without leading zeros. If the
operator configures a tag prefix, prepend it literally, for example
release-ios-en-gb-v2. Untagged folders are not served. Each language advances to its
highest tagged version; intermediate versions need not be imported individually.
Publish corrections under a higher version. Moving an already imported tag does not rewrite history. Deleting only the newest tag does not roll clients back. Removing every tag for a language withdraws it after a successful complete tag listing; retained data becomes available again when its tags reappear.
Changed and removed strings are sent as differences. Clients whose versions predate the DC’s available history, or exceed its current version, receive a full replacement pack. A failed update leaves the last successful version available. Matching live clients, including clients still signing in, receive a hint to fetch the new difference. A base language’s publication also notifies clients using a language derived from it.
Configure the DC
In /admin, set these managed server options and restart:
| Option | Value |
|---|---|
langPackRepository | An HTTPS clone URL, for example https://git.example.com/team/translations.git |
langPackRepositoryToken | Optional read-only token, masked as a secret; empty means anonymous |
langPackRepositoryUsername | HTTP Basic username; defaults to x-access-token; set the username your host requires |
langPackPathPrefix | Optional folder prefix, for example translations; defaults to empty |
langPackTagPrefix | Optional literal tag prefix, for example release-; defaults to empty |
langPackSyncInterval | Poll interval in seconds; defaults to 900, minimum 60 |
Some hosts rate-limit anonymous git traffic even for public repositories. An optional read-only token with no other scopes is enough; it is sent as the HTTP Basic password only to the repository’s origin and is dropped on cross-origin redirects. Rotating credentials preserves imported history. Usernames must be nonempty without colons, and neither usernames nor tokens may contain control characters.
Use a public repository URL without embedded credentials, query strings or fragments.
Scheme and host are case-insensitive, the default HTTPS port and one trailing slash are
removed, and the remaining path (including .git) is significant. The folder prefix has
its leading and trailing slashes trimmed and cannot contain . or .. components.
The server must support HTTPS smart-HTTP with git protocol v2, partial-clone filters
(tree:0, blob:none), and fetching reachable objects by id. A self-hosted
git http-backend needs uploadpack.allowFilter=true and
uploadpack.allowAnySHA1InWant=true (or uploadpack.allowReachableSHA1InWant=true),
and its proxy must pass the Git-Protocol header through.
A background pass starts at each server startup without delaying client service. Subsequent
passes use the maintenance queue. No repository means no synchronization or network
traffic, and language-pack RPCs return LANG_PACK_INVALID. The RPCs are available before
sign-in after the client’s normal connection initialization; bot sessions cannot call them.
Changing the repository, folder prefix or tag prefix replaces the DC’s imported state on the next pass. Clients recover with a full pack when necessary. Each DC keeps its own cache and version history in its business database, included in ordinary database backups.