Как стать автором
Обновить

Автоматический перевод слова в нужный падеж

// Приветствую, хабрамены!



Введение


Тебе приходилось, %username%, видеть на многих проектах надписи:

  • «Подружиться с Дмитрий»
  • «Опубликован Артем» (подразумевалось кем-то)
  • Etc.

Или как, например, сделал ВКонтакте:

  • «Подружиться с Андреем»
  • «Отправить Ярославу подарок»

Сегодня я расскажу, как сделать такую штуку, как «автоматический перевод слова в нужный падеж».


Из курса начальной школы мы все знаем, что существует 6 падежей:

  • Именительный (Кто? Что?)
  • Родительный (Кого? Чего?)
  • Дательный (Кому? Чему?)
  • Винительный (Кого? Что?)
  • Творительный (Кем? Чем?)
  • Предложный (О ком? О чём?)

Все мы понимаем, что без «пинка» в нужное место, слова не будут переводиться в нужный падеж, для этого я и реализовал функцию toCase():



toCase( String str, String case );

Например:

  1. var words={"Мама":"р", "Хабр":"д", "Поросёнок":"в", "Ночь":"т", "Отец":"п"}, result="";
  2. for(var i in words){
  3.     result+=words[i].toUpperCase()+".: "+toCase(i,words[i])+"\n"
  4. }
  5. return result
* This source code was highlighted with Source Code Highlighter.

Выдаст следующее:



Р.: Мамы
Д.: Хабру
В.: Поросёнка
Т.: Ночью
П.: Отце

Развитие событий


Предлагаю сами внутренности функции:

  1. function toCase(str, choice) {
  2.     var strPub = { // правила для окончаний
  3.         "а": ["ы", "е", "у", "ой", "е"],
  4.         "(ш/ж/к/ч)а": ["%и", "%е", "%у", "%ой", "%е"],
  5.         "б/в/м/г/д/л/ж/з/к/н/п/т/ф/ч/ц/щ/р/х": ["%а", "%у", "%а", "%ом", "%е"],
  6.         "и": ["ей", "ям", "%", "ями", "ях"],
  7.         "ый": ["ого", "ому", "%", "ым", "ом"],
  8.         "й": ["я", "ю", "я", "ем", "е"],
  9.         "о": ["а", "у", "%", "ом", "е"],
  10.         "с/ш": ["%а", "%у", "%", "%ом", "%е"],
  11.         "ы": ["ов", "ам", "%", "ами", "ах"],
  12.         "ь": ["я", "ю", "я", "ем", "е"],
  13.         "уль": ["ули", "уле", "улю", "улей", "уле"],
  14.         "(ч/ш/д/т)ь": ["%и", "%и", "%ь", "%ью", "%и"],
  15.         "я": ["и", "е", "ю", "ей", "е"]
  16.     },
  17.     cases = { // номера для падежей, не считая Именительный
  18.         "р": 0,
  19.         "д": 1,
  20.         "в": 2,
  21.         "т": 3,
  22.         "п": 4
  23.     },
  24.     exs = { // исключения, сколько символов забирать с конца
  25.         "ц": 2,
  26.         "ок": 2
  27.     },
  28.     lastIndex,reformedStr,forLong,splitted,groupped,forPseudo;
  29.     for(var i in strPub){
  30.         if(i.length > 1 && str.slice(-i.length) == i){ // для окончаний, длиной >1
  31.             lastIndex = i;
  32.             reformedStr = str.slice(0, -lastIndex.length);
  33.             break;
  34.         }
  35.         else if(/[\(\)]+/g.test(i)){ // фича: группировка окончаний
  36.             i.replace(/\(([^\(\)]+)\)([^\(\)]+)?/g, function(a, b, c){
  37.                 splitted = b.split("/");
  38.                 for(var o = 0; o < splitted.length; o++){
  39.                     groupped = splitted[o] + c;
  40.                     strPub[groupped] = strPub[i];
  41.                     if(str.slice(-groupped.length) == groupped){
  42.                         for(var x = 0, eachSplited = strPub[groupped];x < eachSplited.length; x++){
  43.                             eachSplited[x] = eachSplited[x].replace("%", splitted[o]);
  44.                         }
  45.                         reformedStr = str.slice(0, -groupped.length);
  46.                         forPseudo = groupped;
  47.                     }
  48.                 }
  49.             })
  50.         }
  51.         else{ // дефолт
  52.             lastIndex = str.slice(-1);
  53.             reformedStr = str.slice(0, -(forPseudo || lastIndex).length);
  54.         }
  55.         if(/\//.test(i) && !(/[\(\)]+/g.test(i)) && new RegExp(lastIndex).test(i))forLong = i; // группированные окончания, разделающиеся слешем
  56.         for(var o in exs){ // поиск исключений
  57.             if(str.slice(-o.length) == o)reformedStr = str.slice(0, -exs[o]);
  58.         }
  59.     }
  60.     return reformedStr + strPub[(forPseudo || forLong || lastIndex)][cases[choice]].replace("%", lastIndex)
  61. }
* This source code was highlighted with Source Code Highlighter.

В объекте с правилами (strPub) знак процента (%) принимает 2 вида, в зависимости от:

  • если в правиле имеются скобки, то знак (%) будет равен их содержимому;
  • иначе будет равен всем символам правил

Конечно, идея и реализация далеко не идеальны, поэтому буду рад вашим фидбэкам.


Попробовать (демо).


Всех с наступившими новогодними праздниками!

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.