Translations#

Starting with version 0.4.7, napari codebase include internationalization (i18n) and now offers the possibility of installing language packs, which provide localization (l10n) enabling the user interface to be displayed in different languages.

To learn more about the current languages that are in the process of translation, visit the language packs repository

This guide is limited to providing translations to the napari core codebase. We will soon provide more information on how to make your napari plugins localizable.

How to make strings translatable?#

To make your code translatable (localizable), please use the trans helper provided by the napari utilities.

from napari.utils.translations import trans

trans is a convenience wrapper on top of the gettext module of the standard library, which provides the l10n and i18n facilities in Python. To learn more about gettext visit the Python documentation.

trans provides 4 methods that can be used to handle localization.

The following examples make use of a widget to illustrate the workflow, but this also applies to other strings that may be surfaced to the user, e.g. custom exceptions.

f-strings do not work with localizable strings, so any strings that use them need to be converted. The Plural strings section below explains this in more detail.

Singular strings#

Strings that need to provide a 1:1 translation, can use the trans._ method:

from qtpy.QtWidgets import QComboBox, QWidget

from napari.utils.translations import trans


class SomeWidget(QWidget):

    def __init__(self):
        self.channel_combo_box = QComboBox(self)        
        self.channel_combo_box.addItem(trans._("red"), "red")
        self.channel_combo_box.addItem(trans._("green"), "green")
        self.channel_combo_box.addItem(trans._("blue"), "blue")

On this example, we add a RGB channel combo box selector. The first argument of addItem is the actual display name of that combobox item, whereas the second argument is the data associated to that item, in this case the original channel name.

For the English (US) translation the options displayed would be the same, since gettext uses the source language as the key to find translations. In this case:

  • red

  • green

  • blue

For the Spanish (Spain) translation the options displayed would be:

  • rojo

  • verde

  • azul

trans._ is a wrapper on top of gettext.gettext with enhanced functionality.

Singular strings with context#

Strings that need some additional context to disambiguate the source string, can use the trans._p method:

The word Tab can mean different things in the english language:

  • A spacer, when the Tab key of a keyboard is used inside a text editor.

  • A tablature, a simplified version of sheet music used for stringed insruments.

  • A user interface graphical element, like the one provide by QTabWidget.

from qtpy.QtWidgets import QComboBox, QWidget

from napari.utils.translations import trans


class SomeWidget(QWidget):

    def __init__(self):
        self.context_combo_box = QComboBox(self)        
        self.context_combo_box.addItem(trans._p("character", "tab", ), "tab")
        self.context_combo_box.addItem(trans._p("music", "tab"), "tab")
        self.context_combo_box.addItem(trans._p("ui-element", "tab"), "tab")

On this example, we add the word tab three time to a combo box selector. The first argument of trans._p provides the context string that will help to disambiguate the translation.

For the English (US) translation the options displayed would be the same, since gettext uses the source language as the key to find translations. In this case:

  • tab

  • tab

  • tab

For the Spanish (Spain) translation the options displayted would be:

  • tabulación

  • tablatura

  • pestaña

trans._p is a wrapper on top of gettext.pgettext with enhanced functionality.

Plural strings#

Some strings or sentences might need to be handled differently when needing pluralization, that is depending on the amount of items included in the string.

from qtpy.QtWidgets import QLabel, QWidget

from napari.utils.translations import trans


class SomeWidget(QWidget):

    def __init__(self, amount):
        string = trans._n("{n} item", "{n} items", n=amount)
        self.label = QLabel(string)

On this example, the label string depends on the n parameter. The first argument of trans._n provides the singular version of the string. The second argument provides the plural version of the string. The third argument provides the quantity that will allow to know which string should be used, in this case amount. Notice that by default trans._n will try to interpolate the value of n in the string, if found.

For the English (US) translation the string displayed for different values of amount would be:

  • For amount=0, "0 items"

  • For amount=1, "1 item"

  • For amount=2, "2 items"

For the Spanish (Spain) translation the options displayted would be:

  • For amount=0, "0 ítems"

  • For amount=1, "1 ítem"

  • For amount=2, "2 ítems"

Take into account that different languages will handle pluralization differently. Having clear variable names within strings (e.g. {amount}) of what the variable represents makes the internationalization process much easier and pleasant for translators.

trans._n is a wrapper on top of gettext.ngettext with enhanced functionality.

Plural strings with context#

This is similar to plural strings, but an additional context can be supplied as explained in the Singular strings with context section.

Deferred translations#

For some strings we might not want to provide a translation right away. This could be the case of running napari in scripts, where providing the original english error messages might provide a better experience for users and developers.

For these case, all the trans._* methods provide an extra parameter deferred. When used and set to True, the methods will return an instance of a TranslationString which provides two methods, TranslationString.value() and TranslationString.translation(). The former provides the original untranslated string and the later provides the translated string. Additionally, the string representation of this object will default to use the original string.

With this, we can also provide the correct translations when using the napari application and displaying messages on the notifications manager in the language selected by the users.

Additional arguments inside strings#

Since f-strings do not work with localizable strings, we loose their convenience and have to use str.format. However, when using deferred translations, we need to be able to use any variables the original string needs to be able to render itself. To handle this, the trans helpers provide the ability to receive any extra keyword arguments whcih will be used for rendering the deferred strings on demand.

An example of how this works with Spanish and defered string yields:

from napari.utils.translations import trans

deferred_string = trans._("Hello {word}", deferred=True, word="world!")

str(deferred_string) == "Hello world!"  # True
deferred_string.value() == "Hello world!"  # True
deferred_string.translation() == "¡Hola mundo!"  # True

An example of how this works for non-deferred string yields:

from napari.utils.translations import trans

normal_string = trans._("Hello {word}", word="world!")
normal_string == "¡Hola mundo!"  # True

Contributing translations#

To be able to provide translations for different languages, the napari team chose to use the Crowdin which provides a simple web interface where the localizable strings can be translated one by one with the help of volunteers and the napari user community.

To start translating, please visit the Crowdin project page.