Yandex Translate API: PHP и небольшое исследование сервиса

После закрытия Гуглом своих API для перевода проблема поиска онлайн-сервис для машинного перевода стала особенно актуальной.
В Интернете много сервисов перевода с громкими именами: Промт, Прагма и пр. Нет никакой проблемы в PHP смоделировать обращения к страницам сервисов и получить результаты перевода. Но есть проблема: почти все сервисы в ответ на простой GET или POST запрос отдают не результат перевода, а целиком страницу во всей красе, начиная с DTD. Как говорят у нас на Украине, “дурных нэма”.
После анализа было выяснено, что есть только два сервиса, которые отдают в ответ на запрос только результат перевода: Яндекс и Bing от Microsoft.

Забегая значительно вперед, укажем области применения и особенности:

Яндекс проще в использовании, прекрасно переводит с русского и на русский, но тут же и недостаток: Яндекс переводит только с русского или только на русский. Перевести Яндексом с украинского на английский в одну операцию невозможно.

Bing этим не страдает, но зато:
— переводы, в которых участвует русский или украинский, страдают сильным “акцентом” и обязательно требуют правки
— использование Bing в режиме free имеет некоторые ограничения
— для использования Bing требуется некий идентификатор веб-приложения — appID, само по себе получение которого не связано с юридическими трудностями — это фактически просто регистрация, но которая представляет собой увлекательный и длинный квест.


Итак, какие задачи должна решать библиотека/класс для перевода?

1. Получение языков, с которых и на которые можно переводить, и их допустимых комбинаций
2. Собственно сам перевод текста

Сразу ремарка. Из соображений здравого смысла ясно, что в один заход перевести “Войну и мир” не получится. Приземление на технический уровень дает более четкое ограничение: переводчик Янекса использует GET-запросы, соотв. — очень грубо — примерно 2000 символов за один раз, не более. Это совсем немного, примерно 2 небольших абзаца текста, даже небольшая публикация на сайте выйдет за эти рамки.
Отсюда следующая задача:

3. Перевод больших фрагментов текста.

Ну и представим задачу: мультиязычный сайт. Гонять каждый раз переводчик за переводом элементов интерфейса и прочих текстов на сайте — это, мягко говоря, неразумно. Соответственно задача:

4. Кеширование.

Кеширование нужно еще для одной цели: переводчик от Яндекса хорош, но не идеален, особенно с учетом богатства русского языка. Зачастую хотелось бы поправить результат перевода, а для этого нужно его где-то хранить.

Итак, Яндекс.Переводчик
Исходники доступны в репозитории Гугла и задокументированы на русском языке.

1) Языки перевода.
Класс Yandex_Translate содержит три метода с говорящими названиями:
yandexGetLangsPairs() — получение доступных пар языков FROM->TO
yandexGet_FROM_Langs()
yandexGet_TO_Langs()

Пример (этот пример — полный, ниже подключение файлов, создание экземпляра класса, элементы форматирования вывода и пр. буду опущены.)

<?php
include_once 'Yandex_Translate.php';
$pairs = $translator->yandexGetLangsPairs();
print_r($pairs);


Получим вот такие комбинации (они, кстати, меняются время от времени):
[0] => en-ru
[1] => ru-en
[2] => ru-uk
[3] => uk-ru
[4] => pl-ru
[5] => ru-pl
[6] => tr-ru
[7] => ru-tr
[8] => de-ru
[9] => ru-de
[10] => fr-ru
[11] => ru-fr
[12] => it-ru
[13] => es-ru
[14] => ru-es

Обратите внимание, что во всех парах есть язык ru, ну об этом выше уже говорилось.

Два других метода дают языки по отдельности и могут быть использованы, например, для формирования селектов или других элементов выбора.

2. Перевод
Один метод, три аргумента: с какого, на какой и собственно переводимый текст.
Обратите внимание также на важное свойство eolSymbol — окончание строки. Если его установить неверно, не будет форматирования выходного текста (см. комментарии в исходниках).

Пример:
$text = file_get_contents('text.txt');
$translatedText = $translator->yandexTranslate('ru', 'uk', $text);
echo $translatedText;


Начало файла text.txt:
Марио Пьюзо Крестный отец
Посвящается Энтони Клири
КНИГА ПЕРВАЯ
За всяким большим состоянием кроется преступление.


Результат выполнения скрипта:
Маріо п’юзо Хрещений батько
Присвячується Ентоні Клірі
ПЕРША КНИГА
За кожним великим станом криється злочин.


Сразу обратим внимание — перевод хороший, но правка требуется.

3. Перевод больших текстов
Для перевода больших текстов служит абстрактный класс Big_Text_Translate
Принцип следующий.
Сначала текст разбивается на предложения, используя разделитель sentensesDelimiter — по умолчанию точка.
Правильнее конечно было бы использовать точку с пробелом, но в реальных, например, “каментах” пробел после точки запросто может “ацуцтвовать”. Поэтому так, проблем в реальной работе это не вызывает, но свойство можно переопределить.
Затем предложения собираются в текстовые фрагменты, размер которых не превышает заданного значения symbolLimit — по умолчанию 2000.
Текстовые фрагменты готовы для перевода, семантика и форматирование — сохранены. Формированием фрагментов занимается статический метод toBigPieces, на выходе — массив.
Метод fromBigPieces склеивает переведенные фрагменты обратно в цельный текст.
Пример

$bigText = file_get_contents('text_big.txt');
$textArray = Big_Text_Translate::toBigPieces($bigText);

$numberOfTextItems = count($textArray);

foreach ($textArray as $key=>$textItem){

//Показываем прогресс перевода
echo 'Переведен фрагмент '.$key.' из '.$numberOfTextItems;
flush();

$translatedItem = $translator->yandexTranslate('ru', 'uk', $textItem);
$translatedArray[$key] = $translatedItem;
}

$translatedBigText = Big_Text_Translate::fromBigPieces($translatedArray);

echo $translatedBigText;

Выполнение примера пробуйте сами — все есть в репозитории.

Уважаемые хабражители! Если материал представляет интерес, то готовится его продолжение, включающее разделы:
— кеширование результатов перевода в несколько уровней
— работа с сервисом Bing
— полноценное демо: построение мультиязычного сайта.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 20

    +4
    Класс Yandex_Translate содержит три метода с говорящими названиями:
    yandexGetLangsPairs() — получение доступных пар языков FROM->TO
    yandexGet_FROM_Langs()
    yandexGet_TO_Langs()

    навеяло.
    $Yandex_Translator->yandexGetYandexLangsYandexPairsYandex('Yandex');
      –1
      $Yandex_Translator->yandex_Get_Yandex_Langs_Yandex_Pairs_Yandex('Yandex');
        0
        Поясните, пожалуйста, вы ярый противник camel case?
        Или от такого подхода Yandex станет меньше в названии метода? :)
          +1
          Я ярый противник использования и CamelCase, и подчёркивания для разделения слов в именах. Что-нибудь одно.
            0
            ИМХО codestyle тут решит. Согласитесь, же, что если в корпоративном стиле для классов у нас '_', все слова в переменных разделяются восьмеркой, а функции начинаются с заглавной буквы — все вроде бы нормально :D

            Но это уже наболевшее, спасибо за ответ, просмотрел, что у автора классы так называются.
        +2
        Ехал Яндекс через Яндекс…
        +3
        Я русский бы выучил только за то, что им разговаривал Яндекс.
          –1
          а что делать если сайт написано не на php как исходники яндекса?
          +2
          А чем не понравился красивый json, который отдаёт translate.ru? Да, он отдаёт не только результат, но распарсить json, наверное, не проблема.
            0
            наверное, не проблема


            Почему так неуверенно? json_encode($data) -> получаем нативный массив
            0
            Спасибо, отличный класс и давно хотел сравнить переводы с Bing
              0
              в исходном коде смутила проверка на возможность перевода, например не проверяется возможность перевода с русского на итальянский (ru-it), которой нет в возвращаемом json. поэтому не лучше ли было заменить эту конструкцию:

              public function yandexTranslate($fromLang, $toLang, $text) {
                      //один из языков должен быть ru - проверяем, хотя переводчик и так вернет текст - сообщение об ошибке
              
                      if ($fromLang != 'ru' AND $toLang != 'ru'){
                          return 'Sorry, translation directly from '.$fromLang.' to '.$toLang.' is impossible';
                      }


              следующей:

              public function yandexTranslate($fromLang, $toLang, $text) {
                      $langPair = $fromLang."-".$toLang;
              
                      // проверяем возможность перевода с $fromLang на $toLang
                      if (!in_array($langPair, $this->yandexGetLangsPairs())){
                          return 'Sorry, translation directly from '.$fromLang.' to '.$toLang.' is impossible';
                      }
                0
                Эта конструкция служит только для того, чтобы не дергать переводчик вследствие заведомо некорректных запросов.
                Можно ее вообще в принципе убрать — в случае некорректной пары языков переводчик просто вернет текст с сообщением о невозможности ее обработать.
                  0
                  понятно. просто сама идея проверки не понравилась: наличие в langPair русского языка, (а вдруг впоследствии яндекс научится переводить не только с ru / на ru, или разучится как в примере с итальянским). в любом случае спасибо за статью.

                  кстати, а может имеет смысл поместить полученние массива langPairs в конструктор класса, и впоследствие уже сверять с имеющимся значением, или тоже лишняя нагрузка на переводчик?
                0
                Всё равно ни яндекс, ни бинг не альтернатива гуглу — набор языков совершенно неудовлетворительный…
                  0
                  Яндекс переводит с русского на русский. Долго осознавал глубину фразы!
                    0
                    Библиотека больше не работает.
                    Чтобы оживить нужно:
                    1. Получить ключ API api.yandex.ru/key/form.xml?service=trnsl
                    2. Заменить URL protected $rootURL = 'https://translate.yandex.net/api/v1.5/tr.json';
                    3. Передавать ключ с каждым запросом, обратно будет приходить JSON.
                      0
                      Да.
                      Я в самом ближайшем времени выложу на Гитхабе новую версию: для Яндекса и Гугла.
                      0
                      Константин, подскажите, пожалуйста, ссылку на GitHub. Там актуальная версия?

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое