Mercurial > babel > old > babel-test
annotate babel/core.py @ 22:7d37639a7411
Implemented babel.numbers.format_percent
author | jonas |
---|---|
date | Thu, 31 May 2007 19:52:57 +0000 |
parents | 76985c08a339 |
children | 710090104678 |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
3 # Copyright (C) 2007 Edgewall Software |
1 | 4 # All rights reserved. |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
8 # are also available at http://babel.edgewall.org/wiki/License. | |
9 # | |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
12 # history and logs, available at http://babel.edgewall.org/log/. | |
13 | |
14 """Core locale representation and locale data access gateway.""" | |
15 | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
16 import os |
1 | 17 import pickle |
18 try: | |
19 import threading | |
20 except ImportError: | |
21 import dummy_threading as threading | |
22 | |
23 __all__ = ['Locale', 'negotiate', 'parse'] | |
24 __docformat__ = 'restructuredtext en' | |
25 | |
26 | |
27 class Locale(object): | |
28 """Representation of a specific locale. | |
29 | |
30 >>> locale = Locale('en', territory='US') | |
31 >>> repr(locale) | |
32 '<Locale "en_US">' | |
33 >>> locale.display_name | |
34 u'English (United States)' | |
35 | |
36 A `Locale` object can also be instantiated from a raw locale string: | |
37 | |
38 >>> locale = Locale.parse('en-US', sep='-') | |
39 >>> repr(locale) | |
40 '<Locale "en_US">' | |
41 | |
42 `Locale` objects provide access to a collection of locale data, such as | |
43 territory and language names, number and date format patterns, and more: | |
44 | |
45 >>> locale.number_symbols['decimal'] | |
46 u'.' | |
47 | |
48 :see: `IETF RFC 3066 <http://www.ietf.org/rfc/rfc3066.txt>`_ | |
49 """ | |
50 _cache = {} | |
51 _cache_lock = threading.Lock() | |
52 | |
53 def __new__(cls, language, territory=None, variant=None): | |
54 """Create new locale object, or load it from the cache if it had already | |
55 been instantiated. | |
56 | |
57 >>> l1 = Locale('en') | |
58 >>> l2 = Locale('en') | |
59 >>> l1 is l2 | |
60 True | |
61 | |
62 :param language: the language code | |
63 :param territory: the territory (country or region) code | |
64 :param variant: the variant code | |
65 :return: new or existing `Locale` instance | |
66 :rtype: `Locale` | |
67 """ | |
68 key = (language, territory, variant) | |
69 cls._cache_lock.acquire() | |
70 try: | |
71 self = cls._cache.get(key) | |
72 if self is None: | |
73 self = super(Locale, cls).__new__(cls, language, territory, | |
74 variant) | |
75 cls._cache[key] = self | |
76 return self | |
77 finally: | |
78 self._cache_lock.release() | |
79 | |
80 def __init__(self, language, territory=None, variant=None): | |
81 """Initialize the locale object from the given identifier components. | |
82 | |
83 >>> locale = Locale('en', 'US') | |
84 >>> locale.language | |
85 'en' | |
86 >>> locale.territory | |
87 'US' | |
88 | |
89 :param language: the language code | |
90 :param territory: the territory (country or region) code | |
91 :param variant: the variant code | |
92 """ | |
93 self.language = language | |
94 self.territory = territory | |
95 self.variant = variant | |
96 self.__data = None | |
97 | |
98 def parse(cls, identifier, sep='_'): | |
99 """Create a `Locale` instance for the given locale identifier. | |
100 | |
101 >>> l = Locale.parse('de-DE', sep='-') | |
102 >>> l.display_name | |
103 u'Deutsch (Deutschland)' | |
104 | |
105 If the `identifier` parameter is not a string, but actually a `Locale` | |
106 object, that object is returned: | |
107 | |
108 >>> Locale.parse(l) | |
109 <Locale "de_DE"> | |
110 | |
111 :param identifier: the locale identifier string | |
112 :param sep: optional component separator | |
113 :return: a corresponding `Locale` instance | |
114 :rtype: `Locale` | |
115 :raise `ValueError`: if the string does not appear to be a valid locale | |
116 identifier | |
117 """ | |
118 if type(identifier) is cls: | |
119 return identifier | |
120 return cls(*parse(identifier, sep=sep)) | |
121 parse = classmethod(parse) | |
122 | |
123 def __repr__(self): | |
124 return '<Locale "%s">' % str(self) | |
125 | |
126 def __str__(self): | |
127 return '_'.join(filter(None, [self.language, self.territory, | |
128 self.variant])) | |
129 | |
130 def _data(self): | |
131 if self.__data is None: | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
132 filename = os.path.join(os.path.dirname(__file__), |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
133 'localedata/%s.dat' % self) |
1 | 134 fileobj = open(filename, 'rb') |
135 try: | |
136 self.__data = pickle.load(fileobj) | |
137 finally: | |
138 fileobj.close() | |
139 return self.__data | |
140 _data = property(_data) | |
141 | |
142 def display_name(self): | |
143 retval = self.languages.get(self.language) | |
144 if self.territory: | |
145 variant = '' | |
146 if self.variant: | |
147 variant = ', %s' % self.variants.get(self.variant) | |
148 retval += ' (%s%s)' % (self.territories.get(self.territory), variant) | |
149 return retval | |
150 display_name = property(display_name, doc="""\ | |
151 The localized display name of the locale. | |
152 | |
153 >>> Locale('en').display_name | |
154 u'English' | |
155 >>> Locale('en', 'US').display_name | |
156 u'English (United States)' | |
157 | |
158 :type: `unicode` | |
159 """) | |
160 | |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
161 #{ General Locale Display Names |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
162 |
1 | 163 def languages(self): |
164 return self._data['languages'] | |
165 languages = property(languages, doc="""\ | |
166 Mapping of language codes to translated language names. | |
167 | |
168 >>> Locale('de', 'DE').languages['ja'] | |
169 u'Japanisch' | |
170 | |
171 :type: `dict` | |
172 :see: `ISO 639 <http://www.loc.gov/standards/iso639-2/>`_ | |
173 """) | |
174 | |
175 def scripts(self): | |
176 return self._data['scripts'] | |
177 scripts = property(scripts, doc="""\ | |
178 Mapping of script codes to translated script names. | |
179 | |
180 >>> Locale('en', 'US').scripts['Hira'] | |
181 u'Hiragana' | |
182 | |
183 :type: `dict` | |
184 :see: `ISO 15924 <http://www.evertype.com/standards/iso15924/>`_ | |
185 """) | |
186 | |
187 def territories(self): | |
188 return self._data['territories'] | |
189 territories = property(territories, doc="""\ | |
190 Mapping of script codes to translated script names. | |
191 | |
192 >>> Locale('es', 'CO').territories['DE'] | |
193 u'Alemania' | |
194 | |
195 :type: `dict` | |
196 :see: `ISO 3166 <http://www.iso.org/iso/en/prods-services/iso3166ma/>`_ | |
197 """) | |
198 | |
199 def variants(self): | |
200 return self._data['variants'] | |
201 variants = property(variants, doc="""\ | |
202 Mapping of script codes to translated script names. | |
203 | |
204 >>> Locale('de', 'DE').variants['1901'] | |
205 u'alte deutsche Rechtschreibung' | |
206 | |
207 :type: `dict` | |
208 """) | |
209 | |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
210 #{ Number Formatting |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
211 |
1 | 212 def number_symbols(self): |
213 return self._data['number_symbols'] | |
214 number_symbols = property(number_symbols, doc="""\ | |
215 Symbols used in number formatting. | |
216 | |
217 >>> Locale('fr', 'FR').number_symbols['decimal'] | |
218 u',' | |
219 | |
220 :type: `dict` | |
221 """) | |
222 | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
223 def decimal_formats(self): |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
224 return self._data['decimal_formats'] |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
225 decimal_formats = property(decimal_formats, doc="""\ |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
226 Locale patterns for decimal number formatting. |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
227 |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
228 >>> Locale('en', 'US').decimal_formats[None] |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
229 <NumberPattern u'#,##0.###'> |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
230 |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
231 :type: `dict` |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
232 """) |
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
233 |
22 | 234 def percent_formats(self): |
235 return self._data['percent_formats'] | |
236 percent_formats = property(percent_formats, doc="""\ | |
237 Locale patterns for percent number formatting. | |
238 | |
239 >>> Locale('en', 'US').percent_formats[None] | |
240 <NumberPattern u'#,##0%'> | |
241 | |
242 :type: `dict` | |
243 """) | |
244 | |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
245 #{ Calendar Information and Date Formatting |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
246 |
1 | 247 def periods(self): |
248 return self._data['periods'] | |
249 periods = property(periods, doc="""\ | |
250 Locale display names for day periods (AM/PM). | |
251 | |
252 >>> Locale('en', 'US').periods['am'] | |
253 u'AM' | |
254 | |
255 :type: `dict` | |
256 """) | |
257 | |
258 def days(self): | |
259 return self._data['days'] | |
260 days = property(days, doc="""\ | |
261 Locale display names for weekdays. | |
262 | |
15 | 263 >>> Locale('de', 'DE').days['format']['wide'][3] |
1 | 264 u'Donnerstag' |
265 | |
266 :type: `dict` | |
267 """) | |
268 | |
269 def months(self): | |
270 return self._data['months'] | |
271 months = property(months, doc="""\ | |
272 Locale display names for months. | |
273 | |
274 >>> Locale('de', 'DE').months['format']['wide'][10] | |
275 u'Oktober' | |
276 | |
277 :type: `dict` | |
278 """) | |
279 | |
280 def quarters(self): | |
281 return self._data['quarters'] | |
282 quarters = property(quarters, doc="""\ | |
283 Locale display names for quarters. | |
284 | |
285 >>> Locale('de', 'DE').quarters['format']['wide'][1] | |
286 u'1. Quartal' | |
287 | |
288 :type: `dict` | |
289 """) | |
290 | |
291 def eras(self): | |
292 return self._data['eras'] | |
293 eras = property(eras, doc="""\ | |
294 Locale display names for eras. | |
295 | |
296 >>> Locale('en', 'US').eras['wide'][1] | |
297 u'Anno Domini' | |
298 >>> Locale('en', 'US').eras['abbreviated'][0] | |
299 u'BC' | |
300 | |
301 :type: `dict` | |
302 """) | |
303 | |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
304 def first_week_day(self): |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
305 return self._data['week_data']['first_day'] |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
306 first_week_day = property(first_week_day, doc="""\ |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
307 The first day of a week. |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
308 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
309 >>> Locale('de', 'DE').first_week_day |
15 | 310 0 |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
311 >>> Locale('en', 'US').first_week_day |
15 | 312 6 |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
313 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
314 :type: `int` |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
315 """) |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
316 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
317 def weekend_start(self): |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
318 return self._data['week_data']['weekend_start'] |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
319 weekend_start = property(weekend_start, doc="""\ |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
320 The day the weekend starts. |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
321 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
322 >>> Locale('de', 'DE').weekend_start |
15 | 323 5 |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
324 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
325 :type: `int` |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
326 """) |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
327 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
328 def weekend_end(self): |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
329 return self._data['week_data']['weekend_end'] |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
330 weekend_end = property(weekend_end, doc="""\ |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
331 The day the weekend ends. |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
332 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
333 >>> Locale('de', 'DE').weekend_end |
15 | 334 6 |
8
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
335 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
336 :type: `int` |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
337 """) |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
338 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
339 def min_week_days(self): |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
340 return self._data['week_data']['min_days'] |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
341 min_week_days = property(min_week_days, doc="""\ |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
342 The minimum number of days in a week so that the week is counted as the |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
343 first week of a year or month. |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
344 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
345 >>> Locale('de', 'DE').min_week_days |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
346 4 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
347 |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
348 :type: `int` |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
349 """) |
9132c9218745
Pull in some supplemental data from the CLDR, for things like the first day of the week.
cmlenz
parents:
1
diff
changeset
|
350 |
1 | 351 def date_formats(self): |
352 return self._data['date_formats'] | |
353 date_formats = property(date_formats, doc="""\ | |
354 Locale patterns for date formatting. | |
355 | |
356 >>> Locale('en', 'US').date_formats['short'] | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
357 <DateTimePattern u'M/d/yy'> |
1 | 358 >>> Locale('fr', 'FR').date_formats['long'] |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
359 <DateTimePattern u'd MMMM yyyy'> |
1 | 360 |
361 :type: `dict` | |
362 """) | |
363 | |
364 def time_formats(self): | |
365 return self._data['time_formats'] | |
366 time_formats = property(time_formats, doc="""\ | |
367 Locale patterns for time formatting. | |
368 | |
369 >>> Locale('en', 'US').time_formats['short'] | |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
370 <DateTimePattern u'h:mm a'> |
1 | 371 >>> Locale('fr', 'FR').time_formats['long'] |
12
a2c54ef107c2
* Removed pkg_resources/setuptools requirement from various places.
cmlenz
parents:
9
diff
changeset
|
372 <DateTimePattern u'HH:mm:ss z'> |
9 | 373 |
374 :type: `dict` | |
375 """) | |
376 | |
1 | 377 |
378 def negotiate(preferred, available): | |
379 """Find the best match between available and requested locale strings. | |
380 | |
381 >>> negotiate(['de_DE', 'en_US'], ['de_DE', 'de_AT']) | |
382 'de_DE' | |
383 >>> negotiate(['de_DE', 'en_US'], ['en', 'de']) | |
384 'de' | |
385 | |
386 :param preferred: the list of locale strings preferred by the user | |
387 :param available: the list of locale strings available | |
388 :return: the locale identifier for the best match, or `None` if no match | |
389 was found | |
390 :rtype: `str` | |
391 """ | |
392 for locale in preferred: | |
393 if locale in available: | |
394 return locale | |
395 parts = locale.split('_') | |
396 if len(parts) > 1 and parts[0] in available: | |
397 return parts[0] | |
398 return None | |
399 | |
400 def parse(identifier, sep='_'): | |
401 """Parse a locale identifier into a ``(language, territory, variant)`` | |
402 tuple. | |
403 | |
404 >>> parse('zh_CN') | |
405 ('zh', 'CN', None) | |
406 | |
407 The default component separator is "_", but a different separator can be | |
408 specified using the `sep` parameter: | |
409 | |
410 >>> parse('zh-CN', sep='-') | |
411 ('zh', 'CN', None) | |
412 | |
413 :param identifier: the locale identifier string | |
414 :param sep: character that separates the different parts of the locale | |
415 string | |
416 :return: the ``(language, territory, variant)`` tuple | |
417 :rtype: `tuple` | |
418 :raise `ValueError`: if the string does not appear to be a valid locale | |
419 identifier | |
420 | |
421 :see: `IETF RFC 3066 <http://www.ietf.org/rfc/rfc3066.txt>`_ | |
422 """ | |
423 parts = identifier.split(sep) | |
424 lang, territory, variant = parts[0].lower(), None, None | |
425 if not lang.isalpha(): | |
426 raise ValueError('expected only letters, got %r' % lang) | |
427 if len(parts) > 1: | |
428 territory = parts[1].upper().split('.', 1)[0] | |
429 if not territory.isalpha(): | |
430 raise ValueError('expected only letters, got %r' % territory) | |
431 if len(parts) > 2: | |
432 variant = parts[2].upper().split('.', 1)[0] | |
433 return lang, territory, variant |