core.py 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
  1. # -*- coding: utf-8 -*-
  2. """
  3. babel.core
  4. ~~~~~~~~~~
  5. Core locale representation and locale data access.
  6. :copyright: (c) 2013 by the Babel Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import os
  10. from babel import localedata
  11. from babel._compat import pickle, string_types
  12. from babel.plural import PluralRule
  13. __all__ = ['UnknownLocaleError', 'Locale', 'default_locale', 'negotiate_locale',
  14. 'parse_locale']
  15. _global_data = None
  16. _default_plural_rule = PluralRule({})
  17. def _raise_no_data_error():
  18. raise RuntimeError('The babel data files are not available. '
  19. 'This usually happens because you are using '
  20. 'a source checkout from Babel and you did '
  21. 'not build the data files. Just make sure '
  22. 'to run "python setup.py import_cldr" before '
  23. 'installing the library.')
  24. def get_global(key):
  25. """Return the dictionary for the given key in the global data.
  26. The global data is stored in the ``babel/global.dat`` file and contains
  27. information independent of individual locales.
  28. >>> get_global('zone_aliases')['UTC']
  29. u'Etc/GMT'
  30. >>> get_global('zone_territories')['Europe/Berlin']
  31. u'DE'
  32. The keys available are:
  33. - ``currency_fractions``
  34. - ``language_aliases``
  35. - ``likely_subtags``
  36. - ``parent_exceptions``
  37. - ``script_aliases``
  38. - ``territory_aliases``
  39. - ``territory_currencies``
  40. - ``territory_languages``
  41. - ``territory_zones``
  42. - ``variant_aliases``
  43. - ``windows_zone_mapping``
  44. - ``zone_aliases``
  45. - ``zone_territories``
  46. .. note:: The internal structure of the data may change between versions.
  47. .. versionadded:: 0.9
  48. :param key: the data key
  49. """
  50. global _global_data
  51. if _global_data is None:
  52. dirname = os.path.join(os.path.dirname(__file__))
  53. filename = os.path.join(dirname, 'global.dat')
  54. if not os.path.isfile(filename):
  55. _raise_no_data_error()
  56. with open(filename, 'rb') as fileobj:
  57. _global_data = pickle.load(fileobj)
  58. return _global_data.get(key, {})
  59. LOCALE_ALIASES = {
  60. 'ar': 'ar_SY', 'bg': 'bg_BG', 'bs': 'bs_BA', 'ca': 'ca_ES', 'cs': 'cs_CZ',
  61. 'da': 'da_DK', 'de': 'de_DE', 'el': 'el_GR', 'en': 'en_US', 'es': 'es_ES',
  62. 'et': 'et_EE', 'fa': 'fa_IR', 'fi': 'fi_FI', 'fr': 'fr_FR', 'gl': 'gl_ES',
  63. 'he': 'he_IL', 'hu': 'hu_HU', 'id': 'id_ID', 'is': 'is_IS', 'it': 'it_IT',
  64. 'ja': 'ja_JP', 'km': 'km_KH', 'ko': 'ko_KR', 'lt': 'lt_LT', 'lv': 'lv_LV',
  65. 'mk': 'mk_MK', 'nl': 'nl_NL', 'nn': 'nn_NO', 'no': 'nb_NO', 'pl': 'pl_PL',
  66. 'pt': 'pt_PT', 'ro': 'ro_RO', 'ru': 'ru_RU', 'sk': 'sk_SK', 'sl': 'sl_SI',
  67. 'sv': 'sv_SE', 'th': 'th_TH', 'tr': 'tr_TR', 'uk': 'uk_UA'
  68. }
  69. class UnknownLocaleError(Exception):
  70. """Exception thrown when a locale is requested for which no locale data
  71. is available.
  72. """
  73. def __init__(self, identifier):
  74. """Create the exception.
  75. :param identifier: the identifier string of the unsupported locale
  76. """
  77. Exception.__init__(self, 'unknown locale %r' % identifier)
  78. #: The identifier of the locale that could not be found.
  79. self.identifier = identifier
  80. class Locale(object):
  81. """Representation of a specific locale.
  82. >>> locale = Locale('en', 'US')
  83. >>> repr(locale)
  84. "Locale('en', territory='US')"
  85. >>> locale.display_name
  86. u'English (United States)'
  87. A `Locale` object can also be instantiated from a raw locale string:
  88. >>> locale = Locale.parse('en-US', sep='-')
  89. >>> repr(locale)
  90. "Locale('en', territory='US')"
  91. `Locale` objects provide access to a collection of locale data, such as
  92. territory and language names, number and date format patterns, and more:
  93. >>> locale.number_symbols['decimal']
  94. u'.'
  95. If a locale is requested for which no locale data is available, an
  96. `UnknownLocaleError` is raised:
  97. >>> Locale.parse('en_XX')
  98. Traceback (most recent call last):
  99. ...
  100. UnknownLocaleError: unknown locale 'en_XX'
  101. For more information see :rfc:`3066`.
  102. """
  103. def __init__(self, language, territory=None, script=None, variant=None):
  104. """Initialize the locale object from the given identifier components.
  105. >>> locale = Locale('en', 'US')
  106. >>> locale.language
  107. 'en'
  108. >>> locale.territory
  109. 'US'
  110. :param language: the language code
  111. :param territory: the territory (country or region) code
  112. :param script: the script code
  113. :param variant: the variant code
  114. :raise `UnknownLocaleError`: if no locale data is available for the
  115. requested locale
  116. """
  117. #: the language code
  118. self.language = language
  119. #: the territory (country or region) code
  120. self.territory = territory
  121. #: the script code
  122. self.script = script
  123. #: the variant code
  124. self.variant = variant
  125. self.__data = None
  126. identifier = str(self)
  127. if not localedata.exists(identifier):
  128. raise UnknownLocaleError(identifier)
  129. @classmethod
  130. def default(cls, category=None, aliases=LOCALE_ALIASES):
  131. """Return the system default locale for the specified category.
  132. >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES']:
  133. ... os.environ[name] = ''
  134. >>> os.environ['LANG'] = 'fr_FR.UTF-8'
  135. >>> Locale.default('LC_MESSAGES')
  136. Locale('fr', territory='FR')
  137. The following fallbacks to the variable are always considered:
  138. - ``LANGUAGE``
  139. - ``LC_ALL``
  140. - ``LC_CTYPE``
  141. - ``LANG``
  142. :param category: one of the ``LC_XXX`` environment variable names
  143. :param aliases: a dictionary of aliases for locale identifiers
  144. """
  145. # XXX: use likely subtag expansion here instead of the
  146. # aliases dictionary.
  147. locale_string = default_locale(category, aliases=aliases)
  148. return cls.parse(locale_string)
  149. @classmethod
  150. def negotiate(cls, preferred, available, sep='_', aliases=LOCALE_ALIASES):
  151. """Find the best match between available and requested locale strings.
  152. >>> Locale.negotiate(['de_DE', 'en_US'], ['de_DE', 'de_AT'])
  153. Locale('de', territory='DE')
  154. >>> Locale.negotiate(['de_DE', 'en_US'], ['en', 'de'])
  155. Locale('de')
  156. >>> Locale.negotiate(['de_DE', 'de'], ['en_US'])
  157. You can specify the character used in the locale identifiers to separate
  158. the differnet components. This separator is applied to both lists. Also,
  159. case is ignored in the comparison:
  160. >>> Locale.negotiate(['de-DE', 'de'], ['en-us', 'de-de'], sep='-')
  161. Locale('de', territory='DE')
  162. :param preferred: the list of locale identifers preferred by the user
  163. :param available: the list of locale identifiers available
  164. :param aliases: a dictionary of aliases for locale identifiers
  165. """
  166. identifier = negotiate_locale(preferred, available, sep=sep,
  167. aliases=aliases)
  168. if identifier:
  169. return Locale.parse(identifier, sep=sep)
  170. @classmethod
  171. def parse(cls, identifier, sep='_', resolve_likely_subtags=True):
  172. """Create a `Locale` instance for the given locale identifier.
  173. >>> l = Locale.parse('de-DE', sep='-')
  174. >>> l.display_name
  175. u'Deutsch (Deutschland)'
  176. If the `identifier` parameter is not a string, but actually a `Locale`
  177. object, that object is returned:
  178. >>> Locale.parse(l)
  179. Locale('de', territory='DE')
  180. This also can perform resolving of likely subtags which it does
  181. by default. This is for instance useful to figure out the most
  182. likely locale for a territory you can use ``'und'`` as the
  183. language tag:
  184. >>> Locale.parse('und_AT')
  185. Locale('de', territory='AT')
  186. :param identifier: the locale identifier string
  187. :param sep: optional component separator
  188. :param resolve_likely_subtags: if this is specified then a locale will
  189. have its likely subtag resolved if the
  190. locale otherwise does not exist. For
  191. instance ``zh_TW`` by itself is not a
  192. locale that exists but Babel can
  193. automatically expand it to the full
  194. form of ``zh_hant_TW``. Note that this
  195. expansion is only taking place if no
  196. locale exists otherwise. For instance
  197. there is a locale ``en`` that can exist
  198. by itself.
  199. :raise `ValueError`: if the string does not appear to be a valid locale
  200. identifier
  201. :raise `UnknownLocaleError`: if no locale data is available for the
  202. requested locale
  203. """
  204. if identifier is None:
  205. return None
  206. elif isinstance(identifier, Locale):
  207. return identifier
  208. elif not isinstance(identifier, string_types):
  209. raise TypeError('Unexpected value for identifier: %r' % (identifier,))
  210. parts = parse_locale(identifier, sep=sep)
  211. input_id = get_locale_identifier(parts)
  212. def _try_load(parts):
  213. try:
  214. return cls(*parts)
  215. except UnknownLocaleError:
  216. return None
  217. def _try_load_reducing(parts):
  218. # Success on first hit, return it.
  219. locale = _try_load(parts)
  220. if locale is not None:
  221. return locale
  222. # Now try without script and variant
  223. locale = _try_load(parts[:2])
  224. if locale is not None:
  225. return locale
  226. locale = _try_load(parts)
  227. if locale is not None:
  228. return locale
  229. if not resolve_likely_subtags:
  230. raise UnknownLocaleError(input_id)
  231. # From here onwards is some very bad likely subtag resolving. This
  232. # whole logic is not entirely correct but good enough (tm) for the
  233. # time being. This has been added so that zh_TW does not cause
  234. # errors for people when they upgrade. Later we should properly
  235. # implement ICU like fuzzy locale objects and provide a way to
  236. # maximize and minimize locale tags.
  237. language, territory, script, variant = parts
  238. language = get_global('language_aliases').get(language, language)
  239. territory = get_global('territory_aliases').get(territory, (territory,))[0]
  240. script = get_global('script_aliases').get(script, script)
  241. variant = get_global('variant_aliases').get(variant, variant)
  242. if territory == 'ZZ':
  243. territory = None
  244. if script == 'Zzzz':
  245. script = None
  246. parts = language, territory, script, variant
  247. # First match: try the whole identifier
  248. new_id = get_locale_identifier(parts)
  249. likely_subtag = get_global('likely_subtags').get(new_id)
  250. if likely_subtag is not None:
  251. locale = _try_load_reducing(parse_locale(likely_subtag))
  252. if locale is not None:
  253. return locale
  254. # If we did not find anything so far, try again with a
  255. # simplified identifier that is just the language
  256. likely_subtag = get_global('likely_subtags').get(language)
  257. if likely_subtag is not None:
  258. language2, _, script2, variant2 = parse_locale(likely_subtag)
  259. locale = _try_load_reducing((language2, territory, script2, variant2))
  260. if locale is not None:
  261. return locale
  262. raise UnknownLocaleError(input_id)
  263. def __eq__(self, other):
  264. for key in ('language', 'territory', 'script', 'variant'):
  265. if not hasattr(other, key):
  266. return False
  267. return (self.language == other.language) and \
  268. (self.territory == other.territory) and \
  269. (self.script == other.script) and \
  270. (self.variant == other.variant)
  271. def __ne__(self, other):
  272. return not self.__eq__(other)
  273. def __hash__(self):
  274. return hash((self.language, self.territory, self.script, self.variant))
  275. def __repr__(self):
  276. parameters = ['']
  277. for key in ('territory', 'script', 'variant'):
  278. value = getattr(self, key)
  279. if value is not None:
  280. parameters.append('%s=%r' % (key, value))
  281. parameter_string = '%r' % self.language + ', '.join(parameters)
  282. return 'Locale(%s)' % parameter_string
  283. def __str__(self):
  284. return get_locale_identifier((self.language, self.territory,
  285. self.script, self.variant))
  286. @property
  287. def _data(self):
  288. if self.__data is None:
  289. self.__data = localedata.LocaleDataDict(localedata.load(str(self)))
  290. return self.__data
  291. def get_display_name(self, locale=None):
  292. """Return the display name of the locale using the given locale.
  293. The display name will include the language, territory, script, and
  294. variant, if those are specified.
  295. >>> Locale('zh', 'CN', script='Hans').get_display_name('en')
  296. u'Chinese (Simplified, China)'
  297. :param locale: the locale to use
  298. """
  299. if locale is None:
  300. locale = self
  301. locale = Locale.parse(locale)
  302. retval = locale.languages.get(self.language)
  303. if self.territory or self.script or self.variant:
  304. details = []
  305. if self.script:
  306. details.append(locale.scripts.get(self.script))
  307. if self.territory:
  308. details.append(locale.territories.get(self.territory))
  309. if self.variant:
  310. details.append(locale.variants.get(self.variant))
  311. details = filter(None, details)
  312. if details:
  313. retval += ' (%s)' % u', '.join(details)
  314. return retval
  315. display_name = property(get_display_name, doc="""\
  316. The localized display name of the locale.
  317. >>> Locale('en').display_name
  318. u'English'
  319. >>> Locale('en', 'US').display_name
  320. u'English (United States)'
  321. >>> Locale('sv').display_name
  322. u'svenska'
  323. :type: `unicode`
  324. """)
  325. def get_language_name(self, locale=None):
  326. """Return the language of this locale in the given locale.
  327. >>> Locale('zh', 'CN', script='Hans').get_language_name('de')
  328. u'Chinesisch'
  329. .. versionadded:: 1.0
  330. :param locale: the locale to use
  331. """
  332. if locale is None:
  333. locale = self
  334. locale = Locale.parse(locale)
  335. return locale.languages.get(self.language)
  336. language_name = property(get_language_name, doc="""\
  337. The localized language name of the locale.
  338. >>> Locale('en', 'US').language_name
  339. u'English'
  340. """)
  341. def get_territory_name(self, locale=None):
  342. """Return the territory name in the given locale."""
  343. if locale is None:
  344. locale = self
  345. locale = Locale.parse(locale)
  346. return locale.territories.get(self.territory)
  347. territory_name = property(get_territory_name, doc="""\
  348. The localized territory name of the locale if available.
  349. >>> Locale('de', 'DE').territory_name
  350. u'Deutschland'
  351. """)
  352. def get_script_name(self, locale=None):
  353. """Return the script name in the given locale."""
  354. if locale is None:
  355. locale = self
  356. locale = Locale.parse(locale)
  357. return locale.scripts.get(self.script)
  358. script_name = property(get_script_name, doc="""\
  359. The localized script name of the locale if available.
  360. >>> Locale('sr', 'ME', script='Latn').script_name
  361. u'latinica'
  362. """)
  363. @property
  364. def english_name(self):
  365. """The english display name of the locale.
  366. >>> Locale('de').english_name
  367. u'German'
  368. >>> Locale('de', 'DE').english_name
  369. u'German (Germany)'
  370. :type: `unicode`"""
  371. return self.get_display_name(Locale('en'))
  372. # { General Locale Display Names
  373. @property
  374. def languages(self):
  375. """Mapping of language codes to translated language names.
  376. >>> Locale('de', 'DE').languages['ja']
  377. u'Japanisch'
  378. See `ISO 639 <http://www.loc.gov/standards/iso639-2/>`_ for
  379. more information.
  380. """
  381. return self._data['languages']
  382. @property
  383. def scripts(self):
  384. """Mapping of script codes to translated script names.
  385. >>> Locale('en', 'US').scripts['Hira']
  386. u'Hiragana'
  387. See `ISO 15924 <http://www.evertype.com/standards/iso15924/>`_
  388. for more information.
  389. """
  390. return self._data['scripts']
  391. @property
  392. def territories(self):
  393. """Mapping of script codes to translated script names.
  394. >>> Locale('es', 'CO').territories['DE']
  395. u'Alemania'
  396. See `ISO 3166 <http://www.iso.org/iso/en/prods-services/iso3166ma/>`_
  397. for more information.
  398. """
  399. return self._data['territories']
  400. @property
  401. def variants(self):
  402. """Mapping of script codes to translated script names.
  403. >>> Locale('de', 'DE').variants['1901']
  404. u'Alte deutsche Rechtschreibung'
  405. """
  406. return self._data['variants']
  407. # { Number Formatting
  408. @property
  409. def currencies(self):
  410. """Mapping of currency codes to translated currency names. This
  411. only returns the generic form of the currency name, not the count
  412. specific one. If an actual number is requested use the
  413. :func:`babel.numbers.get_currency_name` function.
  414. >>> Locale('en').currencies['COP']
  415. u'Colombian Peso'
  416. >>> Locale('de', 'DE').currencies['COP']
  417. u'Kolumbianischer Peso'
  418. """
  419. return self._data['currency_names']
  420. @property
  421. def currency_symbols(self):
  422. """Mapping of currency codes to symbols.
  423. >>> Locale('en', 'US').currency_symbols['USD']
  424. u'$'
  425. >>> Locale('es', 'CO').currency_symbols['USD']
  426. u'US$'
  427. """
  428. return self._data['currency_symbols']
  429. @property
  430. def number_symbols(self):
  431. """Symbols used in number formatting.
  432. .. note:: The format of the value returned may change between
  433. Babel versions.
  434. >>> Locale('fr', 'FR').number_symbols['decimal']
  435. u','
  436. """
  437. return self._data['number_symbols']
  438. @property
  439. def decimal_formats(self):
  440. """Locale patterns for decimal number formatting.
  441. .. note:: The format of the value returned may change between
  442. Babel versions.
  443. >>> Locale('en', 'US').decimal_formats[None]
  444. <NumberPattern u'#,##0.###'>
  445. """
  446. return self._data['decimal_formats']
  447. @property
  448. def currency_formats(self):
  449. """Locale patterns for currency number formatting.
  450. .. note:: The format of the value returned may change between
  451. Babel versions.
  452. >>> Locale('en', 'US').currency_formats['standard']
  453. <NumberPattern u'\\xa4#,##0.00'>
  454. >>> Locale('en', 'US').currency_formats['accounting']
  455. <NumberPattern u'\\xa4#,##0.00;(\\xa4#,##0.00)'>
  456. """
  457. return self._data['currency_formats']
  458. @property
  459. def percent_formats(self):
  460. """Locale patterns for percent number formatting.
  461. .. note:: The format of the value returned may change between
  462. Babel versions.
  463. >>> Locale('en', 'US').percent_formats[None]
  464. <NumberPattern u'#,##0%'>
  465. """
  466. return self._data['percent_formats']
  467. @property
  468. def scientific_formats(self):
  469. """Locale patterns for scientific number formatting.
  470. .. note:: The format of the value returned may change between
  471. Babel versions.
  472. >>> Locale('en', 'US').scientific_formats[None]
  473. <NumberPattern u'#E0'>
  474. """
  475. return self._data['scientific_formats']
  476. # { Calendar Information and Date Formatting
  477. @property
  478. def periods(self):
  479. """Locale display names for day periods (AM/PM).
  480. >>> Locale('en', 'US').periods['am']
  481. u'AM'
  482. """
  483. try:
  484. return self._data['day_periods']['stand-alone']['wide']
  485. except KeyError:
  486. return {}
  487. @property
  488. def day_periods(self):
  489. """Locale display names for various day periods (not necessarily only AM/PM).
  490. These are not meant to be used without the relevant `day_period_rules`.
  491. """
  492. return self._data['day_periods']
  493. @property
  494. def day_period_rules(self):
  495. """Day period rules for the locale. Used by `get_period_id`.
  496. """
  497. return self._data.get('day_period_rules', {})
  498. @property
  499. def days(self):
  500. """Locale display names for weekdays.
  501. >>> Locale('de', 'DE').days['format']['wide'][3]
  502. u'Donnerstag'
  503. """
  504. return self._data['days']
  505. @property
  506. def months(self):
  507. """Locale display names for months.
  508. >>> Locale('de', 'DE').months['format']['wide'][10]
  509. u'Oktober'
  510. """
  511. return self._data['months']
  512. @property
  513. def quarters(self):
  514. """Locale display names for quarters.
  515. >>> Locale('de', 'DE').quarters['format']['wide'][1]
  516. u'1. Quartal'
  517. """
  518. return self._data['quarters']
  519. @property
  520. def eras(self):
  521. """Locale display names for eras.
  522. .. note:: The format of the value returned may change between
  523. Babel versions.
  524. >>> Locale('en', 'US').eras['wide'][1]
  525. u'Anno Domini'
  526. >>> Locale('en', 'US').eras['abbreviated'][0]
  527. u'BC'
  528. """
  529. return self._data['eras']
  530. @property
  531. def time_zones(self):
  532. """Locale display names for time zones.
  533. .. note:: The format of the value returned may change between
  534. Babel versions.
  535. >>> Locale('en', 'US').time_zones['Europe/London']['long']['daylight']
  536. u'British Summer Time'
  537. >>> Locale('en', 'US').time_zones['America/St_Johns']['city']
  538. u'St. John\u2019s'
  539. """
  540. return self._data['time_zones']
  541. @property
  542. def meta_zones(self):
  543. """Locale display names for meta time zones.
  544. Meta time zones are basically groups of different Olson time zones that
  545. have the same GMT offset and daylight savings time.
  546. .. note:: The format of the value returned may change between
  547. Babel versions.
  548. >>> Locale('en', 'US').meta_zones['Europe_Central']['long']['daylight']
  549. u'Central European Summer Time'
  550. .. versionadded:: 0.9
  551. """
  552. return self._data['meta_zones']
  553. @property
  554. def zone_formats(self):
  555. """Patterns related to the formatting of time zones.
  556. .. note:: The format of the value returned may change between
  557. Babel versions.
  558. >>> Locale('en', 'US').zone_formats['fallback']
  559. u'%(1)s (%(0)s)'
  560. >>> Locale('pt', 'BR').zone_formats['region']
  561. u'Hor\\xe1rio %s'
  562. .. versionadded:: 0.9
  563. """
  564. return self._data['zone_formats']
  565. @property
  566. def first_week_day(self):
  567. """The first day of a week, with 0 being Monday.
  568. >>> Locale('de', 'DE').first_week_day
  569. 0
  570. >>> Locale('en', 'US').first_week_day
  571. 6
  572. """
  573. return self._data['week_data']['first_day']
  574. @property
  575. def weekend_start(self):
  576. """The day the weekend starts, with 0 being Monday.
  577. >>> Locale('de', 'DE').weekend_start
  578. 5
  579. """
  580. return self._data['week_data']['weekend_start']
  581. @property
  582. def weekend_end(self):
  583. """The day the weekend ends, with 0 being Monday.
  584. >>> Locale('de', 'DE').weekend_end
  585. 6
  586. """
  587. return self._data['week_data']['weekend_end']
  588. @property
  589. def min_week_days(self):
  590. """The minimum number of days in a week so that the week is counted as
  591. the first week of a year or month.
  592. >>> Locale('de', 'DE').min_week_days
  593. 4
  594. """
  595. return self._data['week_data']['min_days']
  596. @property
  597. def date_formats(self):
  598. """Locale patterns for date formatting.
  599. .. note:: The format of the value returned may change between
  600. Babel versions.
  601. >>> Locale('en', 'US').date_formats['short']
  602. <DateTimePattern u'M/d/yy'>
  603. >>> Locale('fr', 'FR').date_formats['long']
  604. <DateTimePattern u'd MMMM y'>
  605. """
  606. return self._data['date_formats']
  607. @property
  608. def time_formats(self):
  609. """Locale patterns for time formatting.
  610. .. note:: The format of the value returned may change between
  611. Babel versions.
  612. >>> Locale('en', 'US').time_formats['short']
  613. <DateTimePattern u'h:mm a'>
  614. >>> Locale('fr', 'FR').time_formats['long']
  615. <DateTimePattern u'HH:mm:ss z'>
  616. """
  617. return self._data['time_formats']
  618. @property
  619. def datetime_formats(self):
  620. """Locale patterns for datetime formatting.
  621. .. note:: The format of the value returned may change between
  622. Babel versions.
  623. >>> Locale('en').datetime_formats['full']
  624. u"{1} 'at' {0}"
  625. >>> Locale('th').datetime_formats['medium']
  626. u'{1} {0}'
  627. """
  628. return self._data['datetime_formats']
  629. @property
  630. def datetime_skeletons(self):
  631. """Locale patterns for formatting parts of a datetime.
  632. >>> Locale('en').datetime_skeletons['MEd']
  633. <DateTimePattern u'E, M/d'>
  634. >>> Locale('fr').datetime_skeletons['MEd']
  635. <DateTimePattern u'E dd/MM'>
  636. >>> Locale('fr').datetime_skeletons['H']
  637. <DateTimePattern u"HH 'h'">
  638. """
  639. return self._data['datetime_skeletons']
  640. @property
  641. def interval_formats(self):
  642. """Locale patterns for interval formatting.
  643. .. note:: The format of the value returned may change between
  644. Babel versions.
  645. How to format date intervals in Finnish when the day is the
  646. smallest changing component:
  647. >>> Locale('fi_FI').interval_formats['MEd']['d']
  648. [u'E d. \u2013 ', u'E d.M.']
  649. .. seealso::
  650. The primary API to use this data is :py:func:`babel.dates.format_interval`.
  651. :rtype: dict[str, dict[str, list[str]]]
  652. """
  653. return self._data['interval_formats']
  654. @property
  655. def plural_form(self):
  656. """Plural rules for the locale.
  657. >>> Locale('en').plural_form(1)
  658. 'one'
  659. >>> Locale('en').plural_form(0)
  660. 'other'
  661. >>> Locale('fr').plural_form(0)
  662. 'one'
  663. >>> Locale('ru').plural_form(100)
  664. 'many'
  665. """
  666. return self._data.get('plural_form', _default_plural_rule)
  667. @property
  668. def list_patterns(self):
  669. """Patterns for generating lists
  670. .. note:: The format of the value returned may change between
  671. Babel versions.
  672. >>> Locale('en').list_patterns['start']
  673. u'{0}, {1}'
  674. >>> Locale('en').list_patterns['end']
  675. u'{0}, and {1}'
  676. >>> Locale('en_GB').list_patterns['end']
  677. u'{0} and {1}'
  678. """
  679. return self._data['list_patterns']
  680. @property
  681. def ordinal_form(self):
  682. """Plural rules for the locale.
  683. >>> Locale('en').ordinal_form(1)
  684. 'one'
  685. >>> Locale('en').ordinal_form(2)
  686. 'two'
  687. >>> Locale('en').ordinal_form(3)
  688. 'few'
  689. >>> Locale('fr').ordinal_form(2)
  690. 'other'
  691. >>> Locale('ru').ordinal_form(100)
  692. 'other'
  693. """
  694. return self._data.get('ordinal_form', _default_plural_rule)
  695. @property
  696. def measurement_systems(self):
  697. """Localized names for various measurement systems.
  698. >>> Locale('fr', 'FR').measurement_systems['US']
  699. u'am\\xe9ricain'
  700. >>> Locale('en', 'US').measurement_systems['US']
  701. u'US'
  702. """
  703. return self._data['measurement_systems']
  704. @property
  705. def character_order(self):
  706. """The text direction for the language.
  707. >>> Locale('de', 'DE').character_order
  708. 'left-to-right'
  709. >>> Locale('ar', 'SA').character_order
  710. 'right-to-left'
  711. """
  712. return self._data['character_order']
  713. @property
  714. def text_direction(self):
  715. """The text direction for the language in CSS short-hand form.
  716. >>> Locale('de', 'DE').text_direction
  717. 'ltr'
  718. >>> Locale('ar', 'SA').text_direction
  719. 'rtl'
  720. """
  721. return ''.join(word[0] for word in self.character_order.split('-'))
  722. @property
  723. def unit_display_names(self):
  724. """Display names for units of measurement.
  725. .. seealso::
  726. You may want to use :py:func:`babel.units.get_unit_name` instead.
  727. .. note:: The format of the value returned may change between
  728. Babel versions.
  729. """
  730. return self._data['unit_display_names']
  731. def default_locale(category=None, aliases=LOCALE_ALIASES):
  732. """Returns the system default locale for a given category, based on
  733. environment variables.
  734. >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE']:
  735. ... os.environ[name] = ''
  736. >>> os.environ['LANG'] = 'fr_FR.UTF-8'
  737. >>> default_locale('LC_MESSAGES')
  738. 'fr_FR'
  739. The "C" or "POSIX" pseudo-locales are treated as aliases for the
  740. "en_US_POSIX" locale:
  741. >>> os.environ['LC_MESSAGES'] = 'POSIX'
  742. >>> default_locale('LC_MESSAGES')
  743. 'en_US_POSIX'
  744. The following fallbacks to the variable are always considered:
  745. - ``LANGUAGE``
  746. - ``LC_ALL``
  747. - ``LC_CTYPE``
  748. - ``LANG``
  749. :param category: one of the ``LC_XXX`` environment variable names
  750. :param aliases: a dictionary of aliases for locale identifiers
  751. """
  752. varnames = (category, 'LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG')
  753. for name in filter(None, varnames):
  754. locale = os.getenv(name)
  755. if locale:
  756. if name == 'LANGUAGE' and ':' in locale:
  757. # the LANGUAGE variable may contain a colon-separated list of
  758. # language codes; we just pick the language on the list
  759. locale = locale.split(':')[0]
  760. if locale.split('.')[0] in ('C', 'POSIX'):
  761. locale = 'en_US_POSIX'
  762. elif aliases and locale in aliases:
  763. locale = aliases[locale]
  764. try:
  765. return get_locale_identifier(parse_locale(locale))
  766. except ValueError:
  767. pass
  768. def negotiate_locale(preferred, available, sep='_', aliases=LOCALE_ALIASES):
  769. """Find the best match between available and requested locale strings.
  770. >>> negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT'])
  771. 'de_DE'
  772. >>> negotiate_locale(['de_DE', 'en_US'], ['en', 'de'])
  773. 'de'
  774. Case is ignored by the algorithm, the result uses the case of the preferred
  775. locale identifier:
  776. >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at'])
  777. 'de_DE'
  778. >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at'])
  779. 'de_DE'
  780. By default, some web browsers unfortunately do not include the territory
  781. in the locale identifier for many locales, and some don't even allow the
  782. user to easily add the territory. So while you may prefer using qualified
  783. locale identifiers in your web-application, they would not normally match
  784. the language-only locale sent by such browsers. To workaround that, this
  785. function uses a default mapping of commonly used langauge-only locale
  786. identifiers to identifiers including the territory:
  787. >>> negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US'])
  788. 'ja_JP'
  789. Some browsers even use an incorrect or outdated language code, such as "no"
  790. for Norwegian, where the correct locale identifier would actually be "nb_NO"
  791. (Bokmål) or "nn_NO" (Nynorsk). The aliases are intended to take care of
  792. such cases, too:
  793. >>> negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE'])
  794. 'nb_NO'
  795. You can override this default mapping by passing a different `aliases`
  796. dictionary to this function, or you can bypass the behavior althogher by
  797. setting the `aliases` parameter to `None`.
  798. :param preferred: the list of locale strings preferred by the user
  799. :param available: the list of locale strings available
  800. :param sep: character that separates the different parts of the locale
  801. strings
  802. :param aliases: a dictionary of aliases for locale identifiers
  803. """
  804. available = [a.lower() for a in available if a]
  805. for locale in preferred:
  806. ll = locale.lower()
  807. if ll in available:
  808. return locale
  809. if aliases:
  810. alias = aliases.get(ll)
  811. if alias:
  812. alias = alias.replace('_', sep)
  813. if alias.lower() in available:
  814. return alias
  815. parts = locale.split(sep)
  816. if len(parts) > 1 and parts[0].lower() in available:
  817. return parts[0]
  818. return None
  819. def parse_locale(identifier, sep='_'):
  820. """Parse a locale identifier into a tuple of the form ``(language,
  821. territory, script, variant)``.
  822. >>> parse_locale('zh_CN')
  823. ('zh', 'CN', None, None)
  824. >>> parse_locale('zh_Hans_CN')
  825. ('zh', 'CN', 'Hans', None)
  826. The default component separator is "_", but a different separator can be
  827. specified using the `sep` parameter:
  828. >>> parse_locale('zh-CN', sep='-')
  829. ('zh', 'CN', None, None)
  830. If the identifier cannot be parsed into a locale, a `ValueError` exception
  831. is raised:
  832. >>> parse_locale('not_a_LOCALE_String')
  833. Traceback (most recent call last):
  834. ...
  835. ValueError: 'not_a_LOCALE_String' is not a valid locale identifier
  836. Encoding information and locale modifiers are removed from the identifier:
  837. >>> parse_locale('it_IT@euro')
  838. ('it', 'IT', None, None)
  839. >>> parse_locale('en_US.UTF-8')
  840. ('en', 'US', None, None)
  841. >>> parse_locale('de_DE.iso885915@euro')
  842. ('de', 'DE', None, None)
  843. See :rfc:`4646` for more information.
  844. :param identifier: the locale identifier string
  845. :param sep: character that separates the different components of the locale
  846. identifier
  847. :raise `ValueError`: if the string does not appear to be a valid locale
  848. identifier
  849. """
  850. if '.' in identifier:
  851. # this is probably the charset/encoding, which we don't care about
  852. identifier = identifier.split('.', 1)[0]
  853. if '@' in identifier:
  854. # this is a locale modifier such as @euro, which we don't care about
  855. # either
  856. identifier = identifier.split('@', 1)[0]
  857. parts = identifier.split(sep)
  858. lang = parts.pop(0).lower()
  859. if not lang.isalpha():
  860. raise ValueError('expected only letters, got %r' % lang)
  861. script = territory = variant = None
  862. if parts:
  863. if len(parts[0]) == 4 and parts[0].isalpha():
  864. script = parts.pop(0).title()
  865. if parts:
  866. if len(parts[0]) == 2 and parts[0].isalpha():
  867. territory = parts.pop(0).upper()
  868. elif len(parts[0]) == 3 and parts[0].isdigit():
  869. territory = parts.pop(0)
  870. if parts:
  871. if len(parts[0]) == 4 and parts[0][0].isdigit() or \
  872. len(parts[0]) >= 5 and parts[0][0].isalpha():
  873. variant = parts.pop()
  874. if parts:
  875. raise ValueError('%r is not a valid locale identifier' % identifier)
  876. return lang, territory, script, variant
  877. def get_locale_identifier(tup, sep='_'):
  878. """The reverse of :func:`parse_locale`. It creates a locale identifier out
  879. of a ``(language, territory, script, variant)`` tuple. Items can be set to
  880. ``None`` and trailing ``None``\s can also be left out of the tuple.
  881. >>> get_locale_identifier(('de', 'DE', None, '1999'))
  882. 'de_DE_1999'
  883. .. versionadded:: 1.0
  884. :param tup: the tuple as returned by :func:`parse_locale`.
  885. :param sep: the separator for the identifier.
  886. """
  887. tup = tuple(tup[:4])
  888. lang, territory, script, variant = tup + (None,) * (4 - len(tup))
  889. return sep.join(filter(None, (lang, script, territory, variant)))