Edgewall Software

Ticket #129: i18n_choose.patch

File i18n_choose.patch, 10.1 KB (added by palgarvio, 8 years ago)

Extraction currently working now, I think! ;)

  • genshi/filters/i18n.py

    diff --git a/genshi/filters/i18n.py b/genshi/filters/i18n.py
    a b  
    1717"""
    1818
    1919from compiler import ast
    20 from gettext import gettext
     20from gettext import NullTranslations
    2121import re
    2222
    2323from genshi.core import Attrs, Namespace, QName, START, END, TEXT, START_NS, \
     
    3535    """Can extract and translate localizable strings from markup streams and
    3636    templates.
    3737   
    38     For example, assume the followng template:
     38    For example, assume the following template:
    3939   
    4040    >>> from genshi.template import MarkupTemplate
    4141    >>>
     
    9191    INCLUDE_ATTRS = frozenset(['abbr', 'alt', 'label', 'prompt', 'standby',
    9292                               'summary', 'title'])
    9393
    94     def __init__(self, translate=gettext, ignore_tags=IGNORE_TAGS,
     94    def __init__(self, translator=NullTranslations(), ignore_tags=IGNORE_TAGS,
    9595                 include_attrs=INCLUDE_ATTRS, extract_text=True):
    9696        """Initialize the translator.
    9797       
     
    103103                             extracted, or only text in explicit ``gettext``
    104104                             function calls
    105105        """
    106         self.translate = translate
     106        self.translator = translator
    107107        self.ignore_tags = ignore_tags
    108108        self.include_attrs = include_attrs
    109109        self.extract_text = extract_text
     
    126126        """
    127127        ignore_tags = self.ignore_tags
    128128        include_attrs = self.include_attrs
    129         translate = self.translate
     129        try:
     130            # Unicode aware version first
     131            gettext = self.translator.ugettext
     132        except AttributeError:
     133            gettext = self.translator.gettext
     134
    130135        if not self.extract_text:
    131136            search_text = False
    132137        skip = 0
     
    160165                    newval = value
    161166                    if search_text and isinstance(value, basestring):
    162167                        if name in include_attrs:
    163                             newval = self.translate(value)
     168                            newval = gettext(value)
    164169                    else:
    165170                        newval = list(self(_ensure(value), ctxt,
    166171                            search_text=False)
     
    177182                    continue
    178183                elif i18n_msg in attrs:
    179184                    params = attrs.get(i18n_msg)
    180                     if params and type(params) is list: # event tuple
    181                         params = params[0][1]
    182185                    msgbuf = MessageBuffer(params)
    183186                    attrs -= i18n_msg
    184187
     
    188191                if not msgbuf:
    189192                    text = data.strip()
    190193                    if text:
    191                         data = data.replace(text, unicode(translate(text)))
     194                        data = data.replace(text, unicode(gettext(text)))
    192195                    yield kind, data, pos
    193196                else:
    194197                    msgbuf.append(kind, data, pos)
     
    199202            elif not skip and msgbuf and kind is END:
    200203                msgbuf.append(kind, data, pos)
    201204                if not msgbuf.depth:
    202                     for event in msgbuf.translate(translate(msgbuf.format())):
     205                    for event in msgbuf.translate(gettext(msgbuf.format())):
    203206                        yield event
    204207                    msgbuf = None
    205208                    yield kind, data, pos
     
    271274        skip = 0
    272275        i18n_msg = I18N_NAMESPACE['msg']
    273276        i18n_comment = I18N_NAMESPACE['comment']
     277        i18n_choose = I18N_NAMESPACE['choose']
    274278        xml_lang = XML_NAMESPACE['lang']
    275279
    276280        for kind, data, pos in stream:
     
    305309                    msgbuf.append(kind, data, pos)
    306310                elif i18n_msg in attrs:
    307311                    params = attrs.get(i18n_msg)
    308                     if params and type(params) is list: # event tuple
    309                         params = params[0][1]
     312                    msgbuf = MessageBuffer(params, pos[1])
     313                elif i18n_choose in attrs:
     314                    params = attrs.get(i18n_choose)
    310315                    msgbuf = MessageBuffer(params, pos[1])
    311316                if i18n_comment in attrs and msgbuf:
    312317                    msgbuf.comments.append(attrs.get(i18n_comment))
     
    322327            elif not skip and msgbuf and kind is END:
    323328                msgbuf.append(kind, data, pos)
    324329                if not msgbuf.depth:
    325                     yield msgbuf.lineno, None, msgbuf.format(), msgbuf.comments
     330                    if msgbuf.singular or msgbuf.plural:
     331                         yield msgbuf.lineno, 'ngettext', msgbuf.format(), \
     332                                                                msgbuf.comments
     333                    else:
     334                        yield msgbuf.lineno, None, msgbuf.format(), \
     335                                                                msgbuf.comments
    326336                    msgbuf = None
    327337
    328338            elif kind is EXPR or kind is EXEC:
     
    340350                for lineno, funcname, text, comments in messages:
    341351                    yield lineno, funcname, text, comments
    342352
    343 
     353   
    344354class MessageBuffer(object):
    345355    """Helper class for managing internationalized mixed content.
    346356   
     
    355365        :param lineno: the line number on which the first stream event
    356366                       belonging to the message was found
    357367        """
    358         self.params = [name.strip() for name in params.split(',')]
     368        if isinstance(params, (list, tuple)):
     369            self.params = []
     370            for entry in params:
     371                if entry[0] == 'TEXT':
     372                    for param in entry[1].split(','):
     373                        if param:
     374                            self.params.append(param.strip())
     375                elif entry[0] == 'EXPR':
     376                    self.choose_numeral = entry[1]
     377        else:
     378            self.params = params
     379        self.singular_params = self.params[:]
     380        self.plural_params = self.params[:]
    359381        self.lineno = lineno
    360382        self.string = []
     383        self.singular = []
     384        self.plural = []
    361385        self.events = {}
    362386        self.values = {}
     387        self.singular_values = {}
     388        self.plural_values = {}
    363389        self.depth = 1
    364390        self.order = 1
     391        self.choose_order = 1
    365392        self.stack = [0]
    366393        self.comments = []
     394        self.i18n_choose_singular = I18N_NAMESPACE['singular']
     395        self.i18n_choose_plural = I18N_NAMESPACE['plural']
     396        self.choose_singular = False
     397        self.choose_plural = False
    367398
    368399    def append(self, kind, data, pos):
    369400        """Append a stream event to the buffer.
     
    373404        :param pos: the position of the event in the source
    374405        """
    375406        if kind is TEXT:
    376             self.string.append(data)
     407            if self.choose_singular:
     408                self.singular.append(data)
     409            if self.choose_plural:
     410                self.plural.append(data)
     411            else:
     412                self.string.append(data)
    377413            self.events.setdefault(self.stack[-1], []).append(None)
    378414        elif kind is EXPR:
    379             param = self.params.pop(0)
    380             self.string.append('%%(%s)s' % param)
     415            if self.choose_singular:
     416                param = self.singular_params.pop(0)
     417                self.singular.append('%%(%s)s' % param)
     418                self.singular_values[param] = (kind, data, pos)         
     419            elif self.choose_plural:
     420                param = self.plural_params.pop(0)
     421                self.plural.append('%%(%s)s' % param)
     422                self.plural_values[param] = (kind, data, pos)
     423            else:
     424                param = self.params.pop(0)
     425                self.string.append('%%(%s)s' % param)
     426                self.values[param] = (kind, data, pos)
    381427            self.events.setdefault(self.stack[-1], []).append(None)
    382             self.values[param] = (kind, data, pos)
    383428        else:
    384429            if kind is START:
    385                 self.string.append(u'[%d:' % self.order)
     430                if self.choose_singular:
     431                    self.ordered_singular = True
     432                    self.singular.append(u'[%d:' % self.choose_order)
     433                elif self.choose_plural:
     434                    self.ordered_plural = True
     435                    self.plural.append(u'[%d:' % self.choose_order)
     436                    self.choose_order += 1
     437                elif self.i18n_choose_singular in data[1]:
     438                    self.choose_singular = True
     439                    self.choose_plural = False
     440                elif self.i18n_choose_plural in data[1]:
     441                    self.choose_plural = True
     442                    self.choose_singular = False
     443                else:
     444                    self.string.append(u'[%d:' % self.order)
     445                    self.order += 1
    386446                self.events.setdefault(self.order, []).append((kind, data, pos))
    387447                self.stack.append(self.order)
    388448                self.depth += 1
    389                 self.order += 1
    390449            elif kind is END:
    391450                self.depth -= 1
    392451                if self.depth:
     452                    if self.choose_singular:
     453                        self.choose_singular = False
     454                        if self.choose_order > 1:
     455                            self.singular.append(u']')
     456                            self.ordered_singular = False
     457                    elif self.choose_plural:
     458                        self.choose_plural = False
     459                        if self.choose_order > 1:
     460                            self.plural.append(u']')
     461                    else:                       
     462                        self.string.append(u']')
    393463                    self.events[self.stack[-1]].append((kind, data, pos))
    394                     self.string.append(u']')
    395464                    self.stack.pop()
    396465
    397466    def format(self):
    398467        """Return a message identifier representing the content in the
    399468        buffer.
    400469        """
     470        if self.singular or self.plural:
     471            return (u''.join(self.singular).strip(),
     472                    u''.join(self.plural).strip())
    401473        return u''.join(self.string).strip()
    402474
    403475    def translate(self, string, regex=re.compile(r'%\((\w+)\)s')):