Move out of the oslo namespace package
Move the public API out of oslo.i18n to oslo_i18n. Retain the ability to import from the old namespace package for backwards compatibility for this release cycle. bp/drop-namespace-packages Change-Id: I800f121c271d8e69f6e776c4aef509bbb8008170
This commit is contained in:
parent
53635eae0f
commit
ba05e9a9b9
@ -2,12 +2,12 @@
|
|||||||
API
|
API
|
||||||
=====
|
=====
|
||||||
|
|
||||||
oslo.i18n
|
oslo_i18n
|
||||||
=========
|
=========
|
||||||
|
|
||||||
.. automodule:: oslo.i18n
|
.. automodule:: oslo_i18n
|
||||||
|
|
||||||
.. autoclass:: oslo.i18n.TranslatorFactory
|
.. autoclass:: oslo_i18n.TranslatorFactory
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
@ -15,24 +15,24 @@ oslo.i18n
|
|||||||
An example of using a :class:`TranslatorFactory` is provided in
|
An example of using a :class:`TranslatorFactory` is provided in
|
||||||
:ref:`integration-module`.
|
:ref:`integration-module`.
|
||||||
|
|
||||||
.. autofunction:: oslo.i18n.enable_lazy
|
.. autofunction:: oslo_i18n.enable_lazy
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
:ref:`lazy-translation`
|
:ref:`lazy-translation`
|
||||||
|
|
||||||
.. autofunction:: oslo.i18n.translate
|
.. autofunction:: oslo_i18n.translate
|
||||||
|
|
||||||
.. autofunction:: oslo.i18n.get_available_languages
|
.. autofunction:: oslo_i18n.get_available_languages
|
||||||
|
|
||||||
oslo.i18n.log
|
oslo_i18n.log
|
||||||
=============
|
=============
|
||||||
|
|
||||||
.. automodule:: oslo.i18n.log
|
.. automodule:: oslo_i18n.log
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
oslo.i18n.fixture
|
oslo_i18n.fixture
|
||||||
=================
|
=================
|
||||||
|
|
||||||
.. automodule:: oslo.i18n.fixture
|
.. automodule:: oslo_i18n.fixture
|
||||||
:members:
|
:members:
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Text messages the user sees via exceptions or API calls should be
|
Text messages the user sees via exceptions or API calls should be
|
||||||
translated using
|
translated using
|
||||||
:py:attr:`TranslatorFactory.primary <oslo.i18n.TranslatorFactory.primary>`, which should
|
:py:attr:`TranslatorFactory.primary <oslo_i18n.TranslatorFactory.primary>`, which should
|
||||||
be installed as ``_()`` in the integration module.
|
be installed as ``_()`` in the integration module.
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
@ -16,16 +16,16 @@ Creating an Integration Module
|
|||||||
|
|
||||||
To use oslo.i18n in a project, you will need to create a small
|
To use oslo.i18n in a project, you will need to create a small
|
||||||
integration module to hold an instance of
|
integration module to hold an instance of
|
||||||
:class:`~oslo.i18n.TranslatorFactory` and references to
|
:class:`~oslo_i18n.TranslatorFactory` and references to
|
||||||
the marker functions the factory creates.
|
the marker functions the factory creates.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
# app/i18n.py
|
# app/i18n.py
|
||||||
|
|
||||||
from oslo import i18n
|
import oslo_i18n
|
||||||
|
|
||||||
_translators = i18n.TranslatorFactory(domain='myapp')
|
_translators = oslo_i18n.TranslatorFactory(domain='myapp')
|
||||||
|
|
||||||
# The primary translation function using the well-known name "_"
|
# The primary translation function using the well-known name "_"
|
||||||
_ = _translators.primary
|
_ = _translators.primary
|
||||||
@ -151,14 +151,14 @@ To enable lazy translation, call :func:`enable_lazy`.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
from oslo import i18n
|
import oslo_i18n
|
||||||
|
|
||||||
i18n.enable_lazy()
|
oslo_i18n.enable_lazy()
|
||||||
|
|
||||||
Translating Messages
|
Translating Messages
|
||||||
====================
|
====================
|
||||||
|
|
||||||
Use :func:`~oslo.i18n.translate` to translate strings to
|
Use :func:`~oslo_i18n.translate` to translate strings to
|
||||||
a specific locale. :func:`translate` handles delayed translation and
|
a specific locale. :func:`translate` handles delayed translation and
|
||||||
strings that have already been translated immediately. It should be
|
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
|
used at the point where the locale to be used is known, which is often
|
||||||
@ -167,9 +167,9 @@ emitted.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
from oslo import i18n
|
import oslo_i18n
|
||||||
|
|
||||||
trans_msg = i18n.translate(msg, desired_locale=my_locale)
|
trans_msg = oslo_i18n.translate(msg, desired_locale=my_locale)
|
||||||
|
|
||||||
if desired_locale is not specified then the default locale is used.
|
if desired_locale is not specified then the default locale is used.
|
||||||
|
|
||||||
@ -178,14 +178,14 @@ Available Languages
|
|||||||
|
|
||||||
Only the languages that have translations provided are available for
|
Only the languages that have translations provided are available for
|
||||||
translation. To determine which languages are available the
|
translation. To determine which languages are available the
|
||||||
:func:`~oslo.i18n.get_available_languages` is provided. Since different languages
|
:func:`~oslo_i18n.get_available_languages` is provided. Since different languages
|
||||||
can be installed for each domain, the domain must be specified.
|
can be installed for each domain, the domain must be specified.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
from oslo import i18n
|
import oslo_i18n
|
||||||
|
|
||||||
avail_lang = i18n.get_available_languages('myapp')
|
avail_lang = oslo_i18n.get_available_languages('myapp')
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
|
@ -10,7 +10,22 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from ._factory import *
|
import warnings
|
||||||
from ._gettextutils import *
|
|
||||||
from ._lazy import *
|
from oslo_i18n._factory import *
|
||||||
from ._translate import *
|
from oslo_i18n._gettextutils import *
|
||||||
|
from oslo_i18n._lazy import *
|
||||||
|
from oslo_i18n._translate import *
|
||||||
|
|
||||||
|
|
||||||
|
def deprecated():
|
||||||
|
new_name = __name__.replace('.', '_')
|
||||||
|
warnings.warn(
|
||||||
|
('The oslo namespace package is deprecated. Please use %s instead.' %
|
||||||
|
new_name),
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=3,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
deprecated()
|
||||||
|
@ -9,57 +9,5 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
"""Test fixtures for working with oslo.i18n.
|
|
||||||
|
|
||||||
"""
|
from oslo_i18n.fixture import * # noqa
|
||||||
|
|
||||||
import fixtures
|
|
||||||
import six
|
|
||||||
|
|
||||||
from oslo.i18n import _message
|
|
||||||
|
|
||||||
|
|
||||||
class Translation(fixtures.Fixture):
|
|
||||||
"""Fixture for managing translatable strings.
|
|
||||||
|
|
||||||
This class provides methods for creating translatable strings
|
|
||||||
using both lazy translation and immediate translation. It can be
|
|
||||||
used to generate the different types of messages returned from
|
|
||||||
oslo.i18n to test code that may need to know about the type to
|
|
||||||
handle them differently (for example, error handling in WSGI apps,
|
|
||||||
or logging).
|
|
||||||
|
|
||||||
Use this class to generate messages instead of toggling the global
|
|
||||||
lazy flag and using the regular translation factory.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, domain='test-domain'):
|
|
||||||
"""Initialize the fixture.
|
|
||||||
|
|
||||||
:param domain: The translation domain. This is not expected to
|
|
||||||
coincide with an actual set of message
|
|
||||||
catalogs, but it can.
|
|
||||||
:type domain: str
|
|
||||||
"""
|
|
||||||
self.domain = domain
|
|
||||||
|
|
||||||
def lazy(self, msg):
|
|
||||||
"""Return a lazily translated message.
|
|
||||||
|
|
||||||
:param msg: Input message string. May optionally include
|
|
||||||
positional or named string interpolation markers.
|
|
||||||
:type msg: str or unicode
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _message.Message(msg, domain=self.domain)
|
|
||||||
|
|
||||||
def immediate(self, msg):
|
|
||||||
"""Return a string as though it had been translated immediately.
|
|
||||||
|
|
||||||
:param msg: Input message string. May optionally include
|
|
||||||
positional or named string interpolation markers.
|
|
||||||
:type msg: str or unicode
|
|
||||||
|
|
||||||
"""
|
|
||||||
return six.text_type(msg)
|
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
# Copyright 2012 Red Hat, Inc.
|
|
||||||
# Copyright 2013 IBM Corp.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
# not use this file except in compliance with the License. You may obtain
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
@ -14,84 +10,4 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""logging utilities for translation
|
from oslo_i18n.log import * # noqa
|
||||||
"""
|
|
||||||
|
|
||||||
from logging import handlers
|
|
||||||
|
|
||||||
from oslo.i18n import _translate
|
|
||||||
|
|
||||||
|
|
||||||
class TranslationHandler(handlers.MemoryHandler):
|
|
||||||
"""Handler that translates records before logging them.
|
|
||||||
|
|
||||||
When lazy translation is enabled in the application (see
|
|
||||||
:func:`~oslo.i18n.enable_lazy`), the :class:`TranslationHandler`
|
|
||||||
uses its locale configuration setting to determine how to
|
|
||||||
translate LogRecord objects before forwarding them to the
|
|
||||||
logging.Handler.
|
|
||||||
|
|
||||||
When lazy translation is disabled, the message in the LogRecord is
|
|
||||||
converted to unicode without any changes and then forwarded to the
|
|
||||||
logging.Handler.
|
|
||||||
|
|
||||||
The handler can be configured declaratively in the
|
|
||||||
``logging.conf`` as follows::
|
|
||||||
|
|
||||||
[handlers]
|
|
||||||
keys = translatedlog, translator
|
|
||||||
|
|
||||||
[handler_translatedlog]
|
|
||||||
class = handlers.WatchedFileHandler
|
|
||||||
args = ('/var/log/api-localized.log',)
|
|
||||||
formatter = context
|
|
||||||
|
|
||||||
[handler_translator]
|
|
||||||
class = oslo.i18n.log.TranslationHandler
|
|
||||||
target = translatedlog
|
|
||||||
args = ('zh_CN',)
|
|
||||||
|
|
||||||
If the specified locale is not available in the system, the handler will
|
|
||||||
log in the default locale.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, locale=None, target=None):
|
|
||||||
"""Initialize a TranslationHandler
|
|
||||||
|
|
||||||
:param locale: locale to use for translating messages
|
|
||||||
:param target: logging.Handler object to forward
|
|
||||||
LogRecord objects to after translation
|
|
||||||
"""
|
|
||||||
# NOTE(luisg): In order to allow this handler to be a wrapper for
|
|
||||||
# other handlers, such as a FileHandler, and still be able to
|
|
||||||
# configure it using logging.conf, this handler has to extend
|
|
||||||
# MemoryHandler because only the MemoryHandlers' logging.conf
|
|
||||||
# parsing is implemented such that it accepts a target handler.
|
|
||||||
handlers.MemoryHandler.__init__(self, capacity=0, target=target)
|
|
||||||
self.locale = locale
|
|
||||||
|
|
||||||
def setFormatter(self, fmt):
|
|
||||||
self.target.setFormatter(fmt)
|
|
||||||
|
|
||||||
def emit(self, record):
|
|
||||||
# We save the message from the original record to restore it
|
|
||||||
# after translation, so other handlers are not affected by this
|
|
||||||
original_msg = record.msg
|
|
||||||
original_args = record.args
|
|
||||||
|
|
||||||
try:
|
|
||||||
self._translate_and_log_record(record)
|
|
||||||
finally:
|
|
||||||
record.msg = original_msg
|
|
||||||
record.args = original_args
|
|
||||||
|
|
||||||
def _translate_and_log_record(self, record):
|
|
||||||
record.msg = _translate.translate(record.msg, self.locale)
|
|
||||||
|
|
||||||
# In addition to translating the message, we also need to translate
|
|
||||||
# arguments that were passed to the log method that were not part
|
|
||||||
# of the main message e.g., log.info(_('Some message %s'), this_one))
|
|
||||||
record.args = _translate.translate_args(record.args, self.locale)
|
|
||||||
|
|
||||||
self.target.emit(record)
|
|
||||||
|
16
oslo_i18n/__init__.py
Normal file
16
oslo_i18n/__init__.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from ._factory import *
|
||||||
|
from ._gettextutils import *
|
||||||
|
from ._lazy import *
|
||||||
|
from ._translate import *
|
@ -21,9 +21,9 @@ import os
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.i18n import _lazy
|
from oslo_i18n import _lazy
|
||||||
from oslo.i18n import _locale
|
from oslo_i18n import _locale
|
||||||
from oslo.i18n import _message
|
from oslo_i18n import _message
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -75,7 +75,7 @@ class TranslatorFactory(object):
|
|||||||
m = t.gettext if six.PY3 else t.ugettext
|
m = t.gettext if six.PY3 else t.ugettext
|
||||||
|
|
||||||
def f(msg):
|
def f(msg):
|
||||||
"""oslo.i18n.gettextutils translation function."""
|
"""oslo_i18n.gettextutils translation function."""
|
||||||
if _lazy.USE_LAZY:
|
if _lazy.USE_LAZY:
|
||||||
return _message.Message(msg, domain=domain)
|
return _message.Message(msg, domain=domain)
|
||||||
return m(msg)
|
return m(msg)
|
@ -24,8 +24,8 @@ import os
|
|||||||
from babel import localedata
|
from babel import localedata
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.i18n import _factory
|
from oslo_i18n import _factory
|
||||||
from oslo.i18n import _locale
|
from oslo_i18n import _locale
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'install',
|
'install',
|
@ -16,10 +16,10 @@
|
|||||||
"""Translation support for messages in this library.
|
"""Translation support for messages in this library.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from oslo.i18n import _factory
|
from oslo_i18n import _factory
|
||||||
|
|
||||||
# Create the global translation functions.
|
# Create the global translation functions.
|
||||||
_translators = _factory.TranslatorFactory('oslo.i18n')
|
_translators = _factory.TranslatorFactory('oslo_i18n')
|
||||||
|
|
||||||
# The primary translation function using the well-known name "_"
|
# The primary translation function using the well-known name "_"
|
||||||
_ = _translators.primary
|
_ = _translators.primary
|
@ -23,8 +23,8 @@ import os
|
|||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.i18n import _locale
|
from oslo_i18n import _locale
|
||||||
from oslo.i18n import _translate
|
from oslo_i18n import _translate
|
||||||
|
|
||||||
|
|
||||||
class Message(six.text_type):
|
class Message(six.text_type):
|
||||||
@ -149,7 +149,7 @@ class Message(six.text_type):
|
|||||||
return six.text_type(param)
|
return six.text_type(param)
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
from oslo.i18n._i18n import _
|
from oslo_i18n._i18n import _
|
||||||
msg = _('Message objects do not support addition.')
|
msg = _('Message objects do not support addition.')
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ class Message(six.text_type):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
# NOTE(luisg): Logging in python 2.6 tries to str() log records,
|
# NOTE(luisg): Logging in python 2.6 tries to str() log records,
|
||||||
# and it expects specifically a UnicodeError in order to proceed.
|
# and it expects specifically a UnicodeError in order to proceed.
|
||||||
from oslo.i18n._i18n import _
|
from oslo_i18n._i18n import _
|
||||||
msg = _('Message objects do not support str() because they may '
|
msg = _('Message objects do not support str() because they may '
|
||||||
'contain non-ascii characters. '
|
'contain non-ascii characters. '
|
||||||
'Please use unicode() or translate() instead.')
|
'Please use unicode() or translate() instead.')
|
@ -36,7 +36,7 @@ def translate(obj, desired_locale=None):
|
|||||||
it could not be translated
|
it could not be translated
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from oslo.i18n import _message # avoid circular dependency at module level
|
from oslo_i18n import _message # avoid circular dependency at module level
|
||||||
message = obj
|
message = obj
|
||||||
if not isinstance(message, _message.Message):
|
if not isinstance(message, _message.Message):
|
||||||
# If the object to translate is not already translatable,
|
# If the object to translate is not already translatable,
|
65
oslo_i18n/fixture.py
Normal file
65
oslo_i18n/fixture.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
"""Test fixtures for working with oslo_i18n.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import fixtures
|
||||||
|
import six
|
||||||
|
|
||||||
|
from oslo_i18n import _message
|
||||||
|
|
||||||
|
|
||||||
|
class Translation(fixtures.Fixture):
|
||||||
|
"""Fixture for managing translatable strings.
|
||||||
|
|
||||||
|
This class provides methods for creating translatable strings
|
||||||
|
using both lazy translation and immediate translation. It can be
|
||||||
|
used to generate the different types of messages returned from
|
||||||
|
oslo_i18n to test code that may need to know about the type to
|
||||||
|
handle them differently (for example, error handling in WSGI apps,
|
||||||
|
or logging).
|
||||||
|
|
||||||
|
Use this class to generate messages instead of toggling the global
|
||||||
|
lazy flag and using the regular translation factory.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, domain='test-domain'):
|
||||||
|
"""Initialize the fixture.
|
||||||
|
|
||||||
|
:param domain: The translation domain. This is not expected to
|
||||||
|
coincide with an actual set of message
|
||||||
|
catalogs, but it can.
|
||||||
|
:type domain: str
|
||||||
|
"""
|
||||||
|
self.domain = domain
|
||||||
|
|
||||||
|
def lazy(self, msg):
|
||||||
|
"""Return a lazily translated message.
|
||||||
|
|
||||||
|
:param msg: Input message string. May optionally include
|
||||||
|
positional or named string interpolation markers.
|
||||||
|
:type msg: str or unicode
|
||||||
|
|
||||||
|
"""
|
||||||
|
return _message.Message(msg, domain=self.domain)
|
||||||
|
|
||||||
|
def immediate(self, msg):
|
||||||
|
"""Return a string as though it had been translated immediately.
|
||||||
|
|
||||||
|
:param msg: Input message string. May optionally include
|
||||||
|
positional or named string interpolation markers.
|
||||||
|
:type msg: str or unicode
|
||||||
|
|
||||||
|
"""
|
||||||
|
return six.text_type(msg)
|
97
oslo_i18n/log.py
Normal file
97
oslo_i18n/log.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Copyright 2012 Red Hat, Inc.
|
||||||
|
# Copyright 2013 IBM Corp.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""logging utilities for translation
|
||||||
|
"""
|
||||||
|
|
||||||
|
from logging import handlers
|
||||||
|
|
||||||
|
from oslo_i18n import _translate
|
||||||
|
|
||||||
|
|
||||||
|
class TranslationHandler(handlers.MemoryHandler):
|
||||||
|
"""Handler that translates records before logging them.
|
||||||
|
|
||||||
|
When lazy translation is enabled in the application (see
|
||||||
|
:func:`~oslo_i18n.enable_lazy`), the :class:`TranslationHandler`
|
||||||
|
uses its locale configuration setting to determine how to
|
||||||
|
translate LogRecord objects before forwarding them to the
|
||||||
|
logging.Handler.
|
||||||
|
|
||||||
|
When lazy translation is disabled, the message in the LogRecord is
|
||||||
|
converted to unicode without any changes and then forwarded to the
|
||||||
|
logging.Handler.
|
||||||
|
|
||||||
|
The handler can be configured declaratively in the
|
||||||
|
``logging.conf`` as follows::
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = translatedlog, translator
|
||||||
|
|
||||||
|
[handler_translatedlog]
|
||||||
|
class = handlers.WatchedFileHandler
|
||||||
|
args = ('/var/log/api-localized.log',)
|
||||||
|
formatter = context
|
||||||
|
|
||||||
|
[handler_translator]
|
||||||
|
class = oslo_i18n.log.TranslationHandler
|
||||||
|
target = translatedlog
|
||||||
|
args = ('zh_CN',)
|
||||||
|
|
||||||
|
If the specified locale is not available in the system, the handler will
|
||||||
|
log in the default locale.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, locale=None, target=None):
|
||||||
|
"""Initialize a TranslationHandler
|
||||||
|
|
||||||
|
:param locale: locale to use for translating messages
|
||||||
|
:param target: logging.Handler object to forward
|
||||||
|
LogRecord objects to after translation
|
||||||
|
"""
|
||||||
|
# NOTE(luisg): In order to allow this handler to be a wrapper for
|
||||||
|
# other handlers, such as a FileHandler, and still be able to
|
||||||
|
# configure it using logging.conf, this handler has to extend
|
||||||
|
# MemoryHandler because only the MemoryHandlers' logging.conf
|
||||||
|
# parsing is implemented such that it accepts a target handler.
|
||||||
|
handlers.MemoryHandler.__init__(self, capacity=0, target=target)
|
||||||
|
self.locale = locale
|
||||||
|
|
||||||
|
def setFormatter(self, fmt):
|
||||||
|
self.target.setFormatter(fmt)
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
# We save the message from the original record to restore it
|
||||||
|
# after translation, so other handlers are not affected by this
|
||||||
|
original_msg = record.msg
|
||||||
|
original_args = record.args
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._translate_and_log_record(record)
|
||||||
|
finally:
|
||||||
|
record.msg = original_msg
|
||||||
|
record.args = original_args
|
||||||
|
|
||||||
|
def _translate_and_log_record(self, record):
|
||||||
|
record.msg = _translate.translate(record.msg, self.locale)
|
||||||
|
|
||||||
|
# In addition to translating the message, we also need to translate
|
||||||
|
# arguments that were passed to the log method that were not part
|
||||||
|
# of the main message e.g., log.info(_('Some message %s'), this_one))
|
||||||
|
record.args = _translate.translate_args(record.args, self.locale)
|
||||||
|
|
||||||
|
self.target.emit(record)
|
0
oslo_i18n/tests/__init__.py
Normal file
0
oslo_i18n/tests/__init__.py
Normal file
@ -18,9 +18,9 @@ import mock
|
|||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.i18n import _factory
|
from oslo_i18n import _factory
|
||||||
from oslo.i18n import _lazy
|
from oslo_i18n import _lazy
|
||||||
from oslo.i18n import _message
|
from oslo_i18n import _message
|
||||||
|
|
||||||
|
|
||||||
class TranslatorFactoryTest(test_base.BaseTestCase):
|
class TranslatorFactoryTest(test_base.BaseTestCase):
|
38
oslo_i18n/tests/test_fixture.py
Normal file
38
oslo_i18n/tests/test_fixture.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslotest import base as test_base
|
||||||
|
import six
|
||||||
|
|
||||||
|
from oslo_i18n import _message
|
||||||
|
from oslo_i18n import fixture
|
||||||
|
|
||||||
|
|
||||||
|
class FixtureTest(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(FixtureTest, self).setUp()
|
||||||
|
self.trans_fixture = self.useFixture(fixture.Translation())
|
||||||
|
|
||||||
|
def test_lazy(self):
|
||||||
|
msg = self.trans_fixture.lazy('this is a lazy message')
|
||||||
|
self.assertIsInstance(msg, _message.Message)
|
||||||
|
self.assertEqual(msg.msgid, 'this is a lazy message')
|
||||||
|
|
||||||
|
def test_immediate(self):
|
||||||
|
msg = self.trans_fixture.immediate('this is a lazy message')
|
||||||
|
# Python 2.6 does not have assertNotIsInstance
|
||||||
|
self.assertFalse(isinstance(msg, _message.Message))
|
||||||
|
self.assertIsInstance(msg, six.text_type)
|
||||||
|
self.assertEqual(msg, u'this is a lazy message')
|
@ -23,10 +23,10 @@ from oslotest import base as test_base
|
|||||||
from oslotest import moxstubout
|
from oslotest import moxstubout
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.i18n import _factory
|
from oslo_i18n import _factory
|
||||||
from oslo.i18n import _gettextutils
|
from oslo_i18n import _gettextutils
|
||||||
from oslo.i18n import _lazy
|
from oslo_i18n import _lazy
|
||||||
from oslo.i18n import _message
|
from oslo_i18n import _message
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -41,7 +41,7 @@ class GettextTest(test_base.BaseTestCase):
|
|||||||
self.mox = moxfixture.mox
|
self.mox = moxfixture.mox
|
||||||
# remember so we can reset to it later in case it changes
|
# remember so we can reset to it later in case it changes
|
||||||
self._USE_LAZY = _lazy.USE_LAZY
|
self._USE_LAZY = _lazy.USE_LAZY
|
||||||
self.t = _factory.TranslatorFactory('oslo.i18n.test')
|
self.t = _factory.TranslatorFactory('oslo_i18n.test')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
# reset to value before test
|
# reset to value before test
|
106
oslo_i18n/tests/test_handler.py
Normal file
106
oslo_i18n/tests/test_handler.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Copyright 2012 Red Hat, Inc.
|
||||||
|
# Copyright 2013 IBM Corp.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslotest import base as test_base
|
||||||
|
import six
|
||||||
|
|
||||||
|
from oslo_i18n import _message
|
||||||
|
from oslo_i18n import log as i18n_log
|
||||||
|
from oslo_i18n.tests import fakes
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TranslationHandlerTestCase(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TranslationHandlerTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.stream = six.StringIO()
|
||||||
|
self.destination_handler = logging.StreamHandler(self.stream)
|
||||||
|
self.translation_handler = i18n_log.TranslationHandler('zh_CN')
|
||||||
|
self.translation_handler.setTarget(self.destination_handler)
|
||||||
|
|
||||||
|
self.logger = logging.getLogger('localehander_logger')
|
||||||
|
self.logger.setLevel(logging.DEBUG)
|
||||||
|
self.logger.addHandler(self.translation_handler)
|
||||||
|
|
||||||
|
def test_set_formatter(self):
|
||||||
|
formatter = 'some formatter'
|
||||||
|
self.translation_handler.setFormatter(formatter)
|
||||||
|
self.assertEqual(formatter, self.translation_handler.target.formatter)
|
||||||
|
|
||||||
|
@mock.patch('gettext.translation')
|
||||||
|
def test_emit_translated_message(self, mock_translation):
|
||||||
|
log_message = 'A message to be logged'
|
||||||
|
log_message_translation = 'A message to be logged in Chinese'
|
||||||
|
translations = {log_message: log_message_translation}
|
||||||
|
translations_map = {'zh_CN': translations}
|
||||||
|
translator = fakes.FakeTranslations.translator(translations_map)
|
||||||
|
mock_translation.side_effect = translator
|
||||||
|
|
||||||
|
msg = _message.Message(log_message)
|
||||||
|
|
||||||
|
self.logger.info(msg)
|
||||||
|
self.assertIn(log_message_translation, self.stream.getvalue())
|
||||||
|
|
||||||
|
@mock.patch('gettext.translation')
|
||||||
|
def test_emit_translated_message_with_args(self, mock_translation):
|
||||||
|
log_message = 'A message to be logged %s'
|
||||||
|
log_message_translation = 'A message to be logged in Chinese %s'
|
||||||
|
log_arg = 'Arg to be logged'
|
||||||
|
log_arg_translation = 'An arg to be logged in Chinese'
|
||||||
|
|
||||||
|
translations = {log_message: log_message_translation,
|
||||||
|
log_arg: log_arg_translation}
|
||||||
|
translations_map = {'zh_CN': translations}
|
||||||
|
translator = fakes.FakeTranslations.translator(translations_map)
|
||||||
|
mock_translation.side_effect = translator
|
||||||
|
|
||||||
|
msg = _message.Message(log_message)
|
||||||
|
arg = _message.Message(log_arg)
|
||||||
|
|
||||||
|
self.logger.info(msg, arg)
|
||||||
|
self.assertIn(log_message_translation % log_arg_translation,
|
||||||
|
self.stream.getvalue())
|
||||||
|
|
||||||
|
@mock.patch('gettext.translation')
|
||||||
|
def test_emit_translated_message_with_named_args(self, mock_translation):
|
||||||
|
log_message = 'A message to be logged %(arg1)s $(arg2)s'
|
||||||
|
log_message_translation = 'Chinese msg to be logged %(arg1)s $(arg2)s'
|
||||||
|
log_arg_1 = 'Arg1 to be logged'
|
||||||
|
log_arg_1_translation = 'Arg1 to be logged in Chinese'
|
||||||
|
log_arg_2 = 'Arg2 to be logged'
|
||||||
|
log_arg_2_translation = 'Arg2 to be logged in Chinese'
|
||||||
|
|
||||||
|
translations = {log_message: log_message_translation,
|
||||||
|
log_arg_1: log_arg_1_translation,
|
||||||
|
log_arg_2: log_arg_2_translation}
|
||||||
|
translations_map = {'zh_CN': translations}
|
||||||
|
translator = fakes.FakeTranslations.translator(translations_map)
|
||||||
|
mock_translation.side_effect = translator
|
||||||
|
|
||||||
|
msg = _message.Message(log_message)
|
||||||
|
arg_1 = _message.Message(log_arg_1)
|
||||||
|
arg_2 = _message.Message(log_arg_2)
|
||||||
|
|
||||||
|
self.logger.info(msg, {'arg1': arg_1, 'arg2': arg_2})
|
||||||
|
translation = log_message_translation % {'arg1': log_arg_1_translation,
|
||||||
|
'arg2': log_arg_2_translation}
|
||||||
|
self.assertIn(translation, self.stream.getvalue())
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
|
|
||||||
from oslo.i18n import _lazy
|
from oslo_i18n import _lazy
|
||||||
|
|
||||||
|
|
||||||
class LazyTest(test_base.BaseTestCase):
|
class LazyTest(test_base.BaseTestCase):
|
@ -15,7 +15,7 @@
|
|||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
import testscenarios.testcase
|
import testscenarios.testcase
|
||||||
|
|
||||||
from oslo.i18n import _locale
|
from oslo_i18n import _locale
|
||||||
|
|
||||||
|
|
||||||
class LocaleDirVariableTest(testscenarios.testcase.WithScenarios,
|
class LocaleDirVariableTest(testscenarios.testcase.WithScenarios,
|
@ -17,7 +17,7 @@
|
|||||||
import mock
|
import mock
|
||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
|
|
||||||
from oslo.i18n import _factory
|
from oslo_i18n import _factory
|
||||||
|
|
||||||
|
|
||||||
class LogLevelTranslationsTest(test_base.BaseTestCase):
|
class LogLevelTranslationsTest(test_base.BaseTestCase):
|
@ -23,9 +23,9 @@ from oslotest import base as test_base
|
|||||||
import six
|
import six
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from oslo.i18n import _message
|
from oslo_i18n import _message
|
||||||
from tests import fakes
|
from oslo_i18n.tests import fakes
|
||||||
from tests import utils
|
from oslo_i18n.tests import utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
44
oslo_i18n/tests/test_public_api.py
Normal file
44
oslo_i18n/tests/test_public_api.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
"""A few tests that use the public API to ensure the imports work.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
import oslo_i18n
|
||||||
|
from oslo_i18n import _lazy
|
||||||
|
|
||||||
|
|
||||||
|
class PublicAPITest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_create_factory(self):
|
||||||
|
oslo_i18n.TranslatorFactory('domain')
|
||||||
|
|
||||||
|
def test_install(self):
|
||||||
|
with mock.patch('six.moves.builtins'):
|
||||||
|
oslo_i18n.install('domain')
|
||||||
|
|
||||||
|
def test_get_available_languages(self):
|
||||||
|
oslo_i18n.get_available_languages('domains')
|
||||||
|
|
||||||
|
def test_toggle_lazy(self):
|
||||||
|
original = _lazy.USE_LAZY
|
||||||
|
try:
|
||||||
|
oslo_i18n.enable_lazy(True)
|
||||||
|
oslo_i18n.enable_lazy(False)
|
||||||
|
finally:
|
||||||
|
oslo_i18n.enable_lazy(original)
|
||||||
|
|
||||||
|
def test_translate(self):
|
||||||
|
oslo_i18n.translate(u'string')
|
@ -19,10 +19,10 @@ from __future__ import unicode_literals
|
|||||||
import mock
|
import mock
|
||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
|
|
||||||
from oslo.i18n import _message
|
from oslo_i18n import _message
|
||||||
from oslo.i18n import _translate
|
from oslo_i18n import _translate
|
||||||
from tests import fakes
|
from oslo_i18n.tests import fakes
|
||||||
from tests import utils
|
from oslo_i18n.tests import utils
|
||||||
|
|
||||||
|
|
||||||
class TranslateTest(test_base.BaseTestCase):
|
class TranslateTest(test_base.BaseTestCase):
|
@ -22,7 +22,7 @@ classifier =
|
|||||||
[files]
|
[files]
|
||||||
packages =
|
packages =
|
||||||
oslo
|
oslo
|
||||||
oslo.i18n
|
oslo_i18n
|
||||||
namespace_packages =
|
namespace_packages =
|
||||||
oslo
|
oslo
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.i18n import _message
|
|
||||||
from oslo.i18n import fixture
|
from oslo.i18n import fixture
|
||||||
|
from oslo_i18n import _message
|
||||||
|
|
||||||
|
|
||||||
class FixtureTest(test_base.BaseTestCase):
|
class FixtureTest(test_base.BaseTestCase):
|
||||||
|
@ -20,9 +20,9 @@ import mock
|
|||||||
from oslotest import base as test_base
|
from oslotest import base as test_base
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.i18n import _message
|
|
||||||
from oslo.i18n import log as i18n_log
|
from oslo.i18n import log as i18n_log
|
||||||
from tests import fakes
|
from oslo_i18n import _message
|
||||||
|
from oslo_i18n.tests import fakes
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import unittest
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from oslo import i18n
|
from oslo import i18n
|
||||||
from oslo.i18n import _lazy
|
from oslo_i18n import _lazy
|
||||||
|
|
||||||
|
|
||||||
class PublicAPITest(unittest.TestCase):
|
class PublicAPITest(unittest.TestCase):
|
||||||
|
61
tests/test_warning.py
Normal file
61
tests/test_warning.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import imp
|
||||||
|
import os
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslotest import base as test_base
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecationWarningTest(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
@mock.patch('warnings.warn')
|
||||||
|
def test_warning(self, mock_warn):
|
||||||
|
import oslo.i18n
|
||||||
|
imp.reload(oslo.i18n)
|
||||||
|
self.assertTrue(mock_warn.called)
|
||||||
|
args = mock_warn.call_args
|
||||||
|
self.assertIn('oslo_i18n', args[0][0])
|
||||||
|
self.assertIn('deprecated', args[0][0])
|
||||||
|
self.assertTrue(issubclass(args[0][1], DeprecationWarning))
|
||||||
|
|
||||||
|
def test_real_warning(self):
|
||||||
|
with warnings.catch_warnings(record=True) as warning_msgs:
|
||||||
|
warnings.resetwarnings()
|
||||||
|
warnings.simplefilter('always', DeprecationWarning)
|
||||||
|
import oslo.i18n
|
||||||
|
|
||||||
|
# Use a separate function to get the stack level correct
|
||||||
|
# so we know the message points back to this file. This
|
||||||
|
# corresponds to an import or reload, which isn't working
|
||||||
|
# inside the test under Python 3.3. That may be due to a
|
||||||
|
# difference in the import implementation not triggering
|
||||||
|
# warnings properly when the module is reloaded, or
|
||||||
|
# because the warnings module is mostly implemented in C
|
||||||
|
# and something isn't cleanly resetting the global state
|
||||||
|
# used to track whether a warning needs to be
|
||||||
|
# emitted. Whatever the cause, we definitely see the
|
||||||
|
# warnings.warn() being invoked on a reload (see the test
|
||||||
|
# above) and warnings are reported on the console when we
|
||||||
|
# run the tests. A simpler test script run outside of
|
||||||
|
# testr does correctly report the warnings.
|
||||||
|
def foo():
|
||||||
|
oslo.i18n.deprecated()
|
||||||
|
|
||||||
|
foo()
|
||||||
|
self.assertEqual(1, len(warning_msgs))
|
||||||
|
msg = warning_msgs[0]
|
||||||
|
self.assertIn('oslo_i18n', six.text_type(msg.message))
|
||||||
|
self.assertEqual('test_warning.py', os.path.basename(msg.filename))
|
Loading…
x
Reference in New Issue
Block a user