Skip to content

L10n Translating Content

Kit Oliynyk edited this page Nov 2, 2026 · 2 revisions

TL;DR: Content translation fills empty English and Ukrainian fields from the Russian source, after a light Russian cleanup. It translates faithfully, never overwrites a non-empty field, keeps official names, and follows a short list of hard rules for punctuation, alphabets, keywords, declension pads and gender markers. Use when: you translate or proofread the text of an area (rooms, mobs, objects, extra descriptions, zone helps) or a help article, or you review such a change. Source of truth: src/xml/xmlmultistring.cpp, src/core/extradescription.h, src/l10n/flexer.cpp, plug-ins/output/mudtags.cpp, the XML of areas and dreamland_world. Verified: 2026-11-02 against dreamland_code 88da8302, dreamland_world 43880467

Area files live in a private repository. Everything here describes the shape of the data and the rules, not file locations. The rules for help articles that this page does not repeat are in Help-Articles. General rules for any player text are in Writing-Player-Text.

Order of work

  1. Russian cleanup, light. The author's voice stays.
  2. Missing important Russian fields, written first so that English and Ukrainian are translated from text, not from a gap.
  3. English, then Ukrainian, only into empty fields.

Rules for every pass:

  • Translate, do not reimagine. Every English and Ukrainian value is a faithful rendering of the cleaned Russian next to it: the same number of sentences, the same concrete nouns, the same navigation hints, the same closing line. Do not condense, reframe or upgrade. If the Russian names a unicorn, a coffin or a (landscape) hook, so does the translation. A translation that is more than 40% shorter than its source has dropped something.
  • Never overwrite a non-empty l="en" or l="ua". If an existing one is bad, report it and leave it.
  • Never move a node between sections. Objects stay in <objects>, rooms in <rooms>, mobiles in <mobiles>, helps in <helps>. A node that looks misplaced is reported, not moved.
  • A node that is plainly a deleted placeholder (Deleted item is here., [deleted]) is removed whole. Anything that only looks wrong (an object with no short description, a mob with no long one, a room with exits and no description, a duplicated vnum) is reported, not deleted.
  • Check the XML parses after each batch, and that section node counts did not change.

Russian cleanup

In scope:

  • Typos, double spaces, missing commas, subject-verb agreement.
  • Punctuation to ASCII: — and spaced - become --, … becomes ..., «» becomes ". A hyphen in a compound without spaces (a-b) is left alone.
  • Hard line breaks: join wrapped prose lines, keep blank-line paragraph breaks, indented lines and structure blocks. Clients wrap, the server must not. The same join rules as for helps apply, see Help-Articles.
  • Old keyboard glitches: a Latin-looking capital Я glued onto a word (котораЯ, тебЯ).
  • Вы/вы to ты in direct address, and the matching forms (Перед вами becomes Перед тобой, Вас атакует becomes Тебя атакует). In Ukrainian the same with ти. Leave вы that an NPC addresses to a group inside a dialogue. English is you.
  • Past-tense verbs that show to both sexes get gender markers, see below.
  • Stub descriptions (Яблочко тут., Здесь стоит стол., a description that only repeats the short description) count as missing. Rewrite them in the zone's voice with one or two sentences and a concrete detail. Short optional smell and sound texts are translated, not rewritten.
  • Questionable author text (???, shouting capitals, broken constructions, ?? тут лежит ??) is not rewritten and not kept silently. Collect the list and ask the maintainers, item by item.

Out of scope: the author's style, jokes, archaisms and good prose that you would phrase differently.

Hard rules in every language

  • One alphabet per word. Pyatая is a defect. Write a gloss as two full words: Fifth Epoch (П'ята Епоха). dreamland_world/scripts/find-foreign-fragments.py finds Latin words and homoglyph letters inside Cyrillic text. Gender markers produce harmless pseudo-words after colour stripping.
  • ASCII punctuation only. No «», curly quotes, …, —. The engine converts text to KOI8-U at load, and a character outside that table breaks loading or disappears.
  • No &quot;, &apos;, &gt; in element text. Write ", ', >. Keep &lt; and &amp; escaped, the XML parser needs them. The <props> blocks hold engine-written JSON with escapes that must stay.
  • No ё unless the surrounding Russian text uses it consistently. No э and no ё in Ukrainian, Russian Тэра is Ukrainian Тера.
  • Ukrainian apostrophe is ʼ (U+02BC) inside words: мʼязами, обʼєкт. ASCII ' is for quote framing like 'новий талос'. The engine turns ʼ into ' before the text reaches KOI8-U, so players see an ordinary apostrophe. Saving a zone through the in-game editor also writes plain ' (measured 2026-08 on one saved zone). That drift is cosmetic and need not be cleaned up. The convention exists to tell an apostrophe from a quote.
  • Address the player as ты or ти or you.
  • Dreamland is an 18+ game. Sexual content is rendered plainly, not through euphemism. Swearing up to the medium level (сука, говно, курва, shit, asshole, bitch) is fine. The hardest words are out (хуй, пизда, єбати and their derivatives, English cunt, fuck), and so are real-world ethnic or identity slurs. Match the author's intensity, do not raise it.

Keywords per language

Object, mob and exit keywords are multi-language fields. Write one element per language, each with its own aliases:

<keyword l="en">palace key</keyword>
<keyword l="ru">дворцовый ключ</keyword>
<keyword l="ua">ключ палацовий палац</keyword>

An extra description (ExtraDescription) keyword is the opposite. It is one string for all languages, not a multi-language field. Write a single element with the words of every language separated by spaces:

<keyword>tablet табличка диба</keyword>

An extra description keeps a single keyword string, so several <keyword l="..."> siblings on one extra description are not merged. The engine keeps only the last non-empty sibling when the zone loads, the other languages are gone at once, and the next save writes that one word back. look <noun> then silently stops working in the other languages. Fill the blob in all three languages: when you translate the extra description, make sure it ends with a typable noun in English, Russian and Ukrainian. Russian and Ukrainian often differ (шар, куля, стена, стіна, зеркало, дзеркало), and a word counts for a language only if it is correctly spelled there. Append, never remove. Book pages use numbered forms so each page is reachable (page1 страница1 сторінка1).

The (keyword) markers in descriptions are how players find extra descriptions. Keep every one when you translate, in the translation's own language, and keep the exact hook set. See Text-Markup.

Declension pads in translations

Names that decline are stored as Flexer pads, see L10n. When you fill a Ukrainian pad:

  • Mirror the cell count of the Russian pad. Six pipes, seven fields, empty cell ||. A compound repeats the pattern for every noun: дракон||а|у|а|ом|і-лейтенант||а|у|а|ом|і.
  • Do not write full words in the cells. The engine joins root and ending, so full words produce городянингородянина.
  • Locative: animate masculine -ові or -еві, inanimate -у or -і, feminine in -а takes -і.
  • Velar alternation (к/г/х to ц/з/с) in the dative and prepositional means the root must stop before that letter: собака is соба|ка|ки|ці|ку|кою|ці, нога is но|га|ги|зі|гу|гою|зі. The root is the longest prefix shared by all six forms.
  • Stems in ж/ч/ш/щ form the mixed group: masculine instrumental -ем, feminine -ею. Copying the Russian endings gives non-words.
  • Render the pad and read the six forms before you ship it. The genitive plural is the one that goes wrong most often.
  • English short descriptions never get a pad. No | in an English name. A padded English word renders wrongly (cup||||||board shows cup). Remove the pads: nominative is the root plus the first cell for every word, and a plain removal of | is safe only when all cells are empty.
  • English short descriptions have no leading article: iron sword.

Natural Ukrainian over matched gender: when the natural Ukrainian noun differs in grammatical gender from the Russian (скриня against сундук), write the natural noun. Gender is stored once per object for all three languages. The pronoun table is Russian anyway, so matching gender buys nothing today. Where a %G or a pad in the same string must agree, make it agree inside the string.

Gender markers

A past-tense verb that shows to any player needs a marker, or a rewrite into the present tense. See Text-Markup for the syntax. Two points specific to translation:

  • Say the female form before choosing the marker. попал and попала share a stem, so попал{Sfа{Sx works. вошел and вошла do not, so write вош{Sfла{Smел{Sx.
  • A present-tense rewrite works in Ukrainian only with an imperfective verb. A perfective verb has no present tense (шубовснути has no я шубовсаю). Use its imperfective partner (стрибнути to стрибати) or keep the marker.
  • Mobs that are clearly female keep their feminine forms. Do not wrap those in markers.

{Tn{Td{Tx day and night text is not a defect. Keep both branches and the shared tail. A checker that strips {T and then sees a stray d or n in a Cyrillic word is giving a false alarm.

Link anchors

{hh<id>text{x: the id never changes between languages, only the anchor text does. Before changing an anchor, open the target article, read its keyword in your language and the way its body names the thing, and match that. If the id does not exist, report it, do not invent an article. See Help-Articles.

Help bodies are single-language blocks all the way down: zone names, anchors, race, clan and class names, quoted phrases, all in that language. After translating, scan for stray Cyrillic in the English block and stray Latin in the Russian and Ukrainian blocks (not counting commands, codes and ids).

Official names

Look up every name before you write it, in this order, and stop at the first hit:

  1. The XML the engine reads for that kind of thing: the skill, command, race, religion, profession, clan files in dreamland_world, and the area's own <name l> fields.
  2. World-Canon.
  3. Names already used in other area files. Search before you invent. If the zone is internally consistent, keep it.
  4. A genuinely new local name: coin it once, spell it the same way everywhere in the zone.

Two kinds of name are never coined silently: places (cities, regions, rivers, mountains, kingdoms, named geography) and gods or religious institutions. If a place or a god in the Russian text is not in the canon, ask the maintainers whether it is local colour, a known zone under another name, or a typo. A wrong canonical name spreads, because the next translator reuses it.

Rules of thumb that come up all the time:

  • Greek th becomes т in invented place names (Thera is Тера in Ukrainian, Тэра in Russian). Classical Greek personal names follow the classical Ukrainian literary forms instead: keep the consonant of the Russian spelling and convert и to і, э to е.
  • Gods whose names look like real-world deities are one word: Ahuramazda, not Ahura Mazda. Check the religion file.
  • Crossover zones (names from other fiction) keep the canonical spelling of the source in English and a transliteration in Ukrainian that follows the Russian one.
  • A pseudo-foreign name is transliterated consistently within the zone and not translated, even when it looks like a Russian word.
  • Where the original English text exists, use it. DreamLand descends from Anatolia MUD (github.com/jaromil/anatoliamud). Class titles, help articles, skill and spell messages and old area strings have English originals there. Titles are in title_table[class][level][sex] of src/const.c. DreamLand has reworked a lot since, so align by level and meaning and do not assume a 1:1 match. Classes with a direct counterpart: cleric, thief, warrior, paladin, anti-paladin, ninja, ranger, samurai, vampire, necromancer. Witch, druid and warlock have none.
  • Ukrainian flavour overrides exist on purpose. The old market square of New Thalos is Привоз, the common square is Майдан, and references to the same square in neighbouring zones follow it. The Bazaar's own central square stays Базарна Площа.

Things that look like errors and are not:

  • githyanki is one race in DreamLand. Any Russian variant (гизерай, гитзерай, гитианки) becomes English githyanki and Ukrainian гітіанки. Never githzerai.
  • Швайнокарась is a Ukrainian war meme (a pig-fish hybrid). Ukrainian keeps it, English says pigfish, Russian is left alone. Keep the toothy, reclaimed reading.
  • The shouted ЪУЪ emote is an intended meme and renders correctly. It stays in Russian and Ukrainian.
  • ASCII-art maps and tablets are copied byte for byte into every language, typos included.
  • Author-chosen English keywords are locked. Match the Ukrainian to them, do not overwrite them.
  • A clan nickname that is a pun (БТР) stays as written in Russian and Ukrainian, with the bare acronym in English.

Races and shapeshifters in descriptions

The engine has no shape changing. A mob has one race. Before translating, read each mob's <race> against its description. A true dragon (dragon) is winged, large and does not mingle. A humanoid with draconic features that walks the city, holds a guild post or carries a townsman, soldier or priest role is draconian. For a half-X hybrid, use the visible race that dominates. Lore that says a creature transforms ("suddenly assumes humanoid form") is rewritten as description of one form, with the other form kept as backstory.

Ukrainian

  • Voice: deadpan narrator over absurd content, a little funnier than the Russian where the source has colour. One homely concrete noun in every elevated description. Surzhyk is seasoning, not substrate. Discourse markers (отож, либонь, мовляв, трясця, хай йому грець) or a mild swear carry the register. One diminutive per paragraph at most. Two short sentences with one odd detail beat one long elegant one. Do not carry Soviet-only vocabulary into fantasy settings. Draft a room, a mob and an object description and check them before you do a whole zone.
  • Government is not Russian: позбавити takes a bare genitive, not від. Use звільнити від, очистити від, зняти. Check the verb in a dictionary of the period before copying the Russian pattern. The corpus is not a reference, it has both forms.
  • Check declension group (see pads), not only the stem shape.
  • A word the game prints must be accepted by the parser. A Ukrainian argument word in a hint has to exist in the synonyms table.
  • Keep to ти. Exceptions need a reason (a ceremonial text can keep Ви).

English

  • Keep the funny, dark, postmodern voice. Idiomatic where there is a punchline, literal elsewhere.
  • Proper nouns: canon first, then Anatolia, then reasonable English.
  • Fenia and catalog strings follow L10n-Fenia.

Damage and flag words in help bodies

Use the prose forms, not the engine strings from plug-ins/fight_core/damageflags.conf, which are case templates for combat messages.

Code Prose (Russian)
bash дробящий удар
pierce колющий удар, колющее оружие
slash рубящий удар
fire огонь
cold холод
lightning молния
acid кислота
poison яд
negative тёмная или некромантическая энергия
holy святая сила
energy силовой удар
mental ментальная атака
disease болезнь
drowning вода, утопление
light лучистая энергия, свет
harm боль
charm чары очарования
sound звук

Broad flags: weapon is обычное оружие, spell is заклинания, magic is магия, prayer is молитвы, other is иной урон, water is вода. There is no canonical Ukrainian table yet, translate from the Russian column. Take race immunities and resistances from the race file, never from memory.

Editing safely

  • A vnum is not unique across sections. The same number can be a room, a mob and an object in one file. Always scope a search or edit to one section and say which kind you mean.
  • Do not patch area XML with a regex over the whole file. Object and mob nodes contain nested <node> elements (affects, resets, exits, extra descriptions), so a non-greedy match stops at the first inner </node>. Use an XML parser and walk the section.
  • A tool that reports "nothing left to translate" has not necessarily covered optional smell and sound texts or extra-description keywords. Cross-check with a parser pass over every en and ua leaf whose ru sibling is not empty.
  • Commit messages say what you did. "Translate X from Russian to English and Ukrainian" is right. A message that describes the work as retelling, reframing or novelising means the translation is not faithful.

See also

Clone this wiki locally