i18n = internationalisation (18 est le nombre de lettres entre le i du début et le n de la fin). On rencontre aussi souvent l10n.
Il s'agit des techniques à mettre en jeu pour qu'un programme, ou un site, soit capable d'afficher ses informations en plusieurs langues. Il y a deux méthodes recommandées :
gettext
avec PHP
n'est pas toujours simple,
en particulier car il est nécessaire de disposer d'un module
particulier sur le serveur, ce qui n'est pas toujours possible. Grâce
au Zend Framework il est maintenant possible d'utiliser des fichiers de
traductions gettext
sans ce module.Ce site utilise ces deux techniques. La maquette générale de la page est
générée par programme, en particulier le menu, alors que le texte de
chaque page est écrit en html. Le contrôleur général détermine
automatiquement la langue à utiliser selon celle du navigateur de
l'internaute et paramètre le module gettext
qui transpose
les textes programmés. Ensuite, le sous-contrôleur de vue sélectionne le
fichier HTML dans cette langue, s'il est disponible.
Il est possible de choisir explicitement une autre langue :
$tr->_("accueil")
sont des chaines de caractères traduite par le module gettext
.gettext
. Il faut ensuite, bien sûr,
ajouter les nouvelles traductions et mettre à jour celles qui ont changé et qui sont automatiquement repérées.gettext
pour générer et mettre à jour les fichiers de traduction à partir des sources.<?php if (isset($_SERVER["HTTP_ACCEPT"]) and stristr($_SERVER["HTTP_ACCEPT"],"application/xhtml+xml")) { header('Content-type: application/xhtml+xml'); } else header ('content-type: text/html; charset="utf-8"'); error_reporting(E_ALL & ~E_DEPRECATED & ~E_NOTICE); // error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE); set_include_path(get_include_path() . PATH_SEPARATOR . '../includes/:..'); require_once 'lib/functions.php'; //session require_once 'Zend/Session/Namespace.php'; $session = new Zend_Session_Namespace(); //languages, define available translation require_once("Zend/Translate.php"); $tr = new Zend_Translate('gettext', 'i18n/fr.mo', 'fr'); $tr->addTranslation('i18n/en.mo', 'en'); if ($request->lang and in_array($request->lang, $tr->getList())) { //lang in request parameter $tr->setLocale($request->lang); // in session $session->lang = $request->lang; } elseif (isset($session->lang) ) $tr->setLocale($session->lang); // lang already set in session else { //get best gess language from browser require_once 'Zend/Locale.php'; $locale = new Zend_locale(); if (in_array($locale->getLanguage(), $tr->getList())) $tr->setLocale($locale->getLanguage()); else $tr->setLocale('en'); // in session $session->lang = $tr->getLocale(); } require_once 'hop/hop.php'; // target page $sujet = $request->get('sujet', 'accueil'); if ($sujet == 'hop') $sujet_lang = "views/{$sujet}_en.html"; else $sujet_lang = "views/{$sujet}_{$session->lang}.html"; include 'adm/compteurs.php'; // cache management require_once 'Zend/Cache.php'; $ref_file = "views/$sujet.php"; //check for most recently modified file if (file_exists($sujet_lang)) { if (filemtime($sujet_lang) > filemtime($ref_file)) $ref_file = $sujet_lang; } elseif (file_exists("views/{$sujet}_fr.html") and filemtime("views/{$sujet}_fr.html") > filemtime($ref_file)) $ref_file = "views/{$sujet}_fr.html"; if (filemtime("views/theme.php") > filemtime($ref_file)) $ref_file = "views/theme.php"; $cache_page = Zend_Cache::factory('File', 'File', array('lifetime' => null, 'master_file' => $ref_file), array('cache_dir' => 'cache')); //, 'automatic_serialization' => true $cache_page_id = "theme_{$sujet}_{$session->lang}"; if (!(isset($session->style) and $session->style != 'views/a_x.css') and $cache_page->test($cache_page_id)) { $page_h = $cache_page->load($cache_page_id); } else { // page generation $page = include 'views/theme.php'; //save in cache if (!$cache_page->test($cache_page_id)) $cache_page->save($page_h = $page->render(), $cache_page_id); //render and save if needed //no cache if (isset($session->style) and $session->style != 'views/a_x.css') { $page->head->feuille_style->set_href($session->style); if ($session->style == 'views/vide.css') $page->head->hop_menu->set_href($session->style); $page_h = $page->render(); } } // output html echo $page_h; ?>
<?php // génération page // require_once 'hop/hop.php'; $page = Hop_page::factory(array('title' =>"a/X : {$tr->_("développement web 2")} - {$tr->_($sujet)}", 'lang' => $session->lang)); $page->head->add($hop->meta('keywords')->auto_name()->set_attr('content', $tr->_("développement web, indépendant, web2, web 2, xhtml, php, javascript, ajax, jquery, prototype, mysql"))); $page->head->add($hop->link('feuille_style')->auto_id() ->set_rel('stylesheet')->set_type('text/css')->set_media('screen')->set_href('views/a_x.css')); $page->head ->add($hop->link()->rel('alternate')->type('text/html')->hreflang('fr')->href('?lang=fr')->title('français')) ->add($hop->link()->rel('alternate')->type('text/html')->hreflang('en')->href('?lang=en')->title('English')) ->add($hop->script()->set_type('text/javascript')->set_src('lib_js/prototype.js')) ->add($hop->script()->set_type('text/javascript')->set_src('lib_js/effects.js')) ->add($hop->script()->set_type('text/javascript')->set_src('lib_js/dragdrop.js')) ->add($hop->script()->set_type('text/javascript')->set_src('js/a_x.js')) ->add($hop->script()->set_type('text/javascript')->set_src('js/ajax_menu.js')) ; //SVG vector image //commented out : Safari is the only browser to display and scale // $page->body->add($hop->object('fond_logo')->auto_id() // ->set_type('image/svg+xml') // ->set_data('views/logo.svg') // ); //titre page $page->body ->add($hop->p($hop->a($tr->getLocale() == 'en' ? 'français' : 'English')->set_href($tr->getLocale() == 'en' ? '?lang=fr' : '?lang=en'), 'lang')->auto_id()) ->add($hop->h1('<span class="a_X">a/X</span> ' . $tr->_("développeur en vacances"),'titre_page')->auto_id()) ; // menu $menu = Hop_menu::factory(array( 'id' => 'menu_g', 'page' => $page, 'css' => 'views/hop_menu_IE6.css', 'items' => array( 'accueil' => $hop->a($tr->_("accueil"))->set_href('accueil.html'), 'contributions' => $tr->_("contributions") // ,'web2' => $hop->a("Ajax, web 2")->set_href('web2.html'), // 'xhtml' => $hop->a("XHTML")->set_href('xhtml.html'), // 'css' => $hop->a("CSS")->set_href('css.html'), // 'javascript' => $hop->a("JavaScript")->set_href('javascript.html'), // 'php' => $hop->a("PHP")->set_href('php.html'), // 'zend' => $hop->a("Zend framework")->set_href('zend.html'), // 'mvc' => $hop->a("MVC")->set_href('mvc.html'), // 'mysql' => $hop->a("MySQL")->set_href('mysql.html'), // 'i18n' => $hop->a("i18n")->set_href('i18n.html'), // 'spip' => $hop->a("CMS, SPIP")->set_href('spip.html') ))) ->sub_menu(Hop_menu::factory(array( 'id'=>'logiciels', 'items' => array( 'hop' => $hop->a("Ho<sub>in</sub>P")->set_href('hop.html') // ,'menus' => $hop->a($tr->_("menus en CSS"))->set_href('menus.html') ))) , 'contributions') ->auto_id() ; if (isset($hop->pool()->$sujet)) $hop->pool()->$sujet->add_class('actif'); //pool access for submenu items $page->head->hop_menu->auto_id(); //for javascript vivibility //menu div $page->body->add($hop->div('menu')->auto_id()->add($menu)); // subject zone $page->body->add($hop->div('travail')->auto_id()->add(include('cache_sujet.php'))); //footer $page->body->add($hop->p('<a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10-blue" alt="Valid XHTML 1.0 Strict" height="15" width="44" /></a>') ->set_class('pied')); // return as object return $page; ?>
<?php require_once 'lib/widgets.php'; $xhtml = new Hop_node; if (file_exists("views/{$sujet}_{$session->lang}.html")) $f_source = "views/{$sujet}_{$session->lang}.html"; else $f_source = "views/{$sujet}_fr.html"; $xhtml->add(new Hop_node_from_file($f_source)); $xhtml->add(div_masque('index', htmlspecialchars(file_get_contents("index.php")), "index.php")); $xhtml->add(div_masque('theme', htmlspecialchars(file_get_contents("views/theme.php")), 'views/theme.php')); $xhtml->add(div_masque('i18n_', htmlspecialchars(file_get_contents("views/i18n.php")), "views/i18n.php")); $xhtml->add(div_masque('en_po', htmlspecialchars(file_get_contents("i18n/en.po")), "i18n/en.po")); $xhtml->add(div_masque('makefile', htmlspecialchars(' appli = a_X php = $(wildcard $(appli)/*.php) $(wildcard $(appli)/views/*.php) trads_bin = $(patsubst %.po, %.mo , $(wildcard $(appli)/i18n/*.po)) %.po : global.pot msgmerge $@ $< -o $@ %.mo : %.po [ -f $@ ] && rm $@; msgfmt --no-hash $< -o $@ gettext : $(trads_bin) global.pot : $(php) xgettext --foreign-user --from-code utf-8 -k_ -o $@ $(php) # 2> xgettext_out.pro --no-location #grep -vE "attention: co|langage C" xgettext_out.pro || true '), "makefile")); $xhtml->add(div_masque('html_', htmlspecialchars(file_get_contents($f_source)), $f_source)); return $xhtml; ?>
# a_X Web2 development. # This file is put in the public domain. # FIRST AUTHOR Max Barel <a_x_nospam_@ac-mb.info>, 2007. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2007-11-25 23:38+0100\n" "PO-Revision-Date: 2007-08-07 17:00+0200\n" "Last-Translator: Max Barel <a_x_nospam_@ac-mb.info>\n" "Language-Team: Max Barel <a_x_nospam_@ac-mb.info>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: a_X/views/javascript.php:20 msgid "extrait de" msgstr "excerpt from" #: a_X/views/theme.php:8 msgid "" "développement web, indépendant, web2, web 2, xhtml, php, javascript, ajax, " "jquery, prototype, mysql" msgstr "web develpment, free-lance, web2, web 2, xhtml, php, javascript, ajax, " "jquery, prototype, mysql" #: a_X/views/theme.php:28 msgid "développement web 2" msgstr "web 2 development" #: a_X/views/theme.php:37 msgid "accueil" msgstr "home" #: a_X/views/theme.php:38 msgid "contributions" msgstr "" #: a_X/views/theme.php:54 msgid "menus en CSS" msgstr "CSS menus" #: a_X/views/xhtml.php:11 msgid "rendu d'objet HoP" msgstr "rendered HoP object" #~ msgid "logiciels" #~ msgstr "software"
appli = a_X php = $(wildcard $(appli)/*.php) $(wildcard $(appli)/views/*.php) trads_bin = $(patsubst %.po, %.mo , $(wildcard $(appli)/i18n/*.po)) %.po : global.pot msgmerge $@ $< -o $@ %.mo : %.po [ -f $@ ] && rm $@; msgfmt --no-hash $< -o $@ gettext : $(trads_bin) global.pot : $(php) xgettext --foreign-user --from-code utf-8 -k_ -o $@ $(php) # 2> xgettext_out.pro --no-location #grep -vE "attention: co|langage C" xgettext_out.pro || true
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <title>i18n_fr</title> <meta name="generator" content="Amaya, see http://www.w3.org/Amaya/" /> </head> <body> <p><em>i18n</em> = internationalisation (18 est le nombre de lettres entre le i du début et le n de la fin). On rencontre aussi souvent <abbr title="localization">l10n.</abbr></p> <p>Il s'agit des techniques à mettre en jeu pour qu'un programme, ou un site, soit capable d'afficher ses informations en plusieurs langues. Il y a deux méthodes recommandées :</p> <dl class="clear"> <dt>Fichiers html multiples</dt> <dd>C'est la méthode la plus évidente. Elle implique de dupliquer chaque fichier html composant le site, pour chaque nouvelle langue, et de le traduire. Il reste encore à définir une méthode pour accéder au bon jeu de fichier, si possible de manière transparente pour l'utilisateur. Cette méthode est adaptée aux pages contenant beaucoup de texte statique, ne changeant jamais ou très rarement. Elle ne convient pas du tout aux éléments dont le contenu est défini dynamiquement par programme.</dd> <dt>Fichiers de traduction gettext</dt> <dd>C'est la meilleure méthode pour les éléments dynamiques. Le programme est écrit de manière à générer le texte dans la langue par défaut de l'application (du site), mais il est capable de transposer chaque élément dans toute autre langue disponible. Utiliser <code>gettext</code> avec <code>PHP</code> n'est pas toujours simple, en particulier car il est nécessaire de disposer d'un module particulier sur le serveur, ce qui n'est pas toujours possible. Grâce au <span class="contrib">Zend Framework</span> il est maintenant possible d'utiliser des fichiers de traductions <code>gettext</code> sans ce module.</dd> </dl> <p>Ce site utilise ces deux techniques. La maquette générale de la page est générée par programme, en particulier le menu, alors que le texte de chaque page est écrit en html. Le contrôleur général détermine automatiquement la langue à utiliser selon celle du navigateur de l'internaute et paramètre le module <code>gettext</code> qui transpose les textes programmés. Ensuite, le sous-contrôleur de vue sélectionne le fichier <abbr title="HyperText Markup Language">HTML</abbr> dans cette langue, s'il est disponible.</p> <p>Il est possible de choisir explicitement une autre langue :</p> <form action="index.php"> <div> <select onchange="this.form.submit()" name="lang"> <option value=""></option> <option value="fr">Voir le site en français</option> <option value="en">This site in English (few pages translated)</option> </select> </div> </form> <ul> <li><a href="#index" onclick="Element.show($('index'))">index.php</a>: selection de la langue</li> <li><a href="#theme" onclick="Element.show($('theme'))">views/theme.php</a>: les expressions de la forme <code>\$tr->_("accueil")</code> sont des chaines de caractères traduite par le module <code>gettext</code>.</li> <li><a href="#en_po" onclick="Element.show($('en_po'))">Le fichier de traduction en anglais</a>: Ce fichier est automatiquement généré et mis à jour par les outils <code>gettext</code>. Il faut ensuite, bien sûr, ajouter les nouvelles traductions et mettre à jour celles qui ont changé et qui sont automatiquement repérées.</li> <li><a href="#makefile" onclick="Element.show($('makefile'))">le makefile</a>: Il montre l'utilisation des outils <code>gettext</code> pour générer et mettre à jour les fichiers de traduction à partir des sources.</li> <li><a href="#i18n_" onclick="Element.show($('i18n_'))">le sous contrôleur de page</a>: sélectionne le fichier <abbr title="HyperText Markup Language">HTML</abbr> approprié (français par défaut).</li> <li><a href="#html_" onclick="Element.show($('html_'))">Un fichier html</a>: le texte en <abbr title="HyperText Markup Language">HTML</abbr>, dans la langue sélectionnée.</li> </ul> <p></p> </body> </html>