import copy
import gettext
-import logging.handlers
+import logging
import os
import re
-import UserString
+try:
+ import UserString as _userString
+except ImportError:
+ import collections as _userString
from babel import localedata
import six
_localedir = os.environ.get('cinder'.upper() + '_LOCALEDIR')
_t = gettext.translation('cinder', localedir=_localedir, fallback=True)
-_AVAILABLE_LANGUAGES = []
+_AVAILABLE_LANGUAGES = {}
+USE_LAZY = False
+
+
+def enable_lazy():
+ """Convenience function for configuring _() to use lazy gettext
+
+ Call this at the start of execution to enable the gettextutils._
+ function to use lazy gettext functionality. This is useful if
+ your project is importing _ directly instead of using the
+ gettextutils.install() way of importing the _ function.
+ """
+ global USE_LAZY
+ USE_LAZY = True
def _(msg):
- return _t.ugettext(msg)
+ if USE_LAZY:
+ return Message(msg, 'cinder')
+ else:
+ return _t.ugettext(msg)
def install(domain, lazy=False):
unicode=True)
-class Message(UserString.UserString, object):
+class Message(_userString.UserString, object):
"""Class used to encapsulate translatable messages."""
def __init__(self, msg, domain):
# _msg is the gettext msgid and should never change
self._msg = msg
self._left_extra_msg = ''
self._right_extra_msg = ''
+ self._locale = None
self.params = None
- self.locale = None
self.domain = domain
@property
return six.text_type(full_msg)
+ @property
+ def locale(self):
+ return self._locale
+
+ @locale.setter
+ def locale(self, value):
+ self._locale = value
+ if not self.params:
+ return
+
+ # This Message object may have been constructed with one or more
+ # Message objects as substitution parameters, given as a single
+ # Message, or a tuple or Map containing some, so when setting the
+ # locale for this Message we need to set it for those Messages too.
+ if isinstance(self.params, Message):
+ self.params.locale = value
+ return
+ if isinstance(self.params, tuple):
+ for param in self.params:
+ if isinstance(param, Message):
+ param.locale = value
+ return
+ for param in self.params.values():
+ if isinstance(param, Message):
+ param.locale = value
+
def _save_dictionary_parameter(self, dict_param):
full_msg = self.data
# look for %(blah) fields in string;
def __getstate__(self):
to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg',
- 'domain', 'params', 'locale']
+ 'domain', 'params', '_locale']
new_dict = self.__dict__.fromkeys(to_copy)
for attr in to_copy:
new_dict[attr] = copy.deepcopy(self.__dict__[attr])
if name in ops:
return getattr(self.data, name)
else:
- return UserString.UserString.__getattribute__(self, name)
+ return _userString.UserString.__getattribute__(self, name)
def get_available_languages(domain):
:param domain: the domain to get languages for
"""
- if _AVAILABLE_LANGUAGES:
- return _AVAILABLE_LANGUAGES
+ if domain in _AVAILABLE_LANGUAGES:
+ return copy.copy(_AVAILABLE_LANGUAGES[domain])
localedir = '%s_LOCALEDIR' % domain.upper()
find = lambda x: gettext.find(domain,
# NOTE(mrodden): en_US should always be available (and first in case
# order matters) since our in-line message strings are en_US
- _AVAILABLE_LANGUAGES.append('en_US')
+ language_list = ['en_US']
# NOTE(luisg): Babel <1.0 used a function called list(), which was
# renamed to locale_identifiers() in >=1.0, the requirements master list
# requires >=0.9.6, uncapped, so defensively work with both. We can remove
locale_identifiers = list_identifiers()
for i in locale_identifiers:
if find(i) is not None:
- _AVAILABLE_LANGUAGES.append(i)
- return _AVAILABLE_LANGUAGES
+ language_list.append(i)
+ _AVAILABLE_LANGUAGES[domain] = language_list
+ return copy.copy(language_list)
def get_localized_message(message, user_locale):
"""Gets a localized version of the given message in the given locale."""
- if (isinstance(message, Message)):
+ if isinstance(message, Message):
if user_locale:
message.locale = user_locale
return unicode(message)
from xml.dom import minidom
+import gettext
+import mock
import webob.dec
import webob.exc
from cinder.api import common
from cinder.api.openstack import wsgi
+from cinder import exception
from cinder.openstack.common import gettextutils
from cinder.openstack.common import jsonutils
from cinder import test
self.assertNotIn('resizeNotAllowed', resp.body)
self.assertIn('forbidden', resp.body)
- def test_raise_localized_explanation(self):
+ def test_raise_http_with_localized_explanation(self):
params = ('blah', )
expl = gettextutils.Message("String with params: %s" % params, 'test')
self.assertIn(("Mensaje traducido"), resp.body)
self.stubs.UnsetAll()
+ @mock.patch('cinder.openstack.common.gettextutils.gettext.translation')
+ def test_raise_invalid_with_localized_explanation(self, mock_translation):
+ msg_template = gettextutils.Message("Invalid input: %(reason)s", "")
+ reason = gettextutils.Message("Value is invalid", "")
+
+ class MockESTranslations(gettext.GNUTranslations):
+ def ugettext(self, msgid):
+ if "Invalid input" in msgid:
+ return "Entrada invalida: %(reason)s"
+ elif "Value is invalid" in msgid:
+ return "El valor es invalido"
+ return msgid
+
+ def translation(domain, localedir=None, languages=None, fallback=None):
+ return MockESTranslations()
+
+ mock_translation.side_effect = translation
+
+ @webob.dec.wsgify
+ def raiser(req):
+ class MyInvalidInput(exception.InvalidInput):
+ message = msg_template
+
+ ex = MyInvalidInput(reason=reason)
+ raise wsgi.Fault(exception.ConvertedException(code=ex.code,
+ explanation=ex.msg))
+
+ req = webob.Request.blank("/.json")
+ resp = req.get_response(raiser)
+ self.assertEqual(resp.content_type, "application/json")
+ self.assertEqual(resp.status_int, 400)
+ # This response was comprised of Message objects from two different
+ # exceptions, here we are testing that both got translated
+ self.assertIn("Entrada invalida: El valor es invalido", resp.body)
+
def test_fault_has_status_int(self):
"""Ensure the status_int is set correctly on faults"""
fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='what?'))