Index: babel/core.py
===================================================================
--- babel/core.py	(revision 411)
+++ babel/core.py	(working copy)
@@ -285,7 +285,7 @@
     #{ General Locale Display Names
 
     def languages(self):
-        return self._data['languages']
+        return localedata.AliasResolvingDict(self._data)['languages']
     languages = property(languages, doc="""\
         Mapping of language codes to translated language names.
         
@@ -297,7 +297,7 @@
         """)
 
     def scripts(self):
-        return self._data['scripts']
+        return localedata.AliasResolvingDict(self._data)['scripts']
     scripts = property(scripts, doc="""\
         Mapping of script codes to translated script names.
         
@@ -309,7 +309,7 @@
         """)
 
     def territories(self):
-        return self._data['territories']
+        return localedata.AliasResolvingDict(self._data)['territories']
     territories = property(territories, doc="""\
         Mapping of script codes to translated script names.
         
@@ -321,12 +321,12 @@
         """)
 
     def variants(self):
-        return self._data['variants']
+        return localedata.AliasResolvingDict(self._data)['variants']
     variants = property(variants, doc="""\
         Mapping of script codes to translated script names.
         
         >>> Locale('de', 'DE').variants['1901']
-        u'alte deutsche Rechtschreibung'
+        u'Alte deutsche Rechtschreibung'
         
         :type: `dict`
         """)
@@ -334,7 +334,7 @@
     #{ Number Formatting
 
     def currencies(self):
-        return self._data['currency_names']
+        return localedata.AliasResolvingDict(self._data)['currency_names']
     currencies = property(currencies, doc="""\
         Mapping of currency codes to translated currency names.
         
@@ -347,7 +347,7 @@
         """)
 
     def currency_symbols(self):
-        return self._data['currency_symbols']
+        return localedata.AliasResolvingDict(self._data)['currency_symbols']
     currency_symbols = property(currency_symbols, doc="""\
         Mapping of currency codes to symbols.
         
@@ -360,7 +360,7 @@
         """)
 
     def number_symbols(self):
-        return self._data['number_symbols']
+        return localedata.AliasResolvingDict(self._data)['number_symbols']
     number_symbols = property(number_symbols, doc="""\
         Symbols used in number formatting.
         
@@ -371,7 +371,7 @@
         """)
 
     def decimal_formats(self):
-        return self._data['decimal_formats']
+        return localedata.AliasResolvingDict(self._data)['decimal_formats']
     decimal_formats = property(decimal_formats, doc="""\
         Locale patterns for decimal number formatting.
         
@@ -382,7 +382,7 @@
         """)
 
     def currency_formats(self):
-        return self._data['currency_formats']
+        return localedata.AliasResolvingDict(self._data)['currency_formats']
     currency_formats = property(currency_formats, doc=r"""\
         Locale patterns for currency number formatting.
         
@@ -393,7 +393,7 @@
         """)
 
     def percent_formats(self):
-        return self._data['percent_formats']
+        return localedata.AliasResolvingDict(self._data)['percent_formats']
     percent_formats = property(percent_formats, doc="""\
         Locale patterns for percent number formatting.
         
@@ -404,7 +404,7 @@
         """)
 
     def scientific_formats(self):
-        return self._data['scientific_formats']
+        return localedata.AliasResolvingDict(self._data)['scientific_formats']
     scientific_formats = property(scientific_formats, doc="""\
         Locale patterns for scientific number formatting.
         
@@ -417,7 +417,7 @@
     #{ Calendar Information and Date Formatting
 
     def periods(self):
-        return self._data['periods']
+        return localedata.AliasResolvingDict(self._data)['periods']
     periods = property(periods, doc="""\
         Locale display names for day periods (AM/PM).
         
@@ -428,7 +428,7 @@
         """)
 
     def days(self):
-        return self._data['days']
+        return localedata.AliasResolvingDict(self._data)['days']
     days = property(days, doc="""\
         Locale display names for weekdays.
         
@@ -439,7 +439,7 @@
         """)
 
     def months(self):
-        return self._data['months']
+        return localedata.AliasResolvingDict(self._data)['months']
     months = property(months, doc="""\
         Locale display names for months.
         
@@ -450,7 +450,7 @@
         """)
 
     def quarters(self):
-        return self._data['quarters']
+        return localedata.AliasResolvingDict(self._data)['quarters']
     quarters = property(quarters, doc="""\
         Locale display names for quarters.
         
@@ -461,7 +461,7 @@
         """)
 
     def eras(self):
-        return self._data['eras']
+        return localedata.AliasResolvingDict(self._data)['eras']
     eras = property(eras, doc="""\
         Locale display names for eras.
         
@@ -474,20 +474,20 @@
         """)
 
     def time_zones(self):
-        return self._data['time_zones']
+        return localedata.AliasResolvingDict(self._data)['time_zones']
     time_zones = property(time_zones, doc="""\
         Locale display names for time zones.
         
         >>> Locale('en', 'US').time_zones['Europe/London']['long']['daylight']
         u'British Summer Time'
         >>> Locale('en', 'US').time_zones['America/St_Johns']['city']
-        u'St. John\u2019s'
+        u"St. John's"
         
         :type: `dict`
         """)
 
     def meta_zones(self):
-        return self._data['meta_zones']
+        return localedata.AliasResolvingDict(self._data)['meta_zones']
     meta_zones = property(meta_zones, doc="""\
         Locale display names for meta time zones.
         
@@ -502,7 +502,7 @@
         """)
 
     def zone_formats(self):
-        return self._data['zone_formats']
+        return localedata.AliasResolvingDict(self._data)['zone_formats']
     zone_formats = property(zone_formats, doc=r"""\
         Patterns related to the formatting of time zones.
         
@@ -516,7 +516,7 @@
         """)
 
     def first_week_day(self):
-        return self._data['week_data']['first_day']
+        return localedata.AliasResolvingDict(self._data)['week_data']['first_day']
     first_week_day = property(first_week_day, doc="""\
         The first day of a week.
         
@@ -529,7 +529,7 @@
         """)
 
     def weekend_start(self):
-        return self._data['week_data']['weekend_start']
+        return localedata.AliasResolvingDict(self._data)['week_data']['weekend_start']
     weekend_start = property(weekend_start, doc="""\
         The day the weekend starts.
         
@@ -540,7 +540,7 @@
         """)
 
     def weekend_end(self):
-        return self._data['week_data']['weekend_end']
+        return localedata.AliasResolvingDict(self._data)['week_data']['weekend_end']
     weekend_end = property(weekend_end, doc="""\
         The day the weekend ends.
         
@@ -551,7 +551,7 @@
         """)
 
     def min_week_days(self):
-        return self._data['week_data']['min_days']
+        return localedata.AliasResolvingDict(self._data)['week_data']['min_days']
     min_week_days = property(min_week_days, doc="""\
         The minimum number of days in a week so that the week is counted as the
         first week of a year or month.
@@ -563,7 +563,7 @@
         """)
 
     def date_formats(self):
-        return self._data['date_formats']
+        return localedata.AliasResolvingDict(self._data)['date_formats']
     date_formats = property(date_formats, doc="""\
         Locale patterns for date formatting.
         
@@ -576,7 +576,7 @@
         """)
 
     def time_formats(self):
-        return self._data['time_formats']
+        return localedata.AliasResolvingDict(self._data)['time_formats']
     time_formats = property(time_formats, doc="""\
         Locale patterns for time formatting.
         
@@ -589,7 +589,7 @@
         """)
 
     def datetime_formats(self):
-        return self._data['datetime_formats']
+        return localedata.AliasResolvingDict(self._data)['datetime_formats']
     datetime_formats = property(datetime_formats, doc="""\
         Locale patterns for datetime formatting.
         
Index: babel/localedata.py
===================================================================
--- babel/localedata.py	(revision 411)
+++ babel/localedata.py	(working copy)
@@ -23,6 +23,7 @@
     import threading
 except ImportError:
     import dummy_threading as threading
+from UserDict import DictMixin
 
 __all__ = ['exists', 'load']
 __docformat__ = 'restructuredtext en'
@@ -31,6 +32,7 @@
 _cache_lock = threading.RLock()
 _dirname = os.path.join(os.path.dirname(__file__), 'localedata')
 
+
 def exists(name):
     """Check whether locale data is available for the given locale.
     
@@ -42,6 +44,7 @@
         return True
     return os.path.exists(os.path.join(_dirname, '%s.dat' % name))
 
+
 def list():
     """Return a list of all locale identifiers for which locale data is
     available.
@@ -54,6 +57,7 @@
         os.path.splitext(filename) for filename in os.listdir(_dirname)
     ] if extension == '.dat' and stem != 'root']
 
+
 def load(name):
     """Load the locale data for the given locale.
     
@@ -61,16 +65,16 @@
     the Common Locale Data Repository (CLDR). This data is stored as a
     collection of pickle files inside the ``babel`` package.
     
-    >>> d = load('en_US')
-    >>> d['languages']['sv']
+    >> d = load('en_US')
+    >> d['languages']['sv']
     u'Swedish'
     
     Note that the results are cached, and subsequent requests for the same
     locale return the same dictionary:
     
-    >>> d1 = load('en_US')
-    >>> d2 = load('en_US')
-    >>> d1 is d2
+    >> d1 = load('en_US')
+    >> d2 = load('en_US')
+    >> d1 is d2
     True
     
     :param name: the locale identifier string (or "root")
@@ -107,17 +111,85 @@
     finally:
         _cache_lock.release()
 
+
 def merge(dict1, dict2):
     """Merge the data from `dict2` into the `dict1` dictionary, making copies
     of nested dictionaries.
     
+    >>> d = {1: 'foo', 3: 'baz'}
+    >>> merge(d, {1: 'Foo', 2: 'Bar'})
+    >>> d
+    {1: 'Foo', 2: 'Bar', 3: 'baz'}
+    
+    >>> d = {1: {1: 'FOO', 2: 'BAR'}, 2: 'foo'}
+    >>> merge(d, {1: {3: 'BAZ'}})
+    >>> d
+    {1: {1: 'FOO', 2: 'BAR', 3: 'BAZ'}, 2: 'foo'}
+    
     :param dict1: the dictionary to merge into
     :param dict2: the dictionary containing the data that should be merged
     """
-    for key, value in dict2.items():
-        if value is not None:
-            if type(value) is dict:
-                dict1[key] = dict1.get(key, {}).copy()
-                merge(dict1[key], value)
+    for key, val2 in dict2.items():
+        if val2 is not None:
+            if type(val2) is dict:
+                val1 = dict1.get(key, {})
+                if type(val1) is Alias:
+                    dict1[key] = (val1, val2)
+                else:
+                    dict1[key] = val1.copy()
+                    merge(dict1[key], val2)
+            elif type(val2) is Alias:
+                dict1[key] = (val2, dict1[key])
             else:
-                dict1[key] = value
+                val1 = dict1.get(key)
+                if type(val1) is Alias:
+                    dict1[key] = (val1, val2)
+                else:
+                    dict1[key] = val2
+
+
+class Alias(object):
+
+    def __init__(self, keys):
+        self.keys = keys
+
+    def __repr__(self):
+        return '<%s %r>' % (type(self).__name__, self.keys)
+
+    def resolve(self, data):
+        base = data
+        for key in self.keys:
+            data = data[key]
+        if type(data) is Alias:
+            data = data.resolve(base)
+        return data
+
+
+class AliasResolvingDict(DictMixin, object):
+
+    def __init__(self, data, base=None):
+        self.data = data
+        if base is None:
+            base = self
+        self.base = base
+
+    def __getitem__(self, key):
+        val = self.data[key]
+        if type(val) is Alias: # resolve an alias
+            val = val.resolve(self.base)
+        if type(val) is tuple: # Merge a partial dict with an alias
+            alias, others = val
+            val = alias.resolve(self.base).copy()
+            merge(val, others)
+        if type(val) is dict: # Return a nested alias-resolving dict
+            val = AliasResolvingDict(val, base=self.base)
+        return val
+
+    def __setitem__(self, key, value):
+        self.data[key] = value
+
+    def copy(self):
+        return AliasResolvingDict(self.data.copy(), base=self.base)
+
+    def keys(self):
+        return self.data.keys()
Index: babel/tests/localedata.py
===================================================================
--- babel/tests/localedata.py	(revision 411)
+++ babel/tests/localedata.py	(working copy)
@@ -16,9 +16,53 @@
 
 from babel import localedata
 
+
+class MergeResolveTestCase(unittest.TestCase):
+
+    def test_merge_items(self):
+        d = {1: 'foo', 3: 'baz'}
+        localedata.merge(d, {1: 'Foo', 2: 'Bar'})
+        self.assertEqual({1: 'Foo', 2: 'Bar', 3: 'baz'}, d)
+
+    def test_merge_nested_dict(self):
+        d1 = {
+            'x': {'a': 1, 'b': 2, 'c': 3}
+        }
+        d2 = {
+            'x': {'a': 1, 'b': 12, 'd': 14}
+        }
+        localedata.merge(d1, d2)
+        self.assertEqual({
+            'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}
+        }, d1)
+
+
+    def test_merge_alias(self):
+        alias = localedata.Alias('x')
+        d1 = {
+            'x': {'a': 1, 'b': 2, 'c': 3},
+            'y': alias
+        }
+        d2 = {
+            'x': {'a': 1, 'b': 12, 'd': 14},
+            'y': {'b': 22, 'e': 25}
+        }
+        localedata.merge(d1, d2)
+        self.assertEqual({
+            'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14},
+            'y': (alias, {'b': 22, 'e': 25})
+        }, d1)
+        d = localedata.AliasResolvingDict(d1)
+        self.assertEqual({
+            'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14},
+            'y': {'a': 1, 'b': 22, 'c': 3, 'd': 14, 'e': 25}
+        }, d)
+
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocTestSuite(localedata))
+    suite.addTest(unittest.makeSuite(MergeResolveTestCase))
     return suite
 
 if __name__ == '__main__':
Index: babel/tests/dates.py
===================================================================
--- babel/tests/dates.py	(revision 411)
+++ babel/tests/dates.py	(working copy)
@@ -29,6 +29,11 @@
         fmt = dates.DateTimeFormat(d, locale='cs_CZ')
         self.assertEqual('1.', fmt['LLL'])
 
+    def test_abbreviated_month_alias(self):
+        d = date(2006, 3, 8)
+        fmt = dates.DateTimeFormat(d, locale='de_DE')
+        self.assertEqual(u'Mär', fmt['LLL'])
+
     def test_week_of_year_first(self):
         d = date(2006, 1, 8)
         fmt = dates.DateTimeFormat(d, locale='de_DE')
@@ -187,7 +192,7 @@
         tz = timezone('Europe/Paris')
         t = time(15, 30, tzinfo=tz)
         fmt = dates.DateTimeFormat(t, locale='fr_FR')
-        self.assertEqual(u'Heure de l’Europe centrale', fmt['vvvv'])
+        self.assertEqual(u'heure d’Europe centrale', fmt['vvvv'])
 
     def test_hour_formatting(self):
         l = 'en_US'
Index: babel/numbers.py
===================================================================
--- babel/numbers.py	(revision 411)
+++ babel/numbers.py	(working copy)
@@ -165,9 +165,9 @@
     >>> format_currency(1099.98, 'USD', locale='en_US')
     u'$1,099.98'
     >>> format_currency(1099.98, 'USD', locale='es_CO')
-    u'US$ 1.099,98'
+    u'US$\\xa01.099,98'
     >>> format_currency(1099.98, 'EUR', locale='de_DE')
-    u'1.099,98 \\u20ac'
+    u'1.099,98\\xa0\\u20ac'
     
     The pattern can also be specified explicitly:
     
Index: babel/dates.py
===================================================================
--- babel/dates.py	(revision 411)
+++ babel/dates.py	(working copy)
@@ -189,7 +189,7 @@
     string is used for GMT:
     
     >>> get_timezone_gmt(dt, 'long', locale='fr_FR')
-    u'HMG-08:00'
+    u'UTC-08:00'
     
     :param datetime: the ``datetime`` object; if `None`, the current date and
                      time in UTC is used
Index: scripts/import_cldr.py
===================================================================
--- scripts/import_cldr.py	(revision 411)
+++ scripts/import_cldr.py	(working copy)
@@ -16,6 +16,7 @@
 from optparse import OptionParser
 import os
 import pickle
+import re
 import sys
 try:
     from xml.etree.ElementTree import parse
@@ -26,6 +27,7 @@
 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..'))
 
 from babel import dates, numbers
+from babel.localedata import Alias
 
 weekdays = {'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3, 'fri': 4, 'sat': 5,
             'sun': 6}
@@ -36,6 +38,7 @@
     def any(iterable):
         return filter(None, list(iterable))
 
+
 def _text(elem):
     buf = [elem.text or '']
     for child in elem:
@@ -43,6 +46,27 @@
     buf.append(elem.tail or '')
     return u''.join(filter(None, buf)).strip()
 
+
+NAME_RE = re.compile(r"^\w+$")
+TYPE_ATTR_RE = re.compile(r"^\w+\[@type='(.*?)'\]$")
+
+
+def _translate_alias(ctxt, path):
+    parts = path.split('/')
+    keys = ctxt[:]
+    for part in parts:
+        if part == '..':
+            keys.pop()
+        else:
+            match = TYPE_ATTR_RE.match(part)
+            if match:
+                keys.append(match.group(1))
+            else:
+                assert NAME_RE.match(part)
+                keys.append(part)
+    return keys
+
+
 def main():
     parser = OptionParser(usage='%prog path/to/cldr')
     options, args = parser.parse_args()
@@ -109,6 +133,8 @@
         stem, ext = os.path.splitext(filename)
         if ext != '.xml':
             continue
+        #if stem != 'root':
+        #    break
 
         tree = parse(os.path.join(srcdir, 'main', filename))
         data = {}
@@ -227,34 +253,61 @@
 
             months = data.setdefault('months', {})
             for ctxt in calendar.findall('months/monthContext'):
-                ctxts = months.setdefault(ctxt.attrib['type'], {})
+                ctxt_type = ctxt.attrib['type']
+                ctxts = months.setdefault(ctxt_type, {})
                 for width in ctxt.findall('monthWidth'):
-                    widths = ctxts.setdefault(width.attrib['type'], {})
-                    for elem in width.findall('month'):
-                        if 'draft' in elem.attrib and int(elem.attrib['type']) in widths:
-                            continue
-                        widths[int(elem.attrib.get('type'))] = unicode(elem.text)
+                    width_type = width.attrib['type']
+                    widths = ctxts.setdefault(width_type, {})
+                    for elem in width.getiterator():
+                        if elem.tag == 'month':
+                            if 'draft' in elem.attrib and int(elem.attrib['type']) in widths:
+                                continue
+                            widths[int(elem.attrib.get('type'))] = unicode(elem.text)
+                        elif elem.tag == 'alias':
+                            assert elem.attrib['source'] == 'locale'
+                            ctxts[width_type] = Alias(
+                                _translate_alias(['months', ctxt_type, width_type],
+                                                 elem.attrib['path'])
+                            )
 
             days = data.setdefault('days', {})
             for ctxt in calendar.findall('days/dayContext'):
-                ctxts = days.setdefault(ctxt.attrib['type'], {})
+                ctxt_type = ctxt.attrib['type']
+                ctxts = days.setdefault(ctxt_type, {})
                 for width in ctxt.findall('dayWidth'):
-                    widths = ctxts.setdefault(width.attrib['type'], {})
-                    for elem in width.findall('day'):
-                        dtype = weekdays[elem.attrib['type']]
-                        if 'draft' in elem.attrib and dtype in widths:
-                            continue
-                        widths[dtype] = unicode(elem.text)
+                    width_type = width.attrib['type']
+                    widths = ctxts.setdefault(width_type, {})
+                    for elem in width.getiterator():
+                        if elem.tag == 'day':
+                            dtype = weekdays[elem.attrib['type']]
+                            if 'draft' in elem.attrib and dtype in widths:
+                                continue
+                            widths[dtype] = unicode(elem.text)
+                        elif elem.tag == 'alias':
+                            assert elem.attrib['source'] == 'locale'
+                            ctxts[width_type] = Alias(
+                                _translate_alias(['days', ctxt_type, width_type],
+                                                 elem.attrib['path'])
+                            )
 
             quarters = data.setdefault('quarters', {})
             for ctxt in calendar.findall('quarters/quarterContext'):
+                ctxt_type = ctxt.attrib['type']
                 ctxts = quarters.setdefault(ctxt.attrib['type'], {})
                 for width in ctxt.findall('quarterWidth'):
-                    widths = ctxts.setdefault(width.attrib['type'], {})
-                    for elem in width.findall('quarter'):
-                        if 'draft' in elem.attrib and int(elem.attrib['type']) in widths:
-                            continue
-                        widths[int(elem.attrib.get('type'))] = unicode(elem.text)
+                    width_type = width.attrib['type']
+                    widths = ctxts.setdefault(width_type, {})
+                    for elem in width.getiterator():
+                        if elem.tag == 'quarter':
+                            if 'draft' in elem.attrib and int(elem.attrib['type']) in widths:
+                                continue
+                            widths[int(elem.attrib.get('type'))] = unicode(elem.text)
+                        elif elem.tag == 'alias':
+                            assert elem.attrib['source'] == 'locale'
+                            ctxts[width_type] = Alias(
+                                _translate_alias(['quarters', ctxt_type, width_type],
+                                                 elem.attrib['path'])
+                            )
 
             eras = data.setdefault('eras', {})
             for width in calendar.findall('eras/*'):
@@ -272,11 +325,11 @@
             # AM/PM
             periods = data.setdefault('periods', {})
             for elem in calendar.findall('am'):
-                if 'draft' in elem.attrib and elem.tag in periods:
+                if ('draft' in elem.attrib or 'alt' in elem.attrib) and elem.tag in periods:
                     continue
                 periods[elem.tag] = unicode(elem.text)
             for elem in calendar.findall('pm'):
-                if 'draft' in elem.attrib and elem.tag in periods:
+                if ('draft' in elem.attrib or 'alt' in elem.attrib) and elem.tag in periods:
                     continue
                 periods[elem.tag] = unicode(elem.text)
 
@@ -360,5 +413,6 @@
         finally:
             outfile.close()
 
+
 if __name__ == '__main__':
     main()

