Comments 8
Возможно, я ошибаюсь, но мне кажется, что раз уж вы решили писать по одному классу для каждого языка, код был бы проще, если бы вы предоставляли один-единственный виртуальный метод типа string GetString( TimeSpan value ), который в каждой конкретной реализации делал бы простейшие вычисления типа (EN): value.Minutes == 1? «minute»: «minutes», т.е. без нагромождения абстракций.
Другое дело, как можно было бы попытаться решить эту задачу в рамках одного класса + текстовой (xml) конфигурации. Не берусь утверждать, что такой подход «прокатит» для любого языка, но посмотрите, к примеру, на EN и RU:
Алгоритм на базе Regex.
В конфигурации для каждого языка и каждого слова (час, минута, секунда и т.п.) задано:
1. Базовая строка, являющаяся общей подстрокой для всех вариантов склонения. Может содержать условный знак подстановки числа (например, $).
2. Набор правил её дополнения, представляющий собой мэппинг регулярного выражения, тестирующего преобразуемое число (в виде строки), в простую строку вида «префикс*постфикс», где знак * будет заменен базовой строкой, а знак $ (если есть) — на число.
EN:
base = "$ minute" // общая часть
Правило #1:
@"^1$" => "*"
Т.е. единица заменяется на base без каких-либо дополнений, получается "$ minute", а после подстановок — «1 minute»
Правило #2 (всё остальное):
@"^\d+$" => "*s"
Т.е. в остальных случаях к base добавляется «s», получается "$ minutes"
RU
base = минут
@"\d*1$" => "*а" // число заканчивается на 1 => 321 минута
@"\d*[2-4]$" => "*ы" // число заканчивается на 2, 3 или 4 => 2 минуты
@"\d*[5-9]$" => "*" // иначе 38 минут
Другое дело, как можно было бы попытаться решить эту задачу в рамках одного класса + текстовой (xml) конфигурации. Не берусь утверждать, что такой подход «прокатит» для любого языка, но посмотрите, к примеру, на EN и RU:
Алгоритм на базе Regex.
В конфигурации для каждого языка и каждого слова (час, минута, секунда и т.п.) задано:
1. Базовая строка, являющаяся общей подстрокой для всех вариантов склонения. Может содержать условный знак подстановки числа (например, $).
2. Набор правил её дополнения, представляющий собой мэппинг регулярного выражения, тестирующего преобразуемое число (в виде строки), в простую строку вида «префикс*постфикс», где знак * будет заменен базовой строкой, а знак $ (если есть) — на число.
EN:
base = "$ minute" // общая часть
Правило #1:
@"^1$" => "*"
Т.е. единица заменяется на base без каких-либо дополнений, получается "$ minute", а после подстановок — «1 minute»
Правило #2 (всё остальное):
@"^\d+$" => "*s"
Т.е. в остальных случаях к base добавляется «s», получается "$ minutes"
RU
base = минут
@"\d*1$" => "*а" // число заканчивается на 1 => 321 минута
@"\d*[2-4]$" => "*ы" // число заканчивается на 2, 3 или 4 => 2 минуты
@"\d*[5-9]$" => "*" // иначе 38 минут
К сожалению, предложенное вами решение, например, в русском языке, не справится с нахождением правильной формы для слова «день». Если, конечно, не брать «базой» для него «д» ). На данный момент я не знаю, на какие языки будет переводиться проект, и не берусь решать, попадут ли они все под такие правила словообразования (т.е. будут ли иметь общую часть).
Архитектуру продумывал из расчета того, что бы слова можно было добавлять независимо от логики просчета, такое разделение позволяет использовать этот код не только в сообщениях «относительного времени», т.е. впринципе, я могу добавлять в ресурсы вариации числительных форм для любого слова языка, и использовать их в правильной форме повсеместно в приложении.
Архитектуру продумывал из расчета того, что бы слова можно было добавлять независимо от логики просчета, такое разделение позволяет использовать этот код не только в сообщениях «относительного времени», т.е. впринципе, я могу добавлять в ресурсы вариации числительных форм для любого слова языка, и использовать их в правильной форме повсеместно в приложении.
К сожалению, предложенное вами решение, например, в русском языке, не справится с нахождением правильной формы для слова «день». Если, конечно, не брать «базой» для него «д» ).
А почему не брать базой «д», собственно? База — вообще вещь необязательная, без неё можно обойтись в принципе, определив слова целиком в мэппинге. Она нужна лишь тем, кто любит находить и выделять общее, и позволяет определить в единственном месте порядок следования числа и слова. Но вы не обязаны делать это именно так.
я могу добавлять в ресурсы вариации числительных форм для любого слова языка, и использовать их в правильной форме повсеместно в приложении.
А я не могу разве? :-)
Знак $ не несёт большей смысловой нагрузки, чем место для подстановки простого числа. Т.е. эта конфигурация описывает только две простых вещи:
1. Варианты склонения любого слова-единицы измерения в зависимости от сопутствующего числа.
2. Формат записи (порядок элементов, разделители между ними).
Само собой, это могут быть не только дни или минуты, но так же рубли и копейки, евро и центы, метры и дюймы, etc.
А почему не брать базой «д», собственно? База — вообще вещь необязательная, без неё можно обойтись в принципе, определив слова целиком в мэппинге. Она нужна лишь тем, кто любит находить и выделять общее, и позволяет определить в единственном месте порядок следования числа и слова. Но вы не обязаны делать это именно так.
я могу добавлять в ресурсы вариации числительных форм для любого слова языка, и использовать их в правильной форме повсеместно в приложении.
А я не могу разве? :-)
Знак $ не несёт большей смысловой нагрузки, чем место для подстановки простого числа. Т.е. эта конфигурация описывает только две простых вещи:
1. Варианты склонения любого слова-единицы измерения в зависимости от сопутствующего числа.
2. Формат записи (порядок элементов, разделители между ними).
Само собой, это могут быть не только дни или минуты, но так же рубли и копейки, евро и центы, метры и дюймы, etc.
Честно говоря, мне понравилось то, как представлена информаци о построение множественных форм на сайте из статьи. Я имею ввиду, — разбиение на категории. Я постарался приблизить общую концепцию работы локализации именно к этому виду. Я, как разработчик, не очень бы хотел заполнять файлы ресурсов, но даже я мог бы справится с этим, для языка которого не знаю, имея набор категорий, и пример того, что каждая категория из себя представляет.
Я ни сколько не отрицаю, что ваше решение рабочее, но он требует некоторых технических знаний, для расширения «словаря».
P.S.
Я не очень силен в регекспе, подскажите, пожалуйста, можно ли к вашим правилам добавлять анализ не последней цифры, а нескольких последних? Потому, как предложенные вами правила
@"\d*1$" => "*а" // число заканчивается на 1 => 321 минута
@"\d*[2-4]$" => "*ы" // число заканчивается на 2, 3 или 4 => 2 минуты
не работают для чисел, на конце которых 11 и 12, к примеру.
Я ни сколько не отрицаю, что ваше решение рабочее, но он требует некоторых технических знаний, для расширения «словаря».
P.S.
Я не очень силен в регекспе, подскажите, пожалуйста, можно ли к вашим правилам добавлять анализ не последней цифры, а нескольких последних? Потому, как предложенные вами правила
@"\d*1$" => "*а" // число заканчивается на 1 => 321 минута
@"\d*[2-4]$" => "*ы" // число заканчивается на 2, 3 или 4 => 2 минуты
не работают для чисел, на конце которых 11 и 12, к примеру.
Вы правы, я не учёл *11-*19. Исправленный вариант:
RU
base = «минут»
@"^\d*1\d$" => "*" // числа, заканчивающиеся на 10, 11, ..., 19 => $ минут
@"^\d*1$" => "*а" // число заканчивается на 1 => 321 минута
@"^\d*[2-4]$" => "*ы" // число заканчивается на 2, 3 или 4 => 2 минуты
@"^\d*[5-9]$" => "*" // иначе 38 минут
Регулярные выражения тут простейшие, можете заодно слегка прокачаться.
^ и $ — начало и конец строки. указываются для того, чтобы выражение соответствовало всей строке в целом, а не искало подстроки
1 — просто цифра 1
[2-4] — одна цифра из указанного диапазона, т.е. либо 2, либо 3, либо 4
\d — любая цифра, синоним для [0-9]
\d* — строка любой длины (в т.ч. нулевой), состоящая из любых цифр
\d+ — как минимум одна любая цифра
^\d*1\d$ = любое количество цифр, потом единица, потом в конце одна любая цифра
^\d*1$ = любое количество цифр, потом в конце единица
^\d*[2-4]$ = любое количество цифр, потом в конце цифра 2, 3 или 4
^\d*[5-9]$ = любое количество цифр, потом в конце цифра 5, 6, 7, 8 или 9
Я ни сколько не отрицаю, что ваше решение рабочее, но он требует некоторых технических знаний, для расширения «словаря».
Да, требуется маломальское знание регулярных выражений, однако для внесения поправки или добавления нового слова или нового языка не требуется ничего, кроме редактирования текстового файла конфигурации (xml). Ненадобность компиляции — большой плюс.
RU
base = «минут»
@"^\d*1\d$" => "*" // числа, заканчивающиеся на 10, 11, ..., 19 => $ минут
@"^\d*1$" => "*а" // число заканчивается на 1 => 321 минута
@"^\d*[2-4]$" => "*ы" // число заканчивается на 2, 3 или 4 => 2 минуты
@"^\d*[5-9]$" => "*" // иначе 38 минут
Регулярные выражения тут простейшие, можете заодно слегка прокачаться.
^ и $ — начало и конец строки. указываются для того, чтобы выражение соответствовало всей строке в целом, а не искало подстроки
1 — просто цифра 1
[2-4] — одна цифра из указанного диапазона, т.е. либо 2, либо 3, либо 4
\d — любая цифра, синоним для [0-9]
\d* — строка любой длины (в т.ч. нулевой), состоящая из любых цифр
\d+ — как минимум одна любая цифра
^\d*1\d$ = любое количество цифр, потом единица, потом в конце одна любая цифра
^\d*1$ = любое количество цифр, потом в конце единица
^\d*[2-4]$ = любое количество цифр, потом в конце цифра 2, 3 или 4
^\d*[5-9]$ = любое количество цифр, потом в конце цифра 5, 6, 7, 8 или 9
Я ни сколько не отрицаю, что ваше решение рабочее, но он требует некоторых технических знаний, для расширения «словаря».
Да, требуется маломальское знание регулярных выражений, однако для внесения поправки или добавления нового слова или нового языка не требуется ничего, кроме редактирования текстового файла конфигурации (xml). Ненадобность компиляции — большой плюс.
Спасибо за развернутый ответ по регекспам, хоть что-то вспомнилось :-).
Вы правы, я не учёл *11-*19. Исправленный вариант:
Вот этим мне и понравился сайт из статьи, все варианты уже расписанны и для каждого есть условия. Для русского например:
one → n mod 10 is 1 and n mod 100 is not 11;
few → n mod 10 in 2..4 and n mod 100 not in 12..14;
many → n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
other → everything else
Вы правы, я не учёл *11-*19. Исправленный вариант:
Вот этим мне и понравился сайт из статьи, все варианты уже расписанны и для каждого есть условия. Для русского например:
one → n mod 10 is 1 and n mod 100 is not 11;
few → n mod 10 in 2..4 and n mod 100 not in 12..14;
many → n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
other → everything else
Побуду занудой: Fabric — это ткань, материя, а фабрика — Factory.
Ну а идея и реализация понравились, красиво.
Ну а идея и реализация понравились, красиво.
Sign up to leave a comment.
.Net Локализованные сообщения «относительного времени»