diff options
author | Souheil CHELFOUH <trollfot@gmail.com> | 2018-09-04 11:55:12 +0200 |
---|---|---|
committer | Souheil CHELFOUH <trollfot@gmail.com> | 2018-09-04 11:55:12 +0200 |
commit | b759fc44c2804aa380e8f40ccf608374f8e91305 (patch) | |
tree | ca582b26b42a0a9bef8a0f658dd06b3c5a412a7c | |
parent | ece19b3b7d984657019a896bdc5e3b407f75ac9e (diff) | |
download | zope-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.py | 42 | ||||
-rw-r--r-- | src/zope/i18n/interfaces/__init__.py | 5 | ||||
-rw-r--r-- | src/zope/i18n/tests/de-default.mo | bin | 347 -> 483 bytes | |||
-rw-r--r-- | src/zope/i18n/tests/de-default.po | 6 | ||||
-rw-r--r-- | src/zope/i18n/tests/en-default.mo | bin | 343 -> 478 bytes | |||
-rw-r--r-- | src/zope/i18n/tests/en-default.po | 6 | ||||
-rw-r--r-- | src/zope/i18n/tests/pl-default.po | 22 | ||||
-rw-r--r-- | src/zope/i18n/tests/test_imessagecatalog.py | 4 | ||||
-rw-r--r-- | src/zope/i18n/tests/test_plurals.py | 75 |
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 Binary files differindex 392a0ff..f49b8da 100644 --- a/src/zope/i18n/tests/de-default.mo +++ b/src/zope/i18n/tests/de-default.mo 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 Binary files differindex 4164029..3e9dc9e 100644 --- a/src/zope/i18n/tests/en-default.mo +++ b/src/zope/i18n/tests/en-default.mo 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') |