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:

-- 
To view, visit https://gerrit.wikimedia.org/r/119338
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I6420b22835ec8db9c70806f46bf2efda65944c34
Gerrit-PatchSet: 8
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Ladsgroup <[email protected]>
Gerrit-Reviewer: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: Pyfisch <[email protected]>
Gerrit-Reviewer: Ricordisamoa <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
Pywikibot-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/pywikibot-commits

Reply via email to