Skip to content

l10n guide

Martin Hradil edited this page Sep 27, 2021 · 2 revisions

We use traditional gettext-style localization, split in two repos.

Backend details

Added in https://github.com/ansible/galaxy_ng/pull/882

We're using a _() function, parameters are fine as long as the format string gets passed through _() before interpolation. (That makes python f-strings unsuitable for l10n without modification.)

Do translate error messages in API responses, don't translate log messages.

Simple example:

+from django.utils.translation import gettext_lazy as _

-            errmsg = 'Repository "{pulp_id}" not found while creating synclist'
+            errmsg = _('Repository "{pulp_id}" not found while creating synclist')
             raise ValidationError(errmsg.format(pulp_id=repository_id))

         except IntegrityError as exc:
-            raise ValidationError("Synclist already exists: %s" % exc)
+            raise ValidationError(_("Synclist already exists: %s") % exc)

Extract & compile

String extraction can be triggered using django-admin makemessages (podman exec -it galaxy_ng_api_1 pulpcore-manager makemessages), this creates the .po files, then, those can be compiled to .mo using django-admin compilemessages. All these files live in ./galaxy_ng/locale/*/LC_MESSAGES/.

More details in django translation docs.

Frontend details

Added in https://github.com/ansible/ansible-hub-ui/pull/810

We're using a t`...` template string javascript-side, and a <Trans> component for React JSX.

Current language is autodetected based on browser settings, and compared with the list of availableLanguages in src/l10n.js.

(It may be easier to install https://chrome.google.com/webstore/detail/locale-switcher/kngfjpghaokedippaapkfihdlmmlafcc for switching.)


Examples:

Translating where a string result is expected (no jsx):

import { t } from '@lingui/macro';
const data = "untranslated";
const string = t`Hello ${data}`;

(translators will see Hello {data}, translate to something like Hola {data} resulting in Hola untranslated in the UI)

❗ Prefer using variables in string substition: t`Hello ${data.data}` is using an expression, not a simple variable, so the translators will see Hello {0}, they won't know what {0} means without context.

Translating where a JSX result is expected (string with html):

import { Trans } from '@lingui/macro';
const data = "untranslated";
const MyComponent = () => (
  <Trans>Hello <b>World</b>: {data}</Trans>
);

(translators will see Hello <0>World</0>: {data}, translate to Hola <0>mundo</0>: {data} resulting in Hola mundo: untranslated)

Translating plurals:

import { plural } from '@lingui/macro';
const myCount = 123;
const string = plural(myCount, {
  one: '# byte',
  other: '# bytes',
}); // yields 0 bytes, 1 byte, 2 bytes, ... depending on myCount

(translators will see {myCount, plural, one {# byte} other {# bytes}}, translate to something like {myCount, plural, one {# byt} few {# byty} other {# bytů}}, yielding 0 bytů, 1 byt, 2 byty, ..., 5 bytů depending on myCount)

More details in lingui docs; prefer the macro version for consistency.

Extract & compile

  1. run gettext:extract to extract translatable strings from src/ into locale/*.po
  2. those *.po files get uploaded to a translation service (outside automation)
  3. updated *.po files get downloaded from the service and merged in (use msgmerge to resolve conflicts if necessary)
  4. run gettext:compile to create locale/*.js files for each po counterpart
  5. webpack automatically builds each locale js into a separate bundle
  6. using a dynamic import in src/l10n.ts to import the right one at runtime