Комментарии 40
Мне идея очеь понравилась, буду использовать в своих проектах. Спасибо.
Виноват. Признаюсь, не прочитал целиком, потому что подумал что если вы знаете про gettext, то у вас не должно появляться такого вопроса. И да, gettext действительно не очень удобен, когда несколько числительных идут в одной фразе, хотя такая ситуация и встречается не очень часто.
gettext и для одного числительного не очень хорош, т.к. либо все равно требует некоторых лишних телодвижений с точки зрения программирования, либо избыточного дублирования текста. Пример:
Когда строка короткая, то все хорошо:
Когда строка длинная, то это приводит либо к избыточной работе со стороны переводчика:
либо начинаются пляски с разбиением законченного сообщения на отдельные составляющие, что тоже не всегда полезно:
Поэтому я и полагаю, чтоу уместнее было бы всю логику формирования строки засунуть в саму строку, а программисту ограничиться единичным взызовом типа
Когда строка короткая, то все хорошо:
ngettext("%d comment", "%d comments", $n)
Когда строка длинная, то это приводит либо к избыточной работе со стороны переводчика:
ngettext("Do you really want to delete %d comment? Operation can't be undone.", "Do you really want to delete %d comments? Operation can't be undone.", $n)
либо начинаются пляски с разбиением законченного сообщения на отдельные составляющие, что тоже не всегда полезно:
ngettext("Do you really want to delete %d comment?", "Do you really want to delete %d comments?", $n) + gettext("Operation can't be undone.")
Поэтому я и полагаю, чтоу уместнее было бы всю логику формирования строки засунуть в саму строку, а программисту ограничиться единичным взызовом типа
localize("string", ('param1' => 'value1', 'param2' => 'value2'))
В принципе исходя из своего опыта работы с геттекстом, я тоже не мог бы назвать его удачным решением проблемы, однако многие переводчики уже привыкли работать именно с ним и приходится с этим мириться.
Когда я размышлял о вопросе локализации текстов, то мне на ум приходил вариант, который я бы назвал объектно-ориентированным. Зачастую переводить нужно не только надписи на кнопках и простые фразы, состоящие из одного-двух приложений, а целые письма, состоящие из нескольких абзацев, содержащие медиа-вложения и прочее.
Хотелось бы чтобы для всего этого использовался один подход (хотя бы идеологически). Сама собой приходит идея выделения всех переводных сущностей из текста и некий механизм привязки этих сущностей к тексту. Пока только к сожалению ничего конкретного не удалось придумать, т.к. хватает других задач.
Предложенный вами подход в целом интересен, для простых проектов использовать его может быть целесообразно. Еще раз прошу прощения, что невнимательно прочитал топик в первый раз :)
Когда я размышлял о вопросе локализации текстов, то мне на ум приходил вариант, который я бы назвал объектно-ориентированным. Зачастую переводить нужно не только надписи на кнопках и простые фразы, состоящие из одного-двух приложений, а целые письма, состоящие из нескольких абзацев, содержащие медиа-вложения и прочее.
Хотелось бы чтобы для всего этого использовался один подход (хотя бы идеологически). Сама собой приходит идея выделения всех переводных сущностей из текста и некий механизм привязки этих сущностей к тексту. Пока только к сожалению ничего конкретного не удалось придумать, т.к. хватает других задач.
Предложенный вами подход в целом интересен, для простых проектов использовать его может быть целесообразно. Еще раз прошу прощения, что невнимательно прочитал топик в первый раз :)
Ну переводчики-то работают не с самим gettext, а с .po-файлами. И ничто не мешает нам использовать эти .po файлы как формат хранения строк для перевода и для отдачи переводчикам. А строка внутри .po уже может быть какой угодно, с какими угодно включениями. Надо только объяснить переводчикам про этот микроязык и как им пользоваться.
Думается, для русского языка будет больше трех вариантов для вашего примера. Например 21 файл в 11 папках
java класс ChoiceFormat делает тоже самое
и выглядит формат почти так же :)
{0, choice, 1#is negative| 0#is zero or fraction | 1#is one |1.0> 1+ |2#is two |2<is more than 2}
и выглядит формат почти так же :)
{0, choice, 1#is negative| 0#is zero or fraction | 1#is one |1.0> 1+ |2#is two |2<is more than 2}
Я думал в точности о таком. И даже придумал тупой язык скриптов для этого. Имеет четыре оператора.
% x; — остаток от деления
= x { op } op — условный оператор, проверка на равенство
in x y { op } op — условный оператор, проверка на принадлежность диапазону
take x; — взять вариант x
Русский
; ворона|вороны|ворон
_PluralRule = { %100; in 5 20 { take 3; } %10; =1 { take 1; } in 2 4 { take 2; } }
Английский
; crow|crows
_PluralRule = { =1 { take 1; } }
Если не прошёл ни один take, автоматически берётся последнее, т.е. «ворон» или, соответственно, «crows».
Сейчас я бы то же самое сделал на XML или подобном языке иерархического описания данных — тогда в распоряжении были только INI-файлы.
Кстати, почему именно «вороны» — из-за фразы «считать ворон».
% x; — остаток от деления
= x { op } op — условный оператор, проверка на равенство
in x y { op } op — условный оператор, проверка на принадлежность диапазону
take x; — взять вариант x
Русский
; ворона|вороны|ворон
_PluralRule = { %100; in 5 20 { take 3; } %10; =1 { take 1; } in 2 4 { take 2; } }
Английский
; crow|crows
_PluralRule = { =1 { take 1; } }
Если не прошёл ни один take, автоматически берётся последнее, т.е. «ворон» или, соответственно, «crows».
Сейчас я бы то же самое сделал на XML или подобном языке иерархического описания данных — тогда в распоряжении были только INI-файлы.
Кстати, почему именно «вороны» — из-за фразы «считать ворон».
Про остальные падежи не забывайте: воронам, воронах, и т.п.
Это только в английском всё так просто :)
Это только в английском всё так просто :)
Именительный: ворона|вороны|ворон
Родительный: вороны|ворон|ворон
Дательный: вороне|воронам|воронам
Винительный: ворону|ворон|ворон
Творительный: вороной|воронами|воронами
Предложный: вороне|воронах|воронах
Так что вроде всё в порядке, и если не использовать один текст дважды (стандартное правило интернационализатора), проблем не будет.
Родительный: вороны|ворон|ворон
Дательный: вороне|воронам|воронам
Винительный: ворону|ворон|ворон
Творительный: вороной|воронами|воронами
Предложный: вороне|воронах|воронах
Так что вроде всё в порядке, и если не использовать один текст дважды (стандартное правило интернационализатора), проблем не будет.
когда все уже хабракат ставить научатся
Я написал маленький плагин для Smarty, в итоге все выглядит так:
{decline num=8 words=«сумка, сумки, сумок»}
А что бы не писать кучу раз одно и то же, связал это с системой языков. Можно написать и так:
{decline num=8 word=«сумка»}, и он сам возьмет из языковых пакетов нужные варианты.
{decline num=8 words=«сумка, сумки, сумок»}
А что бы не писать кучу раз одно и то же, связал это с системой языков. Можно написать и так:
{decline num=8 word=«сумка»}, и он сам возьмет из языковых пакетов нужные варианты.
НЛО прилетело и опубликовало эту надпись здесь
Отрезал все лишнее, вот:
Decliner::declOfNum
function smarty_function_decline($params, &$smarty) {
$titles = split(",", $params['titles']);
return Decliner::declOfNum($params['num'], $titles);
}
Decliner::declOfNum
static function declOfNum($number, $titles) {
$cases = array (2, 0, 1, 1, 1, 2);
return $titles[($number % 100 > 4 && $number % 100 < 20) ? 2 : $cases[min($number % 10, 5)]];
}
Ы))
А я написал в свое время модификатор, работает на sprintf() и используется примерно так:
{$vacancies_count|incline_word:«Всего %d вакансия»:«Всего %d вакансии»:«Всего %d вакансий»:«Нет вакансий»} (последний параметр необязательный)
Имхо удобнее)
А я написал в свое время модификатор, работает на sprintf() и используется примерно так:
{$vacancies_count|incline_word:«Всего %d вакансия»:«Всего %d вакансии»:«Всего %d вакансий»:«Нет вакансий»} (последний параметр необязательный)
Имхо удобнее)
Аналогично, только синтаксис такой (и без проверки на ноль, я стараюсь по мере возможности практиковать «thinking forth»):
Всего {$vacancies_count} ваканси{$vacancies_count|sklon:'я, и, й'}
:)
Всего {$vacancies_count} ваканси{$vacancies_count|sklon:'я, и, й'}
:)
У вас неудобно когда 2 слова подряд, например «2 хороших вакансии». Но вообще это все самодеятельность и на будущее стоит посматривать в строону gettext или чего-то такого.
В случае двух слов подряд будет просто «{$vacancies_count} {$vacancies_count|sklon:'хорошая вакансия, хороших вакансии, хороших вакансий'}».
>и на будущее стоит посматривать в строону gettex
Как только он появится на всех хостингах — так сразу :) А пока приходится ориентироваться на хостинг массовый.
>и на будущее стоит посматривать в строону gettex
Как только он появится на всех хостингах — так сразу :) А пока приходится ориентироваться на хостинг массовый.
gettext вроде-бы прекрасно может работать с plural forms?
blog.gate.lv/2008/04/23/gettext-plurals/
blog.gate.lv/2008/04/23/gettext-plurals/
Ну вот мне кажется, что это «прекрасно» несколько преувеличено (см. мои комментарии выше).
Для геттекста есть poedit, геттекст — де-факто стандарт (ок, наряду с еще несколькими программами), А чем может похвастать ваша система?
ЗЫ. Это не к тому, что ваше начинание плохое. Всегда хорошо иметь альтернативы. Но, если посмотреть правде в глаза: приобрести хоть сколько-нибудь заметную популярность ваше решение сможет вряд ли :(
ЗЫ. Это не к тому, что ваше начинание плохое. Всегда хорошо иметь альтернативы. Но, если посмотреть правде в глаза: приобрести хоть сколько-нибудь заметную популярность ваше решение сможет вряд ли :(
Ничто не мешает продолжать использовать формат .po, все написанные для него утилиты типа poedit или того же Pootle, про который я упомянул выше. Но в строках могут содержаться любые строки, в т.ч. и в предложенном мной формате. Переводчик точно также как и раньше осуществляет перевод, мы достаем из скомпилированного .mo локализованный вариант строки, а затем пропускаем через некую функцию (по аналогии со sprintf), которая уже формирует готовую строку на основе данных ей параметров.
Вроде как XLIFF может что-то похожое…
Знаю про XLIFF, но это немного из другой оперы — никто им не будет пользоваться как форматом хранения строк в программе (для этого у всех уже существуют свои методы properties-файлы в Java, rc-файлы в VC, strings-файлы на Маке и т.д. Нужно нечто, что любой бы использовал здесь и сейчас — строки по прежнему хранятся в ресурсах, специфичных для данной платформы/языка, но вместо всяких макросов вида %s или {0} использовался бы простой и удобный микроязык, позволяющий несколько строк в ресурсах «схлопнуть» до одной. Это упрощает и кодирование и локализацию.
И до кучи: форматное выражение должно учитывать порядок слов в разных языках, не факт, что «21 файл в 11 папках» не окажется в китайском «в 11 папках 21 файл». В MSDN про это статьи есть неплохие
Именно! gettext эту задачу опять решить не в силах, перекладывая все на разработчиков, а в предложенном варианте это решается на ура — внутри строки все можно переставить как надо:
В {%D%|0 папках|1 папке|%D% папках|%D% папках} {%F%|файлов не найдено|найден 1 файл|найдено %F% файла|найдено %F% файлов}.
вот два сайта, где подобные проблемы с формами для множественного и единственного числа решаются простыми формулами, учитывая языковые особенности.
Список формул 1
Список формул 2
Список формул 1
Список формул 2
В английском один падеж и одна форма множественного числа: 1 coder, 2 coders. Остальные падежи реализуюся предлогами.
В русском языке шесть падежей (иногда и больше), и к тому-же падеж зависит от предстоящего числительного: 1 стол — именительный., 2 стола — уже родительный, ед. число. 5 столов — род. множ. число.
Нужно как-то и это учитывать для универсальности решения.
В русском языке шесть падежей (иногда и больше), и к тому-же падеж зависит от предстоящего числительного: 1 стол — именительный., 2 стола — уже родительный, ед. число. 5 столов — род. множ. число.
Нужно как-то и это учитывать для универсальности решения.
Множественных форм числа всего три (максимум) в любом случае, иногда формы одинаковы при склонении слова:
Кто? Что? — 1 стол, 2 стола, 5 столов
Кого? Чего? — 1 стола, 2 столов, 5 столов
Кому? Чему? — 1 столу, 2 столам, 5 столам
Кого? Что? — 1 стол, 2 стола, 5 столов
Кем? Чем? — 1 столом, 2 столами, 5 столами
О ком? О чем? — 1 столе, 2 столах, 5 столах
Так что тут все работает.
Кто? Что? — 1 стол, 2 стола, 5 столов
Кого? Чего? — 1 стола, 2 столов, 5 столов
Кому? Чему? — 1 столу, 2 столам, 5 столам
Кого? Что? — 1 стол, 2 стола, 5 столов
Кем? Чем? — 1 столом, 2 столами, 5 столами
О ком? О чем? — 1 столе, 2 столах, 5 столах
Так что тут все работает.
«ни одного файла найдено»
Для больше универсальности надо разнести формат числа и формат слов, связанных с ним.
Вроде того, что когда функция видит %F%, она вставляет значение F в это место.
Когда видит {%F%...} вставляет подходящий текст, но не само число.
Просто сами предложения могут быть достаточно сложными, например:
Найдено 5 файлов в 10-ти папках, они скопированы в архив.
Не найдено файлов в 3-х папках, и никто их никуда копировать не будет
Найден один файл в 1-й папке, он скопирован в архив.
Ну и вторая мысль: надо делать рекурсивную обработку строк, т.е., например у нас есть {%F%a|b|c}, вот в строках a, b и c мы тоже можем писать блоки форматирования.
Тогда можно делать сложные конструкции: найдено 200 файлов, из них удалено 195. Ну а если файлов не найдено, то про удаление можно вообще не писать, и так понятно, что 0 файлов удалено.
Вроде того, что когда функция видит %F%, она вставляет значение F в это место.
Когда видит {%F%...} вставляет подходящий текст, но не само число.
Просто сами предложения могут быть достаточно сложными, например:
Найдено 5 файлов в 10-ти папках, они скопированы в архив.
Не найдено файлов в 3-х папках
Найден один файл в 1-й папке, он скопирован в архив.
Ну и вторая мысль: надо делать рекурсивную обработку строк, т.е., например у нас есть {%F%a|b|c}, вот в строках a, b и c мы тоже можем писать блоки форматирования.
Тогда можно делать сложные конструкции: найдено 200 файлов, из них удалено 195. Ну а если файлов не найдено, то про удаление можно вообще не писать, и так понятно, что 0 файлов удалено.
> Вроде того, что когда функция видит %F%, она вставляет значение F в это место.
> Когда видит {%F%...} вставляет подходящий текст, но не само число.
Вроде так я и описывал.
> Когда видит {%F%...} вставляет подходящий текст, но не само число.
Вроде так я и описывал.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Идея: функция форматирования для удобной локализации строк