#native_company# #native_desc#
#native_cta#

Building a Multilingual PHP Website

By Vojislav Janjic
on August 31, 2011

Fast internet growth has brought many opportunities in the global market. Businesses can reach their customers across many countries, and information sharing is not limited to a local area or country anymore. This is why there is an increasing tendency for multilingual websites. By having a website in multiple languages, you can target local markets more easily. Also, it is more convenient to use a website in your native language.
There are three ways to make your website support more than one language:
  1. Different HTML templates for each language
  2. Loading translated texts from an XML language file
  3. Storing translations in a MySQL database

HTML Language Templates

The simplest and quickest way to make your website multilingual is to create different HTML files for each language. Each language would have its own folder, and the default one would be selected by changing the root index to point to the desired language directory. This method doesn’t require neither a database nor file manipulation in PHP. The setup can be made even by someone with little HTML knowledge. Additionally, different templates can be created for different languages. But, the problem appears when you have to make changes. As you have to do one change multiple times, it is not convenient for larger websites.

XML Language Files

Another method for creating a website in multiple languages is to create XML language files. Actually, the language file can be created using any file format (e.g. Joomla uses .ini file format), but XML files are easy to manipulate with built-in PHP classes. All translations are stored in files, so there is no need to use a database. This is very useful if your website gets many visitors and relies on heavy database use. Also, it is very easy for third parties to translate your website to their language, which is especially good if you are creating a social network, information sharing website or an open-source CMS solution.
A simple XML file would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<language name="en">

 <page name="errors">
  <phase name="empty">Please fill in all the fields</phase>
  <phase name="email_not_found">E-mail not found in database</phase>
  <phase name="login_error">Login error</phase>
  <phase name="email_not_valid">E-mail not valid</phase>
  <phase name="password6">Password must be at least 6 characters</phase>
  <phase name="pass_no_match">Passwords do not match</phase>
  <phase name="template_not_found">Template not found</phase>
 </page>
</language>

The best solution is to name XML files and language tag attribute according to one of the international standards for language codes (I used ISO-639-1, a two-letter international language code standard). Now we need to load phrases to PHP:

<?php
public function load_phrases($lang_id) {
  $xml = new DomDocument('1.0');

  //path to language directory
  $lang_path=(LANG_PATH.$lang_id.'.xml');
  $xml->load($lang_path);

  //phrases are inside page tags, first we must get these
  $page = $xml->getElementsByTagName('page');
  $page_num=$page->length;

  for($i = 0; $i < $page_num; $i++) {
    $page=$xml->getElementsByTagName('page')->item($i);
     
     //get phase tags and store them into array
     foreach($page->getElementsByTagName('phase') as $phase) {
      $phase_name = $phase->getAttribute('name');
      $phrases[$phase_name] = $phase->firstChild->nodeValue;
      $phrases[$phase_name] = str_replace('n','<br/>',$phrases[$phase_name]);
     }
 
  }

 $this->phrases=$phrases;
 }
?>

This function reads an XML file and stores the phrases to a variable. Phrases are accessed the following way:

<?php
public function lang_id() {
  //determine page language
   $lang_id= isset($_COOKIE['lang']) ? $_COOKIE['lang'] : 'en';

  //set the language cookie and update cookie expiration date
    if(!isset($_COOKIE['lang'])) {
      $expiration_date=time()+3600*24*365;
      setcookie('lang',$lang_id,$expiration_date,'/');
    }

 return $lang_id;
 }

 public function change_lang($lang_id) {
     setcookie('lang',$lang_id,$expiration_date,'/');  
 }
?>

Here is the construct function:

<?php
public function __construct() {
  $this->load_phrases($this->lang_id());
 }
?>

Just load the class in each PHP file and you have a working multilingual website.

Storing Language Phrases in a MySQL Database

Another way of creating a multilingual website is to store phrases in a database. This method allows you to have more control over translations. You can edit translations from admin panel, or even create a portal where translations can be discussed, similar to Facebook translations. On the other side, it increases resource consumption, especially if the website gets big. But, consumption can be minimized by loading only the necessary phrases for each page.

Let’s start with the database:

CREATE TABLE IF NOT EXISTS `lang` (
  `lang_id` varchar(2) COLLATE utf8_unicode_ci NOT NULL,
  `lang_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`lang_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `phrases` (
  `phrase_id` int(10) NOT NULL AUTO_INCREMENT,
  `lang_id` varchar(2) COLLATE utf8_unicode_ci NOT NULL,
  `phrase_type` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `phrase_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `phrase_text` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`phrase_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

We have created two tables: supported language names and IDs will be stored in the first one and phrases in the second. Lang_id is the language code (I used ISO-639-1, a two-letter international language code standard). Phrase type is used for minimizing the number of phrases that need to be loaded. Assign the value Global to phrase_type if you are going to use that phrase in more than one page. If a phrase appears on one page only, assign the PHP file name (without the .php) as a phrase type.
We use the following function to load the phrases:

<?php
public function load_phrases($lang_id,$page_name) {
  $this->db->q("SELECT * FROM phrases WHERE lang_id='$lang_id' AND (phrase_type='global' OR phrase_type='$page_name')");
  while($f=$this->db->f_row()) {
   $phrases[$f['phrase_name'] = $f['phrase_text'];
  }
  
  return $phrases;
 }
?>

Language phrases are accessed the following way:

<?php
echo $lang->phrases[' email_not_valid ']; // prints out E-mail not valid; email_not_valid is the value of phrase_name database field
?>

Here is the construct function:

<?php
public function __construct($lang_id,$page_name) {
  $this->db = new db; // load database class
  $this->phrases = $this->load_phrases($lang_id,$page_name);
 }
?>

Page name is a variable here. We determine the page name automatically:

public function get_current_filename() {
   $current_file = $_SERVER["SCRIPT_NAME"];
   $parts = Explode('/', $current_file);
   $current_file = $parts[count($parts) - 1];
   $current_file = substr($current_file,0,strlen($current_file)-4);
   return $current_file;
 }

We could use cookies for checking the language ID, but I will show you a SEO approach. Your website will have multiple URLs, one for each language, which will improve your search engine ranking. So, first we edit .htaccess file:

RewriteRule ^([a-zA-Z]{2})/(.*)$ /$2?lang_id=$1 [L,QSA]

For example, your URL for English language would be: http://www.yourdomain.com/en/. Check if lang_id is set and valid in your init file. Also, set the default language here (in this example, it is English):

<?php
//fetch lang ID
$lang_id = !input::check_varchar($_GET['lang_id'],2,false) ? 'en' : $_GET['lang_id'];
DEFINE('LANG_ID',$lang_id);

//fetch phrases, global and for this page
$page_name = !input::check_varchar($_GET['page_name'],20,false) ? globals::get_current_filename() : $_GET['page_name'];
$lang = new lang($lang_id,$page_name);
?>

Make Your Website Multilingual from the Start

It is best to make your website multilingual at the beginning. It takes more effort to make an already finished website support multiple languages, because you have to replace all the text at once.