Locale нюансы

В данной статье я покажу как работать с локалью — набором параметров, определяющий региональные настройки пользовательского интерфейса, такие как язык, страна, часовой пояс, набор символов и т. п. Напомню, что локаль задается в следующем формате:

<ЯЗЫК>_<ТЕРРИТОРИЯ>.<КОДИРОВКА>[@<МОДИФИКАТОРЫ>]

ЯЗЫК Код языка по стандарту ISO 639
ТЕРРИТОРИЯ Код страны по стандарту ISO 3166
КОДИРОВКА Набор символов или идентификатор кодировки текста, как ISO-8859-1 или UTF-8 (список доступных выводится командой locale -m )

Список всех поддерживаемых (доступных для установки) локалей в текущей системе можно посмотреть командой:

# cat /usr/share/i18n/SUPPORTED

Посмотреть весь список установленных локалей можно командой:

# locale -a
C
C.UTF-8
POSIX
en_US.utf8

Так же, можно просто распечатать содержимое файла:

# cat /etc/locale.gen
en_US.UTF-8 UTF-8

Добавим локаль

Добавим русскую локаль, для этого в файл /etc/locale.gen добавим новую строку/раскомментируем ru_RU.UTF-8 и выполним команду:

# locale-gen

Generating locales (this might take a while)...
en_US.UTF-8... done
ru_RU.UTF-8... done

или просто выполним команду:

# locale-gen ru_RU.UTF-8

Generating locales (this might take a while)...
en_US.UTF-8... done
ru_RU.UTF-8... done

Категории локали

Да, есть в локали и такое понятие. Параметр category - это именованная константа, определяющая категорию функций, на которые будет влиять установка локали:

  • LC_ALL - все нижеперечисленное
  • LC_COLLATE - функции сравнения строк, см. strcoll()
  • LC_CTYPE - функции преобразования и классификации строк, например strtoupper()
  • LC_MONETARY - для функции localeconv()
  • LC_NUMERIC - задает символ десятичного разделения (см. также localeconv())
  • LC_TIME - форматирование даты/времени функцией strftime()
  • LC_MESSAGES - для системных сообщений (доступна, если PHP был скомпилирован с поддержкой libintl

Давайте посмотрим текущие значения категорий локали:

# locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=

Давайте посмотрим текущие значения категорий LC_TELEPHONE:

# locale -c LC_ADDRESS
LC_ADDRESS
%a%N%f%N%d%N%b%N%s %h %e %r%N%C-%z %T%N%c%N

0

ANSI_X3.4-1968

Влияние локали

Теперь проверим, как переключать локаль и на что локаль влияет:

<?php
$float1 = 200.20;
$float2 = 2.5;

echo setlocale(LC_ALL, 0);// C - это значит, что локаль в PHP не указан, поэтому будет использоваться локаль назначенная в операционной системе
echo (string)$float1;// 200.2 - обратите внимание на разделитель в виде точки, это присуще англоязычным странам
var_dump(bcdiv($float1, $float2, 2));// string(5) "80.08" - все отлично поделилось

setlocale(LC_ALL, 'ru_RU.UTF-8');
echo setlocale(LC_ALL, 0);// ru_RU.UTF-8 - как видите, теперь PHP будет использовать новую локаль
echo (string)$float1;// 200,2 - обратите внимание на разделитель в виде запятой, это присуще русскоязычным странам
var_dump(bcdiv($float1, $float2, 2));// NULL - деление не произошло, потому что php привел float-значения к строке, в результате появились числа с запятыми, а числа с запятыми невалидны в PHP

Как же поделить числа используя локаль ru_RU.UTF-8, мой ответ такой:

setlocale(LC_NUMERIC,"en_US.UTF-8");

и надеяться, что никто не сменит category в процессе интерпритации стека вызовов PHP, либо юзать свою функцию bcdiv.

p.s. напомню, что изменить локаль в системе можно с помощью команды localedef

p.s. 2: поймал ошибку вида:

Traceback (most recent call last):
  File "/usr/bin/pip", line 11, in
    sys.exit(main())
  File "/usr/lib/python2.7/dist-packages/pip/__init__.py", line 215, in main
    locale.setlocale(locale.LC_ALL, '')
  File "/usr/lib/python2.7/locale.py", line 581, in setlocale
    return _setlocale(category, locale)
locale.Error: unsupported locale setting

Решается добавлением локали - см. выше параграф "Добавим локаль"


11.07.2018 13:50