v2022.03.14
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
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 |
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.
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.
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:
speaker
- Indicates who is speaking, appears on the bubble on the top left on the message box. NOTE: this should not be changed if the value is Hiro
. This special value is replace by the player character's name. Do not change it.text
- The text that shows up on the main message box.chatter
- The text that shows up on the side message box sometimes used by e.g. party members.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 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.
Some strings need to have something dynamically inserted in them. For example:
item.potion.description.mana=Restores {0} mana.
- the amount of mana needs to be inserted.skillselectionscene.you_have_removed_someperk=You have removed {0}.
- the name of the perk needs to be inserted.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:
skillselectionscene.you_have_reduce_someperk_to_rank_x=You have reduced {0} to Rank {1}.
- gets a perk name at 0, and a number at 1.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.
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.
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.
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:
{nom0}
- replaced with pronouns.<gender>.nominative
, for example: he, she{poss0}
- replaced with pronouns.<gender>.possessive
, for example: his, her{obj0}
- replaced with pronouns.<gender>.objective
, for example: him, herThe 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:
{0, select, m{He leaves the room.} other{She leaves the room.}}
- picks "He leaves the room." if 0 is "m", otherwise picks "She leaves the room."{1, select, f{She walks up to the counter.} m{She walks up to the counter.}}
- picks "She walks up to the counter." if 1 is "f", and picks "She walks up to the counter." if 1 is "m"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.
The following sets of strings all have the enemy's pronoun set as fill 0:
climax.*
battle.anal_message.*
battle.oral_message.*
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.
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):
.ttf
- A TrueType font, similar to the ones you would normally download to install on your computer..fnt
+ .png
- A generated bitmap font. You will need to use the Hiero tool to create these.