Edgewall Software

Ticket #68: cldr_aliases.3.diff

File cldr_aliases.3.diff, 20.5 KB (added by cmlenz, 4 years ago)

Yet another patch update

  • babel/core.py

     
    223223 
    224224    def _data(self): 
    225225        if self.__data is None: 
    226             self.__data = localedata.load(str(self)) 
     226            self.__data = localedata.LocaleDataDict(localedata.load(str(self))) 
    227227        return self.__data 
    228228    _data = property(_data) 
    229229 
     
    326326        Mapping of script codes to translated script names. 
    327327         
    328328        >>> Locale('de', 'DE').variants['1901'] 
    329         u'alte deutsche Rechtschreibung' 
     329        u'Alte deutsche Rechtschreibung' 
    330330         
    331331        :type: `dict` 
    332332        """) 
     
    481481        >>> Locale('en', 'US').time_zones['Europe/London']['long']['daylight'] 
    482482        u'British Summer Time' 
    483483        >>> Locale('en', 'US').time_zones['America/St_Johns']['city'] 
    484         u'St. John\u2019s' 
     484        u"St. John's" 
    485485         
    486486        :type: `dict` 
    487487        """) 
  • babel/localedata.py

     
    2323    import threading 
    2424except ImportError: 
    2525    import dummy_threading as threading 
     26from UserDict import DictMixin 
    2627 
    2728__all__ = ['exists', 'load'] 
    2829__docformat__ = 'restructuredtext en' 
     
    3132_cache_lock = threading.RLock() 
    3233_dirname = os.path.join(os.path.dirname(__file__), 'localedata') 
    3334 
     35 
    3436def exists(name): 
    3537    """Check whether locale data is available for the given locale. 
    3638     
     
    4244        return True 
    4345    return os.path.exists(os.path.join(_dirname, '%s.dat' % name)) 
    4446 
     47 
    4548def list(): 
    4649    """Return a list of all locale identifiers for which locale data is 
    4750    available. 
     
    5457        os.path.splitext(filename) for filename in os.listdir(_dirname) 
    5558    ] if extension == '.dat' and stem != 'root'] 
    5659 
     60 
    5761def load(name): 
    5862    """Load the locale data for the given locale. 
    5963     
     
    107111    finally: 
    108112        _cache_lock.release() 
    109113 
     114 
    110115def merge(dict1, dict2): 
    111116    """Merge the data from `dict2` into the `dict1` dictionary, making copies 
    112117    of nested dictionaries. 
    113118     
     119    >>> d = {1: 'foo', 3: 'baz'} 
     120    >>> merge(d, {1: 'Foo', 2: 'Bar'}) 
     121    >>> d 
     122    {1: 'Foo', 2: 'Bar', 3: 'baz'} 
     123     
     124    >>> d = {1: {1: 'FOO', 2: 'BAR'}, 2: 'foo'} 
     125    >>> merge(d, {1: {3: 'BAZ'}}) 
     126    >>> d 
     127    {1: {1: 'FOO', 2: 'BAR', 3: 'BAZ'}, 2: 'foo'} 
     128     
    114129    :param dict1: the dictionary to merge into 
    115130    :param dict2: the dictionary containing the data that should be merged 
    116131    """ 
    117     for key, value in dict2.items(): 
    118         if value is not None: 
    119             if type(value) is dict: 
    120                 dict1[key] = dict1.get(key, {}).copy() 
    121                 merge(dict1[key], value) 
     132    for key, val2 in dict2.items(): 
     133        if val2 is not None: 
     134            if type(val2) is dict: 
     135                val1 = dict1.get(key, {}) 
     136                if type(val1) is Alias: 
     137                    dict1[key] = (val1, val2) 
     138                else: 
     139                    dict1[key] = val1.copy() 
     140                    merge(dict1[key], val2) 
     141            elif type(val2) is Alias: 
     142                dict1[key] = (val2, dict1[key]) 
    122143            else: 
    123                 dict1[key] = value 
     144                val1 = dict1.get(key) 
     145                if type(val1) is Alias: 
     146                    dict1[key] = (val1, val2) 
     147                else: 
     148                    dict1[key] = val2 
     149 
     150 
     151class Alias(object): 
     152 
     153    def __init__(self, keys): 
     154        self.keys = keys 
     155 
     156    def __repr__(self): 
     157        return '<%s %r>' % (type(self).__name__, self.keys) 
     158 
     159    def resolve(self, data): 
     160        base = data 
     161        for key in self.keys: 
     162            data = data[key] 
     163        if type(data) is Alias: 
     164            data = data.resolve(base) 
     165        return data 
     166 
     167 
     168class LocaleDataDict(DictMixin, dict): 
     169 
     170    def __init__(self, data, base=None): 
     171        dict.__init__(self, data) 
     172        if base is None: 
     173            base = self 
     174        self.base = base 
     175 
     176    def __getitem__(self, key): 
     177        val = dict.__getitem__(self, key) 
     178        if type(val) is Alias: # resolve an alias 
     179            val = val.resolve(self.base) 
     180        if type(val) is tuple: # Merge a partial dict with an alias 
     181            alias, others = val 
     182            val = alias.resolve(self.base).copy() 
     183            merge(val, others) 
     184        if type(val) is dict: # Return a nested alias-resolving dict 
     185            val = LocaleDataDict(val, base=self.base) 
     186        return val 
     187 
     188    def copy(self): 
     189        return LocaleDataDict(dict.copy(self), base=self.base) 
  • babel/tests/localedata.py

     
    1616 
    1717from babel import localedata 
    1818 
     19 
     20class MergeResolveTestCase(unittest.TestCase): 
     21 
     22    def test_merge_items(self): 
     23        d = {1: 'foo', 3: 'baz'} 
     24        localedata.merge(d, {1: 'Foo', 2: 'Bar'}) 
     25        self.assertEqual({1: 'Foo', 2: 'Bar', 3: 'baz'}, d) 
     26 
     27    def test_merge_nested_dict(self): 
     28        d1 = { 
     29            'x': {'a': 1, 'b': 2, 'c': 3} 
     30        } 
     31        d2 = { 
     32            'x': {'a': 1, 'b': 12, 'd': 14} 
     33        } 
     34        localedata.merge(d1, d2) 
     35        self.assertEqual({ 
     36            'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14} 
     37        }, d1) 
     38 
     39 
     40    def test_merge_with_alias_and_resolve(self): 
     41        alias = localedata.Alias('x') 
     42        d1 = { 
     43            'x': {'a': 1, 'b': 2, 'c': 3}, 
     44            'y': alias 
     45        } 
     46        d2 = { 
     47            'x': {'a': 1, 'b': 12, 'd': 14}, 
     48            'y': {'b': 22, 'e': 25} 
     49        } 
     50        localedata.merge(d1, d2) 
     51        self.assertEqual({ 
     52            'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}, 
     53            'y': (alias, {'b': 22, 'e': 25}) 
     54        }, d1) 
     55        d = localedata.LocaleDataDict(d1) 
     56        self.assertEqual({ 
     57            'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}, 
     58            'y': {'a': 1, 'b': 22, 'c': 3, 'd': 14, 'e': 25} 
     59        }, dict(d.items())) 
     60 
     61 
    1962def suite(): 
    2063    suite = unittest.TestSuite() 
    2164    suite.addTest(doctest.DocTestSuite(localedata)) 
     65    suite.addTest(unittest.makeSuite(MergeResolveTestCase)) 
    2266    return suite 
    2367 
    2468if __name__ == '__main__': 
  • babel/tests/dates.py

     
    2929        fmt = dates.DateTimeFormat(d, locale='cs_CZ') 
    3030        self.assertEqual('1.', fmt['LLL']) 
    3131 
     32    def test_abbreviated_month_alias(self): 
     33        d = date(2006, 3, 8) 
     34        fmt = dates.DateTimeFormat(d, locale='de_DE') 
     35        self.assertEqual(u'Mär', fmt['LLL']) 
     36 
    3237    def test_week_of_year_first(self): 
    3338        d = date(2006, 1, 8) 
    3439        fmt = dates.DateTimeFormat(d, locale='de_DE') 
     
    187192        tz = timezone('Europe/Paris') 
    188193        t = time(15, 30, tzinfo=tz) 
    189194        fmt = dates.DateTimeFormat(t, locale='fr_FR') 
    190         self.assertEqual(u'Heure de l’Europe centrale', fmt['vvvv']) 
     195        self.assertEqual(u'heure d’Europe centrale', fmt['vvvv']) 
    191196 
    192197    def test_hour_formatting(self): 
    193198        l = 'en_US' 
  • babel/numbers.py

     
    165165    >>> format_currency(1099.98, 'USD', locale='en_US') 
    166166    u'$1,099.98' 
    167167    >>> format_currency(1099.98, 'USD', locale='es_CO') 
    168     u'US$ 1.099,98' 
     168    u'US$\\xa01.099,98' 
    169169    >>> format_currency(1099.98, 'EUR', locale='de_DE') 
    170     u'1.099,98 \\u20ac' 
     170    u'1.099,98\\xa0\\u20ac' 
    171171     
    172172    The pattern can also be specified explicitly: 
    173173     
  • babel/dates.py

     
    189189    string is used for GMT: 
    190190     
    191191    >>> get_timezone_gmt(dt, 'long', locale='fr_FR') 
    192     u'HMG-08:00' 
     192    u'UTC-08:00' 
    193193     
    194194    :param datetime: the ``datetime`` object; if `None`, the current date and 
    195195                     time in UTC is used 
  • scripts/import_cldr.py

     
    1616from optparse import OptionParser 
    1717import os 
    1818import pickle 
     19import re 
    1920import sys 
    2021try: 
    2122    from xml.etree.ElementTree import parse 
     
    2627sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..')) 
    2728 
    2829from babel import dates, numbers 
     30from babel.localedata import Alias 
    2931 
    3032weekdays = {'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3, 'fri': 4, 'sat': 5, 
    3133            'sun': 6} 
     
    3638    def any(iterable): 
    3739        return filter(None, list(iterable)) 
    3840 
     41 
    3942def _text(elem): 
    4043    buf = [elem.text or ''] 
    4144    for child in elem: 
     
    4346    buf.append(elem.tail or '') 
    4447    return u''.join(filter(None, buf)).strip() 
    4548 
     49 
     50NAME_RE = re.compile(r"^\w+$") 
     51TYPE_ATTR_RE = re.compile(r"^\w+\[@type='(.*?)'\]$") 
     52 
     53NAME_MAP = { 
     54    'dateFormats': 'date_formats', 
     55    'dateTimeFormats': 'datetime_formats', 
     56    'eraAbbr': 'abbreviated', 
     57    'eraNames': 'wide', 
     58    'eraNarrow': 'narrow', 
     59    'timeFormats': 'time_formats' 
     60} 
     61 
     62def _translate_alias(ctxt, path): 
     63    parts = path.split('/') 
     64    keys = ctxt[:] 
     65    for part in parts: 
     66        if part == '..': 
     67            keys.pop() 
     68        else: 
     69            match = TYPE_ATTR_RE.match(part) 
     70            if match: 
     71                keys.append(match.group(1)) 
     72            else: 
     73                assert NAME_RE.match(part) 
     74                keys.append(NAME_MAP.get(part, part)) 
     75    return keys 
     76 
     77 
    4678def main(): 
    4779    parser = OptionParser(usage='%prog path/to/cldr') 
    4880    options, args = parser.parse_args() 
     
    109141        stem, ext = os.path.splitext(filename) 
    110142        if ext != '.xml': 
    111143            continue 
     144        #if stem != 'root': 
     145        #    break 
    112146 
    113147        tree = parse(os.path.join(srcdir, 'main', filename)) 
    114148        data = {} 
     
    227261 
    228262            months = data.setdefault('months', {}) 
    229263            for ctxt in calendar.findall('months/monthContext'): 
    230                 ctxts = months.setdefault(ctxt.attrib['type'], {}) 
     264                ctxt_type = ctxt.attrib['type'] 
     265                ctxts = months.setdefault(ctxt_type, {}) 
    231266                for width in ctxt.findall('monthWidth'): 
    232                     widths = ctxts.setdefault(width.attrib['type'], {}) 
    233                     for elem in width.findall('month'): 
    234                         if 'draft' in elem.attrib and int(elem.attrib['type']) in widths: 
    235                             continue 
    236                         widths[int(elem.attrib.get('type'))] = unicode(elem.text) 
     267                    width_type = width.attrib['type'] 
     268                    widths = ctxts.setdefault(width_type, {}) 
     269                    for elem in width.getiterator(): 
     270                        if elem.tag == 'month': 
     271                            if 'draft' in elem.attrib and int(elem.attrib['type']) in widths: 
     272                                continue 
     273                            widths[int(elem.attrib.get('type'))] = unicode(elem.text) 
     274                        elif elem.tag == 'alias': 
     275                            assert elem.attrib['source'] == 'locale' 
     276                            ctxts[width_type] = Alias( 
     277                                _translate_alias(['months', ctxt_type, width_type], 
     278                                                 elem.attrib['path']) 
     279                            ) 
    237280 
    238281            days = data.setdefault('days', {}) 
    239282            for ctxt in calendar.findall('days/dayContext'): 
    240                 ctxts = days.setdefault(ctxt.attrib['type'], {}) 
     283                ctxt_type = ctxt.attrib['type'] 
     284                ctxts = days.setdefault(ctxt_type, {}) 
    241285                for width in ctxt.findall('dayWidth'): 
    242                     widths = ctxts.setdefault(width.attrib['type'], {}) 
    243                     for elem in width.findall('day'): 
    244                         dtype = weekdays[elem.attrib['type']] 
    245                         if 'draft' in elem.attrib and dtype in widths: 
    246                             continue 
    247                         widths[dtype] = unicode(elem.text) 
     286                    width_type = width.attrib['type'] 
     287                    widths = ctxts.setdefault(width_type, {}) 
     288                    for elem in width.getiterator(): 
     289                        if elem.tag == 'day': 
     290                            dtype = weekdays[elem.attrib['type']] 
     291                            if 'draft' in elem.attrib and dtype in widths: 
     292                                continue 
     293                            widths[dtype] = unicode(elem.text) 
     294                        elif elem.tag == 'alias': 
     295                            assert elem.attrib['source'] == 'locale' 
     296                            ctxts[width_type] = Alias( 
     297                                _translate_alias(['days', ctxt_type, width_type], 
     298                                                 elem.attrib['path']) 
     299                            ) 
    248300 
    249301            quarters = data.setdefault('quarters', {}) 
    250302            for ctxt in calendar.findall('quarters/quarterContext'): 
     303                ctxt_type = ctxt.attrib['type'] 
    251304                ctxts = quarters.setdefault(ctxt.attrib['type'], {}) 
    252305                for width in ctxt.findall('quarterWidth'): 
    253                     widths = ctxts.setdefault(width.attrib['type'], {}) 
    254                     for elem in width.findall('quarter'): 
     306                    width_type = width.attrib['type'] 
     307                    widths = ctxts.setdefault(width_type, {}) 
     308                    for elem in width.getiterator(): 
     309                        if elem.tag == 'quarter': 
     310                            if 'draft' in elem.attrib and int(elem.attrib['type']) in widths: 
     311                                continue 
     312                            widths[int(elem.attrib.get('type'))] = unicode(elem.text) 
     313                        elif elem.tag == 'alias': 
     314                            assert elem.attrib['source'] == 'locale' 
     315                            ctxts[width_type] = Alias( 
     316                                _translate_alias(['quarters', ctxt_type, width_type], 
     317                                                 elem.attrib['path']) 
     318                            ) 
     319 
     320            eras = data.setdefault('eras', {}) 
     321            for width in calendar.findall('eras/*'): 
     322                width_type = NAME_MAP[width.tag] 
     323                widths = eras.setdefault(width_type, {}) 
     324                for elem in width.getiterator(): 
     325                    if elem.tag == 'era': 
    255326                        if 'draft' in elem.attrib and int(elem.attrib['type']) in widths: 
    256327                            continue 
    257328                        widths[int(elem.attrib.get('type'))] = unicode(elem.text) 
     329                    elif elem.tag == 'alias': 
     330                        assert elem.attrib['source'] == 'locale' 
     331                        eras[width_type] = Alias( 
     332                            _translate_alias(['eras', width_type], 
     333                                             elem.attrib['path']) 
     334                        ) 
    258335 
    259             eras = data.setdefault('eras', {}) 
    260             for width in calendar.findall('eras/*'): 
    261                 ewidth = { 
    262                     'eraAbbr': 'abbreviated', 
    263                     'eraNames': 'wide', 
    264                     'eraNarrow': 'narrow', 
    265                 }[width.tag] 
    266                 widths = eras.setdefault(ewidth, {}) 
    267                 for elem in width.findall('era'): 
    268                     if 'draft' in elem.attrib and int(elem.attrib['type']) in widths: 
    269                         continue 
    270                     widths[int(elem.attrib.get('type'))] = unicode(elem.text) 
    271  
    272336            # AM/PM 
    273337            periods = data.setdefault('periods', {}) 
    274338            for elem in calendar.findall('am'): 
    275                 if 'draft' in elem.attrib and elem.tag in periods: 
     339                if ('draft' in elem.attrib or 'alt' in elem.attrib) and elem.tag in periods: 
    276340                    continue 
    277341                periods[elem.tag] = unicode(elem.text) 
    278342            for elem in calendar.findall('pm'): 
    279                 if 'draft' in elem.attrib and elem.tag in periods: 
     343                if ('draft' in elem.attrib or 'alt' in elem.attrib) and elem.tag in periods: 
    280344                    continue 
    281345                periods[elem.tag] = unicode(elem.text) 
    282346 
    283347            date_formats = data.setdefault('date_formats', {}) 
    284             for elem in calendar.findall('dateFormats/dateFormatLength'): 
    285                 if 'draft' in elem.attrib and elem.attrib.get('type') in date_formats: 
    286                     continue 
    287                 try: 
    288                     date_formats[elem.attrib.get('type')] = \ 
    289                         dates.parse_pattern(unicode(elem.findtext('dateFormat/pattern'))) 
    290                 except ValueError, e: 
    291                     print>>sys.stderr, 'ERROR: %s' % e 
     348            for format in calendar.findall('dateFormats'): 
     349                for elem in format.getiterator(): 
     350                    if elem.tag == 'dateFormatLength': 
     351                        if 'draft' in elem.attrib and \ 
     352                                elem.attrib.get('type') in date_formats: 
     353                            continue 
     354                        try: 
     355                            date_formats[elem.attrib.get('type')] = \ 
     356                                dates.parse_pattern(unicode(elem.findtext('dateFormat/pattern'))) 
     357                        except ValueError, e: 
     358                            print>>sys.stderr, 'ERROR: %s' % e 
     359                    elif elem.tag == 'alias': 
     360                        date_formats = Alias(_translate_alias( 
     361                            ['date_formats'], elem.attrib['path']) 
     362                        ) 
    292363 
    293364            time_formats = data.setdefault('time_formats', {}) 
    294             for elem in calendar.findall('timeFormats/timeFormatLength'): 
    295                 if 'draft' in elem.attrib and elem.attrib.get('type') in time_formats: 
    296                     continue 
    297                 try: 
    298                     time_formats[elem.attrib.get('type')] = \ 
    299                         dates.parse_pattern(unicode(elem.findtext('timeFormat/pattern'))) 
    300                 except ValueError, e: 
    301                     print>>sys.stderr, 'ERROR: %s' % e 
     365            for format in calendar.findall('timeFormats'): 
     366                for elem in format.getiterator(): 
     367                    if elem.tag == 'timeFormatLength': 
     368                        if 'draft' in elem.attrib and \ 
     369                                elem.attrib.get('type') in time_formats: 
     370                            continue 
     371                        try: 
     372                            time_formats[elem.attrib.get('type')] = \ 
     373                                dates.parse_pattern(unicode(elem.findtext('timeFormat/pattern'))) 
     374                        except ValueError, e: 
     375                            print>>sys.stderr, 'ERROR: %s' % e 
     376                    elif elem.tag == 'alias': 
     377                        time_formats = Alias(_translate_alias( 
     378                            ['time_formats'], elem.attrib['path']) 
     379                        ) 
    302380 
    303381            datetime_formats = data.setdefault('datetime_formats', {}) 
    304             for elem in calendar.findall('dateTimeFormats/dateTimeFormatLength'): 
    305                 if 'draft' in elem.attrib and elem.attrib.get('type') in datetime_formats: 
    306                     continue 
    307                 try: 
    308                     datetime_formats[elem.attrib.get('type')] = \ 
    309                         unicode(elem.findtext('dateTimeFormat/pattern')) 
    310                 except ValueError, e: 
    311                     print>>sys.stderr, 'ERROR: %s' % e 
     382            for format in calendar.findall('dateTimeFormats'): 
     383                for elem in format.getiterator(): 
     384                    if elem.tag == 'dateTimeFormatLength': 
     385                        if 'draft' in elem.attrib and \ 
     386                                elem.attrib.get('type') in datetime_formats: 
     387                            continue 
     388                        try: 
     389                            datetime_formats[elem.attrib.get('type')] = \ 
     390                                unicode(elem.findtext('dateTimeFormat/pattern')) 
     391                        except ValueError, e: 
     392                            print>>sys.stderr, 'ERROR: %s' % e 
     393                    elif elem.tag == 'alias': 
     394                        datetime_formats = Alias(_translate_alias( 
     395                            ['datetime_formats'], elem.attrib['path']) 
     396                        ) 
    312397 
    313398        # <numbers> 
    314399 
     
    360445        finally: 
    361446            outfile.close() 
    362447 
     448 
    363449if __name__ == '__main__': 
    364450    main()