Encapsuler l'API Deepl dans une source de données


1Présentation

Dans le précédent tutoriel, nous avons vu comment créer une source de données qui encapsule l'API du service OpenWeatherMap. Maintenant, nous allons créer une autre source de données, qui donne accès à l'API du service de traduction Deepl. Cette source de données pourra être utilisée pour traduire facilement des textes.


2Configuration

Dans le fichier de configuration etc/temma.php, on va ajouter la définition d'une source de données un peu spéciale. Comme il ne s'agit pas d'une source de données gérée nativement par Temma, on va préfixer le DSN du nom de l'objet à utiliser (entre crochets).

Le DSN sera de la forme deepl://API_KEY@DEFAULT_LANG
API_KEY étant la clé d'API fournie par OpenWeatherMap, et DEFAULT_LANG étant la langue par défaut dans laquelle seront traduits les textes.

Exemple de configuration :

<?php

return [
    "application" => [
        "dataSources" => [
            "trad" => '[\Deepl\Translate]deepl://0123456789abcdef0123456789abcdef@EN'
        ]
    ]
];

3Code source

Voici le code de notre source de données, que l'on enregistrera dans le fichier lib/Deepl/Translate.php :

<?php

namespace Deepl;

class Translate extends \Temma\Base\Datasource {
    /** Constante : préfixe du DSN. */
    const DSN_SCHEME = 'deepl://';
    /** Constante : URL de l'API Deepl. */
    const API_URL = 'https://api-free.deepl.com/v2/translate';
    /** Clé d'accès à l'API. */
    private string $_key;
    /** Langue de traduction par défaut. */
    private string $_lang;

    /**
     * Factory. Crée une instance de l'objet à partir d'un DSN.
     * @param   string   $dsn   Chaîne de configuration.
     * @return  \Deepl\Translate    L'objet instancié.
     * @throws  \Exception   Si le DSN est incorrect.
     */
    static public function factory(string $dsn) : \Deepl\Translate {
        // interprétation du DSN
        $parts = parse_url($dsn);
        $scheme = $parts['scheme'] ?? null;
        $key = $parts['user'] ?? null;
        $lang = $parts['host'] ?? null;
        // vérifications
        if ($scheme != self::DSN_SCHEME || !$key || !$lang) {
            throw new \Exception("Invalid Deepl DSN '$dsn'.");
        }
        return (new self($key, $lang));
    }
    /**
     * Constructeur.
     * @param   string   $key    Clé d'API.
     * @param   string   $lang   Langue de traduction par défaut.
     */
    public function __construct(string $key, string $lang) {
        $this->_key = $key;
        $this->_lang = $lang;
    }
    /**
     * Traduction d'un texte vers la langue par défaut.
     * @param   string   $text     Texte à traduire.
     * @param   mixed    $default  Paramètre inutilisé (nécessaire pour des raisons d'héritage).
     * @param   mixed    $options  Paramètre inutilisé (nécessaire pour des raisons d'héritage).
     * @return  mixed    La traduction.
     * @throws  \Exceptions      Si une erreur est survenue.
     */
    public function get(string $text, mixed $default=null, mixed $options=null) : mixed {
        $this->translate($text, $this->_lang);
    }
    /**
     * Traduit un texte dans une langue spécifique.
     * @param   string   $text         Texte à traduire.
     * @param   string   $lang         Langue de la traduction.
     * @param   ?string  $sourceLang   (optionnel) Langue du texte à traduire.
     *                                 Par défaut, Deepl la détecte automatiquement.
     * @return  string   La traduction du texte, ou une chaîne vide si le texte n'a pas pu être traduit.
     * @throws  \Exception       Si une erreur est survenue.
    */
    public function translate(string $text, string $lang, ?string $sourceLang=null) : string {
        // constitution des données à envoyer
        $data = [
            'text'        => [$text],
            'target_lang' => $lang,
        ];
        if ($sourceLang) {
            $data['source_lang'] = $sourceLang;
        }
        $data = json_encode($data);

        // appel de l'API
        $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);

        // récupération du résultat
        $result = json_decode($result, true);
        return ($result['translations'][0]['text'] ?? '');
    }
}
  • Lignes 21 à 32 : Méthode factory(), appelée par Temma lors de la création de la source de données. Cette méthode interprète le DSN et appelle le constructeur.
  • Lignes 38 à 41 : Constructeur, utilisable pour créer une instance de l'objet directement.
  • Lignes 50 à 52 : Méthode get(), qui surcharge celle de l'objet parent \Temma\Base\Datasource, et qui appelle la méthode translate() de l'objet. Cette méthode est utilisable directement, mais elle est aussi appelée automatiquement lors d'une lecture de type tableau.
  • Lignes 62 à 93 : Méthode translate(), qui se connecte à l'API Deepl pour effectuer une traduction. Cette méthode prend en paramètre le texte à traduire et la langue de traduction. Elle peut aussi prendre la langue source, dans le cas où on souhaite traduire un texte qui est dans une langue différente de la langue par défaut définie dans la configuration.
  • Lignes 64 à 71 : Constitution du tableau de données qui va être envoyé à l'API Deepl, après avoir été sérialisé en JSON.
  • Lignes 74 à 88 : Connexion à l'API Deepl, avec envoi des données et récupération du retour.
  • Lignes 91 et 92 : Désérialisation des données récupérées et retour de la méthode.

4Utilisation

Dans un contrôleur, la source de données qui a été nommée "trad" dans le fichier de configuration est disponible idans les contrôleurs en écrivant $this->trad. La traduction de textes vers la langue par défaut définie en configuration peut se faire en appelant la méthode get() ou avec l'écriture de type tableau :

// utilisation de la méthode get()
$texteAnglais = $this->trad->get('Bonjour, comment allez-vous ?');
// écriture de type tableau
$texteAnglais = $this->trad['Bonjour, comment allez-vous ?'];

Pour définir une langue de traduction différente, ou pour définir la langue source, il faut utiliser la méthode translate() :

$texteAllemand = $this->trad->translate('Hi, how are you?', 'DE', 'EN');

En dehors des contrôleurs, il faut passer par le composant d'injection de dépendances :

$texteAnglais = $this->_loader->dataSources->trad->get('Bonjour, comment allez-vous ?');
$texteAnglais = $this->_loader->dataSources->trad['Bonjour, comment allez-vous ?'];
$texteAllemand = $this->_loader->dataSources->trad->translate('Hi, how are you?', 'DE', 'EN');