diff options
-rw-r--r-- | oslo_i18n/_message.py | 20 | ||||
-rw-r--r-- | oslo_i18n/tests/test_message.py | 15 |
2 files changed, 35 insertions, 0 deletions
diff --git a/oslo_i18n/_message.py b/oslo_i18n/_message.py index 0bd83c5..6ea94bc 100644 --- a/oslo_i18n/_message.py +++ b/oslo_i18n/_message.py @@ -78,6 +78,26 @@ class Message(six.text_type): :returns: the translated message in unicode """ + # We did a bad thing here. We shadowed the unicode built-in translate, + # which means there are circumstances where this function may be called + # with a desired_locale that is a non-string sequence or mapping type. + # This will not only result in incorrect behavior, it also fails + # because things like lists are not hashable, and we use the value in + # desired_locale as part of a dict key. If we see a non-string + # desired_locale, we know that the caller did not intend to call this + # form of translate and we should instead pass that along to the + # unicode implementation of translate. + # + # Unfortunately this doesn't entirely solve the problem as it would be + # possible for a caller to use a string as the mapping type and in that + # case we can't tell which version of translate they intended to call. + # That doesn't seem to be a common thing to do though. str.maketrans + # returns a dict, and that is probably the way most callers will create + # their mapping. + if (desired_locale is not None and + not isinstance(desired_locale, six.string_types)): + return super(Message, self).translate(desired_locale) + translated_message = Message._translate_msgid(self.msgid, self.domain, desired_locale, diff --git a/oslo_i18n/tests/test_message.py b/oslo_i18n/tests/test_message.py index 559e5d0..629238a 100644 --- a/oslo_i18n/tests/test_message.py +++ b/oslo_i18n/tests/test_message.py @@ -616,6 +616,21 @@ class MessageTestCase(test_base.BaseTestCase): self.assertEqual(zh_translation, msg.translate('zh')) self.assertEqual(fr_translation, msg.translate('fr')) + def test_translate_with_dict(self): + msg = _message.Message('abc') + # This dict is what you get back from str.maketrans('abc', 'xyz') + # We can't actually call that here because it doesn't exist on py2 + # and the string.maketrans that does behaves differently. + self.assertEqual('xyz', msg.translate({97: 120, 98: 121, 99: 122})) + + def test_translate_with_list(self): + msg = _message.Message('abc') + table = [six.unichr(x) for x in range(128)] + table[ord('a')] = 'b' + table[ord('b')] = 'c' + table[ord('c')] = 'd' + self.assertEqual('bcd', msg.translate(table)) + class TranslateMsgidTest(test_base.BaseTestCase): |