Какие компоненты используют переменные locale?

Я прочитал, что каждый процесс имеет набор связанных с ним переменных языка. Например, это переменные языка, связанные с процессом bash в моей системе:

$ locale
LANG="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_CTYPE="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_ALL=

Я хочу знать, кто на самом деле использует эти переменные locale.

Выполняют ли стандартные функции C (например: fwrite()) и системные вызовы Linux? Выполняется ли поведение некоторых стандартных функций C или некоторых системных вызовов Linux в зависимости от значения некоторой переменной локали?

Или это только определенные программы, которые могут использовать эти переменные locale? Например, я могу написать программу, которая будет отображать сообщения пользователю на другом языке в зависимости от значения переменной локали LANG.

Ответ 1

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

  • Обработка символов
  • Collating
  • Форматирование даты и времени
  • Цифровое редактирование
  • Денежное форматирование
  • обмен сообщениями

Документация POSIX setlocale содержит неполный список зависящих от него зависимых от языка функций:

catopen, exec, fprintf, fscanf, isalnum, isalpha, isblank, iscntrl, isdigit, isgran, islower, isprint, ispunct, isspace, isupper, iswalnum, iswalpha, iswblank, iswcntrl, iswctype, iswdigit, iswgraph, iswlower, iswprint, iswpunct, iswspace, iswupper, iswxdigit, isxdigit, localeconv, mblst, mbstowcs, mbtowc, newlocale, nl_langinfo, perror, psiginfo, setlocale, strcoll, strerror, strfmon, strftime, strsignal, strtod, strxfrm, tolower, toupper, towlower, towupper, uselocale, wcscoll, wcstod, wcstombs, wcsxfrm, wctomb

Например:

printf("%'d\n", 1000000000);
printf("Setting LC_ALL to %s\n", getenv("LANG"));
setlocale(LC_ALL, ""); // Set user-preferred locale.
printf("%'d\n", 1000000000);

Выходы:

1000000000
Setting LC_ALL to en_US.UTF-8
1,000,000,000

Ответ 2

Многие программы устанавливают языковой стандарт и используют его, по крайней мере, для интернационализации. Некоторые конкретные примеры:

LANG="en_GB.UTF-8"

Это локаль для любой категории, которую вы специально не настроили на что-то еще. Это позволяет системе добавлять новые языковые переменные обратно совместимым способом.

LC_COLLATE="en_GB.UTF-8"

Это выбирает, какой порядок сортировки языков используется для строк. Например, Ch считается письмом по-испански и придет после Cz. Одна функция библиотеки C, которая использует его, - это команды strcoll() и POSIX, которые включают ls (при сортировке файлов по имени) и sort.

LC_CTYPE="en_GB.UTF-8"

Это определяет текущую кодировку символов. В C11 вы можете установить это, а затем использовать широкоформатный ввод и вывод, например wprintf(). Библиотека будет прозрачно конвертировать между широкими символами и набором символов, используемым внешним миром. Это все еще не работает на Windows, если только вы не делаете какую-то дополнительную магию, но в другом месте UTF-8 стал стандартом. Все большее число программ, таких как clang (начиная с версии 7), больше не поддерживает ничего, кроме UTF-8.

LC_MESSAGES="en_GB.UTF-8"

Это определяет, на каком языке и символе вы видите локализованные сообщения. В C на Unix/Linux они обычно загружаются из файла .po библиотекой gettext.

LC_MONETARY="en_GB.UTF-8"

Это влияет на то, как strfmon() форматирует денежные величины.

LC_NUMERIC="en_GB.UTF-8"

Это определяет форматирование чисел, которые не соответствуют сумме денег.

LC_TIME="en_GB.UTF-8"

Это влияет на форматирование времени. Попробуйте LC_TIME=fr_FR.UTF-8 date в оболочке, чтобы увидеть пример. (Или используйте locale -a | grep UTF чтобы выбрать подходящую экзотическую локаль.) Также неплохо проверить правильность работы вашего часового пояса и ntpd.

LC_ALL=

Вместо этого используйте LANG. Он устанавливает каждую категорию локали одновременно, но переопределяет значения во всех других языковых переменных. Он существует для обратной совместимости.

Например, я использую LANG=en_US.utf8 в своем ящике Linux, но я переопределяю LC_TIME=en_GB.utf8 чтобы получить 24-часовое время на английском языке. Это было бы невозможно, если LC_ALL были установлены LC_ALL.

LANG также позволяет переносить ваши значения по умолчанию в любую другую локальную информацию, поддерживаемую вашей системой, например LC_ADDRESS, LC_IDENTIFICATION, LC_RESPONSE, LC_MEASUREMENT и LC_TELEPHONE.

Ответ 3

Я прочитал, что каждый процесс имеет набор связанных с ним переменных языка.

Это не совсем так, или, по крайней мере, это слишком упрощено.

Многие стандартные библиотечные функции (и нестандартные функции библиотеки) изменяют свое поведение на основе набора локальных конфигураций, которые поддерживаются в некотором скрытом глобальном объекте в рамках стандартной реализации библиотеки. (В некоторых реализациях библиотек конфигурация локали поддерживается в потоке, а не во всем мире, с использованием статических переменных, связанных с потоком). Это может показаться связанным с процессом, поскольку обычно каждый процесс имеет один экземпляр стандартной среды исполнения библиотеки, но важно понимать, что, несмотря на появление, поддержка локалей является частью библиотеки, а не ядром ОС. (Конечно, ничто в любом стандарте не определяет, где находятся границы ядра, или даже то, что может быть ядром. Вы можете запустить свою программу "голый металл" или у вас может быть ОС, которая считает полезным использовать стандартную библиотеку в системных вызовах Я говорю здесь об общих случаях.)

Базовая конфигурация локали определяется стандартом C в разделе 7.11 (стандарта C11), который определяет два интерфейса:

  • setlocale, который изменяет конфигурацию локали библиотеки и

  • localeconv, который запрашивает часть конфигурации локали, позволяя коду пользователя соответствовать стандартным форматам форматирования языкового стандарта (включая денежное форматирование).

Конфигурация локали делится на несколько более или менее независимых компонентов, называемых "категориями". (Стандартная библиотека C++ вызывает эти "грани", что также является общеупотребительным словом.) Существует пять категорий, определенных стандартом C, и еще один определенный Posix, но категории являются открытыми; отдельные стандартные реализации библиотеки могут добавлять дополнительные категории. Например, стандартная библиотека Cn Gnu, используемая на большинстве Linux-систем, в настоящее время насчитывает в общей сложности 12 категорий. (Для текущего списка см. man 7 locale в вашей системе.)

Стандартными категориями являются:

  • LC_CTYPE: классификация символов и преобразование регистров.
  • LC_COLLATE: порядок сортировки.
  • LC_MONETARY: Денежное форматирование.
  • LC_NUMERIC: числовое, неденежное форматирование.
  • LC_TIME: форматы даты и времени.

и расширение Posix:

  • LC_MESSAGES: Форматы информационных и диагностических сообщений и интерактивных ответов.

Помимо localeconv, который обеспечивает доступ только к определенным конфигурациям из категорий LC_NUMERIC и LC_MONETARY, невозможно запросить какую-либо конкретную конфигурацию.

Кроме того, нет стандартного способа настройки единой конфигурации. Все, что вы можете сделать, это использовать setlocale для настройки всей категории с использованием зависимого от библиотеки и нестандартного имени локали (которое является только символьной строкой). Точнее, стандартизованы два названия локали:

  • В стандарте C определяется имя языкового стандарта C

  • Posix определяет имя языкового стандарта POSIX. Однако Posix указывает, что соответствующий языковой стандарт должен быть идентичен языку C

Детали для локализации-именования (или должны быть) подробно описаны в документации по locale для среды, в которой вы работаете, но обычно программа, ориентированная на локаль, никогда не будет вызывать setlocale со строковой константой, отличной от стандартных имен, или пустой строка. (Я займусь этим через минуту).

Интерфейс setlocale позволяет программе устанавливать отдельную категорию локали или устанавливать все категории локалей в одно и то же имя локали. Он также возвращает строку, которая может использоваться для возврата к ранее настроенной категории локали (или полной конфигурации).

Названия категорий, приведенные в списке категорий выше, являются макросами, определенными в <locale.h>. Дополнительный макрос LC_ALL также определяется этим файлом заголовка: LC_ALL. Один из этих макросов должен использоваться в качестве первого аргумента для setlocale.

Стандарты C и Posix требуют, чтобы начальная настройка локали при запуске программы была локалью C Многие аспекты языка C стандартизированы (и некоторые аспекты языка Posix стандартизированы). Эта стандартизация позволяет программисту предсказать, как будут работать числовые преобразования.

Но часто бывает, что программист захочет взаимодействовать с пользователем программы с тем, что пользователь имеет собственные настройки локали. Очевидно, что нежелательно, чтобы каждая отдельная программа имела свой собственный индивидуальный механизм для определения того, какие предпочтения пользовательской локали, поэтому стандартная библиотека предоставляет механизм для установки языкового стандарта (или отдельных категорий локалей) на то, что было настроено по умолчанию для локали: setlocale с пустой строкой ("") в качестве имени локали. В стандарте C не указывается какой-либо конкретный механизм для настройки этой информации; он просто предполагает, что он существует.

(Замечание: вызов setlocale с пустой строкой в качестве имени локали не совпадает с вызовом setlocale с NULL качестве имени локали. NULL сообщает setlocale чтобы не изменять какой-либо параметр локали, но он все равно вернет строку, связанную с текущей локалью. избегает необходимости использования интерфейса getlocale.)

Posix определяет механизм настройки пользовательских настроек, а также настаивает на том, что (большинство) стандартизированных утилит командной строки работают в локали по умолчанию. Этот механизм использует переменные среды, имена которых соответствуют макросам категории setlocale.

В реализации Posix, когда программа вызывает setlocale(LC_X, ""); библиотека перейдет к рассмотрению текущей среды:

  1. Во-первых, он ищет переменную среды LC_ALL. Если это определено и имеет непустое значение, оно используется для определения локали.

  2. В противном случае, если первый аргумент setlocale не был LC_ALL он ищет переменную среды, имя которой совпадает с этим аргументом. Если это определено и имеет непустое значение, оно используется для определения локали.

  3. В противном случае, если переменная среды LANG определена и имеет непустое значение, она используется (в зависимости от реализации) для создания имени локали. (Предполагается, что LANG указывает язык пользователя, что является важной частью их предпочтений локали.)

  4. Наконец, используется некоторое системное значение по умолчанию.

Переменные среды обычно инициализируются программой login (или эквивалентом GUI) на основе файлов конфигурации системы. (Точный механизм варьируется от распределения к распределению, и документация часто бывает трудно найти.)

Как уже упоминалось, почти все стандартные утилиты оболочки требуются Posix для выполнения эквивалента setlocale(LC_ALL, ""); для работы в пользовательской конфигурации. Каждая служебная страница (или другая документация) должна указывать, делает она это или нет, но разумно предположить, что она действует, если нет какой-либо информации об обратном.

Кроме того, многие (но не все) стандартные функции строки библиотеки являются локальными. Интерфейсы библиотек, которые определенно не являются локальными, включают isdigit и isxdigit, которые всегда отвечают на основе языка C и strcmp, который сравнивает строки так же, как memcmp, используя значение char (интерпретируемое как unsigned int) для определить порядок сортировки. (strcoll является локальным, если вы хотите выполнить сравнение в соответствии с LC_COLLATE.) И кодировки символов, используемые для широких и многобайтовых символов, контролируются (каким-то неопределенным образом) по категории LC_CTYPE.