Author: André Rabold
The following class subclasses smarty to add multilanguage support
Last update: April, 30. 2003
Updated : Decembre, 26. 2003: See post http://www.phpinsider.com/smarty-forum/viewtopic.php?p=6859#6859
Updated : January, 29. 2004: Multiline values, corrections and optimizations
<?php
require_once ("Smarty.class.php");
/**
* smarty_prefilter_i18n()
* This function takes the language file, and rips it into the template
* $GLOBALS['_NG_LANGUAGE_'] is not unset anymore
*
* @param $tpl_source
* @return
**/
function smarty_prefilter_i18n($tpl_source, &$smarty) {
if (!is_object($GLOBALS['_NG_LANGUAGE_'])) {
die("Error loading Multilanguage Support");
}
// load translations (if needed)
$GLOBALS['_NG_LANGUAGE_']->loadCurrentTranslationTable();
// Now replace the matched language strings with the entry in the file
return preg_replace_callback('/##(.+?)##/', '_compile_lang', $tpl_source);
}
/**
* _compile_lang
* Called by smarty_prefilter_i18n function it processes every language
* identifier, and inserts the language string in its place.
*
*/
function _compile_lang($key) {
return $GLOBALS['_NG_LANGUAGE_']->getTranslation($key[1]);
}
class smartyML extends Smarty {
var $language;
function smartyML ($locale="") {
$this->Smarty();
// Multilanguage Support
// use $smarty->language->setLocale() to change the language of your template
// $smarty->loadTranslationTable() to load custom translation tables
$this->language = new ngLanguage($locale); // create a new language object
$GLOBALS['_NG_LANGUAGE_'] =& $this->language;
$this->register_prefilter("smarty_prefilter_i18n");
}
function fetch($_smarty_tpl_file, $_smarty_cache_id = null, $_smarty_compile_id = null, $_smarty_display = false) {
// We need to set the cache id and the compile id so a new script will be
// compiled for each language. This makes things really fast ;-)
$_smarty_compile_id = $this->language->getCurrentLanguage().'-'.$_smarty_compile_id;
$_smarty_cache_id = $_smarty_compile_id;
// Now call parent method
return parent::fetch( $_smarty_tpl_file, $_smarty_cache_id, $_smarty_compile_id, $_smarty_display );
}
/**
* test to see if valid cache exists for this template
*
* @param string $tpl_file name of template file
* @param string $cache_id
* @param string $compile_id
* @return string|false results of {@link _read_cache_file()}
*/
function is_cached($tpl_file, $cache_id = null, $compile_id = null)
{
if (!$this->caching)
return false;
if (!isset($compile_id)) {
$compile_id = $this->language->getCurrentLanguage().'-'.$this->compile_id;
$cache_id = $compile_id;
}
return parent::is_cached($tpl_file, $cache_id, $compile_id);
}
}
class ngLanguage {
var $_translationTable; // currently loaded translation table
var $_supportedLanguages; // array of all supported languages
var $_defaultLocale; // the default language
var $_currentLocale; // currently set locale
var $_currentLanguage; // currently loaded language
var $_languageTable; // array of language to file associations
var $_loadedTranslationTables; // array of all loaded translation tables
function ngLanguage($locale="") {
$this->_languageTable = Array(
"de" => "deu",
"en" => "eng",
"en-us" => "eng",
"en-gb" => "eng",
"nl" => "nld",
"zh" => "chn",
"dk" => "dnk",
"es" => "esp",
"fr" => "fra",
"it" => "ita",
"no" => "nor",
"pl" => "pol",
"pt" => "prt",
"ru" => "rus",
"sv" => "swe",
"tr" => "tur"
); // to be continued ...
$this->_translationTable = Array();
$this->_loadedTranslationTables = Array();
foreach ($this->_languageTable as $lang)
$this->_translationTable[$lang] = Array();
$this->_defaultLocale = 'en';
if (empty($locale))
$locale = $this->getHTTPAcceptLanguage();
$this->setCurrentLocale($locale);
}
function getAvailableLocales() {
return array_keys($this->_languageTable);
}
function getAvailableLanguages() {
return array_unique(array_values($this->_languageTable));
}
function getCurrentLanguage() {
return $this->_currentLanguage;
}
function setCurrentLanguage($language) {
$this->_currentLanguage = $language;
}
function getCurrentLocale() {
return $this->_currentLocale;
}
function setCurrentLocale($locale) {
$language = $this->_languageTable[$locale];
if (empty($language)) {
die ("LANGUAGE Error: Unsupported locale '$locale'");
}
$this->_currentLocale = $locale;
return $this->setCurrentLanguage($language);
}
function getDefaultLocale() {
return $this->_defaultLocale;
}
function getHTTPAcceptLanguage() {
$langs = explode(';', $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
$locales = $this->getAvailableLocales();
foreach ($langs as $value_and_quality) {
// Loop through all the languages, to see if any match our supported ones
$values = explode(',', $value_and_quality);
foreach ($values as $value) {
if (in_array($value, $locales)){
// If found, return the language
return $value;
}
}
}
// If we can't find a supported language, we use the default
return $this->getDefaultLocale();
}
// Warning: parameter positions are changed!
function _loadTranslationTable($locale, $path='') {
if (empty($locale))
$locale = $this->getDefaultLocale();
$language = $this->_languageTable[$locale];
if (empty($language)) {
die ("LANGUAGE Error: Unsupported locale '$locale'");
}
if (!is_array($this->_translationTable[$language])) {
die ("LANGUAGE Error: Language '$language' not available");
}
if(empty($path))
$path = 'languages/'.$this->_languageTable[$locale].'/global.lng';
if (isset($this->_loadedTranslationTables[$language])) {
if (in_array($path, $this->_loadedTranslationTables[$language])) {
// Translation table was already loaded
return true;
}
}
if (file_exists($path)) {
$entries = file($path);
$this->_translationTable[$language][$path] = Array();
$this->_loadedTranslationTables[$language][] = $path;
foreach ($entries as $row) {
if (substr(ltrim($row),0,2) == '//') // ignore comments
continue;
$keyValuePair = explode('=',$row);
// multiline values: the first line with an equal sign '=' will start a new key=value pair
if(sizeof($keyValuePair) == 1) {
$this->_translationTable[$language][$path][$key] .= ' ' . chop($keyValuePair[0]);
continue;
}
$key = trim($keyValuePair[0]);
$value = $keyValuePair[1];
if (!empty($key)) {
$this->_translationTable[$language][$path][$key] = chop($value);
}
}
return true;
}
return false;
}
// Warning: parameter positions are changed!
function _unloadTranslationTable($locale, $path) {
$language = $this->_languageTable[$locale];
if (empty($language)) {
die ("LANGUAGE Error: Unsupported locale '$locale'");
}
unset($this->_translationTable[$language][$path]);
foreach($this->_loadedTranslationTables[$language] as $key => $value) {
if ($value == $path) {
unset($this->_loadedTranslationTables[$language][$key]);
break;
}
}
return true;
}
function loadCurrentTranslationTable() {
$this->_loadTranslationTable($this->getCurrentLocale());
}
// Warning: parameter positions are changed!
function loadTranslationTable($locale, $path) {
// This method is only a placeholder and wants to be overwritten by YOU! ;-)
// Here's a example how it could look:
if (empty($locale)) {
// Load default locale of no one has been specified
$locale = $this->getDefaultLocale();
}
// Select corresponding language
$language = $this->_languageTable[$locale];
// Set path and filename of the language file
$path = "languages/$language/$path.lng";
// _loadTranslationTable() does the rest
$this->_loadTranslationTable($locale, $path);
}
// Warning: parameter positions are changed!
function unloadTranslationTable($locale, $path) {
// This method is only a placeholder and wants to be overwritten by YOU! ;-)
$this->_unloadTranslationTable($locale, $path);
}
function getTranslation($key) {
$trans = $this->_translationTable[$this->_currentLanguage];
if (is_array($trans)) {
foreach ($this->_loadedTranslationTables[$this->_currentLanguage] as $table) {
if (isset($trans[$table][$key])) {
return $trans[$table][$key];
}
}
}
return $key;
}
}
?>
I have copied these two classes from a bigger project which uses some more methods
and has alot more functionality. So I'm not sure if the above will work for you,
I haven't tested it yet. Also it might seem some functions are a bit complicated
or have unused parameters. Feel free to modify it ;-)
Addition:
Change these line if translation of dynamic data is needed ;-)
$this->register_prefilter("smarty_prefilter_i18n");
to this
$this->register_outputfilter("smarty_prefilter_i18n");
//this change makes it possible even to translate dynamic data e.g. options because translation is done after compilation of template [xaos, 20050206]
Here are two simple language files
// English language file // put in languages/eng/global.lng NG_OK=Ok NG_ABORT=Abort NG_CANCEL=Cancel NG_MULTILINE=This entry spans 2 lines, this is the first and this is the second. NG_HELLO_WORLD=Hello World!<br>How do you do?
// German language file // put in languages/deu/global.lng NG_OK=Ok NG_ABORT=Abbrechen NG_CANCEL=Abbrechen NG_MULTILINE=Dieser Eintrag umfasst 2 Zeile, diese ist die erste und diese ist die zweite. NG_HELLO_WORLD=Hallo Welt!<br>Wie geht es Dir?
Put these files into the specified paths.
And finally a template with multilanguage support:
<html>
<body>
##NG_HELLO_WORLD##<br>
##NG_MULTILINE##<br>
<input type="button" value="##NG_OK##" onclick="alert('##NG_OK##')">
<input type="button" value="##NG_CANCEL##" onclick="alert('##NG_CANCEL##')">
</body>
</html>
Finally how to use the smartyML class:
<?php
$smarty = new smartyML();
// [...]
// $smarty->assign(...);
$smarty->display("myTemplate.tpl");
?>
Easy, isn't it? The language will be determined by the browser settings. If you want
to change it explicitly you can use something like this:
<?php
$smarty = new smartyML("de"); // now the language will be german regardless of the browser settings
// [...]
// $smarty->assign(...);
$smarty->display("myTemplate.tpl");
?>
Here's a list of some locales returned from browser:
Afrikaans = af
Albanian = sq
Arabic (Algeria) = ar-dz
Arabic (Bahrain) = ar-bh
Arabic (Egypt) = ar-eg
Arabic (Iraq) = ar-iq
Arabic (Jordan) = ar-jo
Arabic (Kuwait) = ar-kw
Arabic (Lebanon) = ar-lb
Arabic (libya) = ar-ly
Arabic (Morocco) = ar-ma
Arabic (Oman) = ar-om
Arabic (Qatar) = ar-qa
Arabic (Saudi Arabia) = ar-sa
Arabic (Syria) = ar-sy
Arabic (Tunisia) = ar-tn
Arabic (U.A.E.) = ar-ae
Arabic (Yemen) = ar-ye
Arabic = ar
Armenian = hy
Assamese = as
Azeri (Cyrillic) = az
Azeri (Latin) = az
Basque = eu
Belarusian = be
Bengali = bn
Bulgarian = bg
Catalan = ca
Chinese (China) = zh-cn
Chinese (Hong Kong SAR) = zh-hk
Chinese (Macau SAR) = zh-mo
Chinese (Singapore) = zh-sg
Chinese (Taiwan) = zh-tw
Chinese = zh
Croatian = hr
Czech = cs
Danish = da
Divehi = div
Dutch (Belgium) = nl-be
Dutch (Netherlands) = nl
English (Australia) = en-au
English (Belize) = en-bz
English (Canada) = en-ca
English (Caribbean) = en
English (Ireland) = en-ie
English (Jamaica) = en-jm
English (New Zealand) = en-nz
English (Philippines) = en-ph
English (South Africa) = en-za
English (Trinidad) = en-tt
English (United Kingdom) = en-gb
English (United States) = en-us
English (Zimbabwe) = en-zw
English = en
Estonian = et
Faeroese = fo
Farsi = fa
Finnish = fi
French (Belgium) = fr-be
French (Canada) = fr-ca
French (France) = fr
French (Luxembourg) = fr-lu
French (Monaco) = fr-mc
French (Switzerland) = fr-ch
FYRO Macedonian = mk
Gaelic = gd
Georgian = ka
German (Austria) = de-at
German (Germany) = de
German (Liechtenstein) = de-li
German (lexumbourg) = de-lu
German (Switzerland) = de-ch
Greek = el
Gujarati = gu
Hebrew = he
Hindi = hi
Hungarian = hu
Icelandic = is
Indonesian = id
Italian (Italy) = it
Italian (Switzerland) = it-ch
Japanese = ja
Kannada = kn
Kazakh = kk
Konkani = kok
Korean = ko
Kyrgyz = kz
Latvian = lv
Lithuanian = lt
Malay (Brunei) = ms
Malay (Malaysia) = ms
Malayalam = ml
Maltese = mt
Marathi = mr
Mongolian (Cyrillic) = mn
Nepali (India) = ne
Norwegian (Bokmal) = nb-no
Norwegian (Bokmal) = no
Norwegian (Nynorsk) = nn-no
Oriya = or
Polish = pl
Portuguese (Brazil) = pt-BR
Portuguese (Portugal) = pt
Punjabi = pa
Rhaeto-Romanic = rm
Romanian (Moldova) = ro-md
Romanian = ro
Russian (Moldova) = ru-md
Russian = ru
Sanskrit = sa
Serbian (Cyrillic) = sr
Serbian (Latin) = sr
Slovak = sk
Slovenian = ls
Sorbian = sb
Spanish (Argentina) = es-ar
Spanish (Bolivia) = es-bo
Spanish (Chile) = es-cl
Spanish (Colombia) = es-co
Spanish (Costa Rica) = es-cr
Spanish (Dominican Republic) = es-do
Spanish (Ecuador) = es-ec
Spanish (El Salvador) = es-sv
Spanish (Guatemala) = es-gt
Spanish (Honduras) = es-hn
Spanish (International Sort) = es
Spanish (Mexico) = es-mx
Spanish (Nicaragua) = es-ni
Spanish (Panama) = es-pa
Spanish (Paraguay) = es-py
Spanish (Peru) = es-pe
Spanish (Puerto Rico) = es-pr
Spanish (Spain) = es-es
Spanish (Traditional Sort) = es
Spanish (United States) = es-us
Spanish (Uruguay) = es-uy
Spanish (Venezuela) = es-ve
Sutu = sx
Swahili = sw
Swedish (Finland) = sv-fi
Swedish = sv
Syriac = syr
Tamil = ta
Tatar = tt
Telugu = te
Thai = th
Tsonga = ts
Tswana = tn
Turkish = tr
Ukrainian = uk
Urdu = ur
Uzbek (Cyrillic) = uz
Uzbek (Latin) = uz
Vietnamese = vi
Xhosa = xh
Yiddish = yi
Zulu = zu