I18n 多元化

我想能够翻译多元化字符串在 i18n 的轨道。一个字符串可以是:

You have 2 kids

或者

You have 1 kid

我知道我可以使用复数助手方法,但是我想把它嵌入到 i18n 翻译中,这样我就不必在将来的任何时候弄乱我的视图。我了解到 :count在某种程度上被用于复数形式的翻译,但是我找不到任何关于如何实现它的实际资源。

注意,我知道可以在转换字符串中传递变量:

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

这个方法很好,但是有一个基本的问题。我需要在复数助手中指定字符串 'kid'。我不想这样做,因为这将导致未来的问题。相反,我希望把所有东西都保留在翻译中,而什么都不保留在视图中。

我该怎么做?

56215 次浏览

Try this:

en.yml :

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

In a view:

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

Updated answer for languages with multiple pluralization (tested with Rails 3.0.7):

File 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`)

File 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

Test:

$ 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!

First, remember that number of plural forms depends on language, for English there are two, for Romanian there are 3 and for Arabic there are 6 !.

If you want to be able to properly use plural forms you have to use gettext.

For Ruby and rails you should check this http://www.yotabanana.com/hiki/ruby-gettext-howto-rails.html

Rails 3 handles this robustly with CLDR consideration and count interpolation variable. See 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

There is actually an alternative to the cumbersome i18n approach. The solution is called Tr8n.

Your above code would simply be:

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

That's it. No need to extract your keys from your code and maintain them in resource bundles, no need to implement pluralization rules for each language. Tr8n comes with numeric context rules for all language. It also comes with gender rules, list rules and language cases.

The full definition of the above translation key would actually look like this:

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

But since we want to save space and time, num is automatically mapped to numeric rules and there is no need to provide all options for the rule values. Tr8n comes with pluralizers and inflectors that will do the work for you on the fly.

The translation for your key in Russian, would simply be:

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

By the way, your translation would be inaccurate in languages that have gender specific rules. For example, in Hebrew, you would actually have to specify at least 2 translations for your example, as "You" would be different based on the gender of the viewing user. Tr8n handles it very well. Here is a transliteration of Hebrew translations:

 "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}}

So your single English key, in this case, needs 4 translations. All translations are done in context - you don't have to break the sentence. Tr8n has a mechanism to map one key to multiple translations based on the language and context - all done on the fly.

One last thing. What if you had to make the count part bold? It would simply be:

<%= tr("You have [bold: {num || kid}]", num: 1, bold: "<strong>{$0}</strong>") %>

Just in case you want to redefine your "bold" later - it would be very easy - you won't have to go through all your YAML files and change them - you just do it in one place.

To learn more, please take a look here:

https://github.com/tr8n/tr8n_rails_clientsdk

Disclosure: I am the developer and the maintainer of Tr8n framework and all its libraries.

I hope Russian-speaking Ruby on Rails programmers could find this. Just want to share my own very precise Russian pluralization formula. It based on Unicode Specs. Here is contents of config/locales/plurals.rb file only, everything else should be done as same as in answer above.

{: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
}
}
}
}
}

Native speakers may enjoy cases such as 111 and 121. And here the test results:

  • zero: 0 запросов/куриц/яблок
  • one: 1 запрос/курица/яблоко
  • few: 3 запроса/курицы/яблока
  • many: 5 запросов/куриц/яблок
  • one: 101 запрос/курица/яблоко
  • few: 102 запроса/курицы/яблока
  • many: 105 запросов/куриц/яблок
  • many: 111 запросов/куриц/яблок
  • many: 119 запросов/куриц/яблок
  • one: 121 запрос/курица/яблоко
  • few: 122 запроса/курицы/яблока
  • many: 125 запросов/куриц/яблок

Thanks for initial answer!

English

It just works out of the box

en.yml:

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

Usage (you can skip I18n in a view file, of course):

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


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

Russian (and other languages with multiple plural forms)

Install rails-18n gem and add translations to your .yml files as in the example:

ru.yml:

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

Usage:

> 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: ''
=> "дети"

About Redmine. If you copy pluralization file rules in config/locales/ as plurals.rb and other not same as locale name (ru.rb, pl.rb .. etc) these not work. You must rename file rules to 'locale'.rb or change method in file /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

and if you have older redmine, add

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

I've found a very good resource with description of locales http://translate.sourceforge.net/wiki/l10n/pluralforms ( alt link ).

For example, for ukrainian, russian, belarusian and few other languages the expression will look like this plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);