Writing · Build Notes
I wrote a transliterator for Afrikaans in Japanese hiragana — then made it a Linux locale
Afrikaans has a shallow, phonetic spelling system.
Afrikaans has a shallow, phonetic spelling system. Hiragana is a syllabary built for a completely different language. I wondered what would happen if I forced one through the other — so I built Afurigana: a rule-based transliterator that writes Afrikaans phonetically in hiragana.
$ python3 afurigana.py "Goeie more, hoe gaan dit?"
ふいー もれ, ふ はーん でぃと?
The name is a pun — Afrikaans + furigana (振り仮名), the little hiragana reading-aids Japanese prints above kanji. Then I took it too far and installed it as a system locale, so my whole Linux desktop renders its Afrikaans UI in kana. More on that at the end.
The core problem: a syllabary can't spell an alphabet
Hiragana's units are vowels (あいうえお) and consonant+vowel pairs (か き く…). Afrikaans is alphabetic, with heavy consonant clusters and more vowel qualities than hiragana has sounds. You can't map letter-to-letter.
So Afurigana borrows the exact strategy Japanese already uses for foreign loanwords:
- Break each word into consonant–vowel syllables.
- Insert a default vowel to split consonant clusters (linguists call this epenthesis — it's why "strike" becomes ストライク, su-to-ra-i-ku).
- Approximate sounds hiragana lacks with the nearest row.
It maps spelling, not perfect pronunciation. It's a "sample system," not IPA — and being honest about that shaped every rule.
The fun part: the sounds hiragana doesn't have
This is where a phonology becomes a lookup table of small, opinionated decisions:
| Afrikaans | sound | hiragana | why |
|---|---|---|---|
| g, ch | raspy [x] | は-row(は ひ ふ へ ほ) | no velar fricative — the same trick that makes Bach → バッハ |
| v, f | [f] | ふ + small vowel(ふぁ ふぃ ふぇ ふぉ) | there's no f-row, so build one from ふ |
| w | [v]/[w] | わ-row(わ うぃ う うぇ うぉ) | |
| l and r | [l], [r] | both → ら-row | hiragana has no L at all; L and R must share |
| sj | [ʃ] | し / しゃ しゅ… | |
| tj | [tʃ] | ち / ちゃ ちゅ… | |
| ng | [ŋ] | ん |
That l and r collapsing into the same row is the kind of lossy, one-way decision that makes transliteration fascinating — かける could map back to several Afrikaans spellings, so the transform is deliberately not reversible.
The syllable rules that make it read right
A handful of rules do most of the work:
- A final consonant gets a default vowel —
kat→ かと — exceptn→ ん andm→ む, which hiragana can end a syllable on. - A doubled consonant becomes a small tsu っ (the gemination mark):
lekker→ れっける. ti/dibecome てぃ / でぃ, so they don't silently collide with ち (=chi) and ぢ.- Long vowels use the chōonpu ー:
more→ もれ, butAfrika→ あふりか keeps its short a's.
A few from the phrasebook, to show the feel of it:
| Afrikaans | Afurigana | English |
|---|---|---|
| Baie dankie | ばいー だんきー | Thank you very much |
| Ek het jou lief | えく へと やう りーふ | I love you |
| Lekker | れっける | Nice / great |
| Vis en tjips | ふぃす えん ちぷす | Fish and chips |
| Suid-Afrika | さいど-あふりか | South Africa |
Then I made it a locale
A transliterator is a script you run. I wanted the operating system to speak it. So I built af_JP — "Afrikaans (Japan)" — a real installed locale whose message catalogue is the Afrikaans desktop UI run through Afurigana.
The install has one non-obvious gotcha worth sharing. You can drop a compiled locale into /usr/share/locale/, but the next time locale-gen runs (an apt upgrade, say), it rebuilds the archive from source definitions and silently drops your locale because there's no source for it. The fix is to give it one:
# give locale-gen a source file named af_JP (a copy of af_ZA) so the
# locale survives future locale-gen runs (apt upgrades, etc.)
sudo cp -n /usr/share/i18n/locales/af_ZA /usr/share/i18n/locales/af_JP
sudo localedef -i af_JP -f UTF-8 af_JP.UTF-8
echo "af_JP.UTF-8 UTF-8" | sudo tee -a /etc/locale.gen
sudo locale-genSet it as your language and menus, buttons, and dialogs come back in Afrikaans-in-hiragana. It is gloriously impractical and I love it.
What I learned
- Transliteration is a series of honest lossy choices. Every "there's no exact hiragana for this" (the front-rounded
u/ui/euespecially) is a design decision, not a bug — you pick the nearest neighbour and document it. - Loanword phonology is a real algorithm. The epenthesis + nearest-row approach isn't a hack; it's exactly how Japanese systematically absorbs foreign words. Implementing it made me see the system behind katakana loanwords.
- The locale layer is more fragile and more fun than expected. That
locale-gendrops unsourced locales is the kind of thing you only learn by having it vanish on you.
A build-story · see the live project → · more writing →