4.1. I18n and OPT

When we create a multilingual website, it would be nice the template not to make this task harder. Open Power Template is very useful in such projects, because it natively supports i18n. Let's take a look at the sample template:

Example 4.1. Sample i18n template

{* put the current date inside the global@date language block *}
{apply($global@date, $current_date)}
<p>{$global@text1}</p>
<p>{$global@text2}</p>
<p>{$global@text3}</p>
<p>{$global@date}</p>

As we see, OPT has a special kind of blocks called language blocks. They read the text directly from your i18n system, so you do not have to assign it manually to the parser. The language block is build of the group ID, and the text ID inside this group, so the texts must be organised somehow in groups. The apply() function allows to insert some dynamic data into a text in the place of printf() C function format tags.

When it comes to the PHP code, OPT supports two styles of coding. First of all, we can use a simple, procedural i18n system, where we link a simple array containing all the language texts and groups. The apply() function operates directly on the language block, overwriting its content. This is a sample implementation:

Example 4.2. Procedural i18n in OPT

<?php 
   define('OPT_DIR', '../lib/');
   require('../lib/opt.class.php');
   // Our language array
   $lang = array(
	   'global' => 
			array(
			'text1' => 'This is text one',
			'text2' => 'This is text two',
			'text3' => 'This is text three',
			'date' => 'Today is %s, good day for fishing'		
		 )
	 );
								 
	 try
	 { 
		 $tpl = new optClass; 
		 $tpl -> root = './templates/';
		 $tpl -> compile = './templates_c/';
		 $tpl -> gzipCompression = 1;
		 $tpl -> httpHeaders(OPT_HTML); 
				 
		 // init the default i18n system
		 $tpl -> setDefaultI18n($lang);
									 
		 $tpl -> assign('current_date', date('d.m.Y')); 
		 $tpl -> parse('example4.tpl'); 
	 }
	 catch(optException $exception)
	 { 
		 optErrorHandler($exception); 
	 } 
?>

As we run the example, we will see something like that:

This is text one
This is text two
This is text three
Today is 15.05.2006, good day for fishing

The second style is called object-oriented i18n. Here, we have to create a class that implements the ioptI18n interface.

Example 4.3. Object-oriented i18n in OPT

<?php
	define('OPT_DIR', '../lib/');
	require('../lib/opt.class.php');
			  
	class i18n implements ioptI18n{
		private $data;
		private $replacements;
		static $instance;
		private $tpl;
			  
		private function __construct()
		{
			// We put the text in the constructor
			// But normally it should be somehow read from a file
			$this -> data = array(
				'global' => 
					array(
						'text1' => 'This is text one',
						'text2' => 'This is text two',
						'text3' => 'This is text three',
						'date' => 'Today is %s, good day for fishing'		
					)
			);
		} // end __construct();
				
		public function setOptInstance(optClass $tpl)
		{
			$this -> tpl = $tpl;
		} // end setOptInstance();
				
		static public function getInstance()
		{
			if(!is_object(self::$instance))
			{
				self::$instance = new i18n;
			}
			return self::$instance;
		} // end getInstance();  
				
		public function put($group, $text_id)
		{
			if(isset($this->replacements[$group][$text_id]))
			{
				return $this->replacements[$group][$text_id];
			}
			return $this->data[$group][$text_id]; 	
		} // end put();
				
		public function apply($group, $text_id)
		{
			$args = func_get_args();
			unset($args[0]);
			unset($args[1]);
			$this -> replacements[$group][$text_id] =
			vsprintf($this -> data[$group][$text_id], $args);
		} // end apply();
				
		public function putApply($group, $text_id)
		{
			$args = func_get_args();
			unset($args[0]);
			unset($args[1]);
			return vsprintf($this -> data[$group][$text_id], $args);
		} // end putApply();  
	}
				
	try
	{ 
		$tpl = new optClass; 
		$tpl -> root = './templates/';
		$tpl -> compile = './templates_c/';
		$tpl -> gzipCompression = 1;
		$tpl -> httpHeaders(OPT_HTML); 
				
		// create an instance of the i18n system
		$i18n = i18n::getInstance();
				
		// pass it to the parser
		$tpl -> setObjectI18n($i18n);
				
		$tpl -> assign('current_date', date('d.m.Y')); 
		$tpl -> parse('example5.tpl'); 
	}
	catch(optException $exception){
	
		optErrorHandler($exception); 
	}
?>

The class creates two buffers: $data, where the original texts are stored, and $replacements for the texts generated by the apply() function. In the i18n::put() method, we return the requested text.

As we have a class, we create its object and put it into the engine using optClass::setObjectI18n() method.

Of course the i18n issue does not end here. Remember that English grammar is not a king of the world, and the programmers should know that other languages like to prefer the structures that do not match to the English patterns. The simplest example is the date. In English, there is no problem to generate it with a computer. However, Polish and almost all Slavonic languages have cases, which are used here. For example, the Polish name for July is czerwiec, but in the date we have to write 15 czerwca 2006. It would be good to remember about such issues, because they decide on the application quality.