Translate episodes - TheXTech/TheXTech GitHub Wiki

[This article is WIP]

Content

Introduction

Since the TheXTech version 1.3.6.1 it's possible to translate episodes and even single levels into other languages! As well, as game itself can be translated into multiple languages!

While the engine itself can be translated via WebLate platform, to make translation for levels and episodes, you are required to do all the work locally: the (currently in-development) program called Moondust Translator should help you to do the work easier.

Important Note: As the Moondust Translator component is under active development, there are only experimental builds available that you can use for the work:

Please report any bugs in the official Discord chat or make an issue on the Moondust Project repository.

If something doesn't work properly on your platform, you may want to build the Moondust Devkit from the source code after clonning of the Moondust Project repository on the wip-translator branch.

Note 2: For asset-customized strings, in both Asset-specific and Engine General, you have to edit them by using 3rd party text editors.

Translation of texts

The translation system is non-destructive: you don't even required to edit level, world and LunaScript files (such as lunadll.txt and lunaworld.txt) in order to translate them: you need to create the i18n sub-directory at your episode (or at your level's data directory), and create new JSON file named translation_XXXX.json (where XXXX - should be a language code, for example: fr or with a country code zh-cn) that will contain the list of translation strings. Once the Moondust Translator program will be released, the whole work will go much easier.

There are two types of translation formats supported:

By item array index (Suggested for translating Old Episodes)

Every string will be mapped to the array index of the object in the level or world files that needs to be translated. This way allows you to make the fully non-destructive translations for existing episodes: you totally aren't required to edit source files at all to translate an episode.

Procs

  • The simple way to translate old episodes without of necessary to edit level/world/script files.
  • You aren't required to provide a separated English translation file until the episode's original language is not English (for example, Spanish, French, German, etc.).

Cons

  • Very bad method for actively developing levels or episodes as it's easy to break the mapping of translations by adding/removing items from level's/world's arrays.
  • You can't share the same string between multiple objects: you will need to copy the same string to multiple objects.

How to use it

This method works automatically if translation entries do contain the "i" integer key and don't contain any "tr-id" keys. If you use the Moondust Translator program, it will automatically build all necessary templates and helper meta-data once you open the project for scanning.

The Tr-ID (Suggested for creating new works)

That means, levels and worlds by theme selves will don't contain actual strings, instead, they will contain a special key strings that will be used to dynamically map the translation string. This method allows you to share the same string between multiple objects in the level or world, and guarantees that you will never break or confuse mappings of translated strings by editing of levels/worlds/scripts. To use this mode, you should use the "tr-id" string key and don't define any "i" keys. You also required to provide the translation_en.json file that will contain English strings as a fallback for missing languages.

Procs

  • Allows sharing the same string between multiple different objects.
  • The best format for actively developing projects: there is full guarantee that mapping of source and translation will never been broken because of in-array reordering.

Cons

  • Until the Moondust Editor will get the necessary support, you can't edit original strings from the Editor itself. You will need to use 3rd party text editors to edit these strings at the translation file manually.
  • You are required to provide the English translation file explicitly as episode files won't contain source strings: only translation-id keys.
  • If you going to translate an existing episode by Tr-Id method, you should convert all strings into translation_en.json and replace strings at level/world/script files with Tr-Id keys.

Extra: Macros

In addition to the translation system, TheXTech also introduces the preprocessor of message boxes: this is a special feature that allows to show different content of the same message box according to various factors. Since the TheXTech version 1.3.6.1, there is only one player() condition supported yet. It's possible to use macros in the original messages inside of level files as well as in translations. The main purpose of the preprocessor feature is allowing to use the correct phrasing depending on different playable characters as that language requires (for example, to respect proper grammar gender or use proper prefixes/suffixes according to the status/profession of a character, or something also).

How it works: If anything triggers a message box (for example, player talks to an NPC, or causes a chain of events that triggers message boxes, etc.), it gets alternated accodring to various factors. For example of this message:

#if player(1)
Hello, Demo, how are you?
#elif player(2)
Hi, Iris, are you okay?
#elif player(3)
Kood? You? Where are you from?
#elif player(4)
Oh, Raocow, I'm gald you came back!
#elif player(5)
Huh? I didn't saw you before... Are you Sheath?
#else
Welcome, stranger, I don't know, who are you?
#endif

If current playable character is Demo (her number is 1), so, the talked NPC will say Hello, Demo, how are you?, or when playing as Raocow (his number is 4), the shown message box will contain Oh, Raocow, I'm gald you came back!.

The syntax is pretty simple:

Something before condition
#if <condition>
Message one
#elif <another condition>
Message two
#else
Message three
#endif
Something after condition

Where <condition> it's possible to use:

  • player(x) or player(x,y,z,...,n) - When current playable character's number is equal to x, or one of multiple. For example, player(3) will match when current playable character is 3, or player(1,2,5) will match when playable character is 1, 2, or 5.

Remember, you always can verify the work of the message box preprocessor through the preview widget of the Moondust Translator program.

Translation of pictures

You also can provide alternative resources for your language, such as pictures, for example, if they contains a text that you also want to translate. You can make the i18n/XXXX/ (where XXXX - should be a language code, for example: fr or with a country code zh-cn) at your episode (or at your level's data directory) where you can put alternative variants of custom graphics where you can make a translated text or something also you want to change in order to localise the thing.

Specification of translation JSON files

[THIS SECTION IS INCOMPLETE]

This is the specification of translaion JSON files that you can use for various purposes like implementing helper tools or just compose them manually. There are two types of translation files: the meta-file (usually ends with _metadata.json) that contains a lot of extra meta-data such as dialogue chains, translation project settings, various commentaries, preserved stuff, etc.; and the translation file that only contains translated strings and assignment key.

Root

  • "(space)episode_title" - The translated title of the episode.
    • Why it must begin with a space? It's a workaround to force it being always first in the file to simplify it's retrieving during the episodes list menu building in the game itself.
  • "(space)episode_world" - The filename of the world file - it's used to figure for which of many world map files this translation is related as there is possible to have multiple world maps at the same episode directory.
  • use-tr-id - Meta-file only. Boolean. Indicate that the whole translation project uses Tr-Id method (i.e. using string keys instead of array indexes).
  • <filename> - The sub-section of data related to the same filename as the title of this sub-section.

Note: Replace (space) with .

<filename>

One of sub-sections that stores translations and setup for one file. For different type of file (level, world, script) are different structures used:

Levels

  • "dialogues" - Meta-file only. A list of dialogue chains (events with a message box that calls each other) - There are helper data built by Moondust Translator to help translators to figure the order of messages shown in the game during various dialogues. Every entry contains the next fields:
    • "note" - The note that describes the dialogue, usually added by author of translation project as a hint for self and others to understand for what this dialogue is used, or anything also.
    • "messages" - The list of specially-formatted strings that lists objects and their string type in order of appearence in the game. The next template is being used: npc-XXX-talk and event-XXXX-msg. Where is npc or event - the type of object referred by this message entry; Where is XXXX - is an array index in the entry; Where is talk or msg - the type of the referred string at the referred object.
  • "events" - List of events and translations for messages shown by events. Every entry contains the next fields:
    • "i" or "tr-id" - Array index of event entry, or the Translation ID key^1. Both can be contained in the meta-file.
    • "msg" - Message box text of the event.
    • "msg-n" - The note of translator (or the note of developer to translators of it's a meta-file).
    • "orig" - Translation file only (not meta-data file). The copy of original message box text. Never used actually, just a hint for translators who edit the JSON file manually.
    • "u" - Boolean. If presented and true, this is an unfinished translation.
    • "v" - Boolean. If presented and true, this is a vanished translation (an old translation for the removed entry).
    • "trig" - Meta-file only. The name of the next event that gets being triggered by this event. Used during dialogues building.
    • "by-npc" - Meta-file only. An array of integers: the list of NPC array indexes for NPCs who triggers this event by any way.
    • "by-event" - Meta-file only. An array of integers: the list of event array indexes for events that triggers this event.
  • "npc" - List of NPCs and translations for talk messages shown by NPC when player talks to them. Every entry contains the next fields:
    • "i" or "tr-id" - Array index of NPC entry, or the Translation ID key^1.
    • "talk" - The text of the message shown on attempt to talk to this NPC.
    • "talk-n" - The note of translator (or the note of developer to translators of it's a meta-file).
    • "orig" - Translation file only (not meta-data file). The copy of original message box text. Never used actually, just a hint for translators who edit the JSON file manually.
    • "u" - Boolean. If presented and true, this is an unfinished translation.
    • "v" - Boolean. If presented and true, this is a vanished translation (an old translation for the removed entry).
    • "trig" - Meta-file only. The name of the event that gets being triggered by this NPC. Used during dialogues building.

Worlds

  • "credits-n" - Translation of credits section.
  • "credits-orig" - The copy of original credits section. Never used in the game, just a hint for translators who prefer edit JSON files directly.
  • "levels" - List of level start points and translations for their titles. Every entry contains the next fields:
    • "i" or "tr-id" - Array index of Level Point entry, or the Translation ID key^1.
    • "f" - Level Filename.
    • "tit" - The translation of level title.
    • "tit-n" - The note of translator for the level title translation.
    • "orig" - Translation file only (not meta-data file). The copy of original level title text. Never used actually, just a hint for translators who edit the JSON file manually.
  • "title" - This field must be same as "(space)episode_title" in the Root section.
  • "title-orig" - The original title of episode. Never used in the game, just a hint for translators who prefer edit JSON files directly.

LunaScript files

  • "lines" - List of lines with ShowText or ShowVar commands in the script file and their translations. Every entry contains the next fields:
    • "i" or "tr-id" - The line number in the script file, or the Translation ID key^1.
    • "src" - The original text at the script file.
    • "src-n" - The note of developer to translators.
    • "tr" - The translation of the text line at the script.
    • "tr-n" - The note of translator.

Note

^1: Depending on desired translation mode, each translatable field will use the tag i for disabling Tr-Id mode, or tr-id for enabling Tr-Id mode.

Examples

Example levels

Download example TrId and picture translation level

This is a simple example of single level translation that uses Tr-Id method and showing how translation of pictures works.

Example JSON files

Meta-file for single level example (with "by array Id")

TrId Demo/i18n/translation_metadata.json

{
    "TrId Demo.lvlx": {
        "dialogues": [
            {
                "messages": [
                    "npc-0-talk"
                ],
                "note": ""
            }
        ],
        "npc": [
            {
                "i": -1,
                "t": -1,
                "talk": ""
            },
            {
                "i": 0,
                "t": 151,
                "talk": "This is a message text at the test sign."
            }
        ],
        "title": "TrId Demo"
    },
    "TrId Demo/lunadll.txt": {
        "lines": [
            {
                "i": 2,
                "src": "Here is nothing!"
            }
        ],
        "title": "TrId Demo/lunadll.txt"
    }
}

Translation file for single level example (with "by array Id")

TrId Demo/i18n/translation_ru.json

{
    "TrId Demo.lvlx": {
        "npc": [
            {
                "i": 0,
                "orig": "This is a message text at the test sign.",
                "talk": "Это текст сообщения на тестовом знаке."
            }
        ],
        "title": "Дэмо TrId",
        "title-orig": "TrId Demo"
    },
    "TrId Demo/lunadll.txt": {
        "lines": [
            {
                "i": 2,
                "orig": "Here is nothing!",
                "tr": "Тут ничего нет!"
            }
        ],
        "title": ""
    }
}

Meta-file for single level example (with TrId)

TrId Demo/i18n/translation_metadata.json

{
    "use-tr-id" : true,
    "TrId Demo.lvlx": {
        "dialogues": [
            {
                "messages": [
                    "npc-0-talk"
                ],
                "note": ""
            }
        ],
        "npc": [
            {
                "i": -1,
                "t": -1,
                "talk": ""
            },
            {
                "i": 0,
                "t": 151,
                "talk": "TR_SIGN"
            }
        ],
        "title": "TrId Demo"
    },
    "TrId Demo/lunadll.txt": {
        "lines": [
            {
                "i": 2,
                "src": "TR_NOTHING"
            }
        ],
        "title": "TrId Demo/lunadll.txt"
    }
}

Translation file for single level example (with TrId)

TrId Demo/i18n/translation_ru.json

{
    "TrId Demo.lvlx": {
        "npc": [
            {
                "tr-id": "TR_SIGN",
                "talk": "This is a message text at the test sign."
            }
        ],
        "title": "Дэмо TrId",
        "title-orig": "TrId Demo"
    },
    "TrId Demo/lunadll.txt": {
        "lines": [
            {
                "tr-id": "TR_NOTHING",
                "tr": "Here is nothing!"
            }
        ],
        "title": ""
    }
}
⚠️ **GitHub.com Fallback** ⚠️