Internationalisation et Laravel

Lorsqu’on créé des sites multi langues, on tombe souvent dans la facilité de faire des urls en /de ou /fr . Mais qu’est ce qui se passe lorsqu’un pays utilise plusieurs langues. Comment Laravel est il capable de mémoriser la langue et d’adapter le contenu. Voyons ensemble le principe du middleware et comment pallier à cette problématique.

La variable « Locale »:

Vous avez le code App::setLocale(‘fr’); et App::getLocale() qui vous permet d’affecter et de récupérer la variable locale que vous avez spécifié dans votre fichier de config app.php. Seulement, sur certaines configurations, le fait de changer de page refait basculer dans la variable locale d’origine et perd celle qu’on vient d’affecté. J’ai testé plusieurs solutions à base de cookies, sessions… mais le plus simple est de passer par une couche intermédire entre votre framework, et votre rendu navigateur qu’on appelle le middleware.

Choix de la langue et du pays:

Ne pensez pas qu’un pays égal une langue !
Anticipez déjà les pays multilangues dans vos urls de bases.
Pensez dès votre architecture aux urls de type /be_fr , /be_nl …
Ensuite, j’imagine que vous êtes capable de faire un formulaire avec une liste des pays avec le paramètre country, et une liste des langues avec le paramètre lang.
L’idée étant d’avoir une url du type ?country=be&lang=fr

Le conseil que je vous donnerais serait d’utiliser cette librairie js très bien faite.

Choix du pays en javascript.

Le middleware:

Le middleware intercepte les requêtes avant la couche de rendu, et peut les modifier à la volée. L’idée est donc de récupérer la requête de changement de langue/pays, de la stocker dans des cookies, puis de rediriger l’internaute vers la bonne version.
La dernière partie interdit des urls qui ne commencent pas par /PAYS_LANGUE. SI vous avez des /cron, /admin pensez à la supprimer, ou à les gérer dans votre partie « routes ».

Pour cela, rajouter donc la classe suivante dans App\Http\Middleware\Langage.php :

<?php

namespace App\Http\Middleware;
use Closure;
use App;

class Language 
{
	//Permet de conserver la langue / locale enregistrée ou den affecter une nouvelle
	public function handle($request, Closure $next){
		//On prend la langue par defaut
		$locale = config('app.locale');
		$country = config('app.locale');
		$infos = explode ("/",$_SERVER["REQUEST_URI"]);

		//Liste de toutes les urls possibles
		$urls = [];
		$countries = config("app.langs");
		foreach ($countries as $c=>$ls){
			foreach ($ls as $l){
				$urls[] = $c."_".$l;
			}
		}
		
		//Default
		if (!isset($_COOKIE["country"])){
			$_COOKIE["country"] = $country;
			setcookie('country', $country);
		}
		if (!isset($_COOKIE["locale"])){
			$_COOKIE["locale"] = $locale;
			setcookie('locale', $locale);
		}
		
		//Recup ancienne valeur
		if (isset($_COOKIE["locale"])){
			$locale = $_COOKIE["locale"];
		}
		if (isset($_COOKIE["country"])){
			$country = $_COOKIE["country"];
		}		

		//On prend la nouveau pays		
		if ($request->input("country") != ""){
			$country = $request->input("country");
		}

		//Si c est pas un pays multilangue => lang = country
		$languesDispos = [];
		if (in_array($country,["fr","de","nl","es","it","en","pl"])){
			$locale = $country;
			$languesDispos[]= $country;
		}else{
			//Si c est un pays multilangue => lang = valeur par default du pays
			switch ($country){				
				case "gb":
					$locale = "en";
					break;
				case "be":
					$locale = "fr";
					break;
				case "ch":
					$locale = "de";
					break;
			}
			$languesDispos = config("app.langs")[$country];			
		}
		
		//On prend la nouvelle langue (sauf si on vient de changer vers un pays multi langue ou on prend la langue par defaut d'abord)
		if ($request->input("lang") != ""){
			if (in_array($request->input("lang"), $languesDispos)){
				$locale = $request->input("lang");
			}
		}else{
			if (in_array($_COOKIE["locale"], $languesDispos)){
				$locale = $_COOKIE["locale"];
			}
		}
		
		//On regarde si on est dans une url possible et si c est le cas, on affecte directement le bon pays
		if (isset($infos[1])){
			//On vire tous apres le ?			
			$infosX = explode ("?",$infos[1]);
			$infos[1] = $infosX[0];
			
			if (in_array($infos[1],$urls)){
				$infosX = explode ("_",$infos[1]);
				if (isset($infosX[1])){
					if ($request->input("lang") == "" and $request->input("country") == ""){
						$country = $infosX[0];
						$locale = $infosX[1];
					}
				}
			}
		}
		
		//On memorise
		$_COOKIE["country"] = $country;
		setcookie('country', $country);//Cette ligne marche (sous Windows)!
		$_COOKIE["locale"] = $locale;
		setcookie('locale', $locale);//Cette ligne marche (sous Windows)!
		
		App::setLocale($locale);


		//Si chgt de pays, ou de langue, on redirige vers la home
		if ($request->input("lang") != "" or $request->input("country") != ""){
			$bRedirect = true;
			if (isset($infos[1])){
				//Si on parle d'un champ dans l admin, alors on redirige pas
				if ($infos[1] == "admin"){
					$bRedirect = false;
				}
			}
			if ($bRedirect){
				return redirect("/".$country."_".$locale);
				exit();
			}
		}
		
		//Si erreur dans les langues, alors on redirige vers la home		
		if (isset($infos[1])){
			if ($infos[1] != "admin" and $infos[1] != "log" and $infos[1] != "shuffle" and $infos[1] != "cron"){
				if (stripos($infos[1],"sitemap") === false){
					$infosX = explode ("_",$infos[1]);

					if (!in_array($infosX[0],config("app.countries"))){
						return redirect("/".$country."_".$locale);
						exit();			
					}
					if (!in_array($infosX[1],$languesDispos)){
						return redirect("/".$country."_".$locale);
						exit();			
					}
				}				
			}
		}
		return $next($request);
	}
}

Les routes:

Idéalement, votre fichier route doit posséder des routes construites de cette manière:

Route::get(‘/{country}_{lang}/products’, ‘Controller@showProducts’);

Même si les 2 paramètres ne nous servent pas forcément dans le controller, on sait jamais…

Les fichiers de traductions:

Les traductions se situent dans resources/lang. (il suffit de créer un répertoire dans la langue de son choix et d’y copier les fichiers de traductions).

Le template:

Ensuite dans votre template, ajouter simplement <?php echo __(« messages.Price »);?> pour afficher la traduction de la variable Price du fichier messages.

Conclusion:

Toute la logique reste identique à la documentation Laravel de base. L’avantage de cette configuration, c’est qu’elle vous permettra de vous affranchir des différents problèmes liés à l’internationalisation de votre site, et de passer des heures sur stackoverflow… A noter que si vous faites du Vue JS, de nouvelles contraintes vont s’ajouter, mais cela fera partie d’un prochain article.

4 réflexions au sujet de “Internationalisation et Laravel”

  1. Salut !
    Merci beaucoup pour ce tutoriel, j’ai une petite question concernant tes routes.
    Lorsque l’utilisateur lance le site tu fais comment pour le rediriger vers la page home avec en par défaut la country/ lang de son navigateur avant de lui laisser la possibilité de changer la langue ?

    Répondre
    • Dans laravel, tu as la route /.
      Il faut modifier la fonction qui est dessus en récupérant le pays du visiteur à l’aide de geoip_country_code_by_name
      Bon courage

      Répondre
      • J’ai essayé geoip mais ça ne marche pas … Malgré mon installation de Laravel/geoip.
        Deuxième gros problème ma redirection de ma page home ne marche pas sur mon serveur … Mais marche en local …

        « `
        Route::get(‘/’, function () {
        return redirect(‘fr/home’);
        });
        « `

        à la fin j’aimerai pouvoir remplacer le fr par mon country code

        Répondre
        • je n’ai pas essayé cette librairie, je ne peux vous dire. Sinon votre redirection devrait fonctionner, tout dépend ou vous l’avez placé. Est ce dans un controller soumis à un controle d’authentification ? Ya t-il un middleware qui bloque son accès ?
          Bon courage

          Répondre

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.