65.9K
CodeProject 正在变化。 阅读更多。
Home

复数形式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (11投票s)

2008年4月15日

LGPL3

2分钟阅读

viewsIcon

36800

downloadIcon

109

用任何语言正确拼写“找到 5 个文件”之类的消息。

引言

像 "%d 个文件找到" 这样的消息很难进行本地化。在英语中,只有两种形式:1 个文件(单数)和 2 个或更多文件(复数),但其他语言最多使用 4 种复数形式。例如,波兰语有 3 种形式

    0 plików
    1 plik
  2-4 pliki
 5-21 plików
22-24 pliki
25-31 plików
      etc.

其他语言(法语、俄语、捷克语等)也使用与英语和彼此不同的规则。

gettext 库从本地化文件中提取 复数形式选择规则。该规则是一个 C 语言表达式,它为每个消息进行评估。这是一个通用的解决方案,但恕我直言,对于这项任务来说,表达式评估器有些过头了。

我的解决方案

我开发了一个更简单的解决方案,适用于 gettext 页面上提到的所有语言。它基于以下观察

  • 所有额外的复数形式都用于某个数字范围,例如斯洛伐克语和捷克语中的 2 到 4。
  • 这种模式通常会**为每 10 或 100 个项目重复**。在俄语中,它听起来像是“二十一个文件”,而不是“二十一个文件”,因为名词与最后一个数字“一”一致。相同的模式重复出现在 30、40 等数字中。
  • 从 10 到 19 的数字(我简称为**“十几”**)通常是规则的例外。就像英语中 16 的拼写与 26、36、46 等不同一样:“sixteen”与“twenty-six”、“thirty-six”和“forty-six”不同。
  • 在某些语言中,**零**的处理方式不同,例如罗马尼亚语。

因此,每个复数形式的规则将由这些组件组成

range_start  range_end  modulo_for_repetition  skip_teens_flag

以下是一些示例

English
singular - range_start = 1, range_end = 1
plural   - all other numbers

Polish
singular - range_start = 1, range_end = 1
plural1  - range_start = 2, range_end = 4, modulo = 10, skip_teens = true
plural2  - all other numbers

Irish
singular - range_start = 1, range_end = 1
plural1  - range_start = 2, range_end = 2
plural2  - all other numbers

Lithuanian
singular - range_start = 1, range_end = 1, modulo = 10, skip_teens = true
plural1  - range_start = 2, range_end = 9, modulo = 10, skip_teens = true
plural2  - all other numbers ("teens")

每种语言的规则可以写入一个简短的 string,并存储在语言文件中(例如,对于立陶宛语,string 是 "1 1 10 t; 2 9 10 t")。

Using the Code

要使用我的解决方案,请在您的项目中包含 plurals.hplurals.c。**接口**由两个函数组成。首先,您调用 PluralsReadCfg string 读取规则。接下来,您将一个数字传递给 PluralsGetForm。它返回该数字的正确复数形式的索引,您可以使用该索引从语言文件中读取 string

PLURAL_INFO plurals;
PluralsReadCfg(&plurals, ReadFromLngFile("PluralRules"));

char lng_str_name[16], message[128];
sprintf(lng_str_name, "FilesFound%d", PluralsGetForm(&plurals, number));
sprintf(message, ReadFromLngFile(lng_str_name), number);

在语言文件中,您为每种复数形式都有 string

PluralRules = "1"
FilesFound0 = "%d file found"
FilesFound1 = "%d files found"

ReadFromLngFile 是您自己的函数。您可以将两个 sprintf 包装在一个更高级别的函数中(当然,使用安全的函数代替 sprintf 以保护您的程序免受缓冲区溢出)。

结论

使用 MSVC++ 编译时,两个函数 PluralsReadCfg PluralsGetForm 在您的可执行文件中占用 500 字节。为了以任何语言正确地拼写您的消息,这很值得。

历史

  • 2008 年 4 月 15 日:初始发布
© . All rights reserved.