i18n Плюрализация


88

Я хочу иметь возможность переводить строки с множественным числом в i18n в рельсы. Строка может быть:

You have 2 kids

или

You have 1 kid

Я знаю, что могу использовать вспомогательный метод множественного числа, но я хочу встроить его в переводы i18n, чтобы мне не приходилось портить свои представления в любой момент в будущем. Я читал, что :countэто как-то используется в переводах множественного числа, но я не могу найти реальных ресурсов о том, как это реализуется.

Обратите внимание: я знаю, что могу передать переменную в строке перевода. Я тоже пробовал что-то вроде:

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

Что отлично работает, но фундаментальная проблема та же идея. Мне нужно указать строку 'kid'в помощнике множественного числа. Я не хочу этого делать, потому что это приведет к появлению проблем в будущем. Вместо этого я хочу сохранить все в переводе и ничего не видеть.

Как я могу это сделать ?


2
Обратите внимание, что "интерполятор" и кавычки "#{....}"в приведенном выше коде не нужны.
Забба

1
у вас неправильный подход, потому что вы предполагаете, что множественное число для других языков работает так же, как и в английском. Смотрите мой ответ для правильного подхода.
sorin

Сорин, спасибо за ответ, я просто не хочу использовать для этого gettext. Я думаю, что решение Zabba отлично подходит для моих нужд с i18n.
Спирос

Rails 3 обрабатывает более надежно, используя CLDR и переменную интерполяции count
Люк В.

Много лет спустя, но вы также можете использовать перевод на строку «ребенок» - так у вас есть: <%= t 'misc.kids', :kids_num => pluralize(1, t('kid')) %>. Возможно, это не сработало в 2011 году (!), Но теперь точно работает на Rails 5.2.2
Джарвис Джонсон,

Ответы:


176

Попробуй это:

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)

Файл 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

Файл 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! 

Извините, но это не работает со многими языками. Множественное число действительно сложно, см. Translate.sourceforge.net/wiki/l10n/pluralforms В связи с этим я думаю, что мой ответ более уместен.
sorin

1
@sorin, обновил свой ответ, чтобы использовать несколько правил множественного числа.
Забба

5
Это нормально, но теперь у вас есть новая работа на полную ставку, чтобы поддерживать словарь множественного числа !.
sorin

Это круто! Чтобы заставить %{count}работать, мне пришлось использовать кавычки вокруг всего блока, т.е. one: "%{count} kid"
firedev

1
@ThePablick, да, поскольку файлы в каталоге '/ initializer' загружаются только один раз - при запуске http сервера.
Zabba 03

37

Я надеюсь, что русскоязычные программисты 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
        } 
      } 
    } 
  } 
}

Носителям языка могут нравиться такие случаи, как 111и 121. А вот результаты тестов:

  • ноль: 0 запросов / куриц / яблоко
  • one: 1 запрос / курица / яблоко
  • немного: 3 запроса / курицы / яблока
  • много: 5 запросов / куриц / яблоко
  • one: 101 запрос / курица / яблоко
  • немного: 102 запроса / курицы / яблока
  • много: 105 запросов / куриц / яблоко
  • много: 111 запросов / куриц / яблоко
  • много: 119 запросов / куриц / яблоко
  • one: 121 запрос / курица / яблоко
  • немного: 122 запроса / курицы / яблока
  • много: 125 запросов / куриц / яблоко

Спасибо за первоначальный ответ!


1
Другой ответ, на который вы ссылались, поместил это в другой файл. Так что при таком подходе ваш контент должен идти, config/locales/plurals.rbа неconfig/initializers/pluralization.rb
silverdr

@silverdr Я исправил имя файла в ответе. Спасибо за чаевые!
сашаегоров

11

Во-первых, помните, что количество форм множественного числа зависит от языка : для английского их два, для румынского - 3, а для арабского - 6!.

Если вы хотите иметь возможность правильно использовать формы множественного числа, вы должны использовать gettext.

Для Ruby и rails вы должны проверить это http://www.yotabanana.com/hiki/ruby-gettext-howto-rails.html


4
Сорин, я тоже об этом подумал, но, похоже, это можно решить, следуя формату CLDR ( unicode.org/repos/cldr-tmp/trunk/diff/supplemental/… ). Я ошибся?
Nikos D

Также есть 1-е, 2-е, 3-е, 4-е, 11-е, 12-е и 13-е, но 21, 22, 23 и так далее.
gnasher729


5

английский

Просто работает из коробки

en.yml :

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

Использование (вы, конечно, можете пропустить I18n в файле просмотра):

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

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

Русский (и другие языки с множественным числом форм)

Установите гем rails-18n и добавьте переводы в свои .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: ''
 => "дети"

4

На самом деле есть альтернатива громоздкому подходу i18n. Решение называется Tr8n.

Ваш код выше будет просто:

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

Вот и все. Нет необходимости извлекать ключи из кода и сохранять их в пакетах ресурсов, нет необходимости реализовывать правила множественного числа для каждого языка. Tr8n поставляется с числовыми контекстными правилами для всех языков. Он также содержит гендерные правила, правила списков и языковые случаи.

Полное определение приведенного выше ключа перевода на самом деле будет выглядеть так:

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

Но поскольку мы хотим сэкономить место и время, num автоматически сопоставляется с числовыми правилами, и нет необходимости предоставлять все параметры для значений правил. Tr8n поставляется с плюрализаторами и инфлекторами, которые сделают всю работу за вас на лету.

Перевод вашего ключа на русский язык будет просто:

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

Кстати, ваш перевод будет неточным на языках с гендерными правилами. Например, на иврите вам нужно будет указать как минимум 2 перевода для вашего примера, так как «Вы» будет отличаться в зависимости от пола просматривающего пользователя. 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>") %>

На всякий случай, если вы захотите переопределить свой «жирный» позже - это будет очень просто - вам не придется просматривать все ваши файлы 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**
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.