jenkins-bot has submitted this change and it was merged.
Change subject: Allow multiple "{{PLURAL}}" with i18n.twntranslate method ......................................................................
Allow multiple "{{PLURAL}}" with i18n.twntranslate method
avoid duplicate code by extracting parts from pywikibot.translate() and i18n.twntranslate() to "internal" method _extract_plural()
enable tuple or list as parameters and check whether the items count is equal to the number of plural items found by re.findall()
Some test stuff added.
Change-Id: I6420b22835ec8db9c70806f46bf2efda65944c34 --- M pywikibot/i18n.py M tests/i18n/test.py M tests/i18n_tests.py 3 files changed, 132 insertions(+), 61 deletions(-)
Approvals: Merlijn van Deen: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/i18n.py b/pywikibot/i18n.py index 8e93f89..40fed49 100644 --- a/pywikibot/i18n.py +++ b/pywikibot/i18n.py @@ -225,6 +225,45 @@ pass
+def _extract_plural(code, message, parameters): + """Check for the plural variants in message and replace them depending on + parameter settings. + @param message: the message to be replaced + @type message: unicode string + @param parameters: plural parameters passed from other methods + @type parameters: int, basestring, tuple, list, dict + + """ + plural_items = re.findall(PLURAL_PATTERN, message) + if plural_items: # we found PLURAL patterns, process it + if len(plural_items) > 1 and isinstance(parameters, (tuple, list)) and \ + len(plural_items) != len(parameters): + raise ValueError("Length of parameter does not match PLURAL " + "occurences.") + i = 0 + for selector, variants in plural_items: + if type(parameters) == dict: + num = int(parameters[selector]) + elif isinstance(parameters, basestring): + num = int(parameters) + elif isinstance(parameters, (tuple, list)): + num = int(parameters[i]) + i += 1 + else: + num = parameters + # TODO: check against plural_rules[code]['nplurals'] + try: + index = plural_rules[code]['plural'](num) + except KeyError: + index = plural_rules['_default']['plural'](num) + except TypeError: + # we got an int, not a function + index = plural_rules[code]['plural'] + repl = variants.split('|')[index] + message = re.sub(PLURAL_PATTERN, repl, message, count=1) + return message + + def translate(code, xdict, parameters=None, fallback=True): """Return the most appropriate translation from a translation dict.
@@ -253,10 +292,6 @@ For PLURAL support have a look at the twntranslate method
""" - param = None - if type(parameters) == dict: - param = parameters - family = pywikibot.config.family # If a site is given instead of a code, use its language if hasattr(code, 'code'): @@ -291,31 +326,11 @@ return trans
# else we check for PLURAL variants - while re.search(PLURAL_PATTERN, trans): + trans = _extract_plural(code, trans, parameters) + if parameters: try: - selector, variants = re.search(PLURAL_PATTERN, trans).groups() - except AttributeError: - pass - else: # we found PLURAL patterns, process it - if type(parameters) == dict: - num = param[selector] - elif isinstance(parameters, basestring): - num = int(parameters) - else: - num = parameters - # TODO: check against plural_rules[lang]['nplurals'] - try: - index = plural_rules[code]['plural'](num) - except KeyError: - index = plural_rules['_default']['plural'](num) - except TypeError: - # we got an int, not a function - index = plural_rules[code]['plural'] - trans = re.sub(PLURAL_PATTERN, variants.split('|')[index], trans, count=1) - if param: - try: - return trans % param - except KeyError: + return trans % parameters + except (KeyError, TypeError): # parameter is for PLURAL variants only, don't change the string pass return trans @@ -388,7 +403,7 @@ contains a plural tag inside which looks like {{PLURAL:<number>|<variant1>|<variant2>[|<variantn>]}} it takes that variant calculated by the plural_rules depending on the number - value. + value. Multiple plurals are allowed.
Examples: If we had a test dictionary in test.py like @@ -429,45 +444,22 @@ import table.
""" - param = None - if type(parameters) == dict: - param = parameters # If a site is given instead of a code, use its language if hasattr(code, 'code'): code = code.code # we send the code via list and get the alternate code back code = [code] - trans = twtranslate(code, twtitle, None) - try: - selector, variants = re.search(PLURAL_PATTERN, trans).groups() - # No PLURAL tag found: nothing to replace - except AttributeError: - pass - else: - if type(parameters) == dict: - num = param[selector] - elif isinstance(parameters, basestring): - num = int(parameters) - else: - num = parameters - # get the alternate language code modified by twtranslate - lang = code.pop() - # we only need the lang or _default, not a _altlang code - # maybe we should implement this to i18n.translate() - # TODO: check against plural_rules[lang]['nplurals'] + trans = twtranslate(code, twtitle) + # get the alternate language code modified by twtranslate + lang = code.pop() + # check for PLURAL variants + trans = _extract_plural(lang, trans, parameters) + # we always have a dict for replacement of translatewiki messages + if parameters and type(parameters) == dict: try: - index = plural_rules[lang]['plural'](num) + return trans % parameters except KeyError: - index = plural_rules['_default']['plural'](num) - except TypeError: - # we got an int not a function - index = plural_rules[lang]['plural'] - repl = variants.split('|')[index] - trans = re.sub(PLURAL_PATTERN, repl, trans) - if param: - try: - return trans % param - except KeyError: + # parameter is for PLURAL variants only, don't change the string pass return trans
diff --git a/tests/i18n/test.py b/tests/i18n/test.py index 9e030ca..048b063 100644 --- a/tests/i18n/test.py +++ b/tests/i18n/test.py @@ -2,6 +2,7 @@ msg = { 'de': { 'test-plural': u'Bot: Ändere %(num)d {{PLURAL:num|Seite|Seiten}}.', + 'test-multiple-plurals': u'Bot: %(action)s %(line)s {{PLURAL:line|Zeile|Zeilen}} von {{PLURAL:%(page)d|einer|mehreren}} {{PLURAL:page|Seite|Seiten}}.', }, 'en': { 'test-localized': u'test-localized EN', diff --git a/tests/i18n_tests.py b/tests/i18n_tests.py index e996fbb..6df9a46 100644 --- a/tests/i18n_tests.py +++ b/tests/i18n_tests.py @@ -154,6 +154,84 @@ i18n.twntranslate('fr', 'test-plural', 1) % {'descr': 'seulement'}, u'Robot: Changer seulement une page.')
+ def testMultiple(self): + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', 1) + % {'action': u'Ändere', 'line': u'eine'}, + u'Bot: Ändere eine Zeile von einer Seite.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', 2) + % {'action': u'Ändere', 'line': u'zwei'}, + u'Bot: Ändere zwei Zeilen von mehreren Seiten.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', 3) + % {'action': u'Ändere', 'line': u'drei'}, + u'Bot: Ändere drei Zeilen von mehreren Seiten.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', (1, 2, 2)) + % {'action': u'Ändere', 'line': u'eine'}, + u'Bot: Ändere eine Zeile von mehreren Seiten.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', [3, 1, 1]) + % {'action': u'Ändere', 'line': u'drei'}, + u'Bot: Ändere drei Zeilen von einer Seite.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', ["3", 1, 1]) + % {'action': u'Ändere', 'line': u'drei'}, + u'Bot: Ändere drei Zeilen von einer Seite.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', "321") + % {'action': u'Ändere', 'line': u'dreihunderteinundzwanzig'}, + u'Bot: Ändere dreihunderteinundzwanzig Zeilen von mehreren Seiten.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', + {'action': u'Ändere', 'line': 1, 'page': 1}), + u'Bot: Ändere 1 Zeile von einer Seite.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', + {'action': u'Ändere', 'line': 1, 'page': 2}), + u'Bot: Ändere 1 Zeile von mehreren Seiten.') + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', + {'action': u'Ändere', 'line': "11", 'page': 2}), + u'Bot: Ändere 11 Zeilen von mehreren Seiten.') + + def testMultipleWrongParameterLength(self): + """ Test wrong parameter lenght""" + with self.assertRaisesRegexp(ValueError, "Length of parameter does not match PLURAL occurences"): + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', (1, 2)) + % {'action': u'Ändere', 'line': u'drei'}, + u'Bot: Ändere drei Zeilen von mehreren Seiten.') + + with self.assertRaisesRegexp(ValueError, "Length of parameter does not match PLURAL occurences"): + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', ["321"]) + % {'action': u'Ändere', 'line': u'dreihunderteinundzwanzig'}, + u'Bot: Ändere dreihunderteinundzwanzig Zeilen von mehreren Seiten.') + + def testMultipleNonNumbers(self): + """ Numbers or string numbers are required for tuple or list items """ + with self.assertRaisesRegexp(ValueError, "invalid literal for int() with base 10: 'drei'"): + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', ["drei", "1", 1]) + % {'action': u'Ändere', 'line': u'drei'}, + u'Bot: Ändere drei Zeilen von einer Seite.') + with self.assertRaisesRegexp(ValueError, "invalid literal for int() with base 10: 'elf'"): + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', + {'action': u'Ändere', 'line': "elf", 'page': 2}), + u'Bot: Ändere elf Zeilen von mehreren Seiten.') + + def testAllParametersExist(self): + with self.assertRaisesRegexp(KeyError, "u'line'"): + # all parameters must be inside twntranslate + self.assertEqual( + i18n.twntranslate('de', 'test-multiple-plurals', + {'line': 1, 'page': 1}) + % {'action': u'Ändere'}, + u'Bot: Ändere 1 Zeile von einer Seite.') +
if __name__ == '__main__': try:
pywikibot-commits@lists.wikimedia.org