Использование языкового модуля Python 2.X для форматирования чисел и валюты - PullRequest
4 голосов
/ 03 ноября 2010

Задача состоит в том, чтобы отформатировать числа, суммы в валюте и даты в виде строк 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 цифры" ... Я не могу найти язык, который можно использовать для изучения этого; попытки типа "хинди" и "хинди-индия" не работают.

(2) Некоторые из данных localeconv () просто неверны. Например. для корейского символ валюты обозначен как '\\', то есть один обратный слеш. Мне известно, что некоторые 7-битные устаревшие кодировки не совместимы с ASCII, и что chr (92) иногда использовался для символа местной валюты, поэтому я ожидал, что '\\' .decode ('949') создаст символ выигрыша, не просто u'\\'

Мне известны такие модули, как babel, но я не особо хочу навязывать такую ​​большую внешнюю зависимость. Могу ли я получить правильность и удобство одновременно? Есть ли что-то в модуле locale, который я пропустил?

1 Ответ

3 голосов
/ 03 ноября 2010

В модуле языковых настроек, который вы, похоже, упустили, заключается в том, что он предоставляет представление о региональных стандартах вашего поставщика операционной системы (на самом деле: поставщика библиотеки C). Поэтому в Windows вам придется использовать имена локалей Windows, использовать документацию поставщика вашей ОС, чтобы узнать, какие имена поддерживаются. Поиск в Google для «названия локали Windows» быстро вызывает этот список .

То, что locale.format действительно не поддерживает Unicode, является ограничением 2.x; попробуйте Python 3.1.

Редактировать : что касается знака Won, я думаю, что история такова: Microsoft присвоила знак Won той же позиции кода, что и обратный слеш в MS-DOS (аналогично для знака Yen на японском языке). версии). Как следствие, символ разделителя файлов был знаком Won и отображался как таковой. Когда они перешли на Windows, а затем и на Unicode, они должны были продолжать поддерживать это, но им также нужно было сохранить свойство обратного слеша файлового разделителя (в частности, в Unicode API). Они решили этот конфликт так, чтобы

  1. Символ \ x5c действительно является обратной косой чертой, а не знаком Won
  2. В терминальном приложении обратная косая черта отображается как знак выигрыша; это только проблема шрифта.
  3. символ валюты обозначается как \ x5c: таким образом, символ валюты действительно равен обратной косой черте.
...