ToA Translation Pack Guide

v2022.03.14

Table of Contents

File structure

Translation packs are installed as folders inside the translations/ directory next to the game. For example, for a translation pack named es:

translations/ es/ manifest.json assets/ Tales of Androgyny.exe

Manifest

The manifest is a JSON file that can have the following data:

{ "languageName": "Español", "localeId": "es" }
Field Description
languageName Name that will appear in the language dropdown in the options menu
localeId ICU locale ID for the language (see list: https://www.localeplanet.com/icu/), used for plural categories and possibly formatting

Assets

Some assets (mostly images) in the game have text and will need to be replaced by a translation. To replace an asset, find it inside the game's jar in the assets/ folder, edit it as desired, then place the edited copy inside the language pack's assets/, with the same name and subfolders as the original. For example, if replacing assets/ui/option/Static_Elements.png, using the example language pack structure above, the edited copy should be placed in translations/es/assets/ui/option/Static_Elements.png.

To find the base assets from the game, open the Tales of Androgyny.jar file using any program that can open zip files - 7-Zip, WinRAR, or rename to Tales of Androgyny.zip and open with Windows Explorer.

Text

There are two main types of text in the game:

Note: all text files should be saved using the UTF-8 encoding. Please ensure your text editor is configured to save files using that encoding, or else characters outside the base latin alphabet will cause issues. Do not use ANSI, UTF-16, "UTF-8 with BOM", or other encodings.

Encounter script

The encounter script is a JSON file at assets/script/encounters.json. The file consists of a number of encounters defined by name (for example, GOBLIN-REUNION), and each encounter itself is a list of items.

Each item may have the following properties that should be translated:

Any other properties in the file are ignored and can be removed.

Each individual encounter in your translation pack will be merged with the base, english version of the encounter with the same ID when it is loaded. If your pack does not have the encounter, it will fall back to using the base version. Also, because changes to encounters may break the flow of the scenes, if your pack's version of the encounter does not have the same amount of items on the list (for example, because the encounter was changed to have more added in the base game), your version of the encounter will be ignored and will have to be updated.

The strings file

The strings file is a .properties format file (https://en.wikipedia.org/wiki/.properties) at assets/translation/strings.properties. The file consists of a list of strings used throughout the game in menus, notifications, etc, generally anything that is not the main text of an encounter.

The file uses a key=value format. When the game needs to display a string, it will look up the data from this file by the key, and display the value. For example, if the game needs to display the text for the Load button on the main menu, it will lookup the key mainmenu.load and find the definition mainmenu.load=Load and get the value Load.

If there is no strings file in the translation, or if a specific key is missing from the translation, the game will use the value from the default language included with the game.

Some strings are not just fixed text and need to have words or expressions inserted into them, which we call Fills. See below for how to use fills.

Fills

Some strings need to have something dynamically inserted in them. For example:

In these cases, the game looks up the string by key like normal, but also passes in either a number or a string respectively to fill in the string. In some cases, multiple fills might be necessary:

The game specifies the fills in a certain order (starting at 0, not 1!). The syntax {0} gets fill zero and inserts it in the string. Same for {1}, {2} and so on.

Some languages need to insert these values in different places, depending on the sentence. When you are translating a string, determine the proper place to insert a fill and use that syntax to insert it there.

In general, what the fills mean should be easy to understand from context. Many string keys have names such as shopscene.you_purchase_the_x that imply what the fill is. For others, the english sentence should contextualize what the fills are.

Fill complications

Depending on your language, some fills are not as simple as inserting the number or word in a sentence. Most languages have plurals, some have gender, and some have grammatical case.

Fill complications - Plurals

When something in the sentence needs to vary based on a plural, use the plural syntax. Here's an example:

mutation.cured_x_bleed_points=Cured {0} bleed {0, plural, one{point.} other{points.}}

The number of bleed points is fill 0. We fill the number in normally where {0} is, then we use a plural conditional on that fill to determine whether to use the singular or plural. To break it down (read from bottom left):

{0, plural, one{point.} other{points.}} ▲ ▲ ▲ ▲ ▲ ▲ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Use this other text. │ │ │ │ │ │ │ │ │ │ If it's any other number... │ │ │ │ │ │ │ │ Use this text. │ │ │ │ │ │ If the fill is 1... │ │ │ │ Use the plural syntax. │ │ The fill number.

The one and other parts are the plural rules. The plural rules you can use depend on your language. For English, for example, you can just use one for the singular, and other for every other case. For Russian, the rules are one for numbers ending in 1, few for certain numbers ending in 2-4, many for some others, and other for the rest, due to Russian have more complex ways of expressing plurals.

To use the proper rules for your language, make sure you specify the proper localeId in your manifest. Then, consult the ICU Language Plural Rules Table. Find your language, then look at the cardinal parts (not ordinal or range). The categories in that table are the rules you can use.

Fill complications - Gender and character pronouns

Some strings require knowning the gender of a character. Usually in these cases one of the fills is a special type called a pronoun set. When you have a pronoun set as one of the fills, you can use one of the following special tokens to insert pronouns:

The 0 above is just an example. If the pronoun set is at a different index, just use that index instead - {nom1}, {poss2}, etc.

If you need the words to be capitalized, there are alternate versions that make the first letter of the pronoun uppercase: {Nom0}, {Poss0}, {Obj0}.

If this is not sufficient to support pronouns and gender for your language, you can use the select syntax to create a conditional on a fill, similar to the plural conditional above. Here are two examples:

This works because a pronoun set fill is automatically turned into m or f.

See the diagram above explaining the plural syntax if this looks confusing. This works much the same, but the difference is it checks text instead of numbers.

Notes

The following sets of strings all have the enemy's pronoun set as fill 0:

Fill complications - (advanced!) Solving grammatical gender, case, and additional requirements with Extra Keys

For advanced requirements for certain languages, we have a system of extra keys. Extra keys allow you to define additional variants of strings, or additional attributes that you can then do conditional logic on.

If your language has grammatical case inflection, you might need different versions of the same word. For example, if you're using the Latin word for "cook":

Modern languages that have grammatical case inflection for nouns include the Slavic languages, German, and Hungarian.

Imagine that we try to turn the examples above into strings:

cook.name=coquus x_is_standing_there={0} ibī stat x_name_is_claudius=nōmen {0} Claudius est

In this case, we cannot just use {0} to fill in the word for cook, because it will incorrectly use the nominative case in both sentences! We can solve this problem using extra keys. In this case, what you can do is the following:

cook.name=coquus cook.name.genitive=coquī x_is_standing_there={0} ibī stat x_name_is_claudius=nōmen {0.genitive} Claudius est

We define an extra key cook.name.genitive. Then, in the sentence that needs the genitive, instead of just using {0} we use {0.genitive} to get the extra key we created. Thus, both sentences get the correct case filled in. Note that, naturally, if you ask for .genitive extra key in a string, you will need to define .genitive extra keys for all items that could end up in that string.

Extra keys can also be used in conditionals. For example, if you have a sentence that depends on gender for something that isn't already configured to support gender (characters), such as grammatical gender in Spanish:

library=biblioteca book=libro where_is_the_x=¿Donde está el {0}?

You would run into an issue as the sentence for where_is_the_x has to be different depending on whether it's about a book or a library. To solve this, you can add extra information in an extra key, and then use that extra key in a select conditional much like we did for character gender:

library=biblioteca library.gender=f book=libro book.gender=m where_is_the_x=¿Donde está {0.gender, select, m{el {0}} f{la {0}}}?

Note: the names of extra keys should start with a letter or underscore and should only contain letters from the base latin alphabet, numbers, and underscores.

Fonts

The fonts included with the game may not contain all the characters required for your language. If that is the case, you will need to find fonts that do and replace two types of font files (using asset replacement):