![]() |
Boost.Locale
|
当标准 C++ facets(应该)提供大部分所需功能时,为什么我们需要本地化库?
std::ctype
facet 完成的std::collate
支持排序,并与 std::locale
良好集成std::num_put
、std::num_get
、std::money_put
、std::money_get
、std::time_put
和 std::time_get
用于数字、时间、货币格式化和解析。std::messages
类,它支持本地化消息格式化。如果我们在标准库中拥有所有功能,那么为什么我们需要这样一个库?
几乎每个(!) facet 都有设计缺陷
std::collate
仅支持一个级别的排序;它不允许您选择是否应执行区分大小写或区分重音符号的比较。std::ctype
假定所有转换都可以在每个字符的基础上完成。对于许多语言来说,这可能是正确的,但在一般情况下是不正确的。toupper
函数在单个字符的基础上工作。char
或 Windows 平台上的两个 wchar_t
表示。这使得 std::ctype
在这些编码中完全无用。std::numpunct
和 std::moneypunct
根本没有指定数字表示的代码点,因此它们无法使用阿拉伯语区域设置下使用的数字格式化数字。例如,数字 "103" 在 ar_EG
区域设置中应显示为 "١٠٣"。std::numpunct
和 std::moneypunct
假定千位分隔符是单个字符。这对于 UTF-8 编码是不正确的,在 UTF-8 编码中,只有 Unicode 0-0x7F 范围可以表示为单个字符。因此,在使用 Unicode "EN SPACE" 字符作为千位分隔符的区域设置(例如俄语)下,本地化数字无法正确表示。std::time_put
和 std::time_get
有几个缺陷std::tm
进行时间表示,忽略了在许多国家/地区,日期可以使用不同的日历显示这一事实。std::tm
甚至根本不包含时区字段。std::time_get
与 std::time_put
不对称,因此您无法解析使用 std::time_put
创建的日期和时间。(此问题在 C++11 和一些 STL 实现(如 Apache 标准 C++ 库)中已得到解决。)std::messages
不提供对复数形式的支持,这使得正确本地化诸如 “目录中有 X 个文件” 这样的简单字符串变得不可能。此外,std::locale
根本不支持许多功能:时区(如上所述)、文本边界分析、数字拼写等等。因此,很明显,标准 C++ 区域设置对于实际应用来说是有问题的。
ICU 是一个非常好的本地化库,但它有几个严重的缺陷
例如:Boost.Locale 提供与 iostream
的直接集成,允许更自然的数据格式化方式。例如
ICU 是可用的最佳本地化/Unicode 库之一。它由大约 50 万行经过良好测试、经过生产验证的源代码组成,这些代码今天提供了最先进的本地化工具。
即使重新实现 ICU 能力的一小部分也是一个不可行的项目,这将需要许多人年。因此,问题不是我们是否需要从头开始重新实现 Unicode 和本地化算法,而是 “我们是否需要在 Boost 中使用一个好的本地化库?”
因此,Boost.Locale 使用现代 C++ 接口包装 ICU,允许将来用更好的替代方案重新实现部分内容,但在今天而不是在不近的将来(如果可能的话)将本地化支持带到 Boost。
是的,整个 ICU API 都隐藏在不透明的指针后面,用户无法访问它。这样做有几个原因
有许多可用的本地化格式。到目前为止,最流行的是 OASIS XLIFF、GNU gettext po/mo 文件、POSIX 目录、Qt ts/tm 文件、Java 属性和 Windows 资源。但是,后三种仅在其特定领域有用,而 POSIX 目录太简单和有限,因此只有两个合理的选择
第一个通常看起来是更正确的本地化解决方案,但它需要 XML 解析来加载文档,它是非常复杂的格式,甚至 ICU 也需要预先编译成 ICU 资源包。
另一方面
因此,即使 GNU Gettext mo 目录格式不是官方批准的文件格式
有几个原因
ptime
– 绝对可以使用,但它有几个问题time()
给出的表示形式与时区无关(通常是 GMT 时间),并且稍后才应以用户请求的时区表示。ptime
已经为时间格式化和解析定义了 operator<<
和 operator>>
。ptime
格式化和解析 facets 没有以用户可以覆盖的方式设计。主要的格式化和解析函数不是虚拟的。除非 Boost.DateTime 库的开发人员决定更改它们,否则这使得重新实现 ptime
的格式化和解析函数变得不可能。ptime
的 facets 在格式化信息和区域设置信息的划分方面没有 “正确” 设计。格式化信息应存储在 std::ios_base
中,而有关区域设置特定格式化的信息应存储在 facet 本身中。因此,在这一点上,ptime
不支持格式化本地化日期和时间。
有几个原因
有两个原因
std::codecvt
API 进行字符集转换可以在任何大小的流上无问题地工作。有几个主要原因
std::locale
类的构建方式。每个功能都使用 std::locale::facet
的子类表示,该子类为它工作的特定操作提供抽象 API,请参阅 C++ 标准库本地化支持简介。有几个原因
char16_t
和 char32_t
定义为不同的类型,因此用类似 uint16_t
或 uint32_t
的东西替换它将不起作用,例如将 uint16_t
写入 uint32_t
流会将数字写入流。std::num_put
这样的标准 facets 安装到 std::locale
的现有实例中时,C++ 区域设置系统才能工作,但是在许多标准 C++ 库中,这些 facets 针对标准库支持的每个特定字符进行了专门化,因此尝试创建新的 facet 将会失败,因为它没有专门化。这些正是 Boost.Locale 在 GCC-4.5(第二个原因)和 MSVC-2010(第一个原因)上使用当前有限的 C++11 字符支持失败的原因
基本上,不可能将非 C++ 字符与 C++ 的区域设置框架一起使用。
最好和最可移植的解决方案是使用 C++ 的 char
类型和 UTF-8 编码。