Internationalization¶
Finally, you created an amazing experiment! How cool would it be to run it with participants from all over the world?
Luckily, PsyNet makes it easy to run experiments in different languages. Here’s what you need to do:
mark which strings need to be translated
perform automatic translation and optionally manually check them
Selecting a language¶
We use the technical term ‘locale’ to refer to the language of your experiment.
Locales are denoted by ISO 639-1 codes,
e.g. en
for English, de
for German, nl
for Dutch, etc.
You can specify the locale by adding the line locale = de
to your config.txt
file.
If you do not specify a locale, the experiment will default to English.
Mark which strings need to be translated¶
Let’s say you have the following info page in your experiment:
from dominate.tags import h1, hr, div
from psynet.page import InfoPage
my_info_page = InfoPage(
div(
h1("Instructions"),
hr(),
p("In this experiment, you will listen to different music clips."),
p("You have to select the music you like most."),
p('Press "Next" to continue.')
),
time_estimate=5
)
You can easily translate it by marking the strings that need to be translated with the _
function from gettext
.
import os
from dominate.tags import h1, hr, div
from psynet.page import InfoPage
from psynet.utils import get_translator
_ = get_translator()
my_info_page = InfoPage(
div(
h1(_("Instructions")),
hr(),
p(_("In this experiment, you will listen to different music clips.")),
p(_("You have to select the music you like most.")),
p(_('Press "Next" to continue.'))
),
time_estimate=5
)
Warning
Under the hood PsyNet searches for strings that are marked with _
. If you use other functions to mark strings for translation
(e.g., my_wrapper = get_translator()
), they will not be recognized (e.g., my_wrapper("Instructions")
) and not translated. So, make sure to use _ = get_translator()
.
Variables¶
To replace variables in the translation, you have to write the variable in capital letters (underscores are also allowed) and use curly brackets around them.
To resolve the variable, you have to use the .format
method, like here:
next_button_name = _("Next")
next_button_text = _('press "{NEXT_BUTTON_NAME}" to continue.').format(NEXT_BUTTON_NAME=next_button_name)
Warning
You have to use the .format
method to replace the variables in the translation. F-strings are not allowed, as it would first replace the variable in the English string and then tries to lookup the translation which would fail.
Summary of best practices¶
Use
_
for most stringsKeep the strings short and simple
Avoid HTML tags in the strings as they might get translated or will lead to word order issues
Keep the use of inline variables to a minimum, e.g. instead of writing
_("Make the stimulus as {TARGET} as possible using the slider").format(TARGET=_("happy"))
, write_("Adjust the slider to match the target:") + _("happy")
.
To see the translation in action, have a look at the translation
demo.
Perform automatic translation¶
Open a terminal in your experiment folder and run the following command:
psynet translate
By default this will translate your experiment to the locale specified in your config.txt
file.
Note
You can instruct PsyNet to create translations in multiple languages via the config variable supported_locales
,
for example supported_locales = ["de", "nl"]
.
Alternatively, you can specify the locales on the command line, e.g. psynet translate de nl
.
Each locale’s translation will be stored in a file of the form locales/<iso_code>/LC_MESSAGES/experiment.po
.
PsyNet currently supports two translators:
OpenAI ChatGPT (
chat_gpt
, which is PsyNet’s default) andGoogle Translator (
google_translate
)
You can set the default translator in your config.txt
or .dallingerconfig
with the following line:
[Translator]
default_translator = <translator_name>
OpenAI ChatGPT¶
To use OpenAI ChatGPT, you need to have an OpenAI API key. You can set it in your .dallingerconfig
file with the following line:
[Translator]
openai_api_key = <your_openai_api_key>
Also you need to install the openai
package by running:
pip install openai
Google Translator¶
To use Google Translator, you need to do the following steps
Create a project in the Google Cloud Console
Enable the Cloud Translation API
Create a service account
In the service account and go to the keys tab. Now create a new key as JSON and store it to your computer (home folder is recommended). Now store the path to your
.dallingerconfig
file:
[Translator]
google_translate_json_path = <path_to_your_json_file>
Also you need to install the google-cloud-translate
package by running:
pip install google-cloud-translate==2.0.1
The translation process¶
Both ChatGPT and Google Translate batch their translations on a file basis. This means that they can intelligently infer the context of the strings in the file. ChatGPT also sees the source code of the file, which can provide additional information for disambiguation.
Manual checking¶
You can manually inspect the machine translation by opening the locales/<iso_code>/LC_MESSAGES/experiment.po
file using
POedit editor and check if strings that you marked with _
are translated properly.
Machine translations are by default marked as ‘fuzzy’ in POedit. Once you have reviewed and confirmed a translation,
you can remove this flag in POEdit. When you subsequently run psynet translate
, non-fuzzy translations will
not be overwritten unless their input text changes. They will still however be used as context for the other
translations in the same file.
Revising translations¶
When you run psynet translate
, all fuzzy (i.e. machine-translated) translations will be overwritten.
Non-fuzzy translations will not be overwritten unless their input text changes.
Texts that no longer occur in the source code will be removed from the translation files.
PsyNet does not make any backup of your translations, so make sure you include your experiments locales
directory in your experiment’s git repository and commit your changes regularly.
Advanced usage¶
Contexts¶
_
assumes the same string is always translated the same way, regardless of the context.
However, sometimes you want to disambiguate the meaning of a string. For example, the word “bank” can mean a financial institution or the side of a river.
To do this you can use _p
:
from psynet.utils import get_translator
_p = get_translator(context=True)
bank_of_river = _p("river", "bank")
financial_institution = _p("financial", "bank")
Note
However, this use-case is quite rare. In most cases, you can use _
and it will work just fine.
Translating a package¶
You can translate an arbitrary Python package for use in PsyNet by navigating to the root of
the package and running psynet translate
. This will create a locales
directory in the package’s
source directory and populate it with the translations for the supported locales.
If you do not specify which locales to translate it to, it will default to PsyNet’s own list of supported locales.
Contributing to PsyNet¶
To contribute to PsyNet you need to:
- have a local version of psynet on your computer e.g.: cd ~ && git clone https://gitlab.com/PsyNetDev/PsyNet
- go to the master branch and pull the latest changes: cd ~/PsyNet && git checkout master && git pull
- create a new branch for your changes: git checkout -b my_new_translations
- optionally translate to the new language: psynet translate <new_locale>
- go to the locale folder and your new locale: cd ~/PsyNet/tests/experiments/translation/locales/<new_locale>/LC_MESSAGES
- open the experiment.po
file with PoEdit, go through each entry and validate it or change it
- save the file, commit your changes, and push them
- create a merge request on the GitLab page of PsyNet
- thank you for your contribution!