asideBlog
Dev diary

Notes from behind the scenes.

How Aside is built, and what every Ren'Py translator should know. Written by filiposakkq, the one person who makes it.

October 20265 min readUnder the hood

A new core in Rust, and nothing changed

Aside began as a Python program with a browser window. The first public build is one fast desktop app. Here is how the engine was replaced without changing what it does.

The rule: no new features during the port

A rewrite that also changes behaviour cannot be checked. So the port had one rule: the new core must give exactly the same answers as the old one. Bugs found on the way were fixed in the old code first, and only then ported.

Recorded answers instead of reading code

Before a line of Rust was written, the old core recorded its answers: fixed inputs (parser cases, archives, checks, whole synthetic projects) and the exact output for each. Every module of the new core had to reproduce them byte for byte, including the files it writes.

749recorded cases, all matching
80operations ported one by one
11milestones, each a reviewed step

Then the real games

Synthetic data only goes so far. The last check opened copies of the translations of eight real games with both cores and asked both the same questions: overview, files, every filter, checks, glossary, the scene and the menu behind sampled lines.

8 968 questions on 8 games. Zero differences.

The same interface, without a server

The interface did not change at all. In the old app it talked to a small local web server; in the new one the same requests go to the core inside the window, so no network port is opened. The interface's own self-test, 139 checks, passes inside the installed app.

The Pro features live in a separate program next to the app. That keeps the free app free of Pro code, and lets Pro update on its own.

↑ All posts

October 20264 min readFor translators

Why one missing {/i} crashes a game

A Ren'Py line is more than text. Some of what you see in a translation file is instructions for the engine, and a mistake there is a mistake in code.

Text tags

Tags in curly braces style the text or control it: {i} for italics, {b} for bold, {w} to wait for a click. A tag that opens must close, in the right order. When a closing tag goes missing in the translation, the game can stop on that line with an exception, in front of the player.

# original
mira "Did you bring the {b}letter{/b}?"
# translation
mira "Přinesl jsi ten {b}dopis?"      ← {/b} is missing
mira "Přinesl jsi ten {b}dopis{/b}?"  ← fine

Variables

Square brackets insert a value: [player_name] becomes the name the player typed. Translate the word inside and the game looks for a variable that does not exist. A typo has the same effect.

mira "Welcome back, [player_name]."
mira "Vítej zpátky, [jméno_hráče]."   ← NameError: not defined

The quieter mistakes

  • A \n line break lost or added changes the layout.
  • A number that differs from the original ("3 days" became "4 dny").
  • A line that is much longer than the original and no longer fits the dialogue box.
  • English words left behind in a translated line.

How Aside handles it

Every translation is compared with its original as you type and on every refresh: tags, variables, format marks, line breaks, numbers, length and the fit in the box, measured with the game's own font. Errors that would crash the game are marked apart from warnings that may be intended. You can try it on the home page.

↑ All posts

October 20264 min readFor translators

The texts the generator never sees

You translated every line in every file, released, and a player sends a screenshot with English in it. It was never in your files.

What the generator writes

Ren'Py's translate command writes blocks for dialogue, menu choices, screen texts and strings marked with _(). Text shown any other way has no block, so it never reaches the translator:

  • show text "Three days later..."
  • prompts of renpy.input("What is your name?")
  • notifications with renpy.notify(...)
  • a chapter title kept in a variable and shown later with [title]
  • the game's own functions that show text

How Aside finds them

Aside reads the game's scripts, compares them with your translation and lists what is missing, split into likely and maybe. You tick what the player sees, and it lands in extra_strings.rpy as lines to translate, in a strings block Ren'Py reads like any other.

24 of 27texts a translator had added by hand were found
185likely texts listed for the same game
0texts written twice

Measured on Being a DIK. The other 3 of those 27 were not really missed: the generator had extracted two of them, and one is assembled from pieces in the code. It is a heuristic, so the list is always yours to review.

↑ All posts