To use oslo.i18n in a project (e.g. myapp), you will need to create a
small integration module to hold an instance of
TranslatorFactory
and references to
the marker functions the factory creates.
Note
Libraries probably do not want to expose the new integration module
as part of their public API, so rather than naming it
myapp.i18n
it should be called myapp._i18n
to indicate that
it is a private implementation detail, and not meant to be used
outside of the library’s own code.
Note
Starting with the Pike series, OpenStack no longer supports log translation. It is not necessary to add translation instructions to new code, and the instructions can be removed from old code. Refer to the email thread understanding log domain change on the openstack-dev mailing list for more details.
# myapp/_i18n.py
import oslo_i18n
DOMAIN = "myapp"
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
# The primary translation function using the well-known name "_"
_ = _translators.primary
# The contextual translation function using the name "_C"
# requires oslo.i18n >=2.1.0
_C = _translators.contextual_form
# The plural translation function using the name "_P"
# requires oslo.i18n >=2.1.0
_P = _translators.plural_form
# Translators for log levels.
#
# NOTE(dhellmann): This is not needed for new projects as of the
# Pike series.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)
Then, in the rest of your code, use the appropriate marker function for each message:
from myapp._i18n import _, _LW, _LE
# ...
variable = "openstack"
LOG.warning(_LW('warning message: %s'), variable)
# ...
try:
# ...
except AnException1:
# Log only
LOG.exception(_LE('exception message'))
except AnException2:
# Raise only
raise RuntimeError(_('exception message'))
else:
# Log and Raise
msg = _('Unexpected error message')
LOG.exception(msg)
raise RuntimeError(msg)
Note
The import of multiple modules from _i18n on a single line is a valid exception to OpenStack Style Guidelines for import statements.
It is important to use the marker functions (e.g. _LI), rather than the longer form of the name, because the tool that scans the source code for translatable strings looks for the marker function names.
Warning
The old method of installing a version of _()
in the builtins
namespace is deprecated. Modifying the global namespace affects
libraries as well as the application, so it may interfere with
proper message catalog lookups. Calls to
gettextutils.install()
should be replaced with the
application or library integration module described here.
The OpenStack Style Guidelines prefer importing modules and accessing names from those modules after import, rather than importing the names directly. For example:
# WRONG
from foo import bar
bar()
# RIGHT
import foo
foo.bar()
The linting tool hacking will typically complain about importing
names from within modules. It is acceptable to bypass this for the
translation marker functions, because they must have specific names
and their use pattern is dictated by the message catalog extraction
tools rather than our style guidelines. To bypass the hacking check
for imports from this integration module, add an import exception to
tox.ini
.
For example:
# tox.ini
[hacking]
import_exceptions = myapp._i18n
Lazy translation delays converting a message string to the translated form as long as possible, including possibly never if the message is not logged or delivered to the user in some other way. It also supports logging translated messages in multiple languages, by configuring separate log handlers.
Lazy translation is implemented by returning a special object from the translation function, instead of a unicode string. That special message object supports some, but not all, string manipulation APIs. For example, concatenation with addition is not supported, but interpolation of variables is supported. Depending on how translated strings are used in an application, these restrictions may mean that lazy translation cannot be used, and so it is not enabled by default.
To enable lazy translation, call enable_lazy()
.
import oslo_i18n
oslo_i18n.enable_lazy()
Use translate()
to translate strings to
a specific locale. translate()
handles delayed translation and
strings that have already been translated immediately. It should be
used at the point where the locale to be used is known, which is often
just prior to the message being returned or a log message being
emitted.
import oslo_i18n
trans_msg = oslo_i18n.translate(msg, my_locale)
If a locale is not specified the default locale is used.
Only the languages that have translations provided are available for
translation. To determine which languages are available the
get_available_languages()
is provided. The integration
module provides a domain defined specific function.
import myapp._i18n
languages = myapp._i18n.get_available_languages()
See also
Several preparations are required to display translated messages in your running application.
You need to specify your preferred language through an environment variable.
The preferred language can be specified by LANGUAGE
, LC_ALL
,
LC_MESSAGES
, or LANGUAGE
(A former one has a priority).
oslo_i18n.translate()
can be used to translate a string to override the
preferred language.
Note
You need to use enable_lazy()
to override the preferred language
by using oslo_i18n.translate()
.
Python gettext
looks for binary mo
files for the given domain
using the path <localedir>/<language>/LC_MESSAGES/<domain>.mo
.
The default locale directory varies on distributions,
and it is /usr/share/locale
in most cases.
If you store message catalogs in a different location,
you need to specify the location via an environment variable
named <DOMAIN>_LOCALEDIR
where <DOMAIN>
is an upper-case
domain name with replacing _
and .
with -
.
For example, NEUTRON_LOCALEDIR
for a domain neutron
and
OSLO_I18N_LOCALEDIR
for a domain oslo_i18n
.
Note
When you specify locale directories via <DOMAIN>_LOCALEDIR
environment variables, you need to specify an environment variable per
domain. More concretely, if your application using a domain myapp`
uses oslo.policy, you need to specify both ``MYAPP_LOCALEDIR
and
OSLO_POLICY_LOCALEDIR
to ensure that translation messages from
both your application and oslo.policy are displayed.
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.