.. _translation:

===========
Translation
===========

Ren'Py contains a comprehensive framework for the translation of
visual novels. There are four main types of things that can be
translated:

Dialogue
    The main dialogue of the script can be translated, including a
    provision for splitting, combining, omitting, and reordering
    lines.
Menus and Interface Strings
    All interface text can be translated.
Images and Files
    It's possible to include variant images and other files that are
    used when a language is selected.
Styles
    It's possible to customize styles based on the language, so that
    the game can automatically switch to a font appropriate for the
    language that was chosen.

Ren'Py's translation support is currently focused on sanctioned
translations, where the game's creators either release the game
scripts to the translator or create translation templates
themselves. Support for unsanctioned translations is more limited.

Primary and Alternate Languages
===============================

Ren'Py expects each game to be written in a single primary
language. This is called the None language, regardless of what
language it actually is. (For example, if the game was written in
English, English will be the None language.)

Alternate languages are referred to by names which can double as
Python identifiers (starts with a letter or underscore, followed by
letters, numbers, and underscores).

When the None language is selected, most of Ren'Py's translation
functionality is disabled, with the notable exception of Renpy's
internal built-in strings, from the accessibility menu for example.
Theses strings are not found in your project's code, yet they will
still be included in the distributed version of the game. You can
find them in the :file:`game/tl/None/common.rpym` file, whose only
purposes are 1) to provide translations to these strings when the None
language is not english, and 2) to allow creators to customize these
strings for their game.

The language of the launcher at the time when the project is
created will be the language this file will initially translate the
internal strings to.

Generating Translation Files
============================

When the project scripts are available, translation files can be
generated by opening the project in the Ren'Py Launcher, and choosing
"Generate Translations". The launcher will prompt you for the name of
the language to generate, and will then proceed to create or update
the translation files.

The translation files live in directories underneath the "tl"
subdirectory of the game directory. For example, if you create a
piglatin translation of the tutorial project, translation files will
be placed under :file:`tutorial/game/tl/piglatin`.

There will be one translation file created per game script file. The
common.rpy file will also be created to contain translations of
strings that are common to all games made using Ren'Py.

Translating Dialogue
====================

As Ren'Py is a visual novel engine, we expect most translation to
involve dialogue. Ren'Py includes a flexible framework that allows
dialogue to be split, combined, reordered, and omitted entirely.

Translation Units
-----------------

The fundamental unit of translation is a block of zero or more
translatable statements, optionally followed by a single say
statement. Translatable statements are the voice and nvl statements. For example,
take the following game::

    label start:
        e "Thank you for taking a look at the Ren'Py translation framework."

        show eileen happy

        e "We aim to provide a comprehensive framework for translating dialogue, strings, images, and styles."

        e "Pretty much everything your game needs!"

This is broken up into multiple translation units. Each unit has an
identifier assigned to it, with the identifier being generated from
the label preceding the unit, and the statements inside the unit. (If
multiple units would be assigned the same translation number, a serial
number to the second and later units to distinguish them.)

In the example above, the first unit generated is assigned the identifier
start_636ae3f5, and contains the statement::

    e "Thank you for taking a look at the Ren'Py translation framework."

The second unit is given the identifier start_bd1ad9e1m and contains::

    e "We aim to provide a comprehensive framework for translating dialogue, strings, images, and styles."

The third unit has the identifier start_9e949aac, and contains::

    e "Pretty much everything your game needs!"

These units are created automatically by Ren'Py when the game script
is loaded.

Translate Statement
-------------------

When you generate translations for a language, Ren'Py will generate a
translate statement corresponding to each translation unit. When
translating the script above, Ren'Py will generate::

    # game/script.rpy:95
    translate piglatin start_636ae3f5:

        # e "Thank you for taking a look at the Ren'Py translation framework."
        e ""

    # game/script.rpy:99
    translate piglatin start_bd1ad9e1:

        # e "We aim to provide a comprehensive framework for translating dialogue, strings, images, and styles."
        e ""

    # game/script.rpy:101
    translate piglatin start_9e949aac:

        # e "Pretty much everything your game needs!"
        e ""

This can be translated by editing the generated files. A finished
translation might look like::

    # game/script.rpy:95
    translate piglatin start_636ae3f5:
        # e "Thank you for taking a look at the Ren'Py translation framework."
        e "Ankthay ouyay orfay akingtay away ooklay atway ethay En'Pyray anslationtray ameworkfray."

    # game/script.rpy:99
    translate piglatin start_bd1ad9e1:

        # e "We aim to provide a comprehensive framework for translating dialogue, strings, images, and styles."
        e "Eway aimway otay ovidepray away omprehensivecay ameworkfray orfay anslatingtray ialogueday, ingsstray, imagesway, andway ylesstay."

    # game/script.rpy:101
    translate piglatin start_9e949aac:

        # e "Pretty much everything your game needs!"
        e "Ettypray uchmay everythingway ouryay amegay eedsnay!"


When a block in the main script is encountered, Ren'Py checks to see
if a translate statement corresponding to that block exists. If so, it
executes the translate statement instead of the translated block,
showing the user the translation.

More Complex Translations
-------------------------

Translate statements do not need to contain 1-to-1 translations of the
original language. For example, a long line could be split::

    # game/script.rpy:99
    translate piglatin start_bd1ad9e1:
        # e "We aim to provide a comprehensive framework for translating dialogue, strings, images, and styles."
        e "Eway aimway otay ovidepray away omprehensivecay ameworkfray..."
        e "...orfay anslatingtray ialogueday, ingsstray, imagesway, andway ylesstay."


Or a statement can be removed, by replacing it with the ``pass`` statement::

    # game/script.rpy:101
    translate piglatin start_9e949aac:

        # e "Pretty much everything your game needs!"
        pass

It's also possible to run non-dialogue statements, such as
conditionals or Python. For example, we can translate::

  e "You scored [points] points!"

into::

    # game/script.rpy:103
    translate piglatin start_36562aba:

        # e "You scored [points] points!"
        $ latin_points = to_roman_numerals(points)
        e "Ouyay oredscay [latin_points] ointspay!"

.. _translation-tips:

Tips
----

Be very careful when changing dialogue that has been translated,
especially when that dialogue is repeated in more than one place
inside a label.

Sometimes it might be desirable to change a line of
dialogue in the original language, without requiring
the translators to retranslate the line. For example,
a typo in English is unlikely to have survived the process
of being translated into Russian.

This can be done by including an ``id`` clause as part of the
say statement, giving the translation ID to use for this
statement. For example::

    label start:
        e "This used to have a typo." id start_61b861a2

Adding labels can also confuse the translation process. To prevent
this, labels that are given the ``hide`` clause are ignored when generating
translations. ::

    label ignored_by_translation hide:
        "..."

While translation blocks may include Python, this Python should not
have side effects visible outside of the block. That's because
changing languages will restart the translation block, causing the
side effects to occur multiple times.

.. _string-translations:

Menu and String Translations
============================

In addition to dialogue, Ren'Py is able to translate text found in
menus and other strings. Interface translations are a 1-to-1
substitution. Wherever a string is found, it will be replaced by a
single replacement.

When generating translations, Ren'Py will scan the script files for
menus, and for strings enclosed inside the ``_()`` function. It will then
place the strings inside a ``translate strings`` block. For example, if we
have the following script::

    define e = Character(_("Eileen"))

    # ...

    menu:

        "Go West":
            # ...

        "Head East":
            # ...

Ren'Py will generate::

    translate piglatin strings:

        old "Eileen"
        new ""

        old "Go West"
        new ""

        old "Head East"
        new ""

Which can then be translated::

    translate piglatin strings:

        old "Eileen"
        new "Eileenway"

        old "Go West"
        new "Ogay Estway"

        old "Head East"
        new "Eadhay Eastway"

String translations are also applied to dialogue strings that are not
translated as dialogue.

When the same string is used in multiple places, the string can
be distinguished using the {#...} text tag. Even though they display the
same, Ren'Py considers all of these distinct strings for the purpose
of translation::

    "New"
    "New{#project}"
    "New{#game}"
    "New{#playlist}"

The ``translate strings`` statement can also be used to translate the None
language. This can be used to when the game is written in a non-English
language, to translate the Ren'Py user interface. ::

    translate None strings:
        old "Start Game"
        new "Artstay Amegay"

Translating Substitutions
-------------------------

String substitutions can be translated using the ``!t`` conversion
flag. So the following will be translatable using a combination of
the dialogue and string translation systems::

    if mood_points > 5:
        $ mood = _("great")
    else:
        $ mood = _("awful")

    "I'm feeling [mood!t]."

.. _extract-merge-translations:

Extracting and Merging String Translations
------------------------------------------

String translations can be extracted from one project, and moved to
another. This is a multiple-step process:

* Select the source project, and choose "Generate Translations".
* Enter the language to extract, and click "Extract String Translations".
* Return to the main menu, select the target project, and choose "Generate Translations".
* Enter the language to merge to (often the same language), and choose "Merge String Translations".

There are a couple of options that control merging:

Replace existing translations
    When checked, this will cause non-trivial existing translations (those
    that are not empty or the source string) to be replaced. By default,
    merging will refuse to overwrite non-trivial translations that already
    exist.

Reverse languages
    Reverses the strings before merging. This can be used, for example,
    to use a set of English -> Russian translations to create a
    Russian -> English translation.

.. _image-file-translation:

Image and File Translations
===========================

When translating a game, it may be necessary to replace a file
with a translated version. For example, if an image contains text, it
might make sense to replace it with a version of the image where the
text is in another language.

Ren'Py handles this by looking in the translation directory for the
image. For example, if the "piglatin" language is in use, and
"library.png" is loaded, Ren'Py will use :file:`game/tl/piglatin/library.png`
in preference to :file:`game/library.png`.

If the file is in a directory under game, that directory should be
included underneath the language. For example, the file :file:`game/gui/main_menu.png`
can be translated by creating the file :file:`game/tl/piglatin/gui/main_menu.png`.

Style Translations
==================

It may be necessary to change styles – especially font-related
styles – when translating a game. Ren'Py handles this with ``translate
style`` blocks and ``translate python`` blocks. These blocks can
change language-related variables and styles. For example::

    translate piglatin style default:
        font "stonecutter.ttf"

More usually, the font used for dialogue is set with :var:`gui.text_font`.
The font used for system text, like the exception screen, the accessibility menu,
and the gui menu, can be customized with :var:`gui.system_font`. The system font
should be able to express both ASCII and the translated language. Together, these
can be customized with.

    translate piglatin python:
        gui.text_font = "stonecutter.ttf"
        gui.system_font = "Noto Sans.ttf"

When a language is activated – either at the start of the game, or
after a language change – Ren'Py resets the styles to their contents
at the end of the init phase. It then runs all ``translate python`` blocks,
all style blocks, and all translate style blocks associated with the current
language, guaranteeing that blocks appearing earlier in a file are executed first.
Finally, it rebuilds styles, allowing the changes to take effect.

Style translations may be added to any .rpy file.

.. _deferred-translations:

Deferred Translation Loading
============================

For a large game, loading all translations can take a long time. To speed
up these games, Ren'Py supports deferred translations. To enable deferred
translations, add::

    define config.defer_tl_scripts = True

To your :file:`options.rpy` file, or any file that loads before the translation
scripts.

When True, this variable will prevent Ren'Py from loading script files
found in directories named :file:`tl/{language}` as it initializes. Instead,
it will load these files when the language is first activated,
either at game start or when Ren'Py switches to the language.

Because the :file:`tl/{language}` directories are not loaded at init time, these
files should not contain any statements that are executed at init time,
like ``init`` blocks or ``python`` blocks, ``screen``, ``image``, ``transform``,
and other statements. The files should consist entirely of ``translate``,
``translate python``, and ``translate style`` blocks.

Default Language
================

The default language is chosen using the following method:

* If the RENPY_LANGUAGE environment variable is set, that language is
  used.
* If :var:`config.language` is set, that language is used, overriding
  all of the following.
* If the game has ever chosen a language in the past, that language is
  used.
* If this is the first time the game has been run and
  :var:`config.enable_language_autodetect` is True, Ren'Py tries to
  autodetect the language using :var:`config.locale_to_language_function`.
* If this is the first time the game has been run,
  :var:`config.default_language` is used.
* Otherwise, the None language is used.

Translation Actions, Functions, and Variables
=============================================

The main way to switch languages is with the Language action.

.. include:: inc/language_action

The Language action can be used to add a language preference to the
preferences screen::

    frame:
        style_prefix "pref"
        has vbox

        label _("Language")
        textbutton "English" action Language(None)
        textbutton "Igpay Atinlay" action Language("piglatin")

There are two translation-related functions:

.. include:: inc/translation_functions

In addition, there are four functions that are related to string
translation:

.. function:: _(s)

    (Single underscore) Returns `s` unchanged. Ren'Py will scan for
    strings enclosed in this function, and add them to the list of
    translatable strings. The strings will not be translated until
    they are displayed.

.. function:: __(s)

    (Double underscore) Returns `s` immediately translated into the
    current language. Strings enclosed in this function will be added
    to the list of translatable strings. Note that the string may be
    double-translated, if it matches a string translation when it
    is displayed.

.. include:: inc/underscore_p

.. include:: inc/translate_string

There are two language-related variables. One is
:var:`config.default_language`, which is used to change the default language
of the game.

.. var:: _preferences.language

    The name of the current language, or None if the default language is
    being used. This should be treated as a read-only variable. To
    change the language, call :func:`renpy.change_language`.


Unsanctioned Translations
=========================

.. note::

    It's best to ask a game's creators for permission before
    creating an unsanctioned translation.

Ren'Py includes a small amount of support for creating translations
without the active assistance of the game's creators. This support
consists of the ability to automatically create a string translation
file from all of the strings in the game. Since string translations
are used for untranslated dialogue, this technique makes it possible
to translate a game.

To create a string translation file, perform the following steps:

* Set the RENPY_LANGUAGE environment variable to the language you want
  to translate to.
* Set the RENPY_UPDATE_STRINGS environment variable to a non-empty
  value.
* Play through the game until all text is seen.

This will update the :file:`game/tl/language/strings.rpy` file with a
translation template that contains all of the strings in it.

If a game doesn't include support for changing the language, it may be
appropriate to use an ``init python`` block to set :var:`config.language`
to the target language.

.. var:: config.language = None

    If not None, sets the language to use at game launch, overriding
    any memorized choice made by the user.

Along with the use of string translations for dialogue, unsanctioned
translators may be interested in using the techniques described above
to translate images and styles.
