Encapsulating the Deepl API in a data source
1Introduction
In the previous tutorial, we saw how to create a data source that encapsulates the OpenWeatherMap service API. Now we're going to create another data source, one that provides access to the Deepl translation service API, which can be used to easily translate texts.
2Configuration
In the etc/temma.php configuration file, we're going to add the definition of a rather special data source. As this is not a data source natively managed by Temma, we'll prefix the DSN with the name of the object to be used (in square brackets).
The DSN will take the form deepl://API_KEY@DEFAULT_LANG
API_KEY being the API key provided by OpenWeatherMap, and DEFAULT_LANG being the
default language into which texts will be translated.
Example configuration:
<?php
return [
"application" => [
"dataSources" => [
"transl" => '[\Deepl\Translate]deepl://0123456789abcdef0123456789abcdef@EN'
]
]
];
3Source code
Here's the code for our data source, which we'll save in the lib/Deepl/Translate.php file:
<?php
namespace Deepl;
class Translate extends \Temma\Base\Datasource {
/** Constant: DSN prefix. */
const DSN_SCHEME = 'deepl://';
/** Constant: URL of Deepl API. */
const API_URL = 'https://api-free.deepl.com/v2/translate';
/** API access key. */
private string $_key;
/** Default translation language. */
private string $_lang;
/**
* Factory. Creates an instance of the object from a DSN.
* @param string $dsn Configuration string.
* @return \Deepl\Translate The instantiated object.
* @throws \Exception If the DSN is incorrect.
*/
static public function factory(string $dsn) : \Deepl\Translate {
// DSN interpretation
$parts = parse_url($dsn);
$scheme = $parts['scheme'] ?? null;
$key = $parts['user'] ?? null;
$lang = $parts['host'] ?? null;
// checks
if ($scheme != self::DSN_SCHEME || !$key || !$lang) {
throw new \Exception("Invalid Deepl DSN '$dsn'.");
}
return (new self($key, $lang));
}
/**
* Constructor.
* @param string $key API access key.
* @param string $lang Default translation language.
*/
public function __construct(string $key, string $lang) {
$this->_key = $key;
$this->_lang = $lang;
}
/**
* Translation of a text into the default language.
* @param string $text Text to be translated.
* @param mixed $default Unused parameter (required for inheritance reasons).
* @param mixed $options Unused parameter (required for inheritance reasons).
* @return mixed The translated text.
* @throws \Exceptions If an error has occurred.
*/
public function get(string $text, mixed $default=null, mixed $options=null) : mixed {
$this->translate($text, $this->_lang);
}
/**
* Translates a text into a specific language.
* @param string $text Text to be translated
* @param string $lang Translation language.
* @param ?string $sourceLang (optionnel) Language of text to be translated.
* Deepl automatically detects it by default.
* @return string The translation of the text, or an empty string if the text could not be translated.
* @throws \Exception If an error has occurred.
*/
public function translate(string $text, string $lang, ?string $sourceLang=null) : string {
// setting up data to be sent
$data = [
'text' => [$text],
'target_lang' => $lang,
];
if ($sourceLang) {
$data['source_lang'] = $sourceLang;
}
$data = json_encode($data);
// API call
$curl = curl_init(self::API_URL);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 3);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
'Authorization: DeepL-Auth-Key ' . $this->_key,
'Content-Type: application/json',
]);
$result = curl_exec($curl);
if ($result === false)
throw new \Exception(curl_error($curl));
curl_close($curl);
// result retrieval
$result = json_decode($result, true);
return ($result['translations'][0]['text'] ?? '');
}
}
- Lines 21 to 32: factory() method, called by Temma when the data source is created. This method interprets the DSN and calls the constructor.
- Lines 38 to 41: Constructor, used to create an instance of the object directly.
- Lines 50 to 52: get() method, which overrides that of the parent \Temma\Base\Datasource object, and calls the object's translate() method. This method can be used directly, but is also called automatically when the object is read as an array.
- Lines 62 to 93: translate() method, which connects to the Deepl API to perform a translation. This method takes the text to be translated and the translation language as parameters. It can also take the source language, if you wish to translate a text in a language other than the default language defined in the configuration.
- Lines 64 to 71: Creation of the data table to be sent to the Deepl API, after being serialized in JSON.
- Lines 74 to 88: Connection to the Deepl API, sending the data and retrieving the return.
- Lines 91 and 92: Unserialize recovered data and return method.
4Usage
In a controller, the data source named "transl" in the configuration file is
available in controllers by writing $this->trad
. Text can be translated
to the default language defined in configuration by calling the get() method or by
using it as an array:
// using the get() method
$englishText = $this->transl->get('Bonjour, comment allez-vous ?');
// write as an array
$englishText = $this->transl['Bonjour, comment allez-vous ?'];
To define a different translation language, or to define the source language, use the translate() method:
$germanText = $this->transl->translate('Hi, how are you?', 'DE', 'EN');
Apart from controllers, you must use the dependency injection component:
$englishText = $this->_loader->dataSources->transl->get('Bonjour, comment allez-vous ?');
$englishText = $this->_loader->dataSources->transl['Bonjour, comment allez-vous ?'];
$germanText = $this->_loader->dataSources->transl->translate('Hi, how are you?', 'DE', 'EN');