Friends of SNCF · A Project We Love

Tatarnetes: teaching Kubernetes to speak Tatar

September 20, 2026 · by the Tatar-Native Computing Foundation, shared with love by SNCF

An editor's note from SNCF. We are a foundation hosted on 💩, and we know craft when we see it. Today we are genuinely delighted to point you at a friendly, independent sister foundation — the Tatar-Native Computing Foundation — and its project Tatarnetes: a Kubernetes wrapper that speaks Tatar.

It is playful and it is real engineering at once — exactly our kind of thing. The article below is theirs, in their own words, republished here with permission and translated into English. Their repos: tatarnetes · tataros · tatarnetes-ui. It reconciles — and now it does so in Tatar.

И туган тел, и матур тел, әткәм-әнкәмнең теле!
"O native tongue, o beautiful tongue, the language of my father and my mother!"
— Ğabdulla Tuqay, "Tuğan Tel" (Native Language), 1909

TL;DR. We shipped Tatarnetes — a national wrapper around Kubernetes in which commands, messages, errors, and even the tea breaks are in Tatar. Next to it: TatarOS Linux (a wrapper over Talos that brings up a cluster running Tatarnetes) and the Tatarnetes UI (a dashboard set against a Tatar kiläm, a woven carpet). All of it under our own Tatarch 2.0 license. This is not only a joke (though we do love to laugh) — it is genuine engineering: i18n in pure bash 3.2 with not a single dependency, three writing systems (Cyrillic, Yañalif, and Arabic script), a deterministic tea schedule kept in sync between the CLI and the browser, and a first draft of Tatar Kubernetes terminology. And we battle-tested all of it on a real CozyStack cluster — with live kubectl/talosctl, RBAC, metrics, and one bug caught in the act. Below: the long version, with code and numbers.

Repositories at tatar-ncf: tatarnetes · tataros · tatarnetes-ui. License — Tatarch 2.0.

A tour of the console on a live cluster

1. Why do this at all

It is tempting to read this project as an April Fools' joke. But we started from an entirely serious thought: the native tongue (tuğan tel) deserves to live not only in poetry, but in the terminal. There is almost no Tatar IT terminology for cloud-native computing: how do you say "reconciliation", "ingress", "replica" in Tatar? We decided not to wait, but to propose — a draft that is arguable but alive, one that anyone can extend through a pull request.

So Tatarnetes is three things at once: (1) a fun tool that lifts your mood; (2) a real engineering sandbox for i18n in bash; (3) a contribution to the digitization of the language — a glossary and a localization any native speaker can edit.

2. Why launch on September 20

The date was not picked at random. Late September for Tatars is mullıq çorı, the season of abundance, when the cimeş (fruits) are gathered from fields and gardens. And in our Tatarch 2.0 license, any work — code, program, book — is called exactly that: "Cimeş", the fruit of labour. The logic is warm: in the season of fruits, we give our own fruit. Räxim itegez — help yourself.

And a happy coincidence: on September 20, 1970, the Tatar writer and poet Marat Käbirov was born — one of the first to drag Tatar literature into the digital world. Literature that went into code; code that began to speak the language of literature — in our own way. So Tatarnetes is a gift to the whole Tatar land and to the international Tatar community, from Kazan to Istanbul, from Helsinki to San Francisco. Millätem danlıqlı, xalqım yumart, kodı irekle.

3. A 30-second demo

Everyone knows kubectl get pods. Now try:

ayda күрсәт кузаклар

ayda (әйдә — "come on, let's go!") translates a Tatar command into kubectl and runs it.

How ayda translates a command into kubectl

Resources got meaningful Tatar names: a pod is kuzak (a husk/pod — and English pod means a husk too), a node is töen (a knot), a service is xezmät, a namespace is mäydan (a square), an ingress is qapqa (a gate), a secret is ser, a volume is külәm.

Tatar names for Kubernetes resources

On success you are met by a kind little face in a tübätäy (skullcap) that praises Tatarstan; on error, the same face, but cross, with some gentle Tatar grumbling. In between — a line of Tuqay or a hot öçpoçmaq (a triangular pastry) as a garnish.

A successful call: a face in a tübätäy, praise, and a garnish

And — yes — plain kubectl works too: ayda get pods passes straight through. There is also a krew plugin: kubectl ayda ....

get nodes: the cluster nodes, in Tatar

4. Architecture: a thin wrapper

Tatarnetes does not fork Kubernetes line by line. It is a thin, pipe-safe wrapper around kubectl: parse the Tatar, translate to kubectl, run it, wrap the response in national decorations. Schematically the flow is:

user → ayda (bash) ───────────────→ kubectl → Kubernetes API
                 1. resolve_verb (the verb, with a Latin fallback)
                 2. resolve_noun (first positional token = resource)
                 3. tea gateway (tea_now?)
                 4. run + capture stderr
                 5. face / praise / scolding + garnish (all to stderr)

The bash line count is 1936 (ayda + tos + lib/), and that includes six writing/logic systems, the i18n engine, the corpus, and the tests. Not a single external dependency — only bash, sed, awk, grep — and the target bash is 3.2 (the very one shipped by default on macOS: no associative arrays, with all the multibyte gotchas). That is a deliberate constraint: the tool must run everywhere.

5. i18n in pure bash 3.2

The most interesting part is how the localization lives without a gettext runtime.

The source of truth is .po. Interface strings live in locale/tt.po (39 msgids) and en.po in ordinary gettext format, where Tatar is canonical:

msgid "err.unknown_verb"
msgstr "Мондый әмер юк: «%s»"

Building the catalog. scripts/build-catalog.sh (awk) generates pure bash from the .po — a case function lib/catalog.sh, so that at runtime there is neither parsing nor dependencies:

catalog_lookup() { # $1=lang $2=key
  case "$1:$2" in
    tt:err.unknown_verb) printf '%s' 'Мондый әмер юк: «%s»' ;;
    en:err.unknown_verb) printf '%s' 'No such command: "%s"' ;;
    ...
    *) printf '%s' "$2" ;;
  esac
}

The t() function fetches a string, substitutes arguments, and — if the language is Tatar — runs it through transliteration:

t() {
  local key="$1"; shift
  local fmt; fmt="$(catalog_lookup "$AYDA_LANG" "$key")"
  local out; out="$(printf "$fmt" "$@")"
  if [ "$AYDA_LANG" = "tt" ]; then printf '%s' "$out" | alif_render; else printf '%s' "$out"; fi
}

Catalog consistency is guarded by CI: a separate job rebuilds catalog.sh from the .po and fails if a diff appears. That keeps the translation and the runtime from drifting apart.

Vowel harmony. We do not hard-code the plural; we generate it from vowel harmony — the suffix -лар/-ләр (back/front row), and after the nasals м/н/ң it becomes -нар/-нәр:

tn() {  # кузак→кузаклар, төен→төеннәр, хезмәт→хезмәтләр, мәйдан→мәйданнар, сер→серләр
  local w="$1" lastvow lastch front=0 nas=0
  lastvow="$(printf '%s' "$w" | grep -oE '[аәоөуүыиеэюёяАӘОӨУҮЫИЕЭ]' | tail -1)"
  lastch="$(printf '%s' "$w" | grep -oE '.$')"
  case "$lastvow" in ә|ө|ү|е|и|э|ю|ё|Ә|Ө|Ү|Е|И|Э) front=1 ;; esac
  case "$lastch"  in м|н|ң|М|Н|Ң) nas=1 ;; esac
  if [ "$front" -eq 1 ]; then [ "$nas" = 1 ] && echo "${w}нәр" || echo "${w}ләр"
  else                        [ "$nas" = 1 ] && echo "${w}нар" || echo "${w}лар"; fi
}

All of this is covered by unit tests (28 checks in tatarnetes, 12 in tataros).

6. Three writing systems of one people

Over a century, Tatar has changed three alphabets — and each is a cultural stratum. Tatarnetes can do all three:

AYDA_ALIF=cyrl  ayda ярдәм    # Cyrillic (default)
AYDA_ALIF=latin ayda ярдәм    # Yañalif — the Latin script of the 1920s–30s
AYDA_ALIF=arab  ayda ярдәм    # Yaña imlä — Tatar Arabic script
The license motto in three writing systems

You can also type commands in Latin (for keyboards without Cyrillic). And we did not create a second dictionary: first we try to recognize the input as-is, and if that fails, we transliterate the input to Cyrillic and try again. The English kubectl verbs are not broken by this:

resolve_noun() {
  local n="$1" r c
  r="$(translate_noun "$n")"; [ "$r" != "$n" ] && { printf '%s' "$r"; return 0; }  # found as-is
  c="$(printf '%s' "$n" | latin_to_cyrl)"                                          # Latin → Cyrillic
  r="$(translate_noun "$c")"
  if [ "$r" != "$c" ]; then printf '%s' "$r"; else printf '%s' "$n"; fi            # otherwise — as entered
}
Entering a command in Latin script

The Arabic script (yaña imlä, the 1920s reform) is phonemic: every sound is its own letter, all vowels are written, and the front/back row is distinguished (o ۇ / ö ۆ, u و / ü ۈ). At the start of a word a vowel takes a carrier-hamza ئ. We output in logical order — the RTL direction and letter-joining are done by the terminal itself. The word-initial hamza is added via awk (per word, byte-safe), and letters are replaced by individual substitutions:

cyrl_to_arab() {
  awk '{ n=split($0,a,/ /); o=""
    for(i=1;i<=n;i++){ w=a[i]
      if (w ~ /^(а|ә|о|ө|у|ү|ы|и|э|е|я|ю|ё|А|Ә|О|Ө|У|Ү|Ы|И|Э|Е)/) w="ئ" w
      o=o (i>1?" ":"") w } print o }' \
  | sed -e 's/я/یا/g' -e 's/а/ا/g' -e 's/ә/ە/g' -e 's/о/ۇ/g' -e 's/ө/ۆ/g' \
        -e 's/у/و/g' -e 's/ү/ۈ/g' -e 's/к/ك/g' -e 's/т/ت/g' ...   # one letter at a time
}

We verified it not by eye, but by codepoints:

Татарнетес → تاتارنېتېس
U+062A TEH · U+0627 ALEF · U+062A TEH · U+0627 ALEF · U+0631 REH · U+0646 NOON
· U+06D0 E · U+062A TEH · U+06D0 E · U+0633 SEEN   =  t-a-t-a-r-n-e-t-e-s
The Arabic-script version (yaña imlä, RTL)

7. The cluster goes for çäy (tea)

Several times a day, Tatarnetes stops for tea with milk and qıstıbıy. Knock during that window and you get a picture and a polite "Qıstırılma, tuğanqay — bezgä dä çäy eçärgä kiräk!" ("Don't butt in, dear friend — we need our tea too!").

A tea break in the console

The schedule is deterministic — derived from the calendar day, three windows of 7 minutes:

tea_windows() {
  today=$(date +%Y%m%d); seed=$(( 10#$today )); i=1
  while [ "$i" -le 3 ]; do
    m=$(( (seed * (i * 37 + 13) + i * 101) % 1440 ))   # minute of the day, 0..1439
    echo "$m 7"; i=$(( i + 1 ))
  done
}

The same formula is mirrored in ui/app.js (teaWindowsFor), so the console and the browser see the same windows down to the minute. The cluster drinks its tea everywhere, for real — in pipes and in CI too: çäy is çäy, exit code 42. Don't want to wait? There's an honest switch, AYDA_NO_TEA=1 (our tests use it too); to force a break for a demo, AYDA_FORCE_TEA=1. A robot, mind you, won't refuse tea either — it just knows the password.

8. Pipe-safety and error translation

All the "art" (the face, the praise, the garnish, the carpet) goes to stderr, while stdout stays clean — so ayda күрсәт кузаклар -o json | jq works like ordinary kubectl. To still intercept and translate an error, we juggle file descriptors: the child's stdout goes straight through (fd 3), and stderr is caught into a variable:

{ ERRTEXT="$("$KUBECTL_BIN" "$kverb" ${kargs[@]+"${kargs[@]}"} 2>&1 1>&3 3>&-)"; RC=$?; } 3>&1

Then, from the error text, we pick a Tatar hint without hiding the original: NotFound"Tabılmadı — isemen yäki mäydannı tikşer" ("Not found — check the name or the namespace"), Forbidden"Röxsät yuq — xoquqlarıňnı (RBAC) tikşer" ("No permission — check your rights (RBAC)"), timeouts, connection refused, and so on.

A real kubectl error with a Tatar hint An unknown command: a cross face and grumbling

9. Character as features

We tried to make "national character" not a stereotype, but a useful feature.

10. A cultural corpus and the fight against hallucinations

Poems and proverbs are kept in data/*.tt (9 poetic fragments, 14 proverbs), so a native speaker can extend them via PR. Here we have an iron rule: the classics must not be distorted. Every line of Tuqay, Cälil, Därdmänd, Sibğat Xäkim and the folk mäqallär is verified against sources, not against a neural network's memory (there is a table of sources in data/VERIFICATION.tt.md). Where a source could not be confirmed, the author did not make it into the corpus — accuracy over completeness.

A folk proverb as a garnish

11. The Tatarch 2.0 license

Legally, Tatarch 2.0 is equivalent to the Apache License 2.0 — the same rights and conditions (copyright grant, patent grant with retaliation, four redistribution conditions, disclaimer of warranty, limitation of liability). We guarded compatibility deliberately. What differs is the form and the spirit:

In short: take it as freely as under Apache 2.0 — only now that freedom wears a tübätäy.

12. TatarOS Linux and the Tatarnetes UI

Three projects add up to a single national stack:

The national stack: TatarOS → Tatarnetes → UI

TatarOS Linux is a wrapper over Talos (the immutable OS for Kubernetes). Its CLI is called tos and differs in an important, ideological way: it brings up a cluster not with Kubernetes, but with Tatarnetes. tos татарнетес-конфиг hands you access — and in the cluster, pure Tatarnetes awaits.

tos sälamätlek — health, live

The Tatarnetes UI is a fork of the official dashboard: a Tatar palette (green-white-red plus gold), a background of a real Tatar kiläm (a carpet, with a tamğa, lalälär tulips, and islimi ornament), and the node identifiers are tamğa, ancestral clan marks. During a break the panel also closes and shows a şamail banner.

The dashboard: pods against a carpet background Töennär: tamğa as node identifiers A tea break in the browser: a şamail banner

13. How we battle-tested it

Here is where it gets interesting — we did not take our own word for it and ran everything on a real CozyStack cluster: an isolated tenant tncf, a tenant Kubernetes on Talos v1.13.6 / k8s v1.35.6 (2 nodes), a separate kubeconfig, every command with an explicit --context.

ayda — the whole battle matrix passed on the live cluster:

CheckResult (live)exit
ayda күрсәт кузаклар/төеннәрreal pods/nodes + decoration in stderr0
ayda kürsät kuzaklar (Latin)identical to Cyrillic0
ayda get pods (passthrough)plain kubectl0
ayda сөйлә кузак yuk-podError … NotFound + "Tabılmadı…"1
RBAC: SA without rightsError … Forbidden … cannot list pods + "Röxsät yuq…"1
FinOps: --replicas=12really scales to 12 + "Aqçanı äräm itmä! 12 replica…"0
pipe-safety: … -o json | jq '.items|length'4 (no art leaked into stdout)0
AYDA_ALIF=latin/arabLatin / RTL Arabic0
ayda абырвалг (gibberish)64
AYDA_FORCE_TEA=1 ayda …the tea screen42

Latency (median of 5 runs): ayda 774 ms vs kubectl 812 ms. The wrapper's overhead is within the noise (≈0, actually −5%). In other words, the "national layer" does not cost you performance.

tos on real Talos. Against a managed cluster, the cluster operations hit the hosted control plane (worker nodes do not forward tenant requests — no request forwarding), so we brought up a separate standalone Talos node v1.11.2 and ran the day-2 verbs live:

And in battle we also caught — and immediately fixed — a bug. tos күрсәт төеннәр mapped to talosctl get nodes, but talosctl has no COSI resource nodes (resource "nodes" is not registered) — the command always failed. We fixed it: төен → members (cluster.Member), updated the dictionary, help, docs and tests, and re-verified the fix on that same live node: tos күрсәт төеннәрtalosctl get members returns a real cluster member. Result: 7/7 tos verbs live — OK, no bugs left.

tos kürsät töennär → get members on a live Talos node (fix confirmed)

This is exactly what battle testing is for — mocks never surface things like this.

14. Gotchas

The most honest section — all of it from the project's own code and from the live run:

15. Results and how to help

Readiness after the battle run: ayda and the UI are demo-ready right away; tos too: on a standalone Talos node it passed 7/7 day-2 verbs live (вакыт, дисклар, сәламәтлек, яңадан-кабыз with a real reboot, татарнетес-конфиг with a working kubeconfig, and күрсәт төеннәрget members after the fix). Against a managed cluster, some operations hit the hosted control plane — a property of the test rig, not a bug.

git clone https://github.com/tatar-ncf/tatarnetes.git
cd tatarnetes && ./install.sh
ayda ярдәм

Helping is easy and pleasant:

Project metrics

Lines of bash (ayda+tos+lib)1936
Localization keys (tt.po)39
Terms in the glossary36
Unit checks (tatarnetes / tataros)28 / 12
Corpus (poems / proverbs)9 / 14
Writing systems3 (Cyrillic / Yañalif / Yaña imlä)
Overhead ayda vs kubectl≈0 (−5%)
Repositories3

Gallery

The full command dictionary Help in Tatar Pod logs Xezmätlär (services) A Tuqay poem as a garnish Sälam! A greeting tos: cluster members (get members) tos help in English UI: xezmätlär (services) UI: urnaştırular (deployments) UI: mäydannar (namespaces)

Screencasts (GIF): a tour of the console (live run) · a tour of the UI.

Links

Organization: github.com/tatar-ncf — the Tatar-Native Computing Foundation

Repositories:

Useful:

Yomğaq (in closing)

We did this because the native tongue deserves to live in the terminal too. Technology need not be cold: let the cluster wear a tübätäy, offer you çäkçäk, and go for tea — while still honestly translating errors, not slowing down, and surviving a battle run. All of it under Tatarch 2.0, all of it for the great Tatar people and Tatars the world over.

Räxmät yauğırı! Tatarstan alğa! Yäşä, irekle kod! 🟢⚪🔴

A friendly project featured by the Sheet-Native Computing Foundation. Original by the Tatar-Native Computing Foundation, republished with permission. Join our community — Slack, Telegram, LinkedIn. ← Back to the blog