Php 5.3 : générer une liste de pays localisée

1 commentaire

Par Seb

Si vous êtes familier des formulaires de contact traduits en 26 langues – et même sans cela – vous vous êtes forcément déjà frotté au problème de la traduction des listes de noms de pays. En plus d’être fastidieux, cette opération nécessite d’être répétée pour chaque langue, et à moins d’avoir à votre disposition une base avec tous les noms de pays dans toutes les langues, c’est un véritable calvaire.

Depuis Php 5.3 (et pour toutes les versions compilées avec la librairie PECL Intl), nous avons à notre disposition la classe Locale qui nous propose notamment une fonction nommée getDisplayRegion(), que nous allons détourner un peu de sa fonction première ici.

Une liste pour les lister tous

Pour commencer, il nous faudra quand même une liste des pays, et notamment de leur code de représentation (ISO 3166-1 alpha-2 pour les intimes). Pour cela, 2 moyens : utiliser une base de donnée, ou une simple inclusion de fichier, au choix :

CREATE TABLE IF NOT EXISTS `pays` (
`code` smallint(3) unsigned NOT NULL,
`iso2` char(2) NOT NULL,
`nom` varchar(100) NOT NULL,
PRIMARY KEY (`code`),
KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Liste des pays';

INSERT INTO `pays` (`code`, `iso2`, `nom`) VALUES
(4, 'af', 'Afghanistan'),
(8, 'al', 'Albania, People\'s Socialist Republic Of'),
...
(887, 'ye', 'Yemen'),
(894, 'zm', 'Zambia, Republic Of');

Télécharger : Liste des pays – déclaration SQL

Ou :

<?php

// Liste des pays
$pays = array(
	'af' => 'Afghanistan',
	'al' => 'Albania, People\'s Socialist Republic Of',
	...
	'ye' => 'Yemen',
	'zm' => 'Zambia, Republic Of'
);

Télécharger : Liste des pays – inclusion Php

Les deux méthodes se valent, après c’est juste une question de goût… Pour notre exemple, nous allons utiliser l’inclusion Php, avec mise en cache.

Classe de gestion

Une fois que nous avons notre liste, nous allons créer une classe pour gérer toutes les opérations dont nous aurons besoin :

<?php
/**
 * Classe de gestion des noms de pays dans diverses langues
 * @version	1.0
 */
class ListingPays {

}

La première étape de la mise en place d’une classe est bien entendu son constructeur. Ici, nous allons permettre le passage d’une locale pour permettre de définir la langue d’affichage de la liste. On laissera aussi un mode automatique, qui tentera de détecter la locale du navigateur de l’internaute. Attention, tous les navigateurs ne fournissent pas cette information, il convient donc de l’utiliser en connaissance de cause.

/**
 * La locale dans laquelle construire le listing
 * @var		string
 */
protected $_locale;

/**
 * Constructeur de la classe
 * @param	string		la locale à utiliser, ou NULL pour utiliser la locale de l'utilisateur (par défaut)
 */
public function __construct($locale = NULL)
{
	// Si la locale n'est pas fournie
	if (is_null($locale))
	{
		// On tente de la récupérer à partir du useragent de l'utilisateur, sinon on prend la locale par défaut
		if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) and strlen($_SERVER['HTTP_ACCEPT_LANGUAGE']) > 0)
		{
			$locale = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
		}
		else
		{
			$locale = Locale::getDefault();
		}
	}

	// Mémorisation
	$this->_locale = $locale;
}

Il va nous falloir ensuite charger la liste des pays. Pour cela, on utilisera une méthode statique avec mise en cache des informations, afin d’accélérer les opérations suivantes :

/**
 * Cache de la liste des noms de pays
 * @var		array
 */
 protected static $_list;

/**
 * Obtention de la liste des pays
 * @return	array		le listing des noms de pays, indexés par leur code ISO-2
 */
protected static function _getList()
{
	// Si pas encore chargé
	if (!isset(self::$_list))
	{
		// Inclusion et stockage
		include('pays.php');
		self::$_list = $pays;
	}

	// Renvoi
	return self::$_list;
}

Pensez bien entendu à faire pointer le chargement du fichier vers le dossier où vous l’avez enregistré. Si vous préférez passer par une table MySQL, c’est ici que vous pouvez insérer votre code de chargement, à la place de l’include.

Construction de la liste

Maintenant que nous avons notre liste de pays et notre locale, il ne reste plus qu’à composer notre SELECT, via une méthode dédiée :

/**
 * Construction du listing
 * @param	string		$name			le nom à donner au champ SELECT
 * @param	string		$current		la valeur actuellement sélectionnée
 * @return	string		le code prêt pour affichage
 */
public function build($name = 'pays', $current = '')
{
	// Récupération de la liste des pays
	$list = self::_getList();

	// Liste des noms traduits
	$listIntl = array();

	// Préparation du listing
	$retour = array();
	$retour[] = '<select name="'.$name.'" id="'.$name.'">';

	// Parcours
	foreach ($list as $iso => $nom)
	{
		// Capitalisation
		$code = strtoupper($iso);

		// On tente de récupérer le nom localisé
		$nomIntl = Locale::getDisplayRegion('en-'.$code, $this->_locale);

		// Si pas trouvé, on garde l'anglais
		if ($nomIntl == $code)
		{
			$nomIntl = $nom;
		}

		// Ajout au listing
		$actif = ($current == $iso) ? ' selected="selected"' : '';
		$retour[] = '<option value="'.$iso.'"'.$actif.'>'.htmlspecialchars($nomIntl).'</option>';
	}

	// Finalisation
	$retour[] = '</select>';
	return implode("\n", $retour);
}

Reste juste un dernier détail : notre liste de pays initiale est triée par ordre alphabétique anglais, mais une fois traduite, ce tri ne sera plus cohérent : on se retrouve rapidement avec des z au milieu des b… Et c’est encore pire avec les caractères non latins.

Pour corriger ça, nous allons utiliser une consœur de la classe Locale : Collator. Cette classe permet de trier des listes en tenant compte des spécificités de chaque langue : ordre des lettres, précédence des caractères spéciaux, etc… Je vous laisse vous référer à la documentation pour plus de détails.

Dans notre cas, nous allons scinder notre boucle foreach en deux, pour permettre de tri les noms de pays après les avoir traduits, mais avant de composer la liste déroulante :

/**
 * Construction du listing
 * @param	string		$name			le nom à donner au champ SELECT
 * @param	string		$current		la valeur actuellement sélectionnée
 * @return	string		le code prêt pour affichage
 */
public function build($name = 'pays', $current = '')
{
	// Récupération de la liste des pays
	$list = self::_getList();

	// Liste des noms traduits
	$listIntl = array();

	// Parcours
	foreach ($list as $iso => $nom)
	{
		// Capitalisation
		$code = strtoupper($iso);

		// On tente de récupérer le nom localisé
		$nomIntl = Locale::getDisplayRegion('en-'.$code, $this->_locale);

		// Si pas trouvé, on garde l'anglais
		if ($nomIntl == $code)
		{
			$nomIntl = $nom;
		}

		// Ajout
		$listIntl[$iso] = $nomIntl;
	}

	// Tri alphabétique
	$collator = new Collator($this->_locale);
	$collator->asort($listIntl);

	// Préparation du listing
	$retour = array();
	$retour[] = '<select name="'.$name.'" id="'.$name.'">';

	// Parcours
	foreach ($listIntl as $iso => $nom)
	{
		// Ajout au listing
		$actif = ($current == $iso) ? ' selected="selected"' : '';
		$retour[] = '<option value="'.$iso.'"'.$actif.'>'.htmlspecialchars($nom).'</option>';
	}

	// Finalisation
	$retour[] = '</select>';
	return implode("\n", $retour);
}

Résultat final

Et voilà ! Vous pouvez maintenant construire une liste de pays dans n’importe quelle langue, sans vous soucier d’embarquer une lourde base. La construction d’une liste se fait simplement de la manière suivante, après inclusion de la classe :

<?php
$listing = new ListingPays();
echo $listing->build();
?>

Ce qui nous donne :

Ou pour un exemple plus parlant, dans une locale bien distincte :

<?php
$listing = new ListingPays('zh');
echo $listing->build();
?>

Qui nous donne :

Voici la classe complète :

<?php
/**
 * Classe de gestion des noms de pays dans diverses langues
 * @version	1.0
 */
class ListingPays {
	/**
	 * La locale dans laquelle construire le listing
	 * @var		string
	 */
	protected $_locale;
	/**
	 * Cache de la liste des noms de pays
	 * @var		array
	 */
	protected static $_list;

	/**
	 * Constructeur de la classe
	 * @param	string		la locale à utiliser, ou NULL pour utiliser la locale de l'utilisateur (par défaut)
	 */
	public function __construct($locale = NULL)
	{
		// Si la locale n'est pas fournie
		if (is_null($locale))
		{
			// On tente de la récupérer à partir du useragent de l'utilisateur, sinon on prend la locale par défaut
			if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) and strlen($_SERVER['HTTP_ACCEPT_LANGUAGE']) > 0)
			{
				$locale = Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']);
			}
			else
			{
				$locale = Locale::getDefault();
			}
		}

		// Mémorisation
		$this->_locale = $locale;
	}

	/**
	 * Construction du listing
	 * @param	string		$name			le nom à donner au champ SELECT
	 * @param	string		$current		la valeur actuellement sélectionnée
	 * @return	string		le code prêt pour affichage
	 */
	public function build($name = 'pays', $current = '')
	{
		// Récupération de la liste des pays
		$list = self::_getList();

		// Liste des noms traduits
		$listIntl = array();

		// Parcours
		foreach ($list as $iso => $nom)
		{
			// Capitalisation
			$code = strtoupper($iso);

			// On tente de récupérer le nom localisé
			$nomIntl = Locale::getDisplayRegion('en-'.$code, $this->_locale);

			// Si pas trouvé, on garde l'anglais
			if ($nomIntl == $code)
			{
				$nomIntl = $nom;
			}

			// Ajout
			$listIntl[$iso] = $nomIntl;
		}

		// Tri alphabétique
		$collator = new Collator($this->_locale);
		$collator->asort($listIntl);

		// Préparation du listing
		$retour = array();
		$retour[] = '<select name="'.$name.'" id="'.$name.'">';

		// Parcours
		foreach ($listIntl as $iso => $nom)
		{
			// Ajout au listing
			$actif = ($current == $iso) ? ' selected="selected"' : '';
			$retour[] = '<option value="'.$iso.'"'.$actif.'>'.htmlspecialchars($nom).'</option>';
		}

		// Finalisation
		$retour[] = '</select>';
		return implode("\n", $retour);
	}

	/**
	 * Obtention de la liste des pays
	 * @return	array		le listing des noms de pays, indexés par leur code ISO-2
	 */
	protected static function _getList()
	{
		// Si pas encore chargé
		if (!isset(self::$_list))
		{
			// Inclusion et stockage
			include('pays.php');
			self::$_list = $pays;
		}

		// Renvoi
		return self::$_list;
	}
}

Un commentaire


Exprimez vous !


(Ne sera pas publié)

Sur ThemeForest

  • Blue Earth Wordpress theme
  • Constellation complete admin skin
  • Blue Earth