Есть слово в именительном падеже, единственном числе. Нужно перевести его в другой падеж и/или число. Для PHP.
Например:
Нашел phpMorphy. В целом работает. Но не справляется с Феодосией (считает, что это мужчина по имени Феодосий). Проект не развивается, непонятно, как обновить словари.
Что еще можно использовать для этой задачи?
Решения на php я не нашел.
Зато нашел следующие альтернативы:
В итоге я написал консольный скрипт на JS, который переводит слово в нужную форму. Скрипт вызываю из PHP через exec(), работает долго, поэтому результаты кеширую.
Для достижения хорошего результата скрипту также приходится указывать исходную форму слова, иначе возможно, что вместо существительного "пень" будет возвращен глагол "пенить".
Вот пример работы скрипта:
$ node ./az.js красный nomn plur Олег nomn gent Феодосия nomn loct Кижи nomn loct
красные
олега
феодосии
кижах
Вот сам скрипт (извините, на JS пишу как умею):
"use strict";
var tasks = [];
// Принимаем аргументы в формате:
//
// СЛОВО ИСХОДНАЯ_ФОРМА ТРЕБУЕМАЯ_ФОРМА [ СЛОВО ИСХОДНАЯ_ФОРМА ТРЕБУЕМАЯ_ФОРМА ... ]
//
// Для каждого запрошенного слова выводит результат в виде нужной формы или пустой строки,
// если преобразовать не получилось.
// Исходная/требуемая форма задается в виде набора граммем через запятую.
// Граммемы: http://opencorpora.org/dict.php?act=gram
//
// Пример:
// node ./az.js пень NOUN,nomn datv,plur Феодосия Geox loct
//
// Переводит слово "пень", которое является существительным (NOUN) (а не глаголом "пенить")
// в именительном падеже (nomn) в дательный падеж (datv) мн. число (plur) --> "пням".
// А также переводит "Феодосия" (город Geox, а не мужское имя Феодосий) к предложному
// падежу --> "Феодосии"
//
// Выводит результат на 2 строчках:
//
// пням
// феодосии
//
// Разбираем аргументы
process.argv.forEach(function (val, index, array) {
// первые два аргумента - node и script
if (index < 2) return;
// номер блока из 3 аргументов (слово, исходная, требуемая) (zero-based)
var block = Math.floor((index + 1) / 3 - 1);
// номер аргумента в блоке (zero-based)
var arg = (index + 1) % 3;
if (typeof tasks[block] === "undefined") {
tasks[block] = { word: null, form: null, inForm: null };
}
if (arg == 0) {
// слово
tasks[block].word = val;
} else if (arg == 1) {
// исходная форма
tasks[block].inForm = val.split(",");
} else {
// требуемая форма
tasks[block].form = val.split(",");
}
});
var Az = require('az');
Az.Morph.init(function() {
// выводим результат построчно
solveTasks(tasks).every(elem => console.log(elem) || true);
function solveTasks(tasks) {
var result = [];
tasks.forEach(function (val, index, array) {
// морфологический разбор
var parses = Az.Morph(val.word, { stutter: false});
// ищем в вариантах слово в нужной форме
var word = null;
for (var i in parses) {
// console.log(parses[i].word);
// console.log(parses[i].tag);
// Если tag содержит все граммемы, которые присутствовали в inForm,
// значит это наше слово.
if (arrayContainsArray(Object.keys(parses[i].tag), val.inForm)) {
// нашли
word = parses[i];
break;
}
}
if (!word) {
// не нашли
result.push("");
// result.push(val.word);
} else {
// нашли
result.push(word.inflect(val.form).word);
}
});
return result;
}
function arrayContainsArray(bigger, smaller) {
// возвращает true, если все элементы массива smaller присутствуют в bigger
// https://stackoverflow.com/questions/15514907/determining-whether-one-array-contains-the-contents-of-another-array-in-javascri
return smaller.every(elem => bigger.indexOf(elem) > -1);
}
});
Продвижение своими сайтами как стратегия роста и независимости