使用Python 2.X的语言环境模块来格式化数字和货币

时间:2010-11-02 22:25:02

标签: python internationalization locale

任务是以区域设置感知的方式将数字,货币金额和日期格式化为unicode字符串。

首次尝试数字的天真尝试带来了希望:

Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.setlocale(locale.LC_ALL, '')
'English_Australia.1252'
>>> locale.format("%d", 12345678, grouping=True)
'12,345,678'
>>> locale.format(u"%d", 12345678, grouping=True)
u'12,345,678'
>>>

现在尝试法语:

>>> locale.setlocale(locale.LC_ALL, 'French_France')
'French_France.1252'
>>> locale.format("%d", 12345678, grouping=True)
'12\xa0345\xa0678'
>>> locale.format(u"%d", 12345678, grouping=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\python27\lib\locale.py", line 190, in format
    return _format(percent, value, grouping, monetary, *additional)
  File "C:\python27\lib\locale.py", line 211, in _format
    formatted, seps = _group(formatted, monetary=monetary)
  File "C:\python27\lib\locale.py", line 160, in _group
    left_spaces + thousands_sep.join(groups) + right_spaces,
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)

这里发生了什么?

>>> locale.localeconv() # output edited for brevity
{'thousands_sep': '\xa0', 'mon_thousands_sep': '\xa0', 'currency_symbol': '\x80'}

华!看起来有点遗留。解决方法表明了自己:

>>> locale.format("%d", 12345678, grouping=True).decode(locale.getpreferredencoding())
u'12\xa0345\xa0678'
>>>

更新1 locale.getpreferredencoding() 要走的路;改为使用locale.getlocale()[1]

Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.getpreferredencoding(), locale.getlocale()
('cp1252', (None, None))
>>> locale.setlocale(locale.LC_ALL, '')
'English_Australia.1252'
>>> locale.getpreferredencoding(), locale.getlocale()
('cp1252', ('English_Australia', '1252'))
>>> locale.setlocale(locale.LC_ALL, 'russian_russia')
'Russian_Russia.1251'
>>> locale.getpreferredencoding(), locale.getlocale()
('cp1252', ('Russian_Russia', '1251')) #### Whoops! ####
>>>

更新2 strftime()系列与str.format()

有非常相似的问题
>>> locale.setlocale(locale.LC_ALL, 'french_france')
'French_France.1252'
>>> format(12345678, 'n')
'12\xa0345\xa0678'
>>> format(12345678, u'n') # type triggers cast to unicode somehow
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2: ordinal not in range(128)
>>> import datetime;datetime.date(1999,12,31).strftime(u'%B') # type is ignored
'd\xe9cembre'
>>>

在所有情况下,解决方法是在调用这些方法时仅使用str个对象,获取str结果,并使用locale.getlocale()[1]获取的编码对其进行解码

其他问题:

(1)在测试/探索Windows语言环境名称不仅与POSIX(“fr_FR”)不同而且冗长且未完全记录时,这是一个相当大的麻烦。例如,显然印度的分组不是“每3个数字”......我找不到用于探索这个的区域设置;像“Hindi”和“Hindi_India”这样的尝试不起作用。

(2)某些localeconv()数据完全错误。例如。对于韩语,货币符号为'\\',即单个反斜杠。我知道有些7位传统字符集不是ASCII兼容的,chr(92)有时用于本地货币符号,所以我期望'\\'。decode('949')产生一个赢符号,而不仅仅是u'\\'

我知道像babel这样的模块,但我并不特别想要强加一个像这样的大外部依赖。我可以同时获得正确性和便利性吗?我错过了locale模块的某些内容吗?

1 个答案:

答案 0 :(得分:3)

您似乎错过了关于语言环境模块的事情是它暴露了您的操作系统供应商(实际上是:C库供应商)的语言环境概念。因此,在Windows上,您必须使用Windows区域设置名称,使用操作系统供应商的文档来查找支持的名称。谷歌搜索“Windows区域设置名称”很快就会显示this list

locale.format实际上不支持Unicode是一个2.x限制;试试Python 3.1。

编辑:对于Won符号,我认为故事是这样的:Microsoft已将Won符号分配到与MS-DOS中的反斜杠相同的代码位置(同样用于日元符号)日文版)。因此,文件分隔符是Won符号,并以此方式呈现。当他们转移到Windows,然后转移到Unicode时,他们必须继续支持这一点,但他们还必须保留文件分隔符是反斜杠的属性(特别是在Unicode API中)。他们解决了这个冲突,以便

  1. 字符\ x5c确实是反斜杠,而不是原始符号
  2. 在终端应用程序中,反斜杠呈现作为赢得标志;这只是一个字体问题。
  3. 货币符号报告为\ x5c:因此货币符号 是反斜杠。