]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
More preparation for msgctxt support (#54).
authorChristopher Lenz <cmlenz@gmail.com>
Wed, 11 Jun 2008 18:56:27 +0000 (18:56 +0000)
committerChristopher Lenz <cmlenz@gmail.com>
Wed, 11 Jun 2008 18:56:27 +0000 (18:56 +0000)
babel/messages/catalog.py
babel/messages/frontend.py
babel/messages/mofile.py
babel/messages/plurals.py
babel/messages/pofile.py
babel/messages/tests/__init__.py
babel/messages/tests/frontend.py
babel/messages/tests/mofile.py
babel/messages/tests/plurals.py [new file with mode: 0644]
babel/messages/tests/pofile.py

index da313e453490eb7c11182c0283faf8484f9bf640..edaba2fb06b422409b76e216eb296d685b2c907a 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -41,7 +41,7 @@ class Message(object):
     """Representation of a single message in a catalog."""
 
     def __init__(self, id, string=u'', locations=(), flags=(), auto_comments=(),
-                 user_comments=(), previous_id=(), lineno=None):
+                 user_comments=(), previous_id=(), lineno=None, context=None):
         """Create the message object.
 
         :param id: the message ID, or a ``(singular, plural)`` tuple for
@@ -56,6 +56,7 @@ class Message(object):
                             tuple for pluralizable messages
         :param lineno: the line number on which the msgid line was found in the
                        PO file, if any
+        :param context: the message context
         """
         self.id = id #: The message ID
         if not string and self.pluralizable:
@@ -74,6 +75,7 @@ class Message(object):
         else:
             self.previous_id = list(previous_id)
         self.lineno = lineno
+        self.context = context
 
     def __repr__(self):
         return '<%s %r (flags: %r)>' % (type(self).__name__, self.id,
@@ -95,7 +97,7 @@ class Message(object):
     def clone(self):
         return Message(self.id, self.string, self.locations, self.flags,
                        self.auto_comments, self.user_comments,
-                       self.previous_id, self.lineno)
+                       self.previous_id, self.lineno, self.context)
 
     def fuzzy(self):
         return 'fuzzy' in self.flags
@@ -534,7 +536,7 @@ class Catalog(object):
             self._messages[key] = message
 
     def add(self, id, string=None, locations=(), flags=(), auto_comments=(),
-            user_comments=(), previous_id=(), lineno=None):
+            user_comments=(), previous_id=(), lineno=None, context=None):
         """Add or update the message with the specified ID.
 
         >>> catalog = Catalog()
@@ -557,9 +559,11 @@ class Catalog(object):
                             tuple for pluralizable messages
         :param lineno: the line number on which the msgid line was found in the
                        PO file, if any
+        :param context: the message context
         """
         self[id] = Message(id, string, list(locations), flags, auto_comments,
-                           user_comments, previous_id, lineno=lineno)
+                           user_comments, previous_id, lineno=lineno,
+                           context=context)
 
     def check(self):
         """Run various validation checks on the translations in the catalog.
index 775ffdc562eba3dcbd57f951ea9f7d9ed105a73d..19cf8459015221d50164c3fd63386c9a61be5fba 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
index 47f04973773cb3dd508ac0ff969f60f0cd5a1b97..59384022dd836e6df1ca7218860a9a9bf3b071d0 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -45,21 +45,21 @@ def read_mo(fileobj):
     catalog = Catalog()
     headers = {}
 
-    unpack = struct.unpack
     filename = getattr(fileobj, 'name', '')
     charset = None
 
     buf = fileobj.read()
     buflen = len(buf)
+    unpack = struct.unpack
 
     # Parse the .mo file header, which consists of 5 little endian 32
     # bit words.
     magic = unpack('<I', buf[:4])[0] # Are we big endian or little endian?
     if magic == LE_MAGIC:
-        version, msgcount, masteridx, transidx = unpack('<4I', buf[4:20])
+        version, msgcount, origidx, transidx = unpack('<4I', buf[4:20])
         ii = '<II'
     elif magic == BE_MAGIC:
-        version, msgcount, masteridx, transidx = unpack('>4I', buf[4:20])
+        version, msgcount, origidx, transidx = unpack('>4I', buf[4:20])
         ii = '>II'
     else:
         raise IOError(0, 'Bad magic number', filename)
@@ -67,7 +67,7 @@ def read_mo(fileobj):
     # Now put all messages from the .mo file buffer into the catalog
     # dictionary
     for i in xrange(0, msgcount):
-        mlen, moff = unpack(ii, buf[masteridx:masteridx + 8])
+        mlen, moff = unpack(ii, buf[origidx:origidx + 8])
         mend = moff + mlen
         tlen, toff = unpack(ii, buf[transidx:transidx + 8])
         tend = toff + tlen
@@ -88,37 +88,29 @@ def read_mo(fileobj):
                 if ':' in item:
                     key, value = item.split(':', 1)
                     lastkey = key = key.strip().lower()
-                    value = value.strip()
-                    headers[key] = value
-                    if key == 'content-type':
-                        charset = value.split('charset=')[1]
+                    headers[key] = value.strip()
                 elif lastkey:
-                    self._info[lastkey] += '\n' + item
-
-        # Note: we unconditionally convert both msgids and msgstrs to
-        # Unicode using the character encoding specified in the charset
-        # parameter of the Content-Type header.  The gettext documentation
-        # strongly encourages msgids to be us-ascii, but some appliations
-        # require alternative encodings (e.g. Zope's ZCML and ZPT).  For
-        # traditional gettext applications, the msgid conversion will
-        # cause no problems since us-ascii should always be a subset of
-        # the charset encoding.  We may want to fall back to 8-bit msgids
-        # if the Unicode conversion fails.
-        if '\x00' in msg:
-            # Plural forms
+                    headers[lastkey] += '\n' + item
+
+        if '\x04' in msg: # context
+            ctxt, msg = msg.split('\x04')
+        else:
+            ctxt = None
+
+        if '\x00' in msg: # plural forms
             msg = msg.split('\x00')
             tmsg = tmsg.split('\x00')
-            if charset:
-                msg = [unicode(x, charset) for x in msg]
-                tmsg = [unicode(x, charset) for x in tmsg]
+            if catalog.charset:
+                msg = [x.decode(catalog.charset) for x in msg]
+                tmsg = [x.decode(catalog.charset) for x in tmsg]
         else:
-            if charset:
-                msg = unicode(msg, charset)
-                tmsg = unicode(tmsg, charset)
-        catalog[msg] = Message(msg, tmsg)
+            if catalog.charset:
+                msg = msg.decode(catalog.charset)
+                tmsg = tmsg.decode(catalog.charset)
+        catalog[msg] = Message(msg, tmsg, context=ctxt)
 
         # advance to next entry in the seek tables
-        masteridx += 8
+        origidx += 8
         transidx += 8
 
     catalog.mime_headers = headers.items()
@@ -193,6 +185,8 @@ def write_mo(fileobj, catalog, use_fuzzy=False):
                 msgstr = message.id.encode(catalog.charset)
             else:
                 msgstr = message.string.encode(catalog.charset)
+        if message.context:
+            msgid = '\x04'.join(message.context.encode(catalog.charset), msgid)
         offsets.append((len(ids), len(msgid), len(strs), len(msgstr)))
         ids += msgid + '\x00'
         strs += msgstr + '\x00'
index 1c0e69257b5c341b484db430d72eedc48c273e10..a5df7fead98ecf1cbd183407432afa1fdcdd5af3 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -75,7 +75,7 @@ PLURALS = {
     # Chuvash
     'cv': (1, '0'),
     # Welsh
-    'cy': (5, 'n==1 ? 1 : n==2 ? 2 : n==3 ? 3 : n==6 ? 4 : 0'),
+    'cy': (5, '(n==1 ? 1 : n==2 ? 2 : n==3 ? 3 : n==6 ? 4 : 0)'),
     # Danish
     'da': (2, '(n != 1)'),
     # German
@@ -105,15 +105,15 @@ PLURALS = {
     # Friulian - From Pootle's PO's
     'fur': (2, '(n > 1)'),
     # Irish
-    'ga': (3, 'n==1 ? 0 : n==2 ? 1 : 2'),
+    'ga': (3, '(n==1 ? 0 : n==2 ? 1 : 2)'),
     # Galician - From Pootle's PO's
     'gl': (2, '(n != 1)'),
     # Hausa - From Pootle's PO's
-    'ha': (2, '(n != 1)'), 
+    'ha': (2, '(n != 1)'),
     # Hebrew
     'he': (2, '(n != 1)'),
     # Hindi - From Pootle's PO's
-    'hi': (2, '(n != 1)'), 
+    'hi': (2, '(n != 1)'),
     # Croatian
     'hr': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
     # Hungarian
index 3e73e1342653867256aa567d054bd57f2b94ff86..fc3af0c7d1881fceee45eb322fd5664c00fea8ec 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -136,6 +136,7 @@ def read_po(fileobj, locale=None, domain=None, ignore_obsolete=False):
     user_comments = []
     auto_comments = []
     obsolete = [False]
+    context = []
     in_msgid = [False]
     in_msgstr = [False]
 
@@ -149,15 +150,20 @@ def read_po(fileobj, locale=None, domain=None, ignore_obsolete=False):
             string = tuple([denormalize(t[1]) for t in translations])
         else:
             string = denormalize(translations[0][1])
+        if context:
+            msgctxt = denormalize('\n'.join(context))
+        else:
+            msgctxt = None
         message = Message(msgid, string, list(locations), set(flags),
-                          auto_comments, user_comments, lineno=offset[0] + 1)
+                          auto_comments, user_comments, lineno=offset[0] + 1,
+                          context=msgctxt)
         if obsolete[0]:
             if not ignore_obsolete:
                 catalog.obsolete[msgid] = message
         else:
             catalog[msgid] = message
-        del messages[:]; del translations[:]; del locations[:];
-        del flags[:]; del auto_comments[:]; del user_comments[:]
+        del messages[:]; del translations[:]; del context[:]; del locations[:];
+        del flags[:]; del auto_comments[:]; del user_comments[:];
         obsolete[0] = False
         counter[0] += 1
 
@@ -182,11 +188,16 @@ def read_po(fileobj, locale=None, domain=None, ignore_obsolete=False):
                 translations.append([int(idx), msg.lstrip()])
             else:
                 translations.append([0, msg])
+        elif line.startswith('msgctxt'):
+            in_msgid[0] = in_msgstr[0] = False
+            context.append(line[7:].lstrip())
         elif line.startswith('"'):
             if in_msgid[0]:
                 messages[-1] += u'\n' + line.rstrip()
             elif in_msgstr[0]:
                 translations[-1][1] += u'\n' + line.rstrip()
+            elif in_msgctxt[0]:
+                context.append(line.rstrip())
 
     for lineno, line in enumerate(fileobj.readlines()):
         line = line.strip().decode(catalog.charset)
index 5fd03a44492608ff4b49e8a9bc758a6c7c4e78f5..5874ec1142276822b200f7210ae622e70a00f976 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
 import unittest
 
 def suite():
-    from babel.messages.tests import catalog, extract, frontend, mofile, pofile
+    from babel.messages.tests import catalog, extract, frontend, mofile, \
+                                     plurals, pofile
     suite = unittest.TestSuite()
     suite.addTest(catalog.suite())
     suite.addTest(extract.suite())
     suite.addTest(frontend.suite())
     suite.addTest(mofile.suite())
+    suite.addTest(plurals.suite())
     suite.addTest(pofile.suite())
     return suite
 
index 6a1cc02f67aac7d87e2fa25d7419d9e99ff2a808..c42bf9e65d3b32af3f287c895bd3a363fb1bb9cf 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
index 5a32b4cdecf919016637824fb835effbbb1a7b16..b10a8031cc33158a298a7174e214ca32f35cb269 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
diff --git a/babel/messages/tests/plurals.py b/babel/messages/tests/plurals.py
new file mode 100644 (file)
index 0000000..211840a
--- /dev/null
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://babel.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://babel.edgewall.org/log/.
+
+import doctest
+import unittest
+
+from babel.messages import plurals
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(doctest.DocTestSuite(plurals))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')
index 11007e2aafe4af189778c1aac8cb842d3be21026..00edaaadb65824e84ad9a97b7a2a563e8652c6af 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2007 Edgewall Software
+# Copyright (C) 2007-2008 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -143,6 +143,26 @@ msgstr "Bahr"
         self.assertEqual(1, len(catalog))
         self.assertEqual(0, len(catalog.obsolete))
 
+    def test_with_context(self):
+        buf = StringIO(r'''# Some string in the menu
+#: main.py:1
+msgctxt "Menu"
+msgid "foo"
+msgstr "Voh"
+
+# Another string in the menu
+#: main.py:2
+msgctxt "Menu"
+msgid "bar"
+msgstr "Bahr"
+''')
+        catalog = pofile.read_po(buf, ignore_obsolete=True)
+        self.assertEqual(2, len(catalog))
+        message = catalog['foo']
+        self.assertEqual('Menu', message.context)
+        message = catalog['bar']
+        self.assertEqual('Menu', message.context)
+
 
 class WritePoTestCase(unittest.TestCase):