Skip to content

Add third party translation (WIP) #125

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ class Module extends \yii\base\Module
*/
public $scanners = [];

/**
* @var array Configuration for a third-party translator (e.g.: Google, Yandex, etc.) that can be used for
* text translation.
*
* The translator should implement the `lajax\translatemanager\translation\Translator` interface.
*/
public $translator;

/**
* @inheritdoc
*/
Expand Down
16 changes: 16 additions & 0 deletions assets/javascripts/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,27 @@ var translate = (function () {
_translateLanguage($this);
}

/**
* @param {$} $btn
*/
function _translateText($btn) {
var $translation = $btn.closest('tr').find('.translation');
console.log($translation);

helpers.post('/translatemanager/language/translate-text', {
id: $translation.data('id'),
language_id: $('#language_id').val()
});
}

return {
init: function () {
$('#translates').on('click', '.source', function () {
_copySourceToTranslation($(this));
});
$('#translates').on('click', '.js-translate-text', function () {
_translateText($(this));
});
$('#translates').on('click', 'button', function () {
_translateLanguage($(this));
});
Expand Down
7 changes: 5 additions & 2 deletions controllers/LanguageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ public function behaviors()
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['list', 'change-status', 'optimizer', 'scan', 'translate', 'save', 'dialog', 'message', 'view', 'create', 'update', 'delete', 'delete-source', 'import', 'export'],
'only' => ['list', 'change-status', 'optimizer', 'scan', 'translate', 'translate-text', 'save', 'dialog', 'message', 'view', 'create', 'update', 'delete', 'delete-source', 'import', 'export'],
'rules' => [
[
'allow' => true,
'actions' => ['list', 'change-status', 'optimizer', 'scan', 'translate', 'save', 'dialog', 'message', 'view', 'create', 'update', 'delete', 'delete-source', 'import', 'export'],
'actions' => ['list', 'change-status', 'optimizer', 'scan', 'translate', 'translate-text', 'save', 'dialog', 'message', 'view', 'create', 'update', 'delete', 'delete-source', 'import', 'export'],
'roles' => $this->module->roles,
],
],
Expand Down Expand Up @@ -71,6 +71,9 @@ public function actions()
'translate' => [
'class' => 'lajax\translatemanager\controllers\actions\TranslateAction',
],
'translate-text' => [
'class' => 'lajax\translatemanager\controllers\actions\TranslateTextAction',
],
'save' => [
'class' => 'lajax\translatemanager\controllers\actions\SaveAction',
],
Expand Down
1 change: 1 addition & 0 deletions controllers/actions/TranslateAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function run()
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'searchEmptyCommand' => $this->controller->module->searchEmptyCommand,
'isTranslationApiAvailable' => !empty($this->controller->module->translator),
'language_id' => Yii::$app->request->get('language_id', ''),
]);
}
Expand Down
77 changes: 77 additions & 0 deletions controllers/actions/TranslateTextAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace lajax\translatemanager\controllers\actions;

use Yii;
use yii\web\Response;
use yii\base\Exception as BaseException;
use lajax\translatemanager\services\Generator;
use lajax\translatemanager\models\LanguageTranslate;
use lajax\translatemanager\models\LanguageSource;
use lajax\translatemanager\translation\Translator;
use lajax\translatemanager\translation\Exception as TranslationException;

/**
* Text translation with third party service.
*
* @author moltam
*/
class TranslateTextAction extends \yii\base\Action
{
/**
* @return array
*/
public function run()
{
Yii::$app->response->format = Response::FORMAT_JSON;

$id = Yii::$app->request->post('id', 0);
$languageId = Yii::$app->request->post('language_id', Yii::$app->language);

$languageTranslate = LanguageTranslate::findOne(['id' => $id, 'language' => $languageId]) ?:
new LanguageTranslate(['id' => $id, 'language' => $languageId]);

try {
$languageTranslate->translation = $this->translateText($id, $languageId);
} catch (BaseException $e) {
Yii::error('Translation failed! ' . $e->getMessage(), 'translatemanager');
$languageTranslate->addError('translation', 'API translation failed!');
}

if ($languageTranslate->validate(null, false) && $languageTranslate->save()) {
$generator = new Generator($this->controller->module, $languageId);
$generator->run();
}

return $languageTranslate->getErrors();
}

/**
* @param int $sourceId
* @param string $languageId
*
* @return string
*
* @throws BaseException
*/
protected function translateText($sourceId, $languageId)
{
if (!$this->controller->module->translator) {
throw new BaseException('No translator configured!');
}

$source = LanguageSource::findOne($sourceId);
if (!$source) {
throw new BaseException('Invalid language source id!');
}

/* @var $translator Translator */
$translator = Yii::createObject($this->controller->module->translator);

try {
return $translator->translate($source->message, $languageId);
} catch (TranslationException $e) {
throw new BaseException('Translation failed: ' . $e->getMessage(), 1, $e);
}
}
}
28 changes: 28 additions & 0 deletions translation/BaseTranslator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace lajax\translatemanager\translation;

use Yii;
use lajax\translatemanager\translation\client\TranslatorApiClient;

/**
* Base class for translators.
*
* @author moltam
*/
abstract class BaseTranslator extends \yii\base\Object implements Translator
{
/**
* @var array|TranslatorApiClient The client used for communication.
*/
public $apiClient = [
'class' => 'lajax\translatemanager\translation\client\CurlApiClient',
];

public function init()
{
parent::init();

$this->apiClient = Yii::createObject($this->apiClient);
}
}
12 changes: 12 additions & 0 deletions translation/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace lajax\translatemanager\translation;

/**
* Base exception for translation related errors.
*
* @author moltam
*/
class Exception extends \yii\base\Exception
{
}
111 changes: 111 additions & 0 deletions translation/GoogleTranslator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

namespace lajax\translatemanager\translation;

use Yii;
use yii\helpers\Json;

/**
* Translator using the Google Cloud Translation API.
*
* More information on this service: https://cloud.google.com/translate/
*
* Required configuration:
*
* ```php
* [
* 'class' => 'lajax\translatemanager\translation\GoogleTranslator',
* 'apiKey' => 'YOUR_API_KEY',
* ]
* ```
*
* @author moltam
*/
class GoogleTranslator extends BaseTranslator
{
/**
* @var string The API key for authentication.
*/
public $apiKey;

/**
* @var string
*/
private static $baseApiUrl = 'https://translation.googleapis.com';

/**
* @inheritdoc
*/
public function translate($text, $target, $source = null, $format = 'html')
{
$response = $this->apiClient->send($this->buildApiUrl('/language/translate/v2'), 'POST', [
'key' => $this->apiKey,
'q' => $text,
'target' => substr($target, 0, 2),
'source' => substr($source, 0, 2),
'format' => $format,
]);

$decodesResponse = $this->decodeResponse($response);
$this->checkResponse($decodesResponse);

return $decodesResponse['data']['translations'][0]['translatedText'];
}

/**
* @inheritdoc
*/
public function detect($text)
{
}

/**
* @inheritdoc
*/
public function getLanguages()
{
}

/**
* @param string $uri
* @param array $queryParams [optional]
*
* @return string
*/
private function buildApiUrl($uri, array $queryParams = [])
{
return self::$baseApiUrl . $uri . '?' . http_build_query($queryParams);
}

/**
* @param string $response
*
* @return array
*
* @throws Exception
*/
private function decodeResponse($response)
{
$decodesResponse = Json::decode($response);
if (!$decodesResponse) {
throw new Exception('Invalid API response: json decode failed!');
}

return $decodesResponse;
}

/**
* @param array $response
*
* @throws Exception
*/
private function checkResponse($response)
{
if (isset($response['error'])) {
$error = $response['error'];
Yii::error('API response: ' . var_export($response, true), 'translatemanager');

throw new Exception("API error: $error[message]", $error['code']);
}
}
}
48 changes: 48 additions & 0 deletions translation/Translator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace lajax\translatemanager\translation;

/**
* A translator that can translate arbitrary text.
*
* @author moltam
*/
interface Translator
{
/**
* Translates the given text to the specified language.
*
* @param string $text The text to translate.
* @param string $target The language code for translation.
* @param string $source [optional]
* <p>The language code of the source text. If not given, the translator tries to detect the language.</p>
* @param string $format [optional]
* <p>The format of the source text. Possible values:
* - html: string with HTML markup,
* - text: plain text without markup.
* </p>
*
* @return string The translation.
*
* @throws Exception If the text cannot be translated, or an error occurred during translation.
*/
public function translate($text, $target, $source = null, $format = 'html');

/**
* Detects the language of the specified text.
*
* @param string $text The analyzed text.
*
* @return string The language code for translation.
*
* @throws Exception If the language cannot be detected, or an error occurred during detection.
*/
public function detect($text);

/**
* Returns the languages supported by the translator.
*
* @return string[] A list of language codes.
*/
public function getLanguages();
}
14 changes: 14 additions & 0 deletions translation/client/ClientException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace lajax\translatemanager\translation\client;

use lajax\translatemanager\translation\Exception;

/**
* Exception for API communication errors.
*
* @author moltam
*/
class ClientException extends Exception
{
}
Loading