summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSouheil CHELFOUH <trollfot@gmail.com>2018-09-04 11:55:12 +0200
committerSouheil CHELFOUH <trollfot@gmail.com>2018-09-04 11:55:12 +0200
commitb759fc44c2804aa380e8f40ccf608374f8e91305 (patch)
treeca582b26b42a0a9bef8a0f658dd06b3c5a412a7c
parentece19b3b7d984657019a896bdc5e3b407f75ac9e (diff)
downloadzope-i18n-b759fc44c2804aa380e8f40ccf608374f8e91305.tar.gz
First implementation of plurals. This is a bit rough, still.
Added Polish translations to have a complex plural rule handy. Currently, only german is tested. We need to test the whole set of languages and probably add more.
-rw-r--r--src/zope/i18n/gettextmessagecatalog.py42
-rw-r--r--src/zope/i18n/interfaces/__init__.py5
-rw-r--r--src/zope/i18n/tests/de-default.mobin347 -> 483 bytes
-rw-r--r--src/zope/i18n/tests/de-default.po6
-rw-r--r--src/zope/i18n/tests/en-default.mobin343 -> 478 bytes
-rw-r--r--src/zope/i18n/tests/en-default.po6
-rw-r--r--src/zope/i18n/tests/pl-default.po22
-rw-r--r--src/zope/i18n/tests/test_imessagecatalog.py4
-rw-r--r--src/zope/i18n/tests/test_plurals.py75
9 files changed, 151 insertions, 9 deletions
diff --git a/src/zope/i18n/gettextmessagecatalog.py b/src/zope/i18n/gettextmessagecatalog.py
index 307e5be..884cf54 100644
--- a/src/zope/i18n/gettextmessagecatalog.py
+++ b/src/zope/i18n/gettextmessagecatalog.py
@@ -22,7 +22,12 @@ from zope.interface import implementer
class _KeyErrorRaisingFallback(object):
def ugettext(self, message):
raise KeyError(message)
+
+ def ungettext(self, singular, plural, n):
+ raise KeyError(singular)
+
gettext = ugettext
+ ngettext = ungettext
@implementer(IGlobalMessageCatalog)
@@ -33,13 +38,20 @@ class GettextMessageCatalog(object):
def __init__(self, language, domain, path_to_file):
"""Initialize the message catalog"""
- self.language = language.decode('utf-8') if isinstance(language, bytes) else language
- self.domain = domain.decode("utf-8") if isinstance(domain, bytes) else domain
+ self.language = (
+ language.decode('utf-8') if isinstance(language, bytes)
+ else language)
+ self.domain = (
+ domain.decode("utf-8") if isinstance(domain, bytes)
+ else domain)
self._path_to_file = path_to_file
self.reload()
catalog = self._catalog
catalog.add_fallback(_KeyErrorRaisingFallback())
- self._gettext = catalog.gettext if str is not bytes else catalog.ugettext
+ self._gettext = (
+ catalog.gettext if str is not bytes else catalog.ugettext)
+ self._ngettext = (
+ catalog.ngettext if str is not bytes else catalog.ungettext)
def reload(self):
'See IMessageCatalog'
@@ -50,13 +62,35 @@ class GettextMessageCatalog(object):
'See IMessageCatalog'
return self._gettext(id)
+ def getPluralMessage(self, singular, plural, n):
+ 'See IMessageCatalog'
+ msg = self._ngettext(singular, plural, n)
+ try:
+ return msg % n
+ except TypeError:
+ return msg
+
+ def queryPluralMessage(self, singular, plural, n, dft1=None, dft2=None):
+ 'See IMessageCatalog'
+ try:
+ msg = self._ngettext(singular, plural, n)
+ except KeyError:
+ if n == 1: # Please FIX using the language rule.
+ msg = dft1
+ else:
+ msg = dft2
+ try:
+ return msg % n
+ except TypeError:
+ return msg
+
def queryMessage(self, id, default=None):
'See IMessageCatalog'
try:
return self._gettext(id)
except KeyError:
return default
-
+
def getIdentifier(self):
'See IMessageCatalog'
return self._path_to_file
diff --git a/src/zope/i18n/interfaces/__init__.py b/src/zope/i18n/interfaces/__init__.py
index 397c8f5..187ba16 100644
--- a/src/zope/i18n/interfaces/__init__.py
+++ b/src/zope/i18n/interfaces/__init__.py
@@ -60,13 +60,16 @@ class IMessageCatalog(Interface):
An exception is raised if the message id is not found.
"""
-
+
def queryMessage(msgid, default=None):
"""Look for the appropriate text for the given message id.
If the message id is not found, default is returned.
"""
+ # FIX ME ADD PLURAL METHODS IF WE DECIDE TO KEEP THEM SEPARATED FROM
+ # THE SINGULAR METHODS.
+
language = TextLine(
title=u"Language",
description=u"The language the catalog translates to.",
diff --git a/src/zope/i18n/tests/de-default.mo b/src/zope/i18n/tests/de-default.mo
index 392a0ff..f49b8da 100644
--- a/src/zope/i18n/tests/de-default.mo
+++ b/src/zope/i18n/tests/de-default.mo
Binary files differ
diff --git a/src/zope/i18n/tests/de-default.po b/src/zope/i18n/tests/de-default.po
index 99a5f8d..7d3b2f6 100644
--- a/src/zope/i18n/tests/de-default.po
+++ b/src/zope/i18n/tests/de-default.po
@@ -6,9 +6,15 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
msgid "short_greeting"
msgstr "Hallo!"
msgid "greeting"
msgstr "Hallo $name, wie geht es Dir?"
+
+msgid "There is one file."
+msgid_plural "There are %d files."
+msgstr[0] "Es gibt eine Datei."
+msgstr[1] "Es gibt %d Dateien."
diff --git a/src/zope/i18n/tests/en-default.mo b/src/zope/i18n/tests/en-default.mo
index 4164029..3e9dc9e 100644
--- a/src/zope/i18n/tests/en-default.mo
+++ b/src/zope/i18n/tests/en-default.mo
Binary files differ
diff --git a/src/zope/i18n/tests/en-default.po b/src/zope/i18n/tests/en-default.po
index a3d843b..fc45a11 100644
--- a/src/zope/i18n/tests/en-default.po
+++ b/src/zope/i18n/tests/en-default.po
@@ -6,9 +6,15 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
msgid "short_greeting"
msgstr "Hello!"
msgid "greeting"
msgstr "Hello $name, how are you?"
+
+msgid "There is one file."
+msgid_plural "There are %d files."
+msgstr[0] "There is one file."
+msgstr[1] "There are %d files."
diff --git a/src/zope/i18n/tests/pl-default.po b/src/zope/i18n/tests/pl-default.po
new file mode 100644
index 0000000..92a0183
--- /dev/null
+++ b/src/zope/i18n/tests/pl-default.po
@@ -0,0 +1,22 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Zope 3\n"
+"PO-Revision-Date: 2018-09-04 11:05+0100\n"
+"Last-Translator: Zope 3 contributors\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+
+
+msgid "short_greeting"
+msgstr "Cześć !"
+
+msgid "greeting"
+msgstr "Cześć $name, jak siê masz?"
+
+msgid "There is %d file."
+msgid_plural "There are %d files."
+msgstr[0] "Istnieje %d plik."
+msgstr[1] "Istnieją %d pliki."
+msgstr[2] "Istnieją %d plików."
diff --git a/src/zope/i18n/tests/test_imessagecatalog.py b/src/zope/i18n/tests/test_imessagecatalog.py
index 2856c67..803d4f0 100644
--- a/src/zope/i18n/tests/test_imessagecatalog.py
+++ b/src/zope/i18n/tests/test_imessagecatalog.py
@@ -43,24 +43,20 @@ class TestIMessageCatalog(unittest.TestCase):
self.assertEqual(catalog.getMessage('short_greeting'), 'Hello!')
self.assertRaises(KeyError, catalog.getMessage, 'foo')
-
def testQueryMessage(self):
catalog = self._catalog
self.assertEqual(catalog.queryMessage('short_greeting'), 'Hello!')
self.assertEqual(catalog.queryMessage('foo'), None)
self.assertEqual(catalog.queryMessage('foo', 'bar'), 'bar')
-
def testGetLanguage(self):
catalog = self._catalog
self.assertEqual(catalog.language, 'en')
-
def testGetDomain(self):
catalog = self._catalog
self.assertEqual(catalog.domain, 'default')
-
def testGetIdentifier(self):
catalog = self._catalog
self.assertEqual(catalog.getIdentifier(), self._getUniqueIndentifier())
diff --git a/src/zope/i18n/tests/test_plurals.py b/src/zope/i18n/tests/test_plurals.py
new file mode 100644
index 0000000..435e670
--- /dev/null
+++ b/src/zope/i18n/tests/test_plurals.py
@@ -0,0 +1,75 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test a gettext implementation of a Message Catalog.
+"""
+import os
+import unittest
+from zope.i18n import tests
+from zope.i18n.gettextmessagecatalog import GettextMessageCatalog
+from zope.i18n.tests import test_imessagecatalog
+
+
+class TestPlurals(unittest.TestCase):
+
+ def _getMessageCatalog(self, locale, variant="default"):
+ path = os.path.dirname(tests.__file__)
+ self._path = os.path.join(path, '%s-%s.mo' % (locale, variant))
+ catalog = GettextMessageCatalog(locale, variant, self._path)
+ return catalog
+
+ def test_GermanMessages(self):
+ catalog = self._getMessageCatalog('de')
+ self.assertEqual(catalog.language, 'de')
+
+ self.assertEqual(catalog.getPluralMessage(
+ 'There is one file.', 'There are %d files.', 1),
+ 'Es gibt eine Datei.')
+ self.assertEqual(catalog.getPluralMessage(
+ 'There is one file.', 'There are %d files.', 3),
+ 'Es gibt 3 Dateien.')
+
+ self.assertEqual(catalog.getPluralMessage(
+ 'There is one file.', 'There are %d files.', 0),
+ 'Es gibt 0 Dateien.')
+
+ # Unknown id
+ self.assertRaises(KeyError, catalog.getPluralMessage,
+ 'There are %d files.', 'bar', 6)
+
+ # Query without default values
+ self.assertEqual(catalog.queryPluralMessage(
+ 'There is one file.', 'There are %d files.', 1),
+ 'Es gibt eine Datei.')
+ self.assertEqual(catalog.queryPluralMessage(
+ 'There is one file.', 'There are %d files.', 3),
+ 'Es gibt 3 Dateien.')
+
+ # Query with default values
+ self.assertEqual(catalog.queryPluralMessage(
+ 'There are %d files.', 'There is one file.', 1,
+ 'Es gibt 1 Datei.', 'Es gibt %d Dateien !', ),
+ 'Es gibt 1 Datei.')
+ self.assertEqual(catalog.queryPluralMessage(
+ 'There are %d files.', 'There is one file.', 3,
+ 'Es gibt 1 Datei.', 'Es gibt %d Dateien !', ),
+ 'Es gibt 3 Dateien !')
+
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(TestPlurals),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')