core.py 36 KB

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