Edgewall Software

Changeset 1118 for trunk


Ignore:
Timestamp:
Apr 21, 2010, 10:00:37 PM (13 years ago)
Author:
cmlenz
Message:

i18n: some cleanup, especially for the pluralization directives.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/genshi/filters/i18n.py

    r1115 r1118  
    2828from types import FunctionType
    2929
    30 from genshi.core import Attrs, Namespace, QName, START, END, TEXT, START_NS, \
    31                         END_NS, XML_NAMESPACE, _ensure, StreamEventKind
     30from genshi.core import Attrs, Namespace, QName, START, END, TEXT, \
     31                        XML_NAMESPACE, _ensure, StreamEventKind
    3232from genshi.template.eval import _ast
    3333from genshi.template.base import DirectiveFactory, EXPR, SUB, _apply_directives
     
    216216        self.params = ctxt.get('_i18n.choose.params', [])[:]
    217217        msgbuf = MessageBuffer(self)
    218 
    219         stream = iter(_apply_directives(stream, directives, ctxt, vars))
    220        
     218        stream = _apply_directives(stream, directives, ctxt, vars)
     219
    221220        previous = stream.next()
    222221        if previous[0] is START:
     
    224223        else:
    225224            msgbuf.append(*previous)
    226            
     225
    227226        try:
    228227            previous = stream.next()
     
    230229            # For example <i18n:singular> or <i18n:plural> directives
    231230            yield MSGBUF, (), -1 # the place holder for msgbuf output
    232             ctxt['_i18n.choose.%s' % type(self).__name__] = msgbuf
     231            ctxt['_i18n.choose.%s' % self.tagname] = msgbuf
    233232            return
    234        
    235         for kind, data, pos in stream:
     233
     234        for event in stream:
    236235            msgbuf.append(*previous)
    237             previous = kind, data, pos
     236            previous = event
    238237        yield MSGBUF, (), -1 # the place holder for msgbuf output
    239238
     
    242241        else:
    243242            msgbuf.append(*previous)
    244         ctxt['_i18n.choose.%s' % type(self).__name__] = msgbuf
    245 
     243        ctxt['_i18n.choose.%s' % self.tagname] = msgbuf
    246244
    247245    def extract(self, translator, stream, gettext_functions=GETTEXT_FUNCTIONS,
     
    362360    def __call__(self, stream, directives, ctxt, **vars):
    363361        ctxt.push({'_i18n.choose.params': self.params,
    364                    '_i18n.choose.SingularDirective': None,
    365                    '_i18n.choose.PluralDirective': None})
     362                   '_i18n.choose.singular': None,
     363                   '_i18n.choose.plural': None})
     364
     365        ngettext = ctxt.get('_i18n.ngettext')
     366        assert hasattr(ngettext, '__call__'), 'No ngettext function available'
     367        dngettext = ctxt.get('_i18n.dngettext')
     368        if not dngettext:
     369            dngettext = lambda d, s, p, n: ngettext(s, p, n)
    366370
    367371        new_stream = []
     
    371375        plural_msgbuf = None
    372376
    373         ngettext = ctxt.get('_i18n.ungettext')
    374         assert hasattr(ngettext, '__call__'), 'No ngettext function available'
    375         dngettext = ctxt.get('_i18n.dngettext')
    376         if not dngettext:
    377             dngettext = lambda d, s, p, n: ngettext(s, p, n)
    378 
    379         for kind, event, pos in stream:
    380             if kind is SUB:
    381                 subdirectives, substream = event
    382                 if isinstance(subdirectives[0],
    383                               SingularDirective) and not singular_stream:
    384                     strip_directive_present = []
    385                     for idx, subdirective in enumerate(subdirectives):
    386                         if isinstance(subdirective, StripDirective):
    387                             # Any strip directive should be applied AFTER
    388                             # the event's have been translated.
    389                             strip_directive_present.append(
    390                                 subdirectives.pop(idx)
    391                             )
    392                     # Apply directives to update context
     377        numeral = self.numeral.evaluate(ctxt)
     378        is_plural = self._is_plural(numeral, ngettext)
     379
     380        for event in stream:
     381            if event[0] is SUB and any(isinstance(d, ChooseBranchDirective)
     382                                       for d in event[1][0]):
     383                subdirectives, substream = event[1]
     384
     385                if isinstance(subdirectives[0], SingularDirective):
    393386                    singular_stream = list(_apply_directives(substream,
    394387                                                             subdirectives,
    395388                                                             ctxt, vars))
    396                     if strip_directive_present:
    397                         singular_stream = list(
    398                             _apply_directives(singular_stream,
    399                                               strip_directive_present,
    400                                               ctxt, vars)
    401                         )
    402                     del strip_directive_present
    403                     new_stream.append((MSGBUF, (), ('', -1))) # msgbuf place holder
    404                     singular_msgbuf = ctxt.get('_i18n.choose.SingularDirective')
    405                 elif isinstance(subdirectives[0],
    406                                 PluralDirective) and not plural_stream:
    407                     strip_directive_present = []
    408                     for idx, subdirective in enumerate(subdirectives):
    409                         if isinstance(subdirective, StripDirective):
    410                             # Any strip directive should be applied AFTER
    411                             # the event's have been translated.
    412                             strip_directive_present.append(
    413                                 subdirectives.pop(idx)
    414                             )
    415                     # Apply directives to update context
    416                     plural_stream = list(_apply_directives(substream,
    417                                                            subdirectives,
    418                                                            ctxt, vars))
    419                     if strip_directive_present:
    420                         plural_stream = list(
    421                             _apply_directives(plural_stream,
    422                                               strip_directive_present,
    423                                               ctxt, vars)
    424                         )
    425                     del strip_directive_present
    426                     plural_msgbuf = ctxt.get('_i18n.choose.PluralDirective')
    427                 else:
    428                     new_stream.append((kind, event, pos))
     389                    new_stream.append((MSGBUF, None, (None, -1, -1)))
     390
     391                elif isinstance(subdirectives[0], PluralDirective):
     392                    if is_plural:
     393                        plural_stream = list(_apply_directives(substream,
     394                                                               subdirectives,
     395                                                               ctxt, vars))
     396
    429397            else:
    430                 new_stream.append((kind, event, pos))
     398                new_stream.append(event)
    431399
    432400        if ctxt.get('_i18n.domain'):
     
    434402                                                 s, p, n)
    435403
    436         # XXX: should we test which form was chosen like this!?!?!?
    437         # There should be no match in any catalogue for these singular and
    438         # plural test strings
    439         singular_test = u'O\x85\xbe\xa9\xa8az\xc3?\xe6\xa1\x02n\x84\x93'
    440         plural_test = u'\xcc\xfb+\xd3Pn\x9d\tT\xec\x1d\xda\x1a\x88\x00'
    441         translation = ngettext(singular_test, plural_test,
    442                                self.numeral.evaluate(ctxt))
    443         if translation == singular_test:
    444             chosen_msgbuf = singular_msgbuf
    445             chosen_stream = singular_stream
     404        singular_msgbuf = ctxt.get('_i18n.choose.singular')
     405        if is_plural:
     406            plural_msgbuf = ctxt.get('_i18n.choose.plural')
     407            msgbuf, choice = plural_msgbuf, plural_stream
    446408        else:
    447             chosen_msgbuf = plural_msgbuf
    448             chosen_stream = plural_stream
    449         del singular_test, plural_test, translation
     409            msgbuf, choice = singular_msgbuf, singular_stream
     410            plural_msgbuf = MessageBuffer(self)
    450411
    451412        for kind, data, pos in new_stream:
    452413            if kind is MSGBUF:
    453                 for skind, sdata, spos in chosen_stream:
    454                     if skind is MSGBUF:
     414                for event in choice:
     415                    if event[0] is MSGBUF:
    455416                        translation = ngettext(singular_msgbuf.format(),
    456417                                               plural_msgbuf.format(),
    457                                                self.numeral.evaluate(ctxt))
    458                         for event in chosen_msgbuf.translate(translation):
    459                             yield event
     418                                               numeral)
     419                        for subevent in msgbuf.translate(translation):
     420                            yield subevent
    460421                    else:
    461                         yield skind, sdata, spos
     422                        yield event
    462423            else:
    463424                yield kind, data, pos
     
    518479            comment_stack[-1:]
    519480
     481    def _is_plural(self, numeral, ngettext):
     482        # XXX: should we test which form was chosen like this!?!?!?
     483        # There should be no match in any catalogue for these singular and
     484        # plural test strings
     485        singular = u'O\x85\xbe\xa9\xa8az\xc3?\xe6\xa1\x02n\x84\x93'
     486        plural = u'\xcc\xfb+\xd3Pn\x9d\tT\xec\x1d\xda\x1a\x88\x00'
     487        return ngettext(singular, plural, numeral) == plural
     488
    520489
    521490class DomainDirective(I18NDirective):
     
    559528                 offset=-1):
    560529        Directive.__init__(self, None, template, namespaces, lineno, offset)
    561         self.domain = value and value.strip() or '__DEFAULT__' 
     530        self.domain = value and value.strip() or '__DEFAULT__'
    562531
    563532    @classmethod
     
    664633        self.extract_text = extract_text
    665634
    666     def __call__(self, stream, ctxt=None, search_text=True):
     635    def __call__(self, stream, ctxt=None, translate_text=True,
     636                 translate_attrs=True):
    667637        """Translate any localizable strings in the given stream.
    668638       
     
    675645        :param stream: the markup event stream
    676646        :param ctxt: the template context (not used)
    677         :param search_text: whether text nodes should be translated (used
    678                             internally)
     647        :param translate_text: whether text nodes should be translated (used
     648                               internally)
     649        :param translate_attrs: whether attribute values should be translated
     650                                (used internally)
    679651        :return: the localized stream
    680652        """
     
    683655        skip = 0
    684656        xml_lang = XML_NAMESPACE['lang']
     657        if not self.extract_text:
     658            translate_text = False
     659            translate_attrs = False
    685660
    686661        if type(self.translate) is FunctionType:
     
    690665        else:
    691666            gettext = self.translate.ugettext
     667            ngettext = self.translate.ungettext
    692668            try:
    693669                dgettext = self.translate.dugettext
    694             except AttributeError:
    695                 dgettext = lambda x, y: gettext(y)
    696             ngettext = self.translate.ungettext
    697             try:
    698670                dngettext = self.translate.dungettext
    699671            except AttributeError:
    700                 dngettext = lambda d, s, p, n: ngettext(s, p, n)
    701 
     672                dgettext = lambda _, y: gettext(y)
     673                dngettext = lambda _, s, p, n: ngettext(s, p, n)
    702674            if ctxt:
    703675                ctxt['_i18n.gettext'] = gettext
    704                 ctxt['_i18n.ugettext'] = gettext
     676                ctxt['_i18n.ngettext'] = ngettext
    705677                ctxt['_i18n.dgettext'] = dgettext
    706                 ctxt['_i18n.ngettext'] = ngettext
    707                 ctxt['_i18n.ungettext'] = ngettext
    708678                ctxt['_i18n.dngettext'] = dngettext
    709679
    710         extract_text = self.extract_text
    711         if not extract_text:
    712             search_text = False
    713 
    714680        if ctxt and ctxt.get('_i18n.domain'):
    715             old_gettext = gettext
    716681            gettext = lambda msg: dgettext(ctxt.get('_i18n.domain'), msg)
    717682
     
    741706                for name, value in attrs:
    742707                    newval = value
    743                     if extract_text and isinstance(value, basestring):
    744                         if name in include_attrs:
     708                    if isinstance(value, basestring):
     709                        if translate_attrs and name in include_attrs:
    745710                            newval = gettext(value)
    746711                    else:
    747712                        newval = list(
    748                             self(_ensure(value), ctxt, search_text=False)
     713                            self(_ensure(value), ctxt, translate_text=False)
    749714                        )
    750715                    if newval != value:
     
    757722                yield kind, (tag, attrs), pos
    758723
    759             elif search_text and kind is TEXT:
     724            elif translate_text and kind is TEXT:
    760725                text = data.strip()
    761726                if text:
     
    768733                for idx, directive in enumerate(directives):
    769734                    # Organize directives to make everything work
     735                    # FIXME: There's got to be a better way to do this!
    770736                    if isinstance(directive, DomainDirective):
    771737                        # Grab current domain and update context
     
    783749                ])
    784750                substream = list(self(substream, ctxt,
    785                                       search_text=not is_i18n_directive))
     751                                      translate_text=not is_i18n_directive,
     752                                      translate_attrs=translate_attrs))
    786753                yield kind, (directives, substream), pos
    787754
     
    10491016        """
    10501017        substream = None
    1051        
     1018
    10521019        def yield_parts(string):
    10531020            for idx, part in enumerate(regex.split(string)):
Note: See TracChangeset for help on using the changeset viewer.