Vinay Sajip avatar Vinay Sajip committed 934f1ef

Made changes dropping compatibility with Python 2.5 and earlier.

Comments (0)

Files changed (21)

 if sys.version_info[0] < 3:
     PY3 = False
 
-    def b(s):
-        return s
-
-    def u(s):
-        return unicode(s, "unicode_escape")
-
-    import StringIO
-    StringIO = BytesIO = StringIO.StringIO
+    from StringIO import StringIO
+    import io
+    BytesIO = io.BytesIO
     text_type = unicode
-    binary_type = str
     string_types = basestring,
     integer_types = (int, long)
     unichr = unichr
 else:
     PY3 = True
 
-    def b(s):
-        return s.encode("latin-1")
-
-    def u(s):
-        return s
-
     import io
     StringIO = io.StringIO
     BytesIO = io.BytesIO
     text_type = str
-    binary_type = bytes
     string_types = str,
     integer_types = int,
 
 long_type = integer_types[-1]
 
 try:
-    from xml.etree import ElementTree
-except ImportError:
-    from elementtree import ElementTree
-
-try:
-    any = any
-except NameError:
-    def any(iterable):
-        return filter(None, list(iterable))
-
-try:
     import threading
 except ImportError:
     import dummy_threading as threading
 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 """Core locale representation and locale data access."""
 
 import os
-from babel.compat import pickle, string_types, u
+from babel.compat import pickle, string_types
 
 from babel import localedata
 
     >>> locale = Locale('en', 'US')
     >>> repr(locale)
     '<Locale "en_US">'
-    >>> locale.display_name == u('English (United States)')
+    >>> locale.display_name == 'English (United States)'
     True
     
     A `Locale` object can also be instantiated from a raw locale string:
     `Locale` objects provide access to a collection of locale data, such as
     territory and language names, number and date format patterns, and more:
     
-    >>> locale.number_symbols['decimal'] == u('.')
+    >>> locale.number_symbols['decimal'] == '.'
     True
     
     If a locale is requested for which no locale data is available, an
         """Initialize the locale object from the given identifier components.
         
         >>> locale = Locale('en', 'US')
-        >>> locale.language
-        'en'
-        >>> locale.territory
-        'US'
+        >>> locale.language == 'en'
+        True
+        >>> locale.territory == 'US'
+        True
         
         :param language: the language code
         :param territory: the territory (country or region) code
         """Create a `Locale` instance for the given locale identifier.
         
         >>> l = Locale.parse('de-DE', sep='-')
-        >>> l.display_name == u('Deutsch (Deutschland)')
+        >>> l.display_name == 'Deutsch (Deutschland)'
         True
         
         If the `identifier` parameter is not a string, but actually a `Locale`
         The display name will include the language, territory, script, and
         variant, if those are specified.
         
-        >>> Locale('zh', 'CN', script='Hans').get_display_name('en') == u('Chinese (Simplified Han, China)')
+        >>> Locale('zh', 'CN', script='Hans').get_display_name('en') == 'Chinese (Simplified Han, China)'
         True
         
         :param locale: the locale to use
                 details.append(locale.variants.get(self.variant))
             details = [_f for _f in details if _f]
             if details:
-                retval += ' (%s)' % u(', ').join(details)
+                retval += ' (%s)' % ', '.join(details)
         return retval
 
     display_name = property(get_display_name, doc="""\
         The localized display name of the locale.
         
-        >>> Locale('en').display_name == u('English')
+        >>> Locale('en').display_name == 'English'
         True
-        >>> Locale('en', 'US').display_name == u('English (United States)')
+        >>> Locale('en', 'US').display_name == 'English (United States)'
         True
-        >>> Locale('sv').display_name == u('svenska')
+        >>> Locale('sv').display_name == 'svenska'
         True
         
         :type: `unicode`
     english_name = property(english_name, doc="""\
         The english display name of the locale.
         
-        >>> Locale('de').english_name == u('German')
+        >>> Locale('de').english_name == 'German'
         True
-        >>> Locale('de', 'DE').english_name == u('German (Germany)')
+        >>> Locale('de', 'DE').english_name == 'German (Germany)'
         True
         
         :type: `unicode`
     languages = property(languages, doc="""\
         Mapping of language codes to translated language names.
         
-        >>> Locale('de', 'DE').languages['ja'] == u('Japanisch')
+        >>> Locale('de', 'DE').languages['ja'] == 'Japanisch'
         True
         
         :type: `dict`
     scripts = property(scripts, doc="""\
         Mapping of script codes to translated script names.
         
-        >>> Locale('en', 'US').scripts['Hira'] == u('Hiragana')
+        >>> Locale('en', 'US').scripts['Hira'] == 'Hiragana'
         True
         
         :type: `dict`
     territories = property(territories, doc="""\
         Mapping of script codes to translated script names.
         
-        >>> Locale('es', 'CO').territories['DE'] == u('Alemania')
+        >>> Locale('es', 'CO').territories['DE'] == 'Alemania'
         True
         
         :type: `dict`
     variants = property(variants, doc="""\
         Mapping of script codes to translated script names.
         
-        >>> Locale('de', 'DE').variants['1901'] == u('Alte deutsche Rechtschreibung')
+        >>> Locale('de', 'DE').variants['1901'] == 'Alte deutsche Rechtschreibung'
         True
         
         :type: `dict`
     currencies = property(currencies, doc="""\
         Mapping of currency codes to translated currency names.
         
-        >>> Locale('en').currencies['COP'] == u('Colombian Peso')
+        >>> Locale('en').currencies['COP'] == 'Colombian Peso'
         True
-        >>> Locale('de', 'DE').currencies['COP'] == u('Kolumbianischer Peso')
+        >>> Locale('de', 'DE').currencies['COP'] == 'Kolumbianischer Peso'
         True
 
         :type: `dict`
     currency_symbols = property(currency_symbols, doc="""\
         Mapping of currency codes to symbols.
         
-        >>> Locale('en', 'US').currency_symbols['USD'] == u('$')
+        >>> Locale('en', 'US').currency_symbols['USD'] == '$'
         True
-        >>> Locale('es', 'CO').currency_symbols['USD'] == u('US$')
+        >>> Locale('es', 'CO').currency_symbols['USD'] == 'US$'
         True
         
         :type: `dict`
     number_symbols = property(number_symbols, doc="""\
         Symbols used in number formatting.
         
-        >>> Locale('fr', 'FR').number_symbols['decimal'] == u(',')
+        >>> Locale('fr', 'FR').number_symbols['decimal'] == ','
         True
         
         :type: `dict`
     periods = property(periods, doc="""\
         Locale display names for day periods (AM/PM).
         
-        >>> Locale('en', 'US').periods['am'] == u('AM')
+        >>> Locale('en', 'US').periods['am'] == 'AM'
         True
         
         :type: `dict`
     days = property(days, doc="""\
         Locale display names for weekdays.
         
-        >>> Locale('de', 'DE').days['format']['wide'][3] == u('Donnerstag')
+        >>> Locale('de', 'DE').days['format']['wide'][3] == 'Donnerstag'
         True
         
         :type: `dict`
     quarters = property(quarters, doc="""\
         Locale display names for quarters.
         
-        >>> Locale('de', 'DE').quarters['format']['wide'][1] == u('1. Quartal')
+        >>> Locale('de', 'DE').quarters['format']['wide'][1] == '1. Quartal'
         True
         
         :type: `dict`
     eras = property(eras, doc="""\
         Locale display names for eras.
         
-        >>> Locale('en', 'US').eras['wide'][1] == u('Anno Domini')
+        >>> Locale('en', 'US').eras['wide'][1] == 'Anno Domini'
         True
-        >>> Locale('en', 'US').eras['abbreviated'][0] == u('BC')
+        >>> Locale('en', 'US').eras['abbreviated'][0] == 'BC'
         True
         
         :type: `dict`
     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['Europe/London']['long']['daylight'] == 'British Summer Time'
         True
-        >>> Locale('en', 'US').time_zones['America/St_Johns']['city'] == u("St. John's")
+        >>> Locale('en', 'US').time_zones['America/St_Johns']['city'] == "St. John's"
         True
         
         :type: `dict`
         Meta time zones are basically groups of different Olson time zones that
         have the same GMT offset and daylight savings time.
         
-        >>> Locale('en', 'US').meta_zones['Europe_Central']['long']['daylight'] == u('Central European Summer Time')
+        >>> Locale('en', 'US').meta_zones['Europe_Central']['long']['daylight'] == 'Central European Summer Time'
         True
         
         :type: `dict`
     zone_formats = property(zone_formats, doc=r"""\
         Patterns related to the formatting of time zones.
         
-        >>> Locale('en', 'US').zone_formats['fallback'] == u('%(1)s (%(0)s)')
+        >>> Locale('en', 'US').zone_formats['fallback'] == '%(1)s (%(0)s)'
         True
-        >>> Locale('pt', 'BR').zone_formats['region'] == u('Hor\xe1rio %s')
+        >>> Locale('pt', 'BR').zone_formats['region'] == 'Hor\xe1rio %s'
         True
         
         :type: `dict`
     datetime_formats = property(datetime_formats, doc="""\
         Locale patterns for datetime formatting.
         
-        >>> Locale('en').datetime_formats['full'] == u('{1} {0}')
+        >>> Locale('en').datetime_formats['full'] == '{1} {0}'
         True
-        >>> Locale('th').datetime_formats['medium'] == u('{1}, {0}')
+        >>> Locale('th').datetime_formats['medium'] == '{1}, {0}'
         True
         
         :type: `dict`
     >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE']:
     ...     os.environ[name] = ''
     >>> os.environ['LANG'] = 'fr_FR.UTF-8'
-    >>> default_locale('LC_MESSAGES')
-    'fr_FR'
+    >>> default_locale('LC_MESSAGES') == 'fr_FR'
+    True
 
     The "C" or "POSIX" pseudo-locales are treated as aliases for the
     "en_US_POSIX" locale:
 
     >>> os.environ['LC_MESSAGES'] = 'POSIX'
-    >>> default_locale('LC_MESSAGES')
-    'en_US_POSIX'
+    >>> default_locale('LC_MESSAGES') == 'en_US_POSIX'
+    True
 
     :param category: one of the ``LC_XXX`` environment variable names
     :param aliases: a dictionary of aliases for locale identifiers
 def negotiate_locale(preferred, available, sep='_', aliases=LOCALE_ALIASES):
     """Find the best match between available and requested locale strings.
     
-    >>> negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT'])
-    'de_DE'
-    >>> negotiate_locale(['de_DE', 'en_US'], ['en', 'de'])
-    'de'
+    >>> negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT']) == 'de_DE'
+    True
+    >>> negotiate_locale(['de_DE', 'en_US'], ['en', 'de']) == 'de'
+    True
     
     Case is ignored by the algorithm, the result uses the case of the preferred
     locale identifier:
     
-    >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at'])
-    'de_DE'
+    >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) == 'de_DE'
+    True
     
-    >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at'])
-    'de_DE'
+    >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) == 'de_DE'
+    True
     
     By default, some web browsers unfortunately do not include the territory
     in the locale identifier for many locales, and some don't even allow the
     function uses a default mapping of commonly used langauge-only locale
     identifiers to identifiers including the territory:
     
-    >>> negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US'])
-    'ja_JP'
+    >>> negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US']) == 'ja_JP'
+    True
     
     Some browsers even use an incorrect or outdated language code, such as "no"
     for Norwegian, where the correct locale identifier would actually be "nb_NO"
     (Bokmål) or "nn_NO" (Nynorsk). The aliases are intended to take care of
     such cases, too:
     
-    >>> negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE'])
-    'nb_NO'
+    >>> negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE']) == 'nb_NO'
+    True
     
     You can override this default mapping by passing a different `aliases`
     dictionary to this function, or you can bypass the behavior althogher by
     
       ``(language, territory, script, variant)``
     
-    >>> parse_locale('zh_CN')
-    ('zh', 'CN', None, None)
-    >>> parse_locale('zh_Hans_CN')
-    ('zh', 'CN', 'Hans', None)
+    >>> parse_locale('zh_CN') == ('zh', 'CN', None, None)
+    True
+    >>> parse_locale('zh_Hans_CN') == ('zh', 'CN', 'Hans', None)
+    True
     
     The default component separator is "_", but a different separator can be
     specified using the `sep` parameter:
     
-    >>> parse_locale('zh-CN', sep='-')
-    ('zh', 'CN', None, None)
+    >>> parse_locale('zh-CN', sep='-') == ('zh', 'CN', None, None)
+    True
     
     If the identifier cannot be parsed into a locale, a `ValueError` exception
     is raised:
     
     Encoding information and locale modifiers are removed from the identifier:
     
-    >>> parse_locale('it_IT@euro')
-    ('it', 'IT', None, None)
-    >>> parse_locale('en_US.UTF-8')
-    ('en', 'US', None, None)
-    >>> parse_locale('de_DE.iso885915@euro')
-    ('de', 'DE', None, None)
+    >>> parse_locale('it_IT@euro') == ('it', 'IT', None, None)
+    True
+    >>> parse_locale('en_US.UTF-8') == ('en', 'US', None, None)
+    True
+    >>> parse_locale('de_DE.iso885915@euro') == ('de', 'DE', None, None)
+    True
     
     :param identifier: the locale identifier string
     :param sep: character that separates the different components of the locale
             variant = parts.pop()
 
     if parts:
-        raise ValueError('%r is not a valid locale identifier' % identifier)
+        raise ValueError('%r is not a valid locale identifier' % str(identifier))
 
     return lang, territory, script, variant
  * ``LANG``
 """
 
-from __future__ import division
+from __future__ import division, unicode_literals
 from datetime import date, datetime, time, timedelta
 import re
 
-from babel.compat import integer_types, u
+from babel.compat import integer_types
 from babel.core import default_locale, get_global, Locale
 from babel.util import UTC
 
 def get_period_names(locale=LC_TIME):
     """Return the names for day periods (AM/PM) used by the locale.
     
-    >>> get_period_names(locale='en_US')['am'] == u('AM')
+    >>> get_period_names(locale='en_US')['am'] == 'AM'
     True
     
     :param locale: the `Locale` object, or a locale string
 def get_day_names(width='wide', context='format', locale=LC_TIME):
     """Return the day names used by the locale for the specified format.
     
-    >>> get_day_names('wide', locale='en_US')[1] == u('Tuesday')
+    >>> get_day_names('wide', locale='en_US')[1] == 'Tuesday'
     True
-    >>> get_day_names('abbreviated', locale='es')[1] == u('mar')
+    >>> get_day_names('abbreviated', locale='es')[1] == 'mar'
     True
-    >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1] == u('D')
+    >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1] == 'D'
     True
     
     :param width: the width to use, one of "wide", "abbreviated", or "narrow"
 def get_month_names(width='wide', context='format', locale=LC_TIME):
     """Return the month names used by the locale for the specified format.
     
-    >>> get_month_names('wide', locale='en_US')[1] == u('January')
+    >>> get_month_names('wide', locale='en_US')[1] == 'January'
     True
-    >>> get_month_names('abbreviated', locale='es')[1] == u('ene')
+    >>> get_month_names('abbreviated', locale='es')[1] == 'ene'
     True
-    >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1] == u('J')
+    >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1] == 'J'
     True
     
     :param width: the width to use, one of "wide", "abbreviated", or "narrow"
 def get_quarter_names(width='wide', context='format', locale=LC_TIME):
     """Return the quarter names used by the locale for the specified format.
     
-    >>> get_quarter_names('wide', locale='en_US')[1] == u('1st quarter')
+    >>> get_quarter_names('wide', locale='en_US')[1] == '1st quarter'
     True
-    >>> get_quarter_names('abbreviated', locale='de_DE')[1] == u('Q1')
+    >>> get_quarter_names('abbreviated', locale='de_DE')[1] == 'Q1'
     True
     
     :param width: the width to use, one of "wide", "abbreviated", or "narrow"
 def get_era_names(width='wide', locale=LC_TIME):
     """Return the era names used by the locale for the specified format.
     
-    >>> get_era_names('wide', locale='en_US')[1] == u('Anno Domini')
+    >>> get_era_names('wide', locale='en_US')[1] == 'Anno Domini'
     True
-    >>> get_era_names('abbreviated', locale='de_DE')[1] == u('n. Chr.')
+    >>> get_era_names('abbreviated', locale='de_DE')[1] == 'n. Chr.'
     True
     
     :param width: the width to use, either "wide", "abbreviated", or "narrow"
     """Return the datetime formatting patterns used by the locale for the
     specified format.
     
-    >>> get_datetime_format(locale='en_US') == u('{1} {0}')
+    >>> get_datetime_format(locale='en_US') == '{1} {0}'
     True
     
     :param format: the format to use, one of "full", "long", "medium", or
     as string indicating the offset from GMT.
     
     >>> dt = datetime(2007, 4, 1, 15, 30)
-    >>> get_timezone_gmt(dt, locale='en') == u('GMT+00:00')
+    >>> get_timezone_gmt(dt, locale='en') == 'GMT+00:00'
     True
     
     >>> from pytz import timezone
     >>> tz = timezone('America/Los_Angeles')
     >>> dt = datetime(2007, 4, 1, 15, 30, tzinfo=tz)
-    >>> get_timezone_gmt(dt, locale='en') == u('GMT-08:00')
+    >>> get_timezone_gmt(dt, locale='en') == 'GMT-08:00'
     True
-    >>> get_timezone_gmt(dt, 'short', locale='en') == u('-0800')
+    >>> get_timezone_gmt(dt, 'short', locale='en') == '-0800'
     True
     
     The long format depends on the locale, for example in France the acronym
     UTC string is used instead of GMT:
     
-    >>> get_timezone_gmt(dt, 'long', locale='fr_FR') == u('UTC-08:00')
+    >>> get_timezone_gmt(dt, 'long', locale='fr_FR') == 'UTC-08:00'
     True
     
     :param datetime: the ``datetime`` object; if `None`, the current date and
     seconds = offset.days * 24 * 60 * 60 + offset.seconds
     hours, seconds = divmod(seconds, 3600)
     if width == 'short':
-        pattern = u('%+03d%02d')
+        pattern = '%+03d%02d'
     else:
         pattern = locale.zone_formats['gmt'] % '%+03d:%02d'
     return pattern % (hours, seconds // 60)
     
     >>> from pytz import timezone
     >>> tz = timezone('America/St_Johns')
-    >>> get_timezone_location(tz, locale='de_DE') == u("Kanada (St. John's)")
+    >>> get_timezone_location(tz, locale='de_DE') == "Kanada (St. John's)"
     True
     >>> tz = timezone('America/Mexico_City')
-    >>> get_timezone_location(tz, locale='de_DE') == u('Mexiko (Mexiko-Stadt)')
+    >>> get_timezone_location(tz, locale='de_DE') == 'Mexiko (Mexiko-Stadt)'
     True
     
     If the timezone is associated with a country that uses only a single
     timezone, just the localized country name is returned:
     
     >>> tz = timezone('Europe/Berlin')
-    >>> get_timezone_name(tz, locale='de_DE') == u('Deutschland')
+    >>> get_timezone_name(tz, locale='de_DE') == 'Deutschland'
     True
     
     :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines
     
     >>> from pytz import timezone
     >>> dt = time(15, 30, tzinfo=timezone('America/Los_Angeles'))
-    >>> get_timezone_name(dt, locale='en_US') == u('Pacific Standard Time')
+    >>> get_timezone_name(dt, locale='en_US') == 'Pacific Standard Time'
     True
-    >>> get_timezone_name(dt, width='short', locale='en_US') == u('PST')
+    >>> get_timezone_name(dt, width='short', locale='en_US') == 'PST'
     True
     
     If this function gets passed only a `tzinfo` object and no concrete
     time of events that recur across DST changes:
     
     >>> tz = timezone('America/Los_Angeles')
-    >>> get_timezone_name(tz, locale='en_US') == u('Pacific Time')
+    >>> get_timezone_name(tz, locale='en_US') == 'Pacific Time'
     True
-    >>> get_timezone_name(tz, 'short', locale='en_US') == u('PT')
+    >>> get_timezone_name(tz, 'short', locale='en_US') == 'PT'
     True
     
     If no localized display name for the timezone is available, and the timezone
     that country is returned, formatted according to the locale:
     
     >>> tz = timezone('Europe/Berlin')
-    >>> get_timezone_name(tz, locale='de_DE') == u('Deutschland')
+    >>> get_timezone_name(tz, locale='de_DE') == 'Deutschland'
     True
-    >>> get_timezone_name(tz, locale='pt_BR') == u('Hor\xe1rio Alemanha')
+    >>> get_timezone_name(tz, locale='pt_BR') == 'Hor\xe1rio Alemanha'
     True
     
     On the other hand, if the country uses multiple timezones, the city is also
     included in the representation:
     
     >>> tz = timezone('America/St_Johns')
-    >>> get_timezone_name(tz, locale='de_DE') == u("Kanada (St. John's)")
+    >>> get_timezone_name(tz, locale='de_DE') == "Kanada (St. John's)"
     True
     
     The `uncommon` parameter can be set to `True` to enable the use of timezone
     common use, so a generic name would be chosen by default:
     
     >>> tz = timezone('Europe/Paris')
-    >>> get_timezone_name(tz, 'short', locale='fr_CA') == u('France')
+    >>> get_timezone_name(tz, 'short', locale='fr_CA') == 'France'
     True
-    >>> get_timezone_name(tz, 'short', uncommon=True, locale='fr_CA') == u('HEC')
+    >>> get_timezone_name(tz, 'short', uncommon=True, locale='fr_CA') == 'HEC'
     True
     
     :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines
     """Return a date formatted according to the given pattern.
     
     >>> d = date(2007, 4, 1)
-    >>> format_date(d, locale='en_US') == u('Apr 1, 2007')
+    >>> format_date(d, locale='en_US') == 'Apr 1, 2007'
     True
-    >>> format_date(d, format='full', locale='de_DE') == u('Sonntag, 1. April 2007')
+    >>> format_date(d, format='full', locale='de_DE') == 'Sonntag, 1. April 2007'
     True
     
     If you don't want to use the locale default formats, you can specify a
     custom date pattern:
     
-    >>> format_date(d, "EEE, MMM d, ''yy", locale='en') == u("Sun, Apr 1, '07")
+    >>> format_date(d, "EEE, MMM d, ''yy", locale='en') == "Sun, Apr 1, '07"
     True
     
     :param date: the ``date`` or ``datetime`` object; if `None`, the current
     r"""Return a date formatted according to the given pattern.
     
     >>> dt = datetime(2007, 4, 1, 15, 30)
-    >>> format_datetime(dt, locale='en_US') == u('Apr 1, 2007 3:30:00 PM')
+    >>> format_datetime(dt, locale='en_US') == 'Apr 1, 2007 3:30:00 PM'
     True
     
     For any pattern requiring the display of the time-zone, the third-party
     
     >>> from pytz import timezone
     >>> format_datetime(dt, 'full', tzinfo=timezone('Europe/Paris'),
-    ...                 locale='fr_FR') == u('dimanche 1 avril 2007 17:30:00 Heure avanc\xe9e de l\u2019Europe centrale')
+    ...                 locale='fr_FR') == 'dimanche 1 avril 2007 17:30:00 Heure avanc\xe9e de l\u2019Europe centrale'
     True
     >>> format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz",
-    ...                 tzinfo=timezone('US/Eastern'), locale='en') == u('2007.04.01 AD at 11:30:00 EDT')
+    ...                 tzinfo=timezone('US/Eastern'), locale='en') == '2007.04.01 AD at 11:30:00 EDT'
     True
     
     :param datetime: the `datetime` object; if `None`, the current date and
     r"""Return a time formatted according to the given pattern.
     
     >>> t = time(15, 30)
-    >>> format_time(t, locale='en_US') == u('3:30:00 PM')
+    >>> format_time(t, locale='en_US') == '3:30:00 PM'
     True
-    >>> format_time(t, format='short', locale='de_DE') == u('15:30')
+    >>> format_time(t, format='short', locale='de_DE') == '15:30'
     True
     
     If you don't want to use the locale default formats, you can specify a
     custom time pattern:
     
-    >>> format_time(t, "hh 'o''clock' a", locale='en') == u("03 o'clock PM")
+    >>> format_time(t, "hh 'o''clock' a", locale='en') == "03 o'clock PM"
     True
     
     For any pattern requiring the display of the time-zone, the third-party
     >>> t = datetime(2007, 4, 1, 15, 30)
     >>> tzinfo = timezone('Europe/Paris')
     >>> t = tzinfo.localize(t)
-    >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR') == u('15:30:00 Heure avanc\xe9e de l\u2019Europe centrale')
+    >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR') == '15:30:00 Heure avanc\xe9e de l\u2019Europe centrale'
     True
     >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=timezone('US/Eastern'),
-    ...             locale='en') == u("09 o'clock AM, Eastern Daylight Time")
+    ...             locale='en') == "09 o'clock AM, Eastern Daylight Time"
     True
     
     As that example shows, when this function gets passed a
     
     >>> t = time(15, 30)
     >>> format_time(t, format='full', tzinfo=timezone('Europe/Paris'),
-    ...             locale='fr_FR') == u('15:30:00 Heure normale de l\u2019Europe centrale')
+    ...             locale='fr_FR') == '15:30:00 Heure normale de l\u2019Europe centrale'
     True
     >>> format_time(t, format='full', tzinfo=timezone('US/Eastern'),
-    ...             locale='en_US') == u('3:30:00 PM Eastern Standard Time')
+    ...             locale='en_US') == '3:30:00 PM Eastern Standard Time'
     True
     
     :param time: the ``time`` or ``datetime`` object; if `None`, the current
 def format_timedelta(delta, granularity='second', threshold=.85, locale=LC_TIME):
     """Return a time delta according to the rules of the given locale.
 
-    >>> format_timedelta(timedelta(weeks=12), locale='en_US') == u('3 mths')
+    >>> format_timedelta(timedelta(weeks=12), locale='en_US') == '3 mths'
     True
-    >>> format_timedelta(timedelta(seconds=1), locale='es') == u('1 s')
+    >>> format_timedelta(timedelta(seconds=1), locale='es') == '1 s'
     True
 
     The granularity parameter can be provided to alter the lowest unit
     presented, which defaults to a second.
     
     >>> format_timedelta(timedelta(hours=3), granularity='day',
-    ...                  locale='en_US') == u('1 day')
+    ...                  locale='en_US') == '1 day'
     True
 
     The threshold parameter can be used to determine at which value the
     presentation switches to the next higher unit. A higher threshold factor
     means the presentation will switch later. For example:
 
-    >>> format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US') == u('1 day')
+    >>> format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US') == '1 day'
     True
-    >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US') == u('23 hrs')
+    >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US') == '23 hrs'
     True
 
     :param delta: a ``timedelta`` object representing the time difference to
             pattern = locale._data['unit_patterns'][unit][plural_form]
             return pattern.replace('{0}', str(value))
 
-    return u('')
+    return ''
 
 def parse_date(string, locale=LC_TIME):
     """Parse a date from a string.
 def parse_pattern(pattern):
     """Parse date, time, and datetime format patterns.
     
-    >>> parse_pattern("MMMMd").format == u('%(MMMM)s%(d)s')
+    >>> parse_pattern("MMMMd").format == '%(MMMM)s%(d)s'
     True
-    >>> parse_pattern("MMM d, yyyy").format == u('%(MMM)s %(d)s, %(yyyy)s')
+    >>> parse_pattern("MMM d, yyyy").format == '%(MMM)s %(d)s, %(yyyy)s'
     True
     
     Pattern can contain literal strings in single quotes:
     
-    >>> parse_pattern("H:mm' Uhr 'z").format == u('%(H)s:%(mm)s Uhr %(z)s')
+    >>> parse_pattern("H:mm' Uhr 'z").format == '%(H)s:%(mm)s Uhr %(z)s'
     True
     
     An actual single quote can be used by using two adjacent single quote
     characters:
     
-    >>> parse_pattern("hh' o''clock'").format == u("%(hh)s o'clock")
+    >>> parse_pattern("hh' o''clock'").format == "%(hh)s o'clock"
     True
     
     :param pattern: the formatting pattern to parse
     elif charbuf:
         append_chars()
 
-    return DateTimePattern(pattern, u('').join(result).replace('\0', "'"))
+    return DateTimePattern(pattern, ''.join(result).replace('\0', "'"))

babel/localedata.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 """Low-level locale data access.
 
 :note: The `Locale` class, which uses this module under the hood, provides a
 """
 
 import os
-from babel.compat import pickle, DictMixin, PY3, u, threading
+from babel.compat import pickle, DictMixin, PY3, threading
 
 __all__ = ['exists', 'locale_identifiers', 'load']
 __docformat__ = 'restructuredtext en'
     collection of pickle files inside the ``babel`` package.
     
     >>> d = load('en_US')
-    >>> d['languages']['sv'] == u('Swedish')
+    >>> d['languages']['sv'] == 'Swedish'
     True
     
     Note that the results are cached, and subsequent requests for the same
     
     >>> d = {1: 'foo', 3: 'baz'}
     >>> merge(d, {1: 'Foo', 2: 'Bar'})
-    >>> items = sorted(d.items()); items
-    [(1, 'Foo'), (2, 'Bar'), (3, 'baz')]
+    >>> items = sorted(d.items()); items == [(1, 'Foo'), (2, 'Bar'), (3, 'baz')]
+    True
     
     :param dict1: the dictionary to merge into
     :param dict2: the dictionary containing the data that should be merged

babel/messages/catalog.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 """Data structures for message catalogs."""
 
 from cgi import parse_header
 import time
 
 from babel import __version__ as VERSION
-from babel.compat import u, string_types, PY3
+from babel.compat import string_types, PY3
 from babel.core import Locale
 from babel.dates import format_datetime
 from babel.messages.plurals import get_plural
 class Message(object):
     """Representation of a single message in a catalog."""
 
-    def __init__(self, id, string=u(''), locations=(), flags=(), auto_comments=(),
+    def __init__(self, id, string='', locations=(), flags=(), auto_comments=(),
                  user_comments=(), previous_id=(), lineno=None, context=None):
         """Create the message object.
 
         """
         self.id = id #: The message ID
         if not string and self.pluralizable:
-            string = (u(''), u(''))
+            string = ('', '')
         self.string = string #: The message translation
         self.locations = list(distinct(locations))
         self.flags = set(flags)
 
     def __repr__(self):
         return '<%s %s (flags: %r)>' % (type(self).__name__, self.id,
-                                        list(self.flags))
+                                        [str(f) for f in self.flags])
 
     def __cmp__(self, obj):
         """Compare Messages, taking into account plural ids"""
     translations are encountered."""
 
 
-DEFAULT_HEADER = u("""\
+DEFAULT_HEADER = """\
 # Translations template for PROJECT.
 # Copyright (C) YEAR ORGANIZATION
 # This file is distributed under the same license as the PROJECT project.
 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#""")
+#"""
 
 
 class Catalog(object):
             name = name.lower()
             if name == 'project-id-version':
                 parts = value.split(' ')
-                self.project = u(' ').join(parts[:-1])
+                self.project = ' '.join(parts[:-1])
                 self.version = parts[-1]
             elif name == 'report-msgid-bugs-to':
                 self.msgid_bugs_address = value
     plural_forms = property(plural_forms, doc="""\
     Return the plural forms declaration for the locale.
 
-    >>> Catalog(locale='en').plural_forms
-    'nplurals=2; plural=(n != 1)'
-    >>> Catalog(locale='pt_BR').plural_forms
-    'nplurals=2; plural=(n > 1)'
+    >>> Catalog(locale='en').plural_forms == 'nplurals=2; plural=(n != 1)'
+    True
+    >>> Catalog(locale='pt_BR').plural_forms == 'nplurals=2; plural=(n > 1)'
+    True
 
     :type: `str`
     """)
         flags = set()
         if self.fuzzy:
             flags |= set(['fuzzy'])
-        yield Message(u(''), '\n'.join(buf), flags=flags)
+        yield Message('', '\n'.join(buf), flags=flags)
         for key in self._messages:
             yield self._messages[key]
 
         """Add or update the message with the specified ID.
 
         >>> catalog = Catalog()
-        >>> catalog[u('foo')] = Message(u('foo'))
-        >>> catalog[u('foo')]
+        >>> catalog['foo'] = Message('foo')
+        >>> catalog['foo']
         <Message foo (flags: [])>
 
         If a message with that ID is already in the catalog, it is updated
         to include the locations and flags of the new message.
 
         >>> catalog = Catalog()
-        >>> catalog[u('foo')] = Message(u('foo'), locations=[('main.py', 1)])
-        >>> catalog[u('foo')].locations
-        [('main.py', 1)]
-        >>> catalog[u('foo')] = Message(u('foo'), locations=[('utils.py', 5)])
-        >>> catalog[u('foo')].locations
-        [('main.py', 1), ('utils.py', 5)]
+        >>> catalog['foo'] = Message('foo', locations=[('main.py', 1)])
+        >>> catalog['foo'].locations == [('main.py', 1)]
+        True
+        >>> catalog['foo'] = Message('foo', locations=[('utils.py', 5)])
+        >>> catalog['foo'].locations == [('main.py', 1), ('utils.py', 5)]
+        True
 
         :param id: the message ID
         :param message: the `Message` object
         """Add or update the message with the specified ID.
 
         >>> catalog = Catalog()
-        >>> catalog.add(u('foo'))
+        >>> catalog.add('foo')
         <Message ...>
-        >>> catalog[u('foo')]
+        >>> catalog['foo']
         <Message foo (flags: [])>
 
         This method simply constructs a `Message` object with the given
         >>> template.add(('salad', 'salads'), locations=[('util.py', 42)])
         <Message ...>
         >>> catalog = Catalog(locale='de_DE')
-        >>> catalog.add('blue', u('blau'), locations=[('main.py', 98)])
+        >>> catalog.add('blue', 'blau', locations=[('main.py', 98)])
         <Message ...>
-        >>> catalog.add('head', u('Kopf'), locations=[('util.py', 33)])
+        >>> catalog.add('head', 'Kopf', locations=[('util.py', 33)])
         <Message ...>
-        >>> catalog.add(('salad', 'salads'), (u('Salat'), u('Salate')),
+        >>> catalog.add(('salad', 'salads'), ('Salat', 'Salate'),
         ...             locations=[('util.py', 38)])
         <Message ...>
 
 
         >>> msg1 = catalog['green']
         >>> msg1.string
-        >>> msg1.locations
-        [('main.py', 99)]
+        >>> msg1.locations == [('main.py', 99)]
+        True
 
         >>> msg2 = catalog['blue']
         >>> print(msg2.string)
         blau
-        >>> msg2.locations
-        [('main.py', 100)]
+        >>> msg2.locations == [('main.py', 100)]
+        True
 
         >>> msg3 = catalog['salad']
         >>> print(msg3.string[0])
         Salat
         >>> print(msg3.string[1])
         Salate
-        >>> msg3.locations
-        [('util.py', 42)]
+        >>> msg3.locations == [('util.py', 42)]
+        True
 
         Messages that are in the catalog but not in the template are removed
         from the main collection, but can still be accessed via the `obsolete`
                 if not isinstance(message.string, (list, tuple)):
                     fuzzy = True
                     message.string = tuple(
-                        [message.string] + ([u('')] * (len(message.id) - 1))
+                        [message.string] + ([''] * (len(message.id) - 1))
                     )
                 elif len(message.string) != self.num_plurals:
                     fuzzy = True
                 message.string = message.string[0]
             message.flags |= oldmsg.flags
             if fuzzy:
-                message.flags |= set([u('fuzzy')])
+                message.flags |= set(['fuzzy'])
             self[message.id] = message
 
         for message in template:

babel/messages/extract.py

 The main entry points into the extraction functionality are the functions
 `extract_from_dir` and `extract_from_file`.
 """
+from __future__ import unicode_literals
 
 import os
 import sys
 from tokenize import generate_tokens, COMMENT, NAME, OP, STRING
 
-from babel.compat import PY3, binary_type
+from babel.compat import PY3
 from babel.util import parse_encoding, pathmatch, relpath
 from textwrap import dedent
 
 
     def readline():
         line = fileobj.readline()
-        if PY3 and isinstance(line, binary_type):
+        if PY3 and isinstance(line, bytes):
             try:
                 line = line.decode(encoding)
             except UnicodeDecodeError:
                 # encoding
                 # https://sourceforge.net/tracker/?func=detail&atid=355470&
                 # aid=617979&group_id=5470
-                value = eval('# coding=%s\n%s' % (encoding, value),
-                             {'__builtins__':{}}, {})
-                if isinstance(value, binary_type):
+                if PY3:
+                    s = '# coding=%s\n%s' % (encoding, value)
+                else:
+                    s = ('# coding=%s\n' % encoding).encode(encoding)
+                    if isinstance(value, bytes):
+                        s += value
+                    else:
+                        s += value.encode(encoding)
+                value = eval(s, {'__builtins__':{}}, {})
+                if isinstance(value, bytes):
                     value = value.decode(encoding)
                 buf.append(value)
             elif tok == OP and value == ',':
     call_stack = -1
 
     data = fileobj.read()
-    if not PY3:
+    if isinstance(data, bytes):
         data = data.decode(encoding)
     for token in tokenize(data):
         if token.type == 'operator' and token.value == '(':

babel/messages/frontend.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 """Frontends for the message extraction functionality."""
 
 from datetime import datetime
 
 from babel import __version__ as VERSION
 from babel import Locale, localedata
-from babel.compat import RawConfigParser, StringIO, string_types, u
+from babel.compat import RawConfigParser, StringIO, string_types
 from babel.core import UnknownLocaleError
 from babel.messages.catalog import Catalog
 from babel.messages.extract import extract_from_dir, DEFAULT_KEYWORDS, \
             raise DistutilsOptionError('no message catalogs found')
 
         for idx, (locale, po_file) in enumerate(po_files):
-            mo_file = mo_files[idx]
+            po_file = str(po_file)
+            mo_file = str(mo_files[idx])
             infile = open(po_file, 'r')
             try:
                 catalog = read_po(infile, locale)
         if options.list_locales:
             identifiers = sorted(localedata.locale_identifiers())
             longest = max([len(identifier) for identifier in identifiers])
-            format = u('%%-%ds %%s') % (longest + 1)
+            format = '%%-%ds %%s' % (longest + 1)
             for identifier in identifiers:
                 locale = Locale.parse(identifier)
                 output = format % (identifier, locale.english_name)
             parser.error('no message catalogs found')
 
         for idx, (locale, po_file) in enumerate(po_files):
-            mo_file = mo_files[idx]
+            po_file = str(po_file)
+            mo_file = str(mo_files[idx])
             infile = open(po_file, 'r')
             try:
                 catalog = read_po(infile, locale)
     >>> len(method_map)
     4
 
-    >>> method_map[0]
-    ('**.py', 'python')
+    >>> method_map[0] == ('**.py', 'python')
+    True
     >>> options_map['**.py']
     {}
-    >>> method_map[1]
-    ('**/templates/**.html', 'genshi')
-    >>> options_map['**/templates/**.html']['include_attrs']
-    ''
-    >>> method_map[2]
-    ('**/templates/**.txt', 'genshi')
-    >>> options_map['**/templates/**.txt']['template_class']
-    'genshi.template:TextTemplate'
-    >>> options_map['**/templates/**.txt']['encoding']
-    'latin-1'
+    >>> method_map[1] == ('**/templates/**.html', 'genshi')
+    True
+    >>> options_map['**/templates/**.html']['include_attrs'] == ''
+    True
+    >>> method_map[2] == ('**/templates/**.txt', 'genshi')
+    True
+    >>> options_map['**/templates/**.txt']['template_class'] == 'genshi.template:TextTemplate'
+    True
+    >>> options_map['**/templates/**.txt']['encoding'] == 'latin-1'
+    True
 
-    >>> method_map[3]
-    ('**/custom/*.*', 'mypackage.module:myfunc')
+    >>> method_map[3] == ('**/custom/*.*', 'mypackage.module:myfunc')
+    True
     >>> options_map['**/custom/*.*']
     {}
 
     """Parse keywords specifications from the given list of strings.
 
     >>> kw = sorted(parse_keywords(['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2']).items())
-    >>> for keyword, indices in sorted(kw):
-    ...     print((keyword, indices))
-    ('_', None)
-    ('dgettext', (2,))
-    ('dngettext', (2, 3))
-    ('pgettext', ((1, 'c'), 2))
+    >>> expected = [
+    ...     ('_', None),
+    ...     ('dgettext', (2,)),
+    ...     ('dngettext', (2, 3)),
+    ...     ('pgettext', ((1, 'c'), 2))
+    ... ]
+    >>> [(keyword, indices) for keyword, indices in sorted(kw)] == expected
+    True
     """
     keywords = {}
     for string in strings:

babel/messages/jslexer.py

 """A simple JavaScript 1.5 lexer which is used for the JavaScript
 extractor.
 """
+from __future__ import unicode_literals
 
 from operator import itemgetter
 import re
 
-from babel.compat import unichr, u
+from babel.compat import unichr
 
 operators = [
     '+', '-', '*', '%', '!=', '==', '<', '>', '<=', '>=', '=',
     if pos < len(string):
         add(string[pos:])
 
-    return u('').join(result)
+    return ''.join(result)
 
 
 def tokenize(source):

babel/messages/mofile.py

 :see: `The Format of MO Files
        <http://www.gnu.org/software/gettext/manual/gettext.html#MO-Files>`_
 """
+from __future__ import unicode_literals
 
 import array
 import struct
 
-from babel.compat import long_type, xrange, u, b, PY3
+from babel.compat import long_type, xrange, PY3
 from babel.messages.catalog import Catalog, Message
 
 __all__ = ['read_mo', 'write_mo']
                 item = item.strip()
                 if not item:
                     continue
-                if b(':') in item:
-                    key, value = item.split(b(':'), 1)
+                if b':' in item:
+                    key, value = item.split(b':', 1)
                     lastkey = key = key.strip().lower()
                     headers[key] = value.strip()
                 elif lastkey:
                     headers[lastkey] += '\n' + item
 
-        if b('\x04') in msg: # context
-            ctxt, msg = msg.split(b('\x04'))
+        if b'\x04' in msg: # context
+            ctxt, msg = msg.split(b'\x04')
         else:
             ctxt = None
 
-        if b('\x00') in msg: # plural forms
-            msg = msg.split(b('\x00'))
-            tmsg = tmsg.split(b('\x00'))
+        if b'\x00' in msg: # plural forms
+            msg = msg.split(b'\x00')
+            tmsg = tmsg.split(b'\x00')
             if catalog.charset:
                 msg = [x.decode(catalog.charset) for x in msg]
                 tmsg = [x.decode(catalog.charset) for x in tmsg]
     >>> catalog = Catalog(locale='en_US')
     >>> catalog.add('foo', 'Voh')
     <Message ...>
-    >>> catalog.add((u('bar'), u('baz')), (u('Bahr'), u('Batz')))
+    >>> catalog.add(('bar', 'baz'), ('Bahr', 'Batz'))
     <Message ...>
     >>> catalog.add('fuz', 'Futz', flags=['fuzzy'])
     <Message ...>
     >>> write_mo(buf, catalog)
     >>> _ = buf.seek(0)
     >>> translations = GNUTranslations(fp=buf)
-    >>> translations.ugettext('foo') == u('Voh')
+    >>> translations.ugettext('foo') == 'Voh'
     True
-    >>> translations.ungettext('bar', 'baz', 1) == u('Bahr')
+    >>> translations.ungettext('bar', 'baz', 1) == 'Bahr'
     True
-    >>> translations.ungettext('bar', 'baz', 2) == u('Batz')
+    >>> translations.ungettext('bar', 'baz', 2) == 'Batz'
     True
-    >>> translations.ugettext('fuz') == u('fuz')
+    >>> translations.ugettext('fuz') == 'fuz'
     True
-    >>> translations.ugettext('Fizz') == u('Fizz')
+    >>> translations.ugettext('Fizz') == 'Fizz'
     True
-    >>> translations.ugettext('Fuzz') == u('Fuzz')
+    >>> translations.ugettext('Fuzz') == 'Fuzz'
     True
-    >>> translations.ugettext('Fuzzes') == u('Fuzzes')
+    >>> translations.ugettext('Fuzzes') == 'Fuzzes'
     True
     
     :param fileobj: the file-like object to write to
         messages[1:] = [m for m in messages[1:] if not m.fuzzy]
     messages.sort()
 
-    ids = strs = b('')
+    ids = strs = b''
     offsets = []
 
     for message in messages:
         # For each string, we need size and file offset.  Each string is NUL
         # terminated; the NUL does not count into the size.
         if message.pluralizable:
-            msgid = b('\x00').join([
+            msgid = b'\x00'.join([
                 msgid.encode(catalog.charset) for msgid in message.id
             ])
             msgstrs = []
                     msgstrs.append(message.id[min(int(idx), 1)])
                 else:
                     msgstrs.append(string)
-            msgstr = b('\x00').join([
+            msgstr = b'\x00'.join([
                 msgstr.encode(catalog.charset) for msgstr in msgstrs
             ])
         else:
             else:
                 msgstr = message.string.encode(catalog.charset)
         if message.context:
-            msgid = b('\x04').join([message.context.encode(catalog.charset),
+            msgid = b'\x04'.join([message.context.encode(catalog.charset),
                                  msgid])
         offsets.append((len(ids), len(msgid), len(strs), len(msgstr)))
-        ids += msgid + b('\x00')
-        strs += msgstr + b('\x00')
+        ids += msgid + b'\x00'
+        strs += msgstr + b'\x00'
 
     # The header is 7 32-bit unsigned integers.  We don't use hash tables, so
     # the keys start right after the index tables.
         voffsets += [l2, o2 + valuestart]
     offsets = koffsets + voffsets
 
-    fileobj.write(struct.pack('Iiiiiii',
+    fileobj.write(struct.pack(str('Iiiiiii'),
         LE_MAGIC,                   # magic
         0,                          # version
         len(messages),              # number of entries
     if PY3:
         fileobj.write(array.array("i", offsets).tobytes())
     else:
-        fileobj.write(array.array("i", offsets).tostring())
+        fileobj.write(array.array(str("i"), offsets).tostring())
     fileobj.write(ids + strs)

babel/messages/pofile.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 """Reading and writing of files in the ``gettext`` PO (portable object)
 format.
 
 import os
 import re
 
-from babel.compat import u, text_type
+from babel.compat import text_type
 from babel.messages.catalog import Catalog, Message
 from babel.util import wraptext
 
 
     >>> for message in catalog:
     ...     if message.id:
-    ...         print('id(s): %s' % (isinstance(message.id, tuple) and u(',').join(message.id) or message.id))
-    ...         print('strings(s): %s' % (isinstance(message.string, tuple) and u(',').join(message.string) or message.string))
+    ...         print('id(s): %s' % (isinstance(message.id, tuple) and ','.join(message.id) or message.id))
+    ...         print('strings(s): %s' % (isinstance(message.string, tuple) and ','.join(message.string) or message.string))
     ...         for loc in message.locations:
     ...             print('file: %s line: %d' % loc)
     ...         print('flags: %s' % ' '.join(sorted(message.flags)))
             context.append(line[7:].lstrip())
         elif line.startswith('"'):
             if in_msgid[0]:
-                messages[-1] += u('\n') + line.rstrip()
+                messages[-1] += '\n' + line.rstrip()
             elif in_msgstr[0]:
-                translations[-1][1] += u('\n') + line.rstrip()
+                translations[-1][1] += '\n' + line.rstrip()
             elif in_msgctxt[0]:
                 context.append(line.rstrip())
 
     # No actual messages found, but there was some info in comments, from which
     # we'll construct an empty header message
     elif not counter[0] and (flags or user_comments or auto_comments):
-        messages.append(u(''))
-        translations.append([0, u('')])
+        messages.append('')
+        translations.append([0, ''])
         _add_message()
 
     return catalog
 
     >>> escape('''Say:
     ...   "hello, world!"
-    ... ''')
-    '"Say:\\n  \\"hello, world!\\"\\n"'
+    ... ''') == '"Say:\\n  \\"hello, world!\\"\\n"'
+    True
 
     :param string: the string to escape
     :return: the escaped string
                                 # separate line
                                 buf.append(chunks.pop())
                             break
-                    lines.append(u('').join(buf))
+                    lines.append(''.join(buf))
             else:
                 lines.append(line)
     else:
     if lines and not lines[-1]:
         del lines[-1]
         lines[-1] += '\n'
-    return u('""\n') + u('\n').join([(prefix + escape(l)) for l in lines])
+    return '""\n' + '\n'.join([(prefix + escape(l)) for l in lines])
 
 def write_po(fileobj, catalog, width=76, no_location=False, omit_header=False,
              sort_output=False, sort_by_file=False, ignore_obsolete=False,
     message catalog to the provided file-like object.
 
     >>> catalog = Catalog()
-    >>> catalog.add(u('foo %(name)s'), locations=[('main.py', 1)],
+    >>> catalog.add('foo %(name)s', locations=[('main.py', 1)],
     ...             flags=('fuzzy',))
     <Message...>
-    >>> catalog.add((u('bar'), u('baz')), locations=[('main.py', 3)])
+    >>> catalog.add(('bar', 'baz'), locations=[('main.py', 3)])
     <Message...>
     >>> from babel.compat import BytesIO
     >>> buf = BytesIO()
                 for line in comment_header.splitlines():
                     lines += wraptext(line, width=width,
                                       subsequent_indent='# ')
-                comment_header = u('\n').join(lines) + u('\n')
+                comment_header = '\n'.join(lines) + '\n'
             _write(comment_header)
 
         for comment in message.user_comments:
             _write_comment(comment, prefix='.')
 
         if not no_location:
-            locs = u(' ').join([u('%s:%d') % (filename.replace(os.sep, '/'), lineno)
+            locs = ' '.join(['%s:%d' % (filename.replace(os.sep, '/'), lineno)
                               for filename, lineno in message.locations])
             _write_comment(locs, prefix=':')
         if message.flags:

babel/messages/tests/catalog.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 import copy
 import datetime
 import doctest
 import unittest
 
-from babel.compat import u
 from babel.messages import catalog
 
 
 
     def test_update_message_changed_to_plural(self):
         cat = catalog.Catalog()
-        cat.add(u('foo'), u('Voh'))
+        cat.add('foo', 'Voh')
         tmpl = catalog.Catalog()
-        tmpl.add((u('foo'), u('foos')))
+        tmpl.add(('foo', 'foos'))
         cat.update(tmpl)
-        self.assertEqual((u('Voh'), ''), cat['foo'].string)
+        self.assertEqual(('Voh', ''), cat['foo'].string)
         assert cat['foo'].fuzzy
 
     def test_update_message_changed_to_simple(self):
         cat = catalog.Catalog()
-        cat.add((u('foofoos')), (u('Voh'), u('V\xf6hs')))
+        cat.add(('foofoos'), ('Voh', 'V\xf6hs'))
         tmpl = catalog.Catalog()
-        tmpl.add(u('foo'))
+        tmpl.add('foo')
         cat.update(tmpl)
-        self.assertEqual(u('Voh'), cat['foo'].string)
+        self.assertEqual('Voh', cat['foo'].string)
         assert cat['foo'].fuzzy
 
     def test_update_message_updates_comments(self):
         cat = catalog.Catalog()
-        cat[u('foo')] = catalog.Message('foo', locations=[('main.py', 5)])
-        self.assertEqual(cat[u('foo')].auto_comments, [])
-        self.assertEqual(cat[u('foo')].user_comments, [])
-        # Update cat[u('foo')] with a new location and a comment
-        cat[u('foo')] = catalog.Message('foo', locations=[('main.py', 7)],
+        cat['foo'] = catalog.Message('foo', locations=[('main.py', 5)])
+        self.assertEqual(cat['foo'].auto_comments, [])
+        self.assertEqual(cat['foo'].user_comments, [])
+        # Update cat['foo'] with a new location and a comment
+        cat['foo'] = catalog.Message('foo', locations=[('main.py', 7)],
                                       user_comments=['Foo Bar comment 1'])
-        self.assertEqual(cat[u('foo')].user_comments, ['Foo Bar comment 1'])
+        self.assertEqual(cat['foo'].user_comments, ['Foo Bar comment 1'])
         # now add yet another location with another comment
-        cat[u('foo')] = catalog.Message('foo', locations=[('main.py', 9)],
+        cat['foo'] = catalog.Message('foo', locations=[('main.py', 9)],
                                       auto_comments=['Foo Bar comment 2'])
-        self.assertEqual(cat[u('foo')].auto_comments, ['Foo Bar comment 2'])
+        self.assertEqual(cat['foo'].auto_comments, ['Foo Bar comment 2'])
 
     def test_update_fuzzy_matching_with_case_change(self):
         cat = catalog.Catalog()

babel/messages/tests/checkers.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 from datetime import datetime
 import time
 import unittest
 
 from babel import __version__ as VERSION
-from babel.compat import StringIO, BytesIO, u
+from babel.compat import StringIO, BytesIO
 from babel.core import Locale, UnknownLocaleError
 from babel.dates import format_datetime
 from babel.messages import checkers
             except UnknownLocaleError:
                 # Just an alias? Not what we're testing here, let's continue
                 continue
-            po_file = (u(r"""\
+            po_file = (r"""\
 # %(english_name)s translations for TestProject.
 # Copyright (C) 2007 FooBar, Inc.
 # This file is distributed under the same license as the TestProject
 msgid_plural "foobars"
 msgstr[0] ""
 
-""") % dict(locale       = _locale,
+""" % dict(locale       = _locale,
            english_name = locale.english_name,
            version      = VERSION,
            year         = time.strftime('%Y'),
             except UnknownLocaleError:
                 # Just an alias? Not what we're testing here, let's continue
                 continue
-            po_file = (u(r"""\
+            po_file = (r"""\
 # %(english_name)s translations for TestProject.
 # Copyright (C) 2007 FooBar, Inc.
 # This file is distributed under the same license as the TestProject
 msgstr[1] ""
 msgstr[2] ""
 
-""") % dict(locale       = _locale,
+""" % dict(locale       = _locale,
            english_name = locale.english_name,
            version      = VERSION,
            year         = time.strftime('%Y'),

babel/messages/tests/extract.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 import codecs
 import doctest
 import sys
 import unittest
 
-from babel.compat import StringIO, BytesIO, u, b
+from babel.compat import StringIO, BytesIO
 from babel.messages import extract
 
 
         self.assertEqual([
                 (1, '_', None, []),
                 (2, 'ungettext', (None, None, None), []),
-                (3, 'ungettext', (u('Babel'), None, None), []),
-                (4, 'ungettext', (None, u('Babels'), None), []),
-                (5, 'ungettext', (u('bunny'), u('bunnies'), None), []),
-                (6, 'ungettext', (None, u('bunnies'), None), []),
+                (3, 'ungettext', ('Babel', None, None), []),
+                (4, 'ungettext', (None, 'Babels', None), []),
+                (5, 'ungettext', ('bunny', 'bunnies', None), []),
+                (6, 'ungettext', (None, 'bunnies', None), []),
                 (7, '_', None, []),
-                (8, 'gettext', u('Rabbit'), []),
-                (9, 'dgettext', (u('wiki'), None), []),
-                (10, 'dngettext', (None, u('Page'), u('Pages'), None), [])],
+                (8, 'gettext', 'Rabbit', []),
+                (9, 'dgettext', ('wiki', None), []),
+                (10, 'dngettext', (None, 'Page', 'Pages', None), [])],
                          messages)
 
     def test_nested_comments(self):
 """)
         messages = list(extract.extract_python(buf, ('ngettext',),
                                                ['TRANSLATORS:'], {}))
-        self.assertEqual([(1, 'ngettext', (u('pylon'), u('pylons'), None), [])],
+        self.assertEqual([(1, 'ngettext', ('pylon', 'pylons', None), [])],
                          messages)
 
     def test_comments_with_calls_that_spawn_multiple_lines(self):
 
                                                {'strip_comment_tags':False}))
         self.assertEqual((6, '_', 'Locale deleted.',
-                          [u('NOTE: This Comment SHOULD Be Extracted')]),
+                          ['NOTE: This Comment SHOULD Be Extracted']),
                          messages[1])
-        self.assertEqual((10, 'ngettext', (u('Foo deleted.'), u('Foos deleted.'),
+        self.assertEqual((10, 'ngettext', ('Foo deleted.', 'Foos deleted.',
                                            None),
-                          [u('NOTE: This Comment SHOULD Be Extracted')]),
+                          ['NOTE: This Comment SHOULD Be Extracted']),
                          messages[2])
         self.assertEqual((3, 'ngettext',
-                           (u('Catalog deleted.'),
-                            u('Catalogs deleted.'), None),
-                           [u('NOTE: This Comment SHOULD Be Extracted')]),
+                           ('Catalog deleted.',
+                            'Catalogs deleted.', None),
+                           ['NOTE: This Comment SHOULD Be Extracted']),
                          messages[0])
-        self.assertEqual((15, 'ngettext', (u('Bar deleted.'), u('Bars deleted.'),
+        self.assertEqual((15, 'ngettext', ('Bar deleted.', 'Bars deleted.',
                                            None),
-                          [u('NOTE: This Comment SHOULD Be Extracted'),
-                           u('NOTE: And This One Too')]),
-                         messages[3])
+                          ['NOTE: This Comment SHOULD Be Extracted',
+                           'NOTE: And This One Too']),
+                          messages[3])
 
     def test_declarations(self):
         buf = StringIO("""\
         messages = list(extract.extract_python(buf,
                                                list(extract.DEFAULT_KEYWORDS.keys()),
                                                [], {}))
-        self.assertEqual([(3, '_', u('Page arg 1'), []),
-                          (3, '_', u('Page arg 2'), []),
-                          (8, '_', u('log entry'), [])],
+        self.assertEqual([(3, '_', 'Page arg 1', []),
+                          (3, '_', 'Page arg 2', []),
+                          (8, '_', 'log entry', [])],
                          messages)
 
     def test_multiline(self):
                  count)
 """)
         messages = list(extract.extract_python(buf, ('ngettext',), [], {}))
-        self.assertEqual([(1, 'ngettext', (u('pylon'), u('pylons'), None), []),
-                          (3, 'ngettext', (u('elvis'), u('elvises'), None), [])],
+        self.assertEqual([(1, 'ngettext', ('pylon', 'pylons', None), []),
+                          (3, 'ngettext', ('elvis', 'elvises', None), [])],
                          messages)
 
     def test_triple_quoted_strings(self):
         messages = list(extract.extract_python(buf,
                                                extract.DEFAULT_KEYWORDS.keys(),
                                                [], {}))
-        self.assertEqual([(1, '_', (u('pylons')), []),
-                          (2, 'ngettext', (u('elvis'), u('elvises'), None), []),
-                          (3, 'ngettext', (u('elvis'), u('elvises'), None), [])],
+        self.assertEqual([(1, '_', ('pylons'), []),
+                          (2, 'ngettext', ('elvis', 'elvises', None), []),
+                          (3, 'ngettext', ('elvis', 'elvises', None), [])],
                          messages)
 
     def test_multiline_strings(self):
                                                [], {}))
         self.assertEqual(
             [(1, '_',
-              u('This module provides internationalization and localization\n'
+              'This module provides internationalization and localization\n'
               'support for your Python programs by providing an interface to '
-              'the GNU\ngettext message catalog library.'), [])],
+              'the GNU\ngettext message catalog library.', [])],
             messages)
 
     def test_concatenated_strings(self):
         messages = list(extract.extract_python(buf,
                                                list(extract.DEFAULT_KEYWORDS.keys()),
                                                [], {}))
-        self.assertEqual(u('foobar'), messages[0][2])
+        self.assertEqual('foobar', messages[0][2])
 
     def test_unicode_string_arg(self):
         buf = StringIO("msg = _(u'Foo Bar')")
         messages = list(extract.extract_python(buf, ('_',), [], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
+        self.assertEqual('Foo Bar', messages[0][2])
 
     def test_comment_tag(self):
         buf = StringIO("""
 msg = _(u'Foo Bar')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
-        self.assertEqual([u('NOTE: A translation comment')], messages[0][3])
+        self.assertEqual('Foo Bar', messages[0][2])
+        self.assertEqual(['NOTE: A translation comment'], messages[0][3])
 
     def test_comment_tag_multiline(self):
         buf = StringIO("""
 msg = _(u'Foo Bar')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
-        self.assertEqual([u('NOTE: A translation comment'), u('with a second line')],
+        self.assertEqual('Foo Bar', messages[0][2])
+        self.assertEqual(['NOTE: A translation comment', 'with a second line'],
                          messages[0][3])
 
     def test_translator_comments_with_previous_non_translator_comments(self):
 msg = _(u'Foo Bar')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
-        self.assertEqual([u('NOTE: A translation comment'), u('with a second line')],
+        self.assertEqual('Foo Bar', messages[0][2])
+        self.assertEqual(['NOTE: A translation comment', 'with a second line'],
                          messages[0][3])
 
     def test_comment_tags_not_on_start_of_comment(self):
 msg = _(u'Foo Bar')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
-        self.assertEqual([u('NOTE: This one will be')], messages[0][3])
+        self.assertEqual('Foo Bar', messages[0][2])
+        self.assertEqual(['NOTE: This one will be'], messages[0][3])
 
     def test_multiple_comment_tags(self):
         buf = StringIO("""
 """)
         messages = list(extract.extract_python(buf, ('_',),
                                                ['NOTE1:', 'NOTE2:'], {}))
-        self.assertEqual(u('Foo Bar1'), messages[0][2])
-        self.assertEqual([u('NOTE1: A translation comment for tag1'),
-                          u('with a second line')], messages[0][3])
-        self.assertEqual(u('Foo Bar2'), messages[1][2])
-        self.assertEqual([u('NOTE2: A translation comment for tag2')], messages[1][3])
+        self.assertEqual('Foo Bar1', messages[0][2])
+        self.assertEqual(['NOTE1: A translation comment for tag1',
+                          'with a second line'], messages[0][3])
+        self.assertEqual('Foo Bar2', messages[1][2])
+        self.assertEqual(['NOTE2: A translation comment for tag2'], messages[1][3])
 
     def test_two_succeeding_comments(self):
         buf = StringIO("""
 msg = _(u'Foo Bar')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
-        self.assertEqual([u('NOTE: one'), u('NOTE: two')], messages[0][3])
+        self.assertEqual('Foo Bar', messages[0][2])
+        self.assertEqual(['NOTE: one', 'NOTE: two'], messages[0][3])
 
     def test_invalid_translator_comments(self):
         buf = StringIO("""
 msg = _(u'Foo Bar')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
+        self.assertEqual('Foo Bar', messages[0][2])
         self.assertEqual([], messages[0][3])
 
     def test_invalid_translator_comments2(self):
 hello = _('Hello')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Hi there!'), messages[0][2])
-        self.assertEqual([u('NOTE: Hi!')], messages[0][3])
-        self.assertEqual(u('Hello'), messages[1][2])
+        self.assertEqual('Hi there!', messages[0][2])
+        self.assertEqual(['NOTE: Hi!'], messages[0][3])
+        self.assertEqual('Hello', messages[1][2])
         self.assertEqual([], messages[1][3])
 
     def test_invalid_translator_comments3(self):
 hithere = _('Hi there!')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Hi there!'), messages[0][2])
+        self.assertEqual('Hi there!', messages[0][2])
         self.assertEqual([], messages[0][3])
 
     def test_comment_tag_with_leading_space(self):
 msg = _(u'Foo Bar')
 """)
         messages = list(extract.extract_python(buf, ('_',), [':'], {}))
-        self.assertEqual(u('Foo Bar'), messages[0][2])
-        self.assertEqual([u(': A translation comment'), u(': with leading spaces')],
+        self.assertEqual('Foo Bar', messages[0][2])
+        self.assertEqual([': A translation comment', ': with leading spaces'],
                          messages[0][3])
 
     def test_different_signatures(self):
 n = ngettext('foo')
 """)
         messages = list(extract.extract_python(buf, ('_', 'ngettext'), [], {}))
-        self.assertEqual((u('foo'), u('bar')), messages[0][2])
-        self.assertEqual((u('hello'), u('there'), None), messages[1][2])
-        self.assertEqual((None, u('hello'), u('there')), messages[2][2])
+        self.assertEqual(('foo', 'bar'), messages[0][2])
+        self.assertEqual(('hello', 'there', None), messages[1][2])
+        self.assertEqual((None, 'hello', 'there'), messages[2][2])
         self.assertEqual((None, None), messages[3][2])
         self.assertEqual(None, messages[4][2])
         self.assertEqual(('foo'), messages[5][2])
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'],
                                                {'encoding': 'utf-8'}))
-        self.assertEqual(u('Bonjour \xe0 tous'), messages[0][2])
-        self.assertEqual([u('NOTE: hello')], messages[0][3])
+        self.assertEqual('Bonjour \xe0 tous', messages[0][2])
+        self.assertEqual(['NOTE: hello'], messages[0][3])
 
     def test_utf8_message_with_magic_comment(self):
         buf = StringIO("""# -*- coding: utf-8 -*-
 msg = _('Bonjour à tous')
 """)
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Bonjour \xe0 tous'), messages[0][2])
-        self.assertEqual([u('NOTE: hello')], messages[0][3])
+        self.assertEqual('Bonjour \xe0 tous', messages[0][2])
+        self.assertEqual(['NOTE: hello'], messages[0][3])
 
     def test_utf8_message_with_utf8_bom(self):
-        buf = BytesIO(codecs.BOM_UTF8 + u("""
+        buf = BytesIO(codecs.BOM_UTF8 + """
 # NOTE: hello
 msg = _('Bonjour \xe0 tous')
-""").encode('utf-8'))
+""".encode('utf-8'))
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Bonjour \xe0 tous'), messages[0][2])
-        self.assertEqual([u('NOTE: hello')], messages[0][3])
+        self.assertEqual('Bonjour \xe0 tous', messages[0][2])
+        self.assertEqual(['NOTE: hello'], messages[0][3])
 
     def test_utf8_raw_strings_match_unicode_strings(self):
-        buf = BytesIO(codecs.BOM_UTF8 + u("""
+        buf = BytesIO(codecs.BOM_UTF8 + """
 msg = _('Bonjour \xe0 tous')
 msgu = _(u'Bonjour \xe0 tous')
-""").encode('utf-8'))
+""".encode('utf-8'))
         messages = list(extract.extract_python(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Bonjour \xe0 tous'), messages[0][2])
+        self.assertEqual('Bonjour \xe0 tous', messages[0][2])
         self.assertEqual(messages[0][2], messages[1][2])
 
     def test_extract_strip_comment_tags(self):
 _('Babatschi')""")
         messages = list(extract.extract('python', buf, comment_tags=['NOTE:', ':'],
                                         strip_comment_tags=True))
-        self.assertEqual(u('Servus'), messages[0][1])
-        self.assertEqual([u('This is a comment with a very simple'),
-                          u('prefix specified')], messages[0][2])
-        self.assertEqual(u('Babatschi'), messages[1][1])
-        self.assertEqual([u('This is a multiline comment with'),
-                          u('a prefix too')], messages[1][2])
+        self.assertEqual('Servus', messages[0][1])
+        self.assertEqual(['This is a comment with a very simple',
+                          'prefix specified'], messages[0][2])
+        self.assertEqual('Babatschi', messages[1][1])
+        self.assertEqual(['This is a multiline comment with',
+                          'a prefix too'], messages[1][2])
 
 
 class ExtractJavaScriptTestCase(unittest.TestCase):
         messages = \
             list(extract.extract('javascript', buf, extract.DEFAULT_KEYWORDS, [],
                                  {}))
-        self.assertEqual([(5, (u('bunny'), u('bunnies')), [], None),
-                          (8, u('Rabbit'), [], None),
-                          (10, (u('Page'), u('Pages')), [], None)], messages)
+        self.assertEqual([(5, ('bunny', 'bunnies'), [], None),
+                          (8, 'Rabbit', [], None),
+                          (10, ('Page', 'Pages'), [], None)], messages)
 
     def test_message_with_line_comment(self):
         buf = StringIO("""\
 msg = _('Bonjour à tous')
 """)
         messages = list(extract.extract_javascript(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Bonjour \xe0 tous'), messages[0][2])
-        self.assertEqual([u('NOTE: hello')], messages[0][3])
+        self.assertEqual('Bonjour \xe0 tous', messages[0][2])
+        self.assertEqual(['NOTE: hello'], messages[0][3])
 
     def test_message_with_multiline_comment(self):
         buf = StringIO("""\
 msg = _('Bonjour à tous')
 """)
         messages = list(extract.extract_javascript(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Bonjour \xe0 tous'), messages[0][2])
-        self.assertEqual([u('NOTE: hello'), 'and bonjour', '  and servus'], messages[0][3])
+        self.assertEqual('Bonjour \xe0 tous', messages[0][2])
+        self.assertEqual(['NOTE: hello', 'and bonjour', '  and servus'], messages[0][3])
 
     def test_ignore_function_definitions(self):
         buf = StringIO("""\
 _('no comment here')
 """)
         messages = list(extract.extract_javascript(buf, ('_',), ['NOTE:'], {}))
-        self.assertEqual(u('Something'), messages[0][2])
-        self.assertEqual([u('NOTE: this will')], messages[0][3])
-        self.assertEqual(u('Something else'), messages[1][2])
-        self.assertEqual([u('NOTE: this will show up'), 'too.'], messages[1][3])
-        self.assertEqual(u('no comment here'), messages[2][2])
+        self.assertEqual('Something', messages[0][2])
+        self.assertEqual(['NOTE: this will'], messages[0][3])
+        self.assertEqual('Something else', messages[1][2])
+        self.assertEqual(['NOTE: this will show up', 'too.'], messages[1][3])
+        self.assertEqual('no comment here', messages[2][2])
         self.assertEqual([], messages[2][3])
 
 
         messages = \
             list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [],
                                  {}))
-        self.assertEqual([(5, (u('bunny'), u('bunnies')), [], None),
-                          (8, u('Rabbit'), [], None),
-                          (10, (u('Page'), u('Pages')), [], None)], messages)
+        self.assertEqual([(5, ('bunny', 'bunnies'), [], None),
+                          (8, 'Rabbit', [], None),
+                          (10, ('Page', 'Pages'), [], None)], messages)
 
     def test_invalid_extract_method(self):
         buf = StringIO('')
             list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [],
                                  {}))
         self.assertEqual(len(messages), 2)
-        self.assertEqual(u('foo'), messages[0][1])
-        self.assertEqual((u('hello'), u('there')), messages[1][1])
+        self.assertEqual('foo', messages[0][1])
+        self.assertEqual(('hello', 'there'), messages[1][1])
 
     def test_empty_string_msgid(self):
         buf = StringIO("""\

babel/messages/tests/mofile.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 import doctest
 import gettext
 import os
 import unittest
 
-from babel.compat import BytesIO, u, text_type
+from babel.compat import BytesIO, text_type
 from babel.messages import mofile, Catalog
 
 
         # can be applied to all subsequent messages by GNUTranslations
         # (ensuring all messages are safely converted to unicode)
         catalog = Catalog(locale='en_US')
-        catalog.add(u(''), '''\
+        catalog.add('', '''\
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n''')
-        catalog.add(u('foo'), 'Voh')
-        catalog.add((u('There is'), u('There are')), (u('Es gibt'), u('Es gibt')))
-        catalog.add(u('Fizz'), '')
+        catalog.add('foo', 'Voh')
+        catalog.add(('There is', 'There are'), ('Es gibt', 'Es gibt'))
+        catalog.add('Fizz', '')
         catalog.add(('Fuzz', 'Fuzzes'), ('', ''))
         buf = BytesIO()
         mofile.write_mo(buf, catalog)
         buf.seek(0)
         translations = gettext.GNUTranslations(fp=buf)
-        self.assertEqual(u('Voh'), translations.ugettext('foo'))
+        self.assertEqual('Voh', translations.ugettext('foo'))
         assert isinstance(translations.ugettext('foo'), text_type)
-        self.assertEqual(u('Es gibt'), translations.ungettext('There is', 'There are', 1))
+        self.assertEqual('Es gibt', translations.ungettext('There is', 'There are', 1))
         assert isinstance(translations.ungettext('There is', 'There are', 1), text_type)
-        self.assertEqual(u('Fizz'), translations.ugettext('Fizz'))
+        self.assertEqual('Fizz', translations.ugettext('Fizz'))
         assert isinstance(translations.ugettext('Fizz'), text_type)
-        self.assertEqual(u('Fuzz'), translations.ugettext('Fuzz'))
+        self.assertEqual('Fuzz', translations.ugettext('Fuzz'))
         assert isinstance(translations.ugettext('Fuzz'), text_type)
-        self.assertEqual(u('Fuzzes'), translations.ugettext('Fuzzes'))
+        self.assertEqual('Fuzzes', translations.ugettext('Fuzzes'))
         assert isinstance(translations.ugettext('Fuzzes'), text_type)
 
     def test_more_plural_forms(self):

babel/messages/tests/pofile.py

 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
 
+from __future__ import unicode_literals
+
 from datetime import datetime
 import doctest
 import unittest
 
-from babel.compat import StringIO, BytesIO, u, b
+from babel.compat import StringIO, BytesIO
 from babel.messages.catalog import Catalog, Message
 from babel.messages import pofile
 from babel.util import FixedOffsetTimezone
         self.assertEqual('mydomain', catalog.domain)
 
     def test_applies_specified_encoding_during_read(self):
-        buf = BytesIO(u('''
+        buf = BytesIO('''
 msgid ""
 msgstr ""
 "Project-Id-Version:  3.15\\n"
 "Generated-By: Babel 1.0dev-r313\\n"
 
 msgid "foo"
-msgstr "bär"''').encode('iso-8859-1'))
+msgstr "bär"'''.encode('iso-8859-1'))
         catalog = pofile.read_po(buf, locale='de_DE')
-        self.assertEqual(u('b\xe4r'), catalog.get('foo').string)
+        self.assertEqual('b\xe4r', catalog.get('foo').string)
 
     def test_read_multiline(self):
         buf = StringIO(r'''msgid ""
 ''')
         catalog = pofile.read_po(buf)
         self.assertEqual(1, len(list(catalog)))
-        self.assertEqual(u('3.15'), catalog.version)
-        self.assertEqual(u('Fliegender Zirkus <fliegender@zirkus.de>'),
+        self.assertEqual('3.15', catalog.version)
+        self.assertEqual('Fliegender Zirkus <fliegender@zirkus.de>',
                          catalog.msgid_bugs_address)
         self.assertEqual(datetime(2007, 9, 27, 11, 19,
                                   tzinfo=FixedOffsetTimezone(7 * 60)),
                          catalog.creation_date)
-        self.assertEqual(u('John <cleese@bavaria.de>'), catalog.last_translator)
-        self.assertEqual(u('German Lang <de@babel.org>'), catalog.language_team)
-        self.assertEqual(u('iso-8859-2'), catalog.charset)
+        self.assertEqual('John <cleese@bavaria.de>', catalog.last_translator)
+        self.assertEqual('German Lang <de@babel.org>', catalog.language_team)
+        self.assertEqual('iso-8859-2', catalog.charset)
         self.assertEqual(True, list(catalog)[0].fuzzy)
 
     def test_obsolete_message(self):
         catalog = pofile.read_po(buf)
         self.assertEqual(1, len(catalog))
         self.assertEqual(1, len(catalog.obsolete))
-        message = catalog.obsolete[u('foo')]
-        self.assertEqual(u('foo'), message.id)
-        self.assertEqual(u('Voh'), message.string)
+        message = catalog.obsolete['foo']
+        self.assertEqual('foo', message.id)
+        self.assertEqual('Voh', message.string)
         self.assertEqual(['This is an obsolete message'], message.user_comments)
 
     def test_obsolete_message_ignored(self):