Edgewall Software

Ticket #129: i18n_choose.patch

File i18n_choose.patch, 10.1 KB (added by palgarvio, 6 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')):