[Python-checkins] python/dist/src/Lib gettext.py,1.15,1.16

loewis@users.sourceforge.net loewis@users.sourceforge.net
Thu, 21 Nov 2002 13:45:34 -0800


Update of /cvsroot/python/python/dist/src/Lib
In directory sc8-pr-cvs1:/tmp/cvs-serv11193/Lib

Modified Files:
	gettext.py 
Log Message:
Patch #633547: Support plural forms. Do TODOs in test suite.


Index: gettext.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/gettext.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** gettext.py	14 Aug 2002 15:09:12 -0000	1.15
--- gettext.py	21 Nov 2002 21:45:32 -0000	1.16
***************
*** 33,36 ****
--- 33,38 ----
  # module.
  #
+ # J. David Ibanez implemented plural forms.
+ #
  # TODO:
  # - Lazy loading of .mo files.  Currently the entire catalog is loaded into
***************
*** 44,53 ****
  #   find this format documented anywhere.
  
! import os
! import sys
! import struct
! import copy
  from errno import ENOENT
  
  __all__ = ["bindtextdomain","textdomain","gettext","dgettext",
             "find","translation","install","Catalog"]
--- 46,54 ----
  #   find this format documented anywhere.
  
! 
! import copy, os, re, struct, sys
  from errno import ENOENT
  
+ 
  __all__ = ["bindtextdomain","textdomain","gettext","dgettext",
             "find","translation","install","Catalog"]
***************
*** 56,59 ****
--- 57,119 ----
  
  
+ def test(condition, true, false):
+     """
+     Implements the C expression:
+ 
+       condition ? true : false
+ 
+     Required to correctly interpret plural forms.
+     """
+     if condition:
+         return true
+     else:
+         return false
+ 
+ 
+ def c2py(plural):
+     """
+     Gets a C expression as used in PO files for plural forms and
+     returns a Python lambda function that implements an equivalent
+     expression.
+     """
+     # Security check, allow only the "n" identifier
+     from StringIO import StringIO
+     import token, tokenize
+     tokens = tokenize.generate_tokens(StringIO(plural).readline)
+     danger = [ x for x in tokens if x[0] == token.NAME and x[1] != 'n' ]
+     if danger:
+         raise ValueError, 'dangerous expression'
+ 
+     # Replace some C operators by their Python equivalents
+     plural = plural.replace('&&', ' and ')
+     plural = plural.replace('||', ' or ')
+ 
+     expr = re.compile(r'\![^=]')
+     plural = expr.sub(' not ', plural)
+ 
+     # Regular expression and replacement function used to transform
+     # "a?b:c" to "test(a,b,c)".
+     expr = re.compile(r'(.*?)\?(.*?):(.*)')
+     def repl(x):
+         return "test(%s, %s, %s)" % (x.group(1), x.group(2),
+                                      expr.sub(repl, x.group(3)))
+ 
+     # Code to transform the plural expression, taking care of parentheses
+     stack = ['']
+     for c in plural:
+         if c == '(':
+             stack.append('')
+         elif c == ')':
+             if len(stack) == 0:
+                 raise ValueError, 'unbalanced parenthesis in plural form'
+             s = expr.sub(repl, stack.pop())
+             stack[-1] += '(%s)' % s
+         else:
+             stack[-1] += c
+     plural = expr.sub(repl, stack.pop())
+ 
+     return eval('lambda n: int(%s)' % plural)
+ 
+ 
  
  def _expand_lang(locale):
***************
*** 122,125 ****
--- 182,193 ----
          return message
  
+     def ngettext(self, msgid1, msgid2, n):
+         if self._fallback:
+             return self._fallback.ngettext(msgid1, msgid2, n)
+         if n == 1:
+             return msgid1
+         else:
+             return msgid2
+ 
      def ugettext(self, message):
          if self._fallback:
***************
*** 127,130 ****
--- 195,206 ----
          return unicode(message)
  
+     def ungettext(self, msgid1, msgid2, n):
+         if self._fallback:
+             return self._fallback.ungettext(msgid1, msgid2, n)
+         if n == 1:
+             return unicode(msgid1)
+         else:
+             return unicode(msgid2)
+ 
      def info(self):
          return self._info
***************
*** 170,175 ****
              tend = toff + tlen
              if mend < buflen and tend < buflen:
                  tmsg = buf[toff:tend]
!                 catalog[buf[moff:mend]] = tmsg
              else:
                  raise IOError(0, 'File is corrupt', filename)
--- 246,259 ----
              tend = toff + tlen
              if mend < buflen and tend < buflen:
+                 msg = buf[moff:mend]
                  tmsg = buf[toff:tend]
!                 if msg.find('\x00') >= 0:
!                     # Plural forms
!                     msgid1, msgid2 = msg.split('\x00')
!                     tmsg = tmsg.split('\x00')
!                     for i in range(len(tmsg)):
!                         catalog[(msgid1, i)] = tmsg[i]
!                 else:
!                     catalog[msg] = tmsg
              else:
                  raise IOError(0, 'File is corrupt', filename)
***************
*** 187,190 ****
--- 271,280 ----
                      if k == 'content-type':
                          self._charset = v.split('charset=')[1]
+                     elif k == 'plural-forms':
+                         v = v.split(';')
+ ##                        nplurals = v[0].split('nplurals=')[1]
+ ##                        nplurals = int(nplurals.strip())
+                         plural = v[1].split('plural=')[1]
+                         self.plural = c2py(plural)
              # advance to next entry in the seek tables
              masteridx += 8
***************
*** 199,202 ****
--- 289,305 ----
              return message
  
+ 
+     def ngettext(self, msgid1, msgid2, n):
+         try:
+             return self._catalog[(msgid1, self.plural(n))]
+         except KeyError:
+             if self._fallback:
+                 return self._fallback.ngettext(msgid1, msgid2, n)
+             if n == 1:
+                 return msgid1
+             else:
+                 return msgid2
+ 
+ 
      def ugettext(self, message):
          try:
***************
*** 209,212 ****
--- 312,327 ----
  
  
+     def ungettext(self, msgid1, msgid2, n):
+         try:
+             tmsg = self._catalog[(msgid1, self.plural(n))]
+         except KeyError:
+             if self._fallback:
+                 return self._fallback.ungettext(msgid1, msgid2, n)
+             if n == 1:
+                 tmsg = msgid1
+             else:
+                 tmsg = msgid2
+         return unicode(tmsg, self._charset)
+ 
  
  # Locate a .mo file using the gettext strategy
***************
*** 312,317 ****
--- 427,447 ----
  
  
+ def dngettext(domain, msgid1, msgid2, n):
+     try:
+         t = translation(domain, _localedirs.get(domain, None))
+     except IOError:
+         if n == 1:
+             return msgid1
+         else:
+             return msgid2
+     return t.ngettext(msgid1, msgid2, n)
+ 
+ 
  def gettext(message):
      return dgettext(_current_domain, message)
+ 
+ 
+ def ngettext(msgid1, msgid2, n):
+     return dngettext(_current_domain, msgid1, msgid2, n)