From 01dead7bdd9a541cd4f575355a5226415a5afa08 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 28 Jun 2017 12:42:18 -0400 Subject: rearrange the documentation to fit into the new standard layout Change-Id: Ibe6548b444a8f030ea7ba034b7d63622e32d3917 Signed-off-by: Doug Hellmann --- doc/source/api.rst | 39 ----- doc/source/contributing.rst | 5 - doc/source/contributor/contributing.rst | 5 + doc/source/contributor/index.rst | 9 ++ doc/source/contributor/policy.rst | 9 ++ doc/source/guidelines.rst | 246 ---------------------------- doc/source/history.rst | 1 - doc/source/index.rst | 22 +-- doc/source/policy.rst | 9 -- doc/source/reference/index.rst | 41 +++++ doc/source/usage.rst | 272 ------------------------------- doc/source/user/guidelines.rst | 246 ++++++++++++++++++++++++++++ doc/source/user/history.rst | 1 + doc/source/user/index.rst | 11 ++ doc/source/user/usage.rst | 274 ++++++++++++++++++++++++++++++++ 15 files changed, 600 insertions(+), 590 deletions(-) delete mode 100644 doc/source/api.rst delete mode 100644 doc/source/contributing.rst create mode 100644 doc/source/contributor/contributing.rst create mode 100644 doc/source/contributor/index.rst create mode 100644 doc/source/contributor/policy.rst delete mode 100644 doc/source/guidelines.rst delete mode 100644 doc/source/history.rst delete mode 100644 doc/source/policy.rst create mode 100644 doc/source/reference/index.rst delete mode 100644 doc/source/usage.rst create mode 100644 doc/source/user/guidelines.rst create mode 100644 doc/source/user/history.rst create mode 100644 doc/source/user/index.rst create mode 100644 doc/source/user/usage.rst (limited to 'doc') diff --git a/doc/source/api.rst b/doc/source/api.rst deleted file mode 100644 index 02095f7..0000000 --- a/doc/source/api.rst +++ /dev/null @@ -1,39 +0,0 @@ -===== - API -===== - -oslo_i18n -========= - -.. automodule:: oslo_i18n - -.. autoclass:: oslo_i18n.TranslatorFactory - :members: - -.. seealso:: - - An example of using a :class:`TranslatorFactory` is provided in - :ref:`integration-module`. - -.. autofunction:: oslo_i18n.enable_lazy - -.. seealso:: - - :ref:`lazy-translation` - -.. autofunction:: oslo_i18n.translate - -.. autofunction:: oslo_i18n.get_available_languages - -oslo_i18n.log -============= - -.. automodule:: oslo_i18n.log - :members: - -oslo_i18n.fixture -================= - -.. automodule:: oslo_i18n.fixture - :members: - :special-members: diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst deleted file mode 100644 index 2ca75d1..0000000 --- a/doc/source/contributing.rst +++ /dev/null @@ -1,5 +0,0 @@ -============== - Contributing -============== - -.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/contributor/contributing.rst b/doc/source/contributor/contributing.rst new file mode 100644 index 0000000..ed5290b --- /dev/null +++ b/doc/source/contributor/contributing.rst @@ -0,0 +1,5 @@ +============== + Contributing +============== + +.. include:: ../../../CONTRIBUTING.rst diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst new file mode 100644 index 0000000..cbb8376 --- /dev/null +++ b/doc/source/contributor/index.rst @@ -0,0 +1,9 @@ +=========================== + Contributing to oslo.i18n +=========================== + +.. toctree:: + :maxdepth: 2 + + contributing + policy diff --git a/doc/source/contributor/policy.rst b/doc/source/contributor/policy.rst new file mode 100644 index 0000000..7ff67af --- /dev/null +++ b/doc/source/contributor/policy.rst @@ -0,0 +1,9 @@ +================ + Policy History +================ + +* `Discussion from Havana Summit `__ +* `Discussion from Icehouse Summit `__ +* `Discussion from Juno Summit `__ +* `I18n team wiki page `__ +* `LoggingStandards wiki page `__ diff --git a/doc/source/guidelines.rst b/doc/source/guidelines.rst deleted file mode 100644 index 9ed87fe..0000000 --- a/doc/source/guidelines.rst +++ /dev/null @@ -1,246 +0,0 @@ -================================= - Guidelines for Use In OpenStack -================================= - -The OpenStack I18N team has a limited capacity to translate messages, -so we want to make their work as effective as possible by identifying -the most useful text for them to translate. All text messages *the -user sees* via exceptions or API calls should be marked for -translation. However, some exceptions are used internally to signal -error conditions between modules and are not intended to be presented -to the user. Those do not need to be translated. - -.. seealso:: - - * :doc:`usage` - * :doc:`api` - -Gettext Contextual Form and Plural Form -======================================= - -Sometimes under different contexts, the same word should be -translated into different phrases using -:py:attr:`TranslatorFactory.contextual_form `. - -And recommend the following code to use contextual form:: - - # The contextual translation function using the name "_C" - _C = _translators.contextual_form - - ... - msg = _C('context', 'string') - -In some languages, sometimes the translated strings are different -with different item counts using -:py:attr:`TranslatorFactory.plural_form ` - -And recommend the following code to use plural form:: - - # The plural translation function using the name "_P" - _P = _translators.plural_form - - ... - msg = _P('single', 'plural', count) - -The contextual form and plural form are used only when needed. -By default, the translation should use the ``_()``. - -.. note:: - These two functions were only available in oslo.i18n >= 2.1.0. - -Log Translation -=============== - -.. 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. - -OpenStack supports translating some log levels using separate message -catalogs, and so has separate marker functions. These well-known names -are used by the build system jobs that extract the messages from the -source code and pass it to the translation tool. - -========== ========== - Level Function -========== ========== - INFO ``_LI()`` - WARNING ``_LW()`` - ERROR ``_LE()`` - CRITICAL ``_LC()`` -========== ========== - -.. note:: - * Debug level log messages are not translated. - * LOG.exception creates an ERROR level log, so when a marker function is - used (see below) ``_LE()`` should be used. - - -Using a Marker Function -======================= -The marker functions are used to mark the translatable strings in the -code. The strings are extracted into catalogs using a tool that -looks for these specific markers, so the function argument must just -be a string. - -For example: **do not do this**:: - - # WRONG - msg = _(variable_containing_msg) - w_msg = _LW(variable_warning_msg) - -Instead, use this style:: - - # RIGHT - msg = _('My message.') - w_msg = _LW('My warning message') - - -Choosing a Marker Function -========================== - -The purpose of the different marker functions is to separate the -translatable messages into different catalogs, which the translation -teams can prioritize translating. It is important to choose the right -marker function, to ensure that strings the user sees will be -translated and to help the translation team manage their work load. - -Everything marked with ``_()`` will be translated. Prioritizing the -catalogs created from strings marked with the log marker functions is -up to the individual translation teams and their users, but it is -expected that they will work on critical and error messages before -warning or info. - -``_()`` is preferred for any user facing message, even if it is also -going to a log file. This ensures that the translated version of the -message will be available to the user. - -The log marker functions (``_LI()``, ``_LW()``, ``_LE()``, and ``_LC()``) -must only be used when the message is only sent directly to the log. -Anytime that the message will be passed outside of the current context -(for example as part of an exception) the ``_()`` marker function -must be used. - -A common pattern is to define a single message object and use it more -than once, for the log call and the exception. In that case, ``_()`` -must be used because the message is going to appear in an exception that -may be presented to the user. - -For example, **do not do this**:: - - # WRONG - msg = _LE('There was an error.') - LOG.exception(msg) - raise LocalExceptionClass(msg) - -Instead, use this style:: - - # RIGHT - msg = _('There was an error.') - LOG.exception(msg) - raise LocalExceptionClass(msg) - -Except in the case above, ``_()`` should not be used for translating -log messages. This avoids having the same string in two message -catalogs, possibly translated differently by two different -translators. The log message will translate properly because when -the message is not found in the log specific catalog the ``_()`` -catalog will be used. - -If a common message is not being used, they should each be treated -separately with respect to choosing a marker function. - -For example, **do not do this**:: - - # WRONG - LOG.exception(_('There was an error.')) - raise LocalExceptionClass(_('An error occurred.')) - -Instead, use this style:: - - # RIGHT - LOG.exception(_LE('There was an error.')) - raise LocalExceptionClass(_('An error occurred.')) - - -Adding Variables to Translated Messages -======================================= - -Translated messages should not be combined with other literal strings -to create partially translated messages. For example, **do not do -this**:: - - # WRONG - raise ValueError(_('some message') + ': variable=%s' % variable) - -Instead, use this style:: - - # RIGHT - raise ValueError(_('some message: variable=%s') % variable) - -Including the variable reference inside the translated message allows -the translator to take into account grammar rules, differences in -left-right vs. right-left rendering, and other factors to make the -translated message more useful to the end user. - -Any message with more than one variable should use named interpolation -instead of positional, to allow translators to move the variables -around in the string to account for differences in grammar and writing -direction. - -For example, **do not do this**:: - - # WRONG - raise ValueError(_('some message: v1=%s v2=%s') % (v1, v2)) - -Instead, use this style:: - - # RIGHT - raise ValueError(_('some message: v1=%(v1)s v2=%(v2)s') % {'v1': v1, 'v2': v2}) - - -Adding Variables to Log Messages -================================ - -String interpolation should be delayed to be handled by the logging -code, rather than being done at the point of the logging call. For -example, **do not do this**:: - - # WRONG - LOG.info(_LI('some message: variable=%s') % variable) - -Instead, use this style:: - - # RIGHT - LOG.info(_LI('some message: variable=%s'), variable) - -This allows the logging package to skip creating the formatted log -message if the message is not going to be emitted because of the -current log level. - -Avoid Forcing the Translation of Translatable Variables -======================================================= - -Translation can also be delayed for variables that potentially contain -translatable objects such as exceptions. - -Whenever possible translation should not be forced by use of :func:`str`, -:func:`unicode`, or :func:`six.text_type` on a message being used with -a format string. - -For example, **do not do this**:: - - # WRONG - LOG.info(_LI('some message: exception=%s'), six.text_type(exc)) - -Instead, use this style:: - - # RIGHT - LOG.info(_LI('some message: exception=%s'), exc) - -This allows the translation of the translatable replacement text to be -delayed until the message is translated. diff --git a/doc/source/history.rst b/doc/source/history.rst deleted file mode 100644 index 69ed4fe..0000000 --- a/doc/source/history.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../ChangeLog diff --git a/doc/source/index.rst b/doc/source/index.rst index fde034a..e52d8a1 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -6,28 +6,14 @@ The oslo.i18n library contain utilities for working with internationalization (i18n) features, especially translation for text strings in an application or library. -Contents -======== - .. toctree:: :maxdepth: 2 - usage - guidelines - api - policy - contributing - -Release Notes -============= - -.. toctree:: - :maxdepth: 1 - - history + user/index + reference/index + contributor/index -Indices and tables -================== +.. rubric:: Indices and tables * :ref:`modindex` * :ref:`search` diff --git a/doc/source/policy.rst b/doc/source/policy.rst deleted file mode 100644 index 7ff67af..0000000 --- a/doc/source/policy.rst +++ /dev/null @@ -1,9 +0,0 @@ -================ - Policy History -================ - -* `Discussion from Havana Summit `__ -* `Discussion from Icehouse Summit `__ -* `Discussion from Juno Summit `__ -* `I18n team wiki page `__ -* `LoggingStandards wiki page `__ diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst new file mode 100644 index 0000000..99ead69 --- /dev/null +++ b/doc/source/reference/index.rst @@ -0,0 +1,41 @@ +.. _api: + +=============== + oslo.i18n API +=============== + +oslo_i18n +========= + +.. automodule:: oslo_i18n + +.. autoclass:: oslo_i18n.TranslatorFactory + :members: + +.. seealso:: + + An example of using a :class:`TranslatorFactory` is provided in + :ref:`integration-module`. + +.. autofunction:: oslo_i18n.enable_lazy + +.. seealso:: + + :ref:`lazy-translation` + +.. autofunction:: oslo_i18n.translate + +.. autofunction:: oslo_i18n.get_available_languages + +oslo_i18n.log +============= + +.. automodule:: oslo_i18n.log + :members: + +oslo_i18n.fixture +================= + +.. automodule:: oslo_i18n.fixture + :members: + :special-members: diff --git a/doc/source/usage.rst b/doc/source/usage.rst deleted file mode 100644 index e80816e..0000000 --- a/doc/source/usage.rst +++ /dev/null @@ -1,272 +0,0 @@ -===================================================== - How to Use oslo.i18n in Your Application or Library -===================================================== - -Installing -========== - -At the command line:: - - $ pip install oslo.i18n - -.. _integration-module: - -Creating an Integration Module -============================== - -To use oslo.i18n in a project (e.g. myapp), you will need to create a -small integration module to hold an instance of -:class:`~oslo_i18n.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. - -.. code-block:: python - - # 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: - -.. code-block:: python - - 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 - :func:`gettextutils.install` should be replaced with the - application or library integration module described here. - - -Handling hacking Objections to Imports -====================================== - -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 - -.. _hacking: https://pypi.python.org/pypi/hacking - -.. _lazy-translation: - -Lazy Translation -================ - -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 :func:`enable_lazy`. - -:: - - import oslo_i18n - - oslo_i18n.enable_lazy() - -Translating Messages -==================== - -Use :func:`~oslo_i18n.translate` to translate strings to -a specific locale. :func:`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. - -Available Languages -=================== - -Only the languages that have translations provided are available for -translation. To determine which languages are available the -:func:`~oslo_i18n.get_available_languages` is provided. The integration -module provides a domain defined specific function. - -.. code-block:: python - - import myapp._i18n - - languages = myapp._i18n.get_available_languages() - -.. seealso:: - - * :doc:`guidelines` - -Displaying translated messages -============================== - -Several preparations are required to display translated messages in your -running application. - -Preferred language - 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()``. - -Locale directory - Python ``gettext`` looks for binary ``mo`` files for the given domain - using the path ``//LC_MESSAGES/.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 ``_LOCALEDIR`` where ```` 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 ``_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. diff --git a/doc/source/user/guidelines.rst b/doc/source/user/guidelines.rst new file mode 100644 index 0000000..faa0a97 --- /dev/null +++ b/doc/source/user/guidelines.rst @@ -0,0 +1,246 @@ +================================= + Guidelines for Use In OpenStack +================================= + +The OpenStack I18N team has a limited capacity to translate messages, +so we want to make their work as effective as possible by identifying +the most useful text for them to translate. All text messages *the +user sees* via exceptions or API calls should be marked for +translation. However, some exceptions are used internally to signal +error conditions between modules and are not intended to be presented +to the user. Those do not need to be translated. + +.. seealso:: + + * :ref:`usage` + * :ref:`api` + +Gettext Contextual Form and Plural Form +======================================= + +Sometimes under different contexts, the same word should be +translated into different phrases using +:py:attr:`TranslatorFactory.contextual_form `. + +And recommend the following code to use contextual form:: + + # The contextual translation function using the name "_C" + _C = _translators.contextual_form + + ... + msg = _C('context', 'string') + +In some languages, sometimes the translated strings are different +with different item counts using +:py:attr:`TranslatorFactory.plural_form ` + +And recommend the following code to use plural form:: + + # The plural translation function using the name "_P" + _P = _translators.plural_form + + ... + msg = _P('single', 'plural', count) + +The contextual form and plural form are used only when needed. +By default, the translation should use the ``_()``. + +.. note:: + These two functions were only available in oslo.i18n >= 2.1.0. + +Log Translation +=============== + +.. 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. + +OpenStack supports translating some log levels using separate message +catalogs, and so has separate marker functions. These well-known names +are used by the build system jobs that extract the messages from the +source code and pass it to the translation tool. + +========== ========== + Level Function +========== ========== + INFO ``_LI()`` + WARNING ``_LW()`` + ERROR ``_LE()`` + CRITICAL ``_LC()`` +========== ========== + +.. note:: + * Debug level log messages are not translated. + * LOG.exception creates an ERROR level log, so when a marker function is + used (see below) ``_LE()`` should be used. + + +Using a Marker Function +======================= +The marker functions are used to mark the translatable strings in the +code. The strings are extracted into catalogs using a tool that +looks for these specific markers, so the function argument must just +be a string. + +For example: **do not do this**:: + + # WRONG + msg = _(variable_containing_msg) + w_msg = _LW(variable_warning_msg) + +Instead, use this style:: + + # RIGHT + msg = _('My message.') + w_msg = _LW('My warning message') + + +Choosing a Marker Function +========================== + +The purpose of the different marker functions is to separate the +translatable messages into different catalogs, which the translation +teams can prioritize translating. It is important to choose the right +marker function, to ensure that strings the user sees will be +translated and to help the translation team manage their work load. + +Everything marked with ``_()`` will be translated. Prioritizing the +catalogs created from strings marked with the log marker functions is +up to the individual translation teams and their users, but it is +expected that they will work on critical and error messages before +warning or info. + +``_()`` is preferred for any user facing message, even if it is also +going to a log file. This ensures that the translated version of the +message will be available to the user. + +The log marker functions (``_LI()``, ``_LW()``, ``_LE()``, and ``_LC()``) +must only be used when the message is only sent directly to the log. +Anytime that the message will be passed outside of the current context +(for example as part of an exception) the ``_()`` marker function +must be used. + +A common pattern is to define a single message object and use it more +than once, for the log call and the exception. In that case, ``_()`` +must be used because the message is going to appear in an exception that +may be presented to the user. + +For example, **do not do this**:: + + # WRONG + msg = _LE('There was an error.') + LOG.exception(msg) + raise LocalExceptionClass(msg) + +Instead, use this style:: + + # RIGHT + msg = _('There was an error.') + LOG.exception(msg) + raise LocalExceptionClass(msg) + +Except in the case above, ``_()`` should not be used for translating +log messages. This avoids having the same string in two message +catalogs, possibly translated differently by two different +translators. The log message will translate properly because when +the message is not found in the log specific catalog the ``_()`` +catalog will be used. + +If a common message is not being used, they should each be treated +separately with respect to choosing a marker function. + +For example, **do not do this**:: + + # WRONG + LOG.exception(_('There was an error.')) + raise LocalExceptionClass(_('An error occurred.')) + +Instead, use this style:: + + # RIGHT + LOG.exception(_LE('There was an error.')) + raise LocalExceptionClass(_('An error occurred.')) + + +Adding Variables to Translated Messages +======================================= + +Translated messages should not be combined with other literal strings +to create partially translated messages. For example, **do not do +this**:: + + # WRONG + raise ValueError(_('some message') + ': variable=%s' % variable) + +Instead, use this style:: + + # RIGHT + raise ValueError(_('some message: variable=%s') % variable) + +Including the variable reference inside the translated message allows +the translator to take into account grammar rules, differences in +left-right vs. right-left rendering, and other factors to make the +translated message more useful to the end user. + +Any message with more than one variable should use named interpolation +instead of positional, to allow translators to move the variables +around in the string to account for differences in grammar and writing +direction. + +For example, **do not do this**:: + + # WRONG + raise ValueError(_('some message: v1=%s v2=%s') % (v1, v2)) + +Instead, use this style:: + + # RIGHT + raise ValueError(_('some message: v1=%(v1)s v2=%(v2)s') % {'v1': v1, 'v2': v2}) + + +Adding Variables to Log Messages +================================ + +String interpolation should be delayed to be handled by the logging +code, rather than being done at the point of the logging call. For +example, **do not do this**:: + + # WRONG + LOG.info(_LI('some message: variable=%s') % variable) + +Instead, use this style:: + + # RIGHT + LOG.info(_LI('some message: variable=%s'), variable) + +This allows the logging package to skip creating the formatted log +message if the message is not going to be emitted because of the +current log level. + +Avoid Forcing the Translation of Translatable Variables +======================================================= + +Translation can also be delayed for variables that potentially contain +translatable objects such as exceptions. + +Whenever possible translation should not be forced by use of :func:`str`, +:func:`unicode`, or :func:`six.text_type` on a message being used with +a format string. + +For example, **do not do this**:: + + # WRONG + LOG.info(_LI('some message: exception=%s'), six.text_type(exc)) + +Instead, use this style:: + + # RIGHT + LOG.info(_LI('some message: exception=%s'), exc) + +This allows the translation of the translatable replacement text to be +delayed until the message is translated. diff --git a/doc/source/user/history.rst b/doc/source/user/history.rst new file mode 100644 index 0000000..f69be70 --- /dev/null +++ b/doc/source/user/history.rst @@ -0,0 +1 @@ +.. include:: ../../../ChangeLog diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst new file mode 100644 index 0000000..b8745ec --- /dev/null +++ b/doc/source/user/index.rst @@ -0,0 +1,11 @@ +================= + Using oslo.i18n +================= + +.. toctree:: + :maxdepth: 2 + + usage + guidelines + history + diff --git a/doc/source/user/usage.rst b/doc/source/user/usage.rst new file mode 100644 index 0000000..d26acd1 --- /dev/null +++ b/doc/source/user/usage.rst @@ -0,0 +1,274 @@ +.. _usage: + +===================================================== + How to Use oslo.i18n in Your Application or Library +===================================================== + +Installing +========== + +At the command line:: + + $ pip install oslo.i18n + +.. _integration-module: + +Creating an Integration Module +============================== + +To use oslo.i18n in a project (e.g. myapp), you will need to create a +small integration module to hold an instance of +:class:`~oslo_i18n.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. + +.. code-block:: python + + # 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: + +.. code-block:: python + + 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 + :func:`gettextutils.install` should be replaced with the + application or library integration module described here. + + +Handling hacking Objections to Imports +====================================== + +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 + +.. _hacking: https://pypi.python.org/pypi/hacking + +.. _lazy-translation: + +Lazy Translation +================ + +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 :func:`enable_lazy`. + +:: + + import oslo_i18n + + oslo_i18n.enable_lazy() + +Translating Messages +==================== + +Use :func:`~oslo_i18n.translate` to translate strings to +a specific locale. :func:`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. + +Available Languages +=================== + +Only the languages that have translations provided are available for +translation. To determine which languages are available the +:func:`~oslo_i18n.get_available_languages` is provided. The integration +module provides a domain defined specific function. + +.. code-block:: python + + import myapp._i18n + + languages = myapp._i18n.get_available_languages() + +.. seealso:: + + * :doc:`guidelines` + +Displaying translated messages +============================== + +Several preparations are required to display translated messages in your +running application. + +Preferred language + 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()``. + +Locale directory + Python ``gettext`` looks for binary ``mo`` files for the given domain + using the path ``//LC_MESSAGES/.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 ``_LOCALEDIR`` where ```` 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 ``_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. -- cgit v1.2.1