changeset 337:662d332c0a2b

More preparation for msgctxt support (#54).
author cmlenz
date Wed, 11 Jun 2008 18:56:27 +0000
parents 6e86b862af57
children 398e7c377165
files 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 babel/messages/tests/pofile.py
diffstat 10 files changed, 105 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/babel/messages/catalog.py
+++ b/babel/messages/catalog.py
@@ -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 @@
     """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 @@
                             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 @@
         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 @@
     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 @@
             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 @@
                             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.
--- a/babel/messages/frontend.py
+++ b/babel/messages/frontend.py
@@ -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
--- a/babel/messages/mofile.py
+++ b/babel/messages/mofile.py
@@ -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 @@
     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 @@
     # 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 @@
                 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
+                    headers[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
+        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 @@
                 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'
--- a/babel/messages/plurals.py
+++ b/babel/messages/plurals.py
@@ -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 @@
     # 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 @@
     # 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
--- a/babel/messages/pofile.py
+++ b/babel/messages/pofile.py
@@ -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 @@
     user_comments = []
     auto_comments = []
     obsolete = [False]
+    context = []
     in_msgid = [False]
     in_msgstr = [False]
 
@@ -149,15 +150,20 @@
             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 @@
                 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)
--- a/babel/messages/tests/__init__.py
+++ b/babel/messages/tests/__init__.py
@@ -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
@@ -14,12 +14,14 @@
 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
 
--- a/babel/messages/tests/frontend.py
+++ b/babel/messages/tests/frontend.py
@@ -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
--- a/babel/messages/tests/mofile.py
+++ b/babel/messages/tests/mofile.py
@@ -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
new file mode 100644
--- /dev/null
+++ b/babel/messages/tests/plurals.py
@@ -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')
--- a/babel/messages/tests/pofile.py
+++ b/babel/messages/tests/pofile.py
@@ -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 @@
         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):
 
Copyright (C) 2012-2017 Edgewall Software