glibc 知:手册07:语言环境和国际化

1. 前言

The GNU C Library Reference Manual for version 2.35

2. 语言环境和国际化

Locales and Internationalization

不同的国家和文化对如何沟通有不同的约定。这些约定的范围从非常简单的约定(例如表示日期和时间的格式)到非常复杂的约定(例如所说的语言)。

软件的国际化意味着对其进行编程以适应用户最喜欢的约定。在 ISO C 中,国际化通过语言环境进行。每个语言环境都指定了一组约定,每个目的一个约定。用户通过指定语言环境(通过环境变量)来选择一组约定。

所有程序都继承所选语言环境作为其环境的一部分。如果程序被编写为遵守语言环境的选择,它们将遵循用户首选的约定。

2.1. 语言环境有什么影响

What Effects a Locale Has

每个语言环境都为多种目的指定约定,包括以下内容:

  • 哪些多字节字符序列是有效的,以及它们是如何被解释的(参见字符集处理)。
  • 本地字符集中哪些字符被视为字母的分类,以及大小写转换约定(请参阅字符处理)。
  • 本地语言和字符集的整理顺序(请参阅整理函数)。
  • 数字和货币金额的格式(请参阅通用数字格式参数)。
  • 日期和时间的格式(请参阅格式化日历时间)。
  • 用于输出的语言,包括错误消息(请参阅消息翻译)。
  • 用户回答是或否问题时使用什么语言(请参阅是或否问题)。
  • 更复杂的用户输入使用什么语言。(C 库还不能帮助您实现这一点。)

适应指定语言环境的某些方面由库子例程自动处理。例如,为了使用所选语言环境的整理顺序,您的程序需要做的就是使用 strcoll 或 strxfrm 来比较字符串。

语言环境的其他方面超出了图书馆的理解范围。例如,该库不能自动将程序的输出消息翻译成其他语言。支持以用户喜欢的语言输出的唯一方法是或多或少地手动编程。C 库提供了轻松处理多种语言翻译的函数。

本章讨论修改当前语言环境的机制。当前语言环境对特定库函数的影响在这些函数的描述中进行了更详细的讨论。

2.2. 选择语言环境

Choosing a Locale

用户选择语言环境的最简单方法是设置环境变量 LANG。这指定了一个用于所有目的的语言环境。例如,用户可以指定一个名为“espana-castellano”的假设语言环境,以使用西班牙大部分地区的标准约定。

支持的语言环境集取决于您使用的操作系统,它们的名称也是如此,但称为“C”或“POSIX”的标准语言环境始终存在。请参阅区域设置名称

为了强制系统始终使用默认语言环境,用户可以将 LC_ALL 环境变量设置为“C”。

用户还可以选择为不同的目的指定不同的语言环境——实际上是选择多个语言环境的混合。请参阅区域设置类别

例如,用户可能会为大多数目的指定区域设置“espana-castellano”,但指定区域设置“usa-english”用于货币格式。如果用户是讲西班牙语的美国人,使用西班牙语工作,但以美元表示货币金额,这可能是有意义的。

请注意,与所有语言环境一样,语言环境“espana-castellano”和“usa-english”都将包含适用于语言环境的所有目的的约定。但是,用户可以选择将每个语言环境用于这些目的的特定子集。

2.3. 语言环境类别

Locale Categories

语言环境服务的目的被分组为类别,以便用户或程序可以独立地为每个类别选择语言环境。这是一个类别表;每个名称既是用户可以设置的环境变量,又是可以用作 setlocale 的第一个参数的宏名称。

环境变量的内容(或 setlocale 的第二个参数中的字符串)必须是有效的语言环境名称。请参阅区域设置名称

LC_COLLATE

此类别适用于字符串排序(函数 strcoll 和 strxfrm);请参阅整理函数

LC_CTYPE

本类别适用于字符的分类和转换,适用于多字节和宽字符;请参阅字符处理字符集处理

LC_MONETARY

此类别适用于格式化货币值;请参阅通用数字格式参数

LC_NUMERIC

此类别适用于格式化非货币数值;请参阅通用数字格式参数

LC_TIME

此类别适用于格式化日期和时间值;请参阅格式化日历时间

LC_MESSAGES

此类别适用于选择用户界面中用于消息翻译的语言(请参阅消息翻译的统一论坛方法;请参阅 X/Open 消息目录处理),并包含用于肯定和否定响应的正则表达式。

LC_ALL

这不是一个类别;它只是一个宏,您可以使用 setlocale 为所有目的设置单个语言环境。设置此环境变量会覆盖其他 LC_* 变量或 LANG 的所有选择。

LANG

如果定义了此环境变量,则其值指定用于所有目的的语言环境,但被上述变量覆盖的情况除外。

在开发消息翻译功能时,感觉上面的变量提供的功能是不够的。例如,应该可以指定多个语言环境名称。以一个德语比英语说得更好的瑞典用户为例,以及一个默认以英语输出消息的程序。应该可以指定首选语言是瑞典语,第二个是德语,如果这也无法使用英语。这可以通过变量 LANGUAGE 实现。有关此 GNU 扩展的进一步描述,请参阅用户对 gettext 的影响

2.4. 程序如何设置语言环境

How Programs Set the Locale

C 程序在启动时会继承其语言环境变量。这会自动发生。但是,这些变量不会自动控制库函数使用的语言环境,因为 ISO C 规定所有程序默认以标准“C”语言环境启动。要使用环境指定的语言环境,您必须调用 setlocale。如下调用它:

setlocale (LC_ALL, "");

根据用户选择的适当环境变量来选择语言环境。

您还可以使用 setlocale 指定特定的语言环境,用于一般用途或特定类别。

本节中的符号在头文件 locale.h 中定义。

函数:char * setlocale (int category, const char *locale)

Preliminary: | MT-Unsafe const:locale env | AS-Unsafe init lock heap corrupt | AC-Unsafe init corrupt lock mem fd | See POSIX Safety Concepts.

函数 setlocale 将类别 category 的当前语言环境设置为语言环境。

如果 category 是 LC_ALL,这将指定用于所有目的的语言环境。category 的其他可能值指定了单一用途(请参阅区域设置类别)。

您还可以使用此函数通过传递一个空指针作为语言环境参数来找出当前语言环境。在这种情况下,setlocale 返回一个字符串,它是当前为类别类别选择的语言环境的名称。

setlocale 返回的字符串可能会被后续调用覆盖,因此如果您想在对 setlocale 的任何进一步调用之后将其保存,则应该复制该字符串(请参阅复制字符串和数组)。(标准库保证永远不会调用 setlocale 本身。)

您不应修改 setlocale 返回的字符串。它可能与之前调用 setlocale 时作为参数传递的字符串相同。一项要求是,在返回字符串的调用中的类别必须与将字符串作为语言环境参数传入时的类别相同。

当您读取类别 LC_ALL 的当前语言环境时,该值对所有类别的选定语言环境的整个组合进行编码。如果在随后的 setlocale 调用中使用 LC_ALL 指定相同的“语言环境名称”,它将恢复相同的语言环境选择组合。

为确保您以后可以使用返回的字符串编码当前选定的语言环境,您必须制作该字符串的副本。不能保证返回的指针随着时间的推移保持有效。

当 locale 参数不是空指针时,setlocale 返回的字符串反映了新修改的语言环境。

如果您为语言环境指定一个空字符串,这意味着读取适当的环境变量并使用其值来选择类别的语言环境。

如果为语言环境提供了非空字符串,则尽可能使用该名称的语言环境。

有效的语言环境名称(setlocale 的第二个参数,或者如果参数是空字符串,则从进程环境获得的名称)必须是有效的语言环境名称。请参阅区域设置名称

如果您指定了无效的语言环境名称,setlocale 将返回一个空指针并保持当前语言环境不变。

这是一个示例,展示了如何使用 setlocale 临时切换到新的语言环境。

#include <stddef.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>

void
with_other_locale (char *new_locale,
                   void (*subroutine) (int),
                   int argument)
{
  char *old_locale, *saved_locale;

  /* Get the name of the current locale.  */
  old_locale = setlocale (LC_ALL, NULL);

  /* Copy the name so it won’t be clobbered by setlocale. */
  saved_locale = strdup (old_locale);
  if (saved_locale == NULL)
    fatal ("Out of memory");

  /* Now change the locale and do some stuff with it. */
  setlocale (LC_ALL, new_locale);
  (*subroutine) (argument);

  /* Restore the original locale. */
  setlocale (LC_ALL, saved_locale);
  free (saved_locale);
}

可移植性说明:一些 ISO C 系统可能会定义额外的语言环境类别,并且该库的未来版本将这样做。为了便于移植,假设任何以‘LC_’开头的符号都可以在 locale.h 中定义。

2.5. 标准语言环境

Standard Locales

您可以指望在所有操作系统上找到的唯一语言环境名称是以下三个标准名称:

“C”

这是标准的 C 语言环境。它提供的属性和行为在 ISO C 标准中指定。当您的程序启动时,它最初默认使用此语言环境。

“POSIX”

这是标准的 POSIX 语言环境。目前,它是标准 C 语言环境的别名。

“”

空名称表示根据环境变量选择语言环境。请参阅区域设置类别。

定义和安装命名语言环境通常是您站点的系统管理员(或安装 GNU C 库的人)的责任。用户也可以创建私有语言环境。所有这些将在稍后描述执行此操作的工具时进行讨论。

如果您的程序需要使用除“C”语言环境之外的其他语言环境,那么如果您使用用户在环境中指定的任何语言环境,而不是尝试通过名称显式指定一些非标准语言环境,它将更具可移植性。请记住,不同的机器可能安装了不同的语言环境集。

2.6. 语言环境名称

Locale Names

以下命令打印系统支持的语言环境列表:

  locale -a

可移植性注意:除了标准语言环境名称“C”和“POSIX”之外,语言环境名称是系统特定的。

大多数语言环境名称都遵循 XPG 语法,最多由四个部分组成:

language[_territory[.codeset]][@modifier]

除了第一部分之外,所有这些都允许丢失。如果未找到完整的指定语言环境,则查找不太具体的语言环境。将按以下顺序剥离各个部分:

codeset
normalized codeset
territory
modifier

例如,语言环境名称“de_AT.iso885915@euro”表示在奥地利使用的德语语言环境,使用 ISO-8859-15 (Latin-9) 字符集,并以欧元作为货币符号。

除了遵循 XPG 语法的语言环境名称之外,系统还可以提供别名,例如“german”。两类名称都不得包含斜杠字符“/”。

如果语言环境名称以斜杠“/”开头,则将其视为相对于已配置语言环境目录的路径;请参阅下面的 LOCPATH。指定路径不能包含组件‘…’,否则名称无效,setlocale 将失败。

可移植性说明:POSIX 建议,如果语言环境名称以斜杠“/”开头,则将其解析为绝对路径。但是,GNU C 库将其视为 LOCPATH 中列出的目录下的相对路径(如果 LOCPATH 未设置,则为默认语言环境目录)。

长于实现定义限制的语言环境名称无效并导致 setlocale 失败。

作为一种特殊情况,与 LC_ALL 一起使用的语言环境名称可以组合多个语言环境,反映不同类别的不同语言环境设置。例如,您可能希望使用 ISO A4 纸张格式的美国语言环境,因此您将 LANG 设置为“en_US.UTF-8”,将 LC_PAPER 设置为“de_DE.UTF-8”。在这种情况下,LC_ALL 风格的组合语言环境名称是

LC_CTYPE=en_US.UTF-8;LC_TIME=en_US.UTF-8;LC_PAPER=de_DE.UTF-8;

其次是此处未显示的其他类别设置。

可以使用 LOCPATH 环境变量设置用于查找语言环境数据的路径。此变量列出了要在其中搜索语言环境定义的目录,以冒号“:”分隔。

查找语言环境数据的默认路径是系统特定的。LOCPATH 默认值的典型值为:

/usr/share/locale

出于安全原因,特权程序会忽略 LOCPATH 的值,并且只使用默认目录。

2.7. 访问区域信息

Accessing Locale Information

有几种方法可以访问语言环境信息。最简单的方法是让 C 库自己完成工作。该库中的一些函数隐式访问区域设置数据,并使用当前选择的区域设置提供的信息。这就是语言环境模型正常工作的方式。

以 strftime 函数为例,它旨在很好地格式化日期和时间信息(请参阅格式化日历时间)。LC_TIME 类别中包含的部分标准信息是月份的名称。strftime 函数不需要程序员负责提供翻译,而是自己完成这一切。格式字符串中的 %A 被 LC_TIME 当前选择的区域设置的适当工作日名称替换。这是一个简单的例子,只要有可能,函数就会以这种方式自动执行操作。

但是很多情况下,根本没有执行任务的功能,或者根本不可能自动完成工作。对于这些情况,有必要直接访问区域设置中的信息。为此,C 库提供了两个函数:localeconv 和 nl_langinfo。前者是 ISO C 的一部分,因此是可移植的,但有一个大脑受损的接口。第二个是 Unix 接口的一部分,只要系统遵循 Unix 标准,它是可移植的。

2.7.1. localeconv:它是可移植的,但是……

localeconv: It is portable but …

ISO C 人连同 setlocale 函数一起发明了 localeconv 函数。这是一个糟糕的设计杰作。它使用起来很昂贵,不可扩展,并且通常不可用,因为它只提供对 LC_MONETARY 和 LC_NUMERIC 相关信息的访问。然而,如果它适用于给定的情况,则应该使用它,因为它非常便携。函数 strfmon 使用此信息根据所选语言环境格式化货币金额。

函数:struct lconv * localeconv (void)

Preliminary: | MT-Unsafe race:localeconv locale | AS-Unsafe | AC-Safe | See POSIX Safety Concepts.

localeconv 函数返回一个指向结构的指针,该结构的组件包含有关如何在当前语言环境中格式化数字和货币值的信息。

您不应修改结构或其内容。该结构可能会被后续调用 localeconv 或调用 setlocale 覆盖,但库中的其他函数不会覆盖此值。

数据类型:struct lconv

localeconv 的返回值就是这种数据类型。它的元素在以下小节中描述。

如果结构 struct lconv 的成员具有 char 类型,并且值为 CHAR_MAX,则表示当前语言环境没有该参数的值。

2.7.1.1. 通用数字格式参数

Generic Numeric Formatting Parameters

这些是 struct lconv 的标准成员;可能还有其他人。

char *decimal_point
char *mon_decimal_point

这些是分别用于格式化非货币和货币数量的小数点分隔符。在“C”语言环境中,decimal_point 的值为“.”,mon_decimal_point 的值为“”。

char *thousands_sep
char *mon_thousands_sep

这些是分别用于在格式化非货币和货币数量时分隔小数点左侧的数字组的分隔符。在“C”语言环境中,两个成员的值都是“”(空字符串)。

char *grouping
char *mon_grouping

这些是指定如何对小数点左侧的数字进行分组的字符串。分组适用于非货币数量,而 mon_grouping 适用于货币数量。使用数千个_sep 或 mon_thousands_sep 来分隔数字组。

这些字符串的每个成员都将被解释为 char 类型的整数值。连续的数字(从左到右)给出连续组的大小(从右到左,从小数点开始。)最后一个成员是 0,在这种情况下,前一个成员被一遍又一遍地用于所有剩余的组,或 CHAR_MAX,在这种情况下不再有分组——或者,换句话说,任何剩余的数字形成一个没有分隔符的大组。

例如,如果分组为“\04\03\02”,则数字 123456787654321 的正确分组为“12”、“34”、“56”、“78”、“765”、“4321”。这在末尾使用一组 4 位数字,前面是一组 3 位数字,前面是一组 2 位数字(根据需要)。使用分隔符“,”,数字将打印为“12,34,56,78,765,4321”。

值“\03”表示重复的三位数组,通常在美国使用。

在标准的“C”语言环境中,grouping 和 mon_grouping 的值都是“”。此值根本不指定任何分组。

char int_frac_digits
char frac_digits

这些是小整数,表示应分别以国际和本地格式的货币值显示多少小数位(小数点右侧)。(大多数情况下,两个成员具有相同的值。)

在标准的“C”语言环境中,这两个成员的值都为 CHAR_MAX,意思是“未指定”。ISO 标准没有说明当你找到这个值时该怎么做。我们建议不要打印小数位数。(此语言环境还为 mon_decimal_point 指定了空字符串,因此打印任何小数位数都会令人困惑!)

2.7.1.2. 打印货币符号

Printing the Currency Symbol

struct lconv 结构的这些成员指定如何打印符号以识别货币价值——美元的国际类似物“$”。

每个国家/地区都有两个标准货币符号。当地货币符号通常在国内使用,而国际货币符号在国际上用于在需要明确表示国家时指代该国家/地区的货币。

例如,许多国家/地区使用美元作为其货币单位,在处理国际货币时,重要的是要指定使用(例如)加元而不是美元或澳元。但是,当已知上下文是加拿大时,没有必要明确说明——美元金额被隐含地假定为加元。

char *currency_symbol

选定区域设置的本地货币符号。

在标准的“C”语言环境中,该成员的值为“”(空字符串),表示“未指定”。ISO 标准没有说明当你找到这个值时该怎么做。我们建议您只打印空字符串,就像打印此变量指向的任何其他字符串一样。

char *int_curr_symbol

所选语言环境的国际货币符号。

int_curr_symbol 的值通常应由国际标准 ISO 4217 货币和资金表示代码确定的三个字母缩写组成,后跟一个字符分隔符(通常是空格)。

在标准的“C”语言环境中,该成员的值为“”(空字符串),表示“未指定”。我们建议您只打印空字符串,就像打印此变量指向的任何其他字符串一样。

char p_cs_precedes
char n_cs_precedes
char int_p_cs_precedes
char int_n_cs_precedes

如果 currency_symbol 或 int_curr_symbol 字符串应该在货币金额的值之前,则这些成员为 1,如果字符串应该在该值之后,则这些成员为 0。p_cs_precedes 和 int_p_cs_precedes 成员适用于正数(或零),n_cs_precedes 和 int_n_cs_precedes 成员适用于负数。

在标准的“C”语言环境中,所有这些成员的值都是 CHAR_MAX,意思是“未指定”。ISO 标准没有说明当你找到这个值时该怎么做。我们建议在金额前打印货币符号,这适用于大多数国家/地区。换句话说,对这些成员中的所有非零值都一视同仁。

具有 int_ 前缀的成员适用于 int_curr_symbol,而其他两个适用于 currency_symbol。

char p_sep_by_space
char n_sep_by_space
char int_p_sep_by_space
char int_n_sep_by_space

如果在 currency_symbol 或 int_curr_symbol 字符串与金额之间应出现空格,则这些成员为 1,如果不应出现空格,则为 0。p_sep_by_space 和 int_p_sep_by_space 成员适用于正数(或零),n_sep_by_space 和 int_n_sep_by_space 成员适用于负数。

在标准的“C”语言环境中,所有这些成员的值都是 CHAR_MAX,意思是“未指定”。ISO标准没有说明当你找到这个值时你应该做什么;我们建议您将其视为 1(打印一个空格)。换句话说,对这些成员中的所有非零值都一视同仁。

具有 int_ 前缀的成员适用于 int_curr_symbol,而其他两个适用于 currency_symbol。不过,int_curr_symbol 有一个特点。由于所有合法值在字符串的末尾都包含一个空格,要么打印这个空格(如果货币符号必须出现在前面并且必须分开),要么必须完全避免打印这个字符(尤其是在结尾时)细绳)。

2.7.1.3. 打印金额的符号

Printing the Sign of a Monetary Amount

struct lconv 结构的这些成员指定如何打印货币值的符号(如果有)。

char *positive_sign
char *negative_sign

这些是用于分别表示正(或零)和负货币数量的字符串。

在标准的“C”语言环境中,这两个成员的值都是“”(空字符串),意思是“未指定”。

ISO 标准没有说明当你找到这个值时该怎么做。我们建议您在找到它时打印 positive_sign,即使它是空的。对于负值,打印你找到的negative_sign,除非它和positive_sign都是空的,在这种情况下打印’-'。(完全没有指示标志似乎相当不合理。)

char p_sign_posn
char n_sign_posn
char int_p_sign_posn
char int_n_sign_posn

这些成员是小整数,分别指示如何定位非负和负货币数量的符号。(用于符号的字符串是用 positive_sign 或negative_sign 指定的。)可能的值如下:

0           货币符号和数量应该用括号括起来。
1           在数量和货币符号之前打印符号字符串。
2           在数量和货币符号之后打印符号字符串。
3           在货币符号之前打印符号字符串。
4           在货币符号之后打印符号字符串。
CHAR_MAX    “未指定”。两个成员在标准的“C”语言环境中都有这个值。

ISO 标准没有说明当值为 CHAR_MAX 时应该做什么。我们建议您在货币符号之后打印符号。

具有 int_ 前缀的成员适用于 int_curr_symbol,而其他两个适用于 currency_symbol。

2.7.2. 精确访问区域设置数据

Pinpoint Access to Locale Data

在编写 X/Open 可移植性指南时,作者意识到 localeconv 函数不足以提供对语言环境信息的合理访问。本应在区域设置中可用的信息(如稍后在 POSIX.1 标准中指定的)需要更多方法来访问它。因此引入了 nl_langinfo 函数。

函数:char * nl_langinfo (nl_item item)

Preliminary: | MT-Safe locale | AS-Safe | AC-Safe | See POSIX Safety Concepts.

nl_langinfo 函数可用于访问语言环境类别的各个元素。与返回所有信息的 localeconv 函数不同,nl_langinfo 允许调用者选择它需要的信息。这非常快,多次调用这个函数也没有问题。

第二个优点是,除了数字和货币格式信息之外,还有来自 LC_TIME 和 LC_MESSAGES 类别的信息。

nl_item 类型在 nl_types.h 中定义。参数项是在标头 langinfo.h 中定义的数值。X/Open 标准定义了以下值:

CODESET

nl_langinfo 返回一个字符串,其中包含所选语言环境中使用的编码字符集的名称。

ABDAY_1
ABDAY_2
ABDAY_3
ABDAY_4
ABDAY_5
ABDAY_6
ABDAY_7

nl_langinfo 返回工作日的缩写名称。ABDAY_1 对应于星期日。

DAY_1
DAY_2
DAY_3
DAY_4
DAY_5
DAY_6
DAY_7

类似于 ABDAY_1 等,但这里的返回值是未缩写的工作日名称。

ABMON_1
ABMON_2
ABMON_3
ABMON_4
ABMON_5
ABMON_6
ABMON_7
ABMON_8
ABMON_9
ABMON_10
ABMON_11
ABMON_12

返回值是月份的缩写名称,在月份构成完整日期的一部分时使用的语法形式。ABMON_1 对应于一月。

MON_1
MON_2
MON_3
MON_4
MON_5
MON_6
MON_7
MON_8
MON_9
MON_10
MON_11
MON_12

与 ABMON_1 等类似,但此处月份名称不缩写。这里的第一个值 MON_1 也对应于一月。

ALTMON_1
ALTMON_2
ALTMON_3
ALTMON_4
ALTMON_5
ALTMON_6
ALTMON_7
ALTMON_8
ALTMON_9
ALTMON_10
ALTMON_11
ALTMON_12

类似于 MON_1 等,但这里的月份名称是月份本身命名时使用的语法形式。strftime 函数使用这些月份名称作为转换说明符 %OB(请参阅格式化日历时间)。

请注意,并非所有语言都需要两种不同形式的月份名称,因此为 MON_… 和 ALTMON_… 返回的字符串可能相同也可能不同,具体取决于语言环境。

注意:当前未提供与 %Ob 转换说明符对应的 ABALTMON_… 常量,但预计将在未来版本中提供。同时,可以使用_NL_ABATMON_…。

AM_STR
PM_STR

返回值是字符串,可用于将时间表示为从 1 到 12 的小时加上 am/pm 说明符。

请注意,在不使用此时间表示的语言环境中,这些字符串可能为空,在这种情况下,根本不能使用 am/pm 格式。

D_T_FMT

返回值可用作 strftime 的格式字符串,以特定于语言环境的方式表示时间和日期。

D_FMT

返回值可用作 strftime 的格式字符串,以特定于语言环境的方式表示日期。

T_FMT

返回值可用作 strftime 的格式字符串,以特定于语言环境的方式表示时间。

T_FMT_AMPM

返回值可以作为 strftime 的格式字符串,以 am/pm 格式表示时间。

请注意,如果 am/pm 格式对所选语言环境没有任何意义,则返回值可能与 T_FMT 的返回值相同。

ERA

返回值表示当前语言环境中使用的时代。

大多数语言环境不定义此值。定义此值的语言环境的一个示例是日语。在日本,日期的传统表示包括与当时天皇在位的时代名称。

通常不需要直接使用这个值。在其格式字符串中指定 E 修饰符会导致 strftime 函数使用此信息。未指定返回字符串的格式,因此您不应假设在不同系统上都知道它。

ERA_YEAR

返回值给出了区域设置相关时代的年份。至于 ERA,应该没有必要直接使用这个值。

ERA_D_T_FMT

此返回值可用作 strftime 的格式字符串,以特定于语言环境的基于时代的方式表示日期和时间。

ERA_D_FMT

此返回值可用作 strftime 的格式字符串,以特定于语言环境的基于时代的方式表示日期。

ERA_T_FMT

此返回值可用作 strftime 的格式字符串,以特定于语言环境的基于时代的方式表示时间。

ALT_DIGITS

返回值是用于表示值 0 到 99 的最多 100 个值的表示形式。对于 ERA,此值不打算直接使用,而是通过 strftime 函数间接使用。当修饰符 O 以其他方式使用数字来表示小时、分钟、秒、工作日、月或周的格式中使用时,将改为使用区域设置的适当值。

INT_CURR_SYMBOL

与 struct lconv 的 int_curr_symbol 元素中 localeconv 返回的值相同。

CURRENCY_SYMBOL
CRNCYSTR

与结构 lconv 的 currency_symbol 元素中 localeconv 返回的值相同。

CRNCYSTR 是 Unix98 仍需要的已弃用别名。

MON_DECIMAL_POINT

与 struct lconv 的 mon_decimal_point 元素中 localeconv 返回的值相同。

MON_THOUSANDS_SEP

与 struct lconv 的 mon_thousands_sep 元素中 localeconv 返回的值相同。

MON_GROUPING

与 struct lconv 的 mon_grouping 元素中 localeconv 返回的值相同。

POSITIVE_SIGN

与 struct lconv 的 positive_sign 元素中 localeconv 返回的值相同。

NEGATIVE_SIGN

与 struct lconv 的negative_sign 元素中localeconv 返回的值相同。

INT_FRAC_DIGITS

与结构 lconv 的 int_frac_digits 元素中 localeconv 返回的值相同。

FRAC_DIGITS

与结构 lconv 的 frac_digits 元素中 localeconv 返回的值相同。

P_CS_PRECEDES

与 struct lconv 的 p_cs_precedes 元素中 localeconv 返回的值相同。

P_SEP_BY_SPACE

与 struct lconv 的 p_sep_by_space 元素中 localeconv 返回的值相同。

N_CS_PRECEDES

与 struct lconv 的 n_cs_precedes 元素中 localeconv 返回的值相同。

N_SEP_BY_SPACE

与 struct lconv 的 n_sep_by_space 元素中 localeconv 返回的值相同。

P_SIGN_POSN

与 struct lconv 的 p_sign_posn 元素中 localeconv 返回的值相同。

N_SIGN_POSN

与 struct lconv 的 n_sign_posn 元素中 localeconv 返回的值相同。

INT_P_CS_PRECEDES

与 struct lconv 的 int_p_cs_precedes 元素中 localeconv 返回的值相同。

INT_P_SEP_BY_SPACE

与 struct lconv 的 int_p_sep_by_space 元素中 localeconv 返回的值相同。

INT_N_CS_PRECEDES

与 struct lconv 的 int_n_cs_precedes 元素中 localeconv 返回的值相同。

INT_N_SEP_BY_SPACE

与 struct lconv 的 int_n_sep_by_space 元素中 localeconv 返回的值相同。

INT_P_SIGN_POSN

与 struct lconv 的 int_p_sign_posn 元素中 localeconv 返回的值相同。

INT_N_SIGN_POSN

与 struct lconv 的 int_n_sign_posn 元素中 localeconv 返回的值相同。

DECIMAL_POINT
RADIXCHAR

与 struct lconv 的 decimal_point 元素中 localeconv 返回的值相同。

名称 RADIXCHAR 是在 Unix98 中仍然使用的已弃用别名。

THOUSANDS_SEP
THOUSEP

与结构 lconv 的数千个_sep 元素中 localeconv 返回的值相同。

THOUSEP 这个名字是一个已弃用的别名,在 Unix98 中仍然使用。

GROUPING

与 struct lconv 的分组元素中 localeconv 返回的值相同。

YESEXPR

返回值是一个正则表达式,可与正则表达式函数一起使用,以识别对是/否问题的肯定响应。GNU C 库提供了 rpmatch 函数,以便在应用程序中更轻松地处理。

NOEXPR

返回值是一个正则表达式,可与正则表达式函数一起使用,以识别对是/否问题的否定响应。

YESSTR

返回值是对是/否问题的肯定响应的特定于语言环境的翻译。

不推荐使用此值,因为它是消息翻译的一种非常特殊的情况,并且由消息翻译函数更好地处理(请参阅消息翻译)。

不推荐使用此符号。相反,应该使用消息翻译。

NOSTR

返回值是对是/否问题的否定响应的特定于语言环境的翻译。YESSTR 所说的在这里也是如此。

不推荐使用此符号。相反,应该使用消息翻译。

文件 langinfo.h 定义了更多的符号,但它们都不是官方的。使用它们是不可移植的,并且返回值的格式可能会改变。因此,我们建议您不要使用它们。

请注意,任何有效参数的返回值都可以在所有情况下使用(上午/下午时间格式代码可能例外)。如果用户没有为适当的类别选择任何语言环境,nl_langinfo 从“C”语言环境返回信息。因此,可以使用此功能,如下例所示。

如果参数项无效,则返回指向空字符串的指针。

nl_langinfo 用法的一个示例是必须以特定于语言环境的方式打印给定日期和时间的函数。一开始可能会认为,由于 strftime 在内部使用语言环境信息,因此编写如下内容就足够了:

size_t
i18n_time_n_data (char *s, size_t len, const struct tm *tp)
{
  return strftime (s, len, "%X %D", tp);
}

该格式不包含工作日或月份名称,因此可在国际上使用。错误的! 产生的输出类似于“hh:mm:ss MM/DD/YY”。此格式仅在美国可识别。其他国家使用不同的格式。因此,该函数应该这样重写:

size_t
i18n_time_n_data (char *s, size_t len, const struct tm *tp)
{
  return strftime (s, len, nl_langinfo (D_T_FMT), tp);
}

现在它使用程序运行时选择的语言环境的日期和时间格式。如果用户正确选择了区域设置,则永远不会对时间和日期格式产生误解。

2.8. 格式化数字的专用函数

A dedicated function to format numbers

我们已经看到 localeconv 返回的结构以及赋予 nl_langinfo 的值允许您检索各种特定于语言环境的信息以格式化数字和货币金额。我们还看到基本规则非常复杂。

因此,X/Open 标准引入了一个使用这种语言环境信息的功能,使用户更容易根据这些规则来格式化数字。

函数:ssize_t strfmon (char *s, size_t maxsize, const char *format, ...)

Preliminary: | MT-Safe locale | AS-Unsafe heap | AC-Unsafe mem | See POSIX Safety Concepts.

strfmon 函数与 strftime 函数相似,因为它需要一个缓冲区、它的大小、一个格式字符串,以及以格式字符串指定的格式作为文本写入缓冲区的值。与 strftime 一样,该函数也返回写入缓冲区的字节数。

有两个不同之处:strfmon 可以接受多个参数,当然,格式规范也不同。与 strftime 一样,格式字符串由按原样输出的普通文本和由“%”指示的格式说明符组成。在“%”之后,您可以选择在主要格式化字符之前指定各种标志和格式化信息,与 printf 类似:

紧跟“%”之后可以有以下一个或多个标志:

‘=f’

该字段使用单字节字符 f 作为数字填充字符。默认情况下,此字符是空格字符。仅当指定了左精度时才使用此字符进行填充。这不仅仅是填充给定的字段宽度。

‘^’

根据当前语言环境的规则,打印数字时不会对数字进行分组。默认情况下启用分组。

‘+’, ‘(’

最多可以使用这些标志之一。他们选择哪种格式来表示货币金额的符号。默认情况下,如果给出“+”,则使用相当于 +/- 的语言环境。如果给出了“(”,则负数括在括号中。确切的格式由程序运行时选择的语言环境的 LC_MONETARY 类别的值确定。

‘!’

输出将不包含货币符号。

‘-’

如果输出未填充整个字段宽度,则输出将被格式化为左对齐而不是右对齐。

规范的下一部分是可选的字段宽度。如果未指定宽度,则取 0。在输出期间,该函数首先确定需要多少空间。如果它至少需要与字段宽度一样多的字符,则使用尽可能多的空间进行输出。否则,通过填充空格字符将其扩展为使用全宽。“-”标志的存在与否决定了这种填充发生在哪一侧。如果存在,则在右侧添加空格,使输出左对齐,反之亦然。

到目前为止,该格式看起来很熟悉,类似于 printf 和 strftime 格式。但是,接下来的两个可选字段引入了一些新内容。第一个是“#”字符,后跟十进制数字字符串。数字字符串的值指定小数点(或等效项)左侧的位数。当未给出“^”标志时,这不包括分组字符。如果打印数字所需的空格没有填满整个宽度,则在字段的左侧用填充字符填充,可以使用“=”标志选择,默认情况下是空格。例如,如果字段宽度选择为 6,数字为 123,则填充字符为“*”,结果将为“***123”。

第二个可选字段以“.”(句点)开头,由另一个十进制数字字符串组成。它的值描述了小数点后打印的字符数。默认值是从当前语言环境中选择的(frac_digits、int_frac_digits,请参阅通用数字格式参数)。如果精确表示需要的位数比字段宽度给出的多,则显示的值将四舍五入。如果选择小数位数为零,则不打印小数点。

作为 GNU 扩展,GNU C 库中的 strfmon 实现允许使用可选的“L”作为格式修饰符。如果给出此修饰符,则参数应为 long double 而不是 double 值。

最后,最后一个组件是格式说明符。定义了三个说明符:

‘i’

使用区域设置规则来格式化国际货币值。

‘n’

使用区域设置规则来格式化国家货币值。

‘%’

在输出中放置一个“%”。不能给定标志、宽度说明符或修饰符,只允许使用“%%”。

至于 printf,该函数从左到右读取格式字符串,并使用格式字符串后面传递给函数的值。这些值应为 double 类型或 long double 类型,具体取决于修饰符“L”的存在。结果存储在 s 指向的缓冲区中。最多存储 maxsize 个字符。

该函数的返回值是存储在 s 中的字符数,包括终止的 NULL 字节。如果存储的字符数超过 maxsize,则函数返回 -1,并且未指定缓冲区 s 的内容。在这种情况下,errno 设置为 E2BIG。

几个例子应该可以清楚地说明这个函数是如何工作的。假设以下所有代码片段都在使用美国语言环境 (en_US) 的程序中执行。最简单的格式是这样的:

strfmon (buf, 100, "@%n@%n@%n@", 123.45, -567.89, 12345.678);

产生的输出是

"@$123.45@-$567.89@$12,345.68@"

我们可以在这里注意到几件事。首先,输出数字的宽度不同。我们没有在格式字符串中指定宽度,所以这不足为奇。其次,第三个数字使用千位分隔符打印。en_US 语言环境的千位分隔符是逗号。数字也是四舍五入的。.678 被四舍五入为 .68,因为格式没有指定精度,并且语言环境中的默认值为 2。最后,请注意,由于使用了“%n”,而不是“i”,因此打印的是国家货币符号。下一个示例显示了我们如何对齐输出。

strfmon (buf, 100, "@%=*11n@%=*11n@%=*11n@", 123.45, -567.89, 12345.678);

这次的输出是:

"@ $123.45@ -$567.89@ $12,345.68@"

有两件事很突出。首先,所有字段都具有相同的宽度(11 个字符),因为这是格式中给出的宽度,并且没有数字需要打印更多字符。第二个重点是不使用填充字符。这是正确的,因为空白不是用来实现“#”修饰符给出的精度,而是填充到给定的宽度。如果我们现在添加宽度规范,差异就会变得很明显。

strfmon (buf, 100, "@%=*11#5n@%=*11#5n@%=*11#5n@",
         123.45-567.8912345.678);

输出是:

"@ $***123.45@-$***567.89@ $12,456.68@"

在这里我们可以看到所有的货币符号现在都对齐了,货币符号和数字之间的空格被选中的填充字符填充。请注意,虽然宽度选择为 5 并且 123.45 的小数点左侧有三位,但该空格是用三个星号填充的。这是正确的,因为如上所述,宽度不包括用于存储千位分隔符的位置。最后一个示例应该解释剩余的功能。

strfmon (buf, 100, "@%=0(16#5.3i@%=0(16#5.3i@%=0(16#5.3i@",
         123.45-567.8912345.678);

这个相当复杂的格式字符串产生以下输出:

"@ USD 000123,450 @(USD 000567.890)@ USD 12,345.678 @"

最显着的变化是表示负数的替代方式。在金融界,这通常使用括号来完成,这就是 ‘(’ 标志选择的内容。填充字符现在是 ‘0’。请注意,这个 ‘0’ 字符不被视为数字零,因此第一个和第二个数字不使用千位分隔符打印。由于我们使用格式说明符“i”而不是“n”,因此使用货币符号的国际形式。这是一个四字母字符串,在本例中为“USD”。最后一点是,由于选择了小数点右边的精度为三,所以打印的第一个和第二个数字末尾多了一个零,第三个数字不四舍五入打印。

2.9. 是或否问题

Yes-or-No Questions

一些非 GUI 程序会问一个是或否的问题。如果消息(尤其是问题)被翻译成外语,请确保您也将答案本地化。用一种语言提出问题并用另一种语言(通常是英语)要求答案是非常糟糕的习惯。

GNU C 库包含 rpmatch,使应用程序可以轻松访问相应的语言环境定义。

函数:int rpmatch (const char *response)

Preliminary: | MT-Safe locale | AS-Unsafe corrupt heap lock dlopen | AC-Unsafe corrupt lock mem fd | See POSIX Safety Concepts.

函数 rpmatch 检查字符串以判断它是否是正确的是或否答案,如果是,是哪一个。检查使用当前所选语言环境的 LC_MESSAGES 类别中的 YESEXPR 和 NOEXPR 数据。返回值如下:

1   用户输入了肯定的答案。
0   用户输入了否定的答案。
-1  答案既不匹配 YESEXPR 也不匹配 NOEXPR 正则表达式。

此函数不是标准化的,但在 GNU C 库中也可用,至少在 IBM AIX 库中也可用。

这个函数通常会像这样使用:

/* Use a safe default.  */
  _Bool doit = false;

  fputs (gettext ("Do you really want to do this? "), stdout);
  fflush (stdout);
  /* Prepare the getline call.  */
  line = NULL;
  len = 0;
  while (getline (&line, &len, stdin) >= 0)
    {
      /* Check the response.  */
      int res = rpmatch (line);
      if (res >= 0)
        {
          /* We got a definitive answer.  */
          if (res > 0)
            doit = true;
          break;
        }
    }
  /* Free what getline allocated.  */
  free (line);

请注意,循环一直持续到检测到读取错误或读取到确定的(肯定或否定)答案。

3. 参考

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值