i18n复数形式化

95
我希望你能够在Rails的国际化(i18n)中翻译复数字符串。一个字符串可能是这样的:
You have 2 kids

或者

You have 1 kid

我知道我可以使用复数化助手方法,但我想将其嵌入i18n翻译中,这样我就不必在未来的任何时间点上弄乱我的视图。我读到:count在翻译中以某种方式用于复数形式,但我找不到任何真正关于它如何实现的资源。

请注意,我知道我可以在翻译字符串中传递变量。我也尝试过类似以下的内容:

<%= t 'misc.kids', :kids_num => pluralize(1, 'kid') %>

这种方法很好用,但是有一个根本性的问题。我需要在pluralize助手中指定字符串'kid'。我不想这样做,因为这将导致将来的视图问题。相反,我希望保留所有内容都在翻译中,而不是在视图中。

我该怎么做?


2
请注意,上述代码中的“插值器”和引号"#{....}"并不是必需的。 - Zabba
1
你的方法是错误的,因为你假设其他语言的复数形式与英语的工作方式相同。请参见我的答案以获得正确的方法。 - sorin
Sorin,谢谢您的回答,我只是不想在这里使用gettext。我认为Zabba的解决方案对我的国际化需求非常适合。 - Spyros
Rails 3使用CLDR和“count”插值变量处理更加健壮:http://guides.rubyonrails.org/i18n.html#pluralization - Luke W
多年以后,您也可以在字符串“kid”上使用翻译 - 这样您就有了: <%= t 'misc.kids', :kids_num => pluralize(1, t('kid')) %>. 也许这在2011年不起作用(!),但在Rails 5.2.2上确实可以。 - Jarvis Johnson
不,贾维斯,这并不是你想的那样。 - Clemens Kofler
8个回答

186
请尝试以下内容: en.yml
en:
  misc:
    kids:
      zero: no kids
      one: 1 kid
      other: %{count} kids

在一个视图中:

You have <%= t('misc.kids', :count => 4) %>

适用于具有多重复数形式的语言的更新答案(已在Rails 3.0.7中测试):

文件 config/initializers/pluralization.rb:

require "i18n/backend/pluralization" 
I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)

File config/locales/plurals.rb:

{:ru => 
  { :i18n => 
    { :plural => 
      { :keys => [:one, :few, :other],
        :rule => lambda { |n| 
          if n == 1
            :one
          else
            if [2, 3, 4].include?(n % 10) && 
               ![12, 13, 14].include?(n % 100) && 
               ![22, 23, 24].include?(n % 100)

              :few 
            else
              :other 
            end
          end
        } 
      } 
    } 
  } 
}

#More rules in this file: https://github.com/svenfuchs/i18n/blob/master/test/test_data/locales/plurals.rb
#(copy the file into `config/locales`)

文件 config/locales/en.yml:

en:
  kids:
    zero: en_zero
    one: en_one
    other: en_other

File config/locales/ru.yml:

ru:
  kids:
    zero: ru_zero
    one: ru_one
    few: ru_few
    other: ru_other

测试:

$ rails c
>> I18n.translate :kids, :count => 1
=> "en_one"
>> I18n.translate :kids, :count => 3
=> "en_other"
>> I18n.locale = :ru
=> :ru
>> I18n.translate :kids, :count => 1
=> "ru_one"
>> I18n.translate :kids, :count => 3
=> "ru_few"  #works! yay! 
>> I18n.translate :kids, :count => 5
=> "ru_other"  #works! yay! 

抱歉,这种方法在许多语言中都不适用。复数形式的处理非常复杂,请参考http://translate.sourceforge.net/wiki/l10n/pluralforms。因此,我认为我的答案更加合适。 - sorin
1
@sorin,我更新了我的答案,使用了多个复数规则。 - Zabba
6
没问题,但现在你有一份新的全职工作,就是维护复数词典! - sorin
1
@ThePablick,是的,因为'/initializer'目录中的文件仅在http服务器启动时加载一次。 - Zabba
你的俄语复数规则是错误的,请参考其他答案 - x-yuri
显示剩余3条评论

40

希望会俄语的Ruby on Rails程序员能找到这篇文章。我想分享我自己非常精确的俄语复数公式。它基于Unicode规范。 以下是config/locales/plurals.rb文件的内容,其他部分应该和上面的答案一样。

{:ru => 
  { :i18n => 
    { :plural => 
      { :keys => [:zero, :one, :few, :many],
        :rule => lambda { |n| 
          if n == 0
            :zero
          elsif
            ( ( n % 10 ) == 1 ) && ( ( n % 100 != 11 ) )
            # 1, 21, 31, 41, 51, 61...
            :one
          elsif
            ( [2, 3, 4].include?(n % 10) \
            && ![12, 13, 14].include?(n % 100) )
            # 2-4, 22-24, 32-34...
            :few
          elsif ( (n % 10) == 0 || \
            ![5, 6, 7, 8, 9].include?(n % 10) || \
            ![11, 12, 13, 14].include?(n % 100) )
            # 0, 5-20, 25-30, 35-40...
            :many
          end
        } 
      } 
    } 
  } 
}

母语者可能会喜欢像111121这样的情况。 以下是测试结果:

  • 零:0个请求/鸡肉/苹果
  • 一个:1个请求/鸡肉/苹果
  • 几个:3个请求/鸡肉/苹果
  • 许多:5个请求/鸡肉/苹果
  • 一个:101个请求/鸡肉/苹果
  • 几个:102个请求/鸡肉/苹果
  • 许多:105个请求/鸡肉/苹果
  • 许多:111个请求/鸡肉/苹果
  • 许多:119个请求/鸡肉/苹果
  • 一个:121个请求/鸡肉/苹果
  • 几个:122个请求/鸡肉/苹果
  • 许多:125个请求/鸡肉/苹果

感谢初步答复!


1
你所提到的另一个答案将这个内容放在了不同的文件中。因此,如果采用这种方法,你的内容应该放在 config/locales/plurals.rb 而不是 config/initializers/pluralization.rb - silverdr
@silverdr 我已经在答案中修复了文件名。感谢您的提示! - sashaegorov
我想知道Unicode网站上的v是什么意思... 另外,最后一个elsif分支可以被替换成else,不是吗? - x-yuri
顺便说一下,在i18n宝石中,他们有[规则](https://github.com/ruby-i18n/i18n/blob/00fc8100135878af7b5cc05aa2213844dcbe4e1b/test/test_data/locales/plurals.rb#L83),但在测试数据中。 - x-yuri

11

首先,要记住复数形式的数量取决于语言,对于英语有两种形式,对于罗马尼亚语有3种形式,而对于阿拉伯语有6种!

如果您想正确使用复数形式,您需要使用gettext

对于Ruby和Rails,您应该查看这个http://www.yotabanana.com/hiki/ruby-gettext-howto-rails.html


4
Sorin,这也是我所想的,但这似乎可以通过遵循CLDR格式(http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)来解决。我错了吗? - Nikos D
还有第1、2、3、4、11、12和13,但是21、22、23等等。 - gnasher729

9

中文

它只是开箱即用工作

en.yml:

en:
  kid:
    zero: 'no kids'
    one: '1 kid'
    other: '%{count} kids'

使用方法(当然,在视图文件中你可以跳过I18n):

> I18n.t :kid, count: 1
 => "1 kid"

> I18n.t :kid, count: 3
 => "3 kids"

俄语(以及其他具有多个复数形式的语言)

安装rails-18n gem并像example中那样将翻译添加到您的.yml文件中。

ru.yml:

ru:
  kid:
    zero: 'нет детей'
    one: '%{count} ребенок'
    few: '%{count} ребенка'
    many: '%{count} детей'
    other: 'дети'

使用方法:

> I18n.t :kid, count: 0
 => "нет детей"

> I18n.t :kid, count: 1
 => "1 ребенок"

> I18n.t :kid, count: 3
 => "3 ребенка"

> I18n.t :kid, count: 5
 => "5 детей"

> I18n.t :kid, count: 21
 => "21 ребенок"

> I18n.t :kid, count: 114
 => "114 детей"

> I18n.t :kid, count: ''
 => "дети"

8
Rails 3 在考虑 CLDR 和计数插值变量的情况下进行了强大的处理。请参见http://guides.rubyonrails.org/i18n.html#pluralization
# in view
t('actors', :count => @movie.actors.size)

# locales file, i.e. config/locales/en.yml
en:
  actors:
    one: Actor
    other: Actors

5

实际上,有一种替代繁琐的i18n方法的解决方案。这个解决方案被称为Tr8n。

您上面的代码将变得非常简单:

 <%= tr("You have {num || kid}", num: 1) %>

就是这样。不需要从代码中提取密钥并在资源包中维护它们,也不需要为每种语言实现复数规则。Tr8n为所有语言提供数字上下文规则。它还提供了性别规则、列表规则和语言情况。

上述翻译键的完整定义实际上看起来像这样:

 <%= tr("You have {num:number || one: kid, other: kids}", num: 1) %>

但是由于我们希望节省空间和时间,num会自动映射到数字规则,因此无需为规则值提供所有选项。Tr8n配备了复数器和屈折变化器,可以即时为您完成工作。

对于您的键的俄语翻译,只需简单地进行以下操作:

 "У вас есть {num || ребенок, ребенка, детей}"

顺便提一下,在有性别规定的语言中,您的翻译可能会不准确。例如,在希伯来语中,根据观看用户的性别,“你”实际上需要指定至少两种翻译。Tr8n处理得非常好。以下是希伯来语翻译的音译:

 "Yesh leha yeled ahad" with {context: {viewing_user: male, num: one}}
 "Yesh leha {num} yeladim" with {context: {viewing_user: male, num: other}}
 "Yesh lah yeled ahad" with {context: {viewing_user: female, num: one}}
 "Yesh lah {num} yeladim" with {context: {viewing_user: female, num: other}}

在这种情况下,你的单个英文键需要进行4次翻译。所有的翻译都是在上下文中完成的,不需要拆分整个句子。Tr8n具有一种机制,可以根据语言和上下文将一个键映射到多个翻译上 - 这些都是即时完成的。
最后再提醒一件事情。如果你需要加粗计数部分,只需简单地这样做:
<%= tr("You have [bold: {num || kid}]", num: 1, bold: "<strong>{$0}</strong>") %>

如果您以后想要重新定义“bold”(粗体) - 这非常容易 - 您不必浏览所有的YAML文件并更改它们 - 您只需要在一个地方做即可。

了解更多信息,请查看这里:

https://github.com/tr8n/tr8n_rails_clientsdk

声明:我是Tr8n框架及其所有库的开发人员和维护者。


1
我希望我知道这些踩票是为了什么,回答似乎没问题。 - doug65536

0
关于Redmine。如果你将复数文件规则复制到config/locales/中,并将其命名为plurals.rb,而不是与语言环境名称相同(ru.rb、pl.rb等),这些规则将无法工作。 你需要将文件规则重命名为'locale'.rb或者在/lib/redmine/i18n.rb文件中修改方法。
def init_translations(locale)
  locale = locale.to_s
  paths = ::I18n.load_path.select {|path| File.basename(path, '.*') == locale}
  load_translations(paths)
  translations[locale] ||= {}
end

如果您使用的是旧版Redmine,请添加以下内容:

module Implementation
        include ::I18n::Backend::Base
        **include ::I18n::Backend::Pluralization**

0

我发现了一个非常好的资源,其中包含有关区域设置 http://translate.sourceforge.net/wiki/l10n/pluralforms (备用链接) 的描述。

例如,对于乌克兰语、俄语、白俄罗斯语和其他一些语言,表达式将如下所示:plural =(n%10 == 1 && n%100!= 11?0:n%10> = 2 && n%10 <= 4 &&(n%100 <10 || n%100> = 20)?1:2);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接