// Приветствую, хабрамены!
Введение
Тебе приходилось, %username%, видеть на многих проектах надписи:
- «Подружиться с Дмитрий»
- «Опубликован Артем» (подразумевалось кем-то)
- Etc.
Или как, например, сделал ВКонтакте:
- «Подружиться с Андреем»
- «Отправить Ярославу подарок»
Сегодня я расскажу, как сделать такую штуку, как «автоматический перевод слова в нужный падеж».
Из курса начальной школы мы все знаем, что существует 6 падежей:
- Именительный (Кто? Что?)
- Родительный (Кого? Чего?)
- Дательный (Кому? Чему?)
- Винительный (Кого? Что?)
- Творительный (Кем? Чем?)
- Предложный (О ком? О чём?)
Все мы понимаем, что без «пинка» в нужное место, слова не будут переводиться в нужный падеж, для этого я и реализовал функцию toCase():
toCase( String str, String case );
Например:
- var words={"Мама":"р", "Хабр":"д", "Поросёнок":"в", "Ночь":"т", "Отец":"п"}, result="";
- for(var i in words){
- result+=words[i].toUpperCase()+".: "+toCase(i,words[i])+"\n"
- }
- return result
* This source code was highlighted with Source Code Highlighter.Выдаст следующее:
Р.: Мамы
Д.: Хабру
В.: Поросёнка
Т.: Ночью
П.: Отце
Развитие событий
Предлагаю сами внутренности функции:
- function toCase(str, choice) {
- var strPub = { // правила для окончаний
- "а": ["ы", "е", "у", "ой", "е"],
- "(ш/ж/к/ч)а": ["%и", "%е", "%у", "%ой", "%е"],
- "б/в/м/г/д/л/ж/з/к/н/п/т/ф/ч/ц/щ/р/х": ["%а", "%у", "%а", "%ом", "%е"],
- "и": ["ей", "ям", "%", "ями", "ях"],
- "ый": ["ого", "ому", "%", "ым", "ом"],
- "й": ["я", "ю", "я", "ем", "е"],
- "о": ["а", "у", "%", "ом", "е"],
- "с/ш": ["%а", "%у", "%", "%ом", "%е"],
- "ы": ["ов", "ам", "%", "ами", "ах"],
- "ь": ["я", "ю", "я", "ем", "е"],
- "уль": ["ули", "уле", "улю", "улей", "уле"],
- "(ч/ш/д/т)ь": ["%и", "%и", "%ь", "%ью", "%и"],
- "я": ["и", "е", "ю", "ей", "е"]
- },
- cases = { // номера для падежей, не считая Именительный
- "р": 0,
- "д": 1,
- "в": 2,
- "т": 3,
- "п": 4
- },
- exs = { // исключения, сколько символов забирать с конца
- "ц": 2,
- "ок": 2
- },
- lastIndex,reformedStr,forLong,splitted,groupped,forPseudo;
- for(var i in strPub){
- if(i.length > 1 && str.slice(-i.length) == i){ // для окончаний, длиной >1
- lastIndex = i;
- reformedStr = str.slice(0, -lastIndex.length);
- break;
- }
- else if(/[\(\)]+/g.test(i)){ // фича: группировка окончаний
- i.replace(/\(([^\(\)]+)\)([^\(\)]+)?/g, function(a, b, c){
- splitted = b.split("/");
- for(var o = 0; o < splitted.length; o++){
- groupped = splitted[o] + c;
- strPub[groupped] = strPub[i];
- if(str.slice(-groupped.length) == groupped){
- for(var x = 0, eachSplited = strPub[groupped];x < eachSplited.length; x++){
- eachSplited[x] = eachSplited[x].replace("%", splitted[o]);
- }
- reformedStr = str.slice(0, -groupped.length);
- forPseudo = groupped;
- }
- }
- })
- }
- else{ // дефолт
- lastIndex = str.slice(-1);
- reformedStr = str.slice(0, -(forPseudo || lastIndex).length);
- }
- if(/\//.test(i) && !(/[\(\)]+/g.test(i)) && new RegExp(lastIndex).test(i))forLong = i; // группированные окончания, разделающиеся слешем
- for(var o in exs){ // поиск исключений
- if(str.slice(-o.length) == o)reformedStr = str.slice(0, -exs[o]);
- }
- }
- return reformedStr + strPub[(forPseudo || forLong || lastIndex)][cases[choice]].replace("%", lastIndex)
- }
* This source code was highlighted with Source Code Highlighter.В объекте с правилами (strPub) знак процента (%) принимает 2 вида, в зависимости от:
- если в правиле имеются скобки, то знак (%) будет равен их содержимому;
- иначе будет равен всем символам правил
Конечно, идея и реализация далеко не идеальны, поэтому буду рад вашим фидбэкам.
Попробовать (демо).
Всех с наступившими новогодними праздниками!