Translate anything

This simple example will create a small form that calls an external translation service to translate the content entered on that form. The result will be displayed underneath the source text that was entered.

The form will be called "translate_anything" and can then be called in a regular SPIP template file using the tag #FORMULAIRE_TRANSLATE_ANYTHING or within an article by using <formulaire|translate_anything>.

As with most CVT forms, it operates using two files:

  • formulaires/translate_anything.html for the HTML section
  • formulaires/translate_anything.php for the PHP analysis and processing functions.

The HTML template

The template for the form will receive two data entry fields of the textarea type: the first for writing the content to be translated, and the second to display the results of the translation once the calculation has been performed. This second field is only displayed when it actually has some content.

<div class="formulaire_spip formulaire_#FORM">

[<p class="reponse_formulaire reponse_formulaire_erreur">(#ENV*{message_erreur})</p>]
[<p class="reponse_formulaire reponse_formulaire_ok">(#ENV*{message_ok})</p>]

<form action="#ENV{action}" method="post"><div>
	#ACTION_FORMULAIRE{#ENV{action}}
	<ul>
		[(#SET{erreurs,[(#ENV**{erreurs}|table_valeur{traduire})]})]
		<li class="editer_traduire obligatoire[ (#GET{erreurs}|oui)erreur]">
			<label for="traduire">Source text</label>
			[<span class='erreur_message'>(#GET{erreurs})</span>]
			<textarea name='traduire' id='champ_traduire'>#ENV{traduire}</textarea>
		</li>
		[
		[(#SET{erreurs,[(#ENV**{erreurs}|table_valeur{traduction})]})]
		<li class="editer_traduction[ (#GET{erreurs}|oui)erreur]">
			<label for="traduction">Translated text</label>
			[<span class='erreur_message'>(#GET{erreurs})</span>]
			<textarea name='traduction' id='champ_traduction'>(#ENV{traduction})</textarea>
		</li>
		]
	</ul>
	<p class="boutons"><input type="submit" class="submit" value="Translate" /></p>
</div></form>
</div>

The two fields named "traduire" and "traduction" (source and destination for the translation). The same template could be written using the "Saisies" plugin with the content between <ul> and </ul> represented as follows:

<ul>
	[(#SAISIE{textarea, traduire, obligatoire=oui, label=Traduire})]
		
	[(#ENV{traduction}|oui)
		[(#SAISIE{textarea, traduction, label=Traduction})]
	]
</ul>

Loading, verifying and processing

The "loading" of the form, declared in the formulaires/translate_anything.php file, must specify that is is adding the two "traduire" and "traduction" fields into the template’s context:

function formulaires_translate_anything_charger_dist() {
	$contexte = array(
		'traduire' => '',
		'traduction' => '',
	);
	return $contexte;
}

The "verify" function simply needs to test if there has actually been some content entered into the "traduire" field and return an error is there hasn’t:

function formulaires_translate_anything_verifier_dist() {
	$erreurs = array();
	if (!_request('traduire')) {
		$erreurs['message_erreur'] = "You have not entered any text to translate - is your keyboard broken?";
		$erreurs['traduire'] = "Normally that is how you enter text, isn't it?";
	}
	return $erreurs;
}

It is with the "process" function that things now get a little complicated. The content needs to be sent to a remote service (we use Google Translate in this example), the return data retrieved and processed, and then displayed on our form.

To do all this, the script starts by calculating the URL for the remote service based on that service’s published API. We use SPIP’s parametre_url PHP function to cleanly add the variables to the service’s URL. Thanks to another function, recuperer_page which is used to retrieve the code returned by a call to an URL, the service’s returned data is stored in the $trad variable.

The service returns the data formatted in JSON format, so it must be extricated using the json_decode function. depending on the information returned, the translation will be determined as having been successful or not. The message is adapted depending on this outcome.

// http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=hello%20world&langpair=en%7Cit
define('URL_GOOGLE_TRANSLATE', "http://ajax.googleapis.com/ajax/services/language/translate");

function formulaires_translate_anything_traiter_dist() {

	// create the google api URL
	$texte = _request('traduire');
	$url = parametre_url(URL_GOOGLE_TRANSLATE, 'v', '1.0', '&');
	$url = parametre_url($url, 'langpair', 'fr|en', '&');
	$url = parametre_url($url, 'q', $texte, '&');

	// load the text as translated by google (returned as JSON code)
	include_spip('inc/distant');
	$trad = recuperer_page($url);

	// warning: uses PHP 5.2
	$trad = json_decode($trad, true); // true = retour array et non classe

	// retrieve the results if OK
	if ($trad['responseStatus'] != 200) {
		set_request('traduction', '');
		return array(
			"editable" => true,
			"message_erreur" => "Bad luck, Google couldn't help!"
		);
	}

	// send the data to be loaded
	set_request('traduction', $trad['responseData']['translatedText']);

	// message
	return array(
		"editable" => true,
		"message_ok" => "And here's the translation!",
	);
}

The set_request() functions forces the saving of a variable value that can then later be retrieved using _request(). This allows the next loading of the form to retrieve the value of the "traduction" field to send it into the template’s context.

Note: It is possible that a cleaner method could be developed for future versions of SPIP in order to transit the data between the processing and loading phases using a new parameter in the processing return table.

Author Mark Baber Published : Updated : 12/03/23

Translations : English, français