• Some users have recently had their accounts hijacked. It seems that the now defunct EVGA forums might have compromised your password there and seems many are using the same PW here. We would suggest you UPDATE YOUR PASSWORD and TURN ON 2FA for your account here to further secure it. None of the compromised accounts had 2FA turned on.
    Once you have enabled 2FA, your account will be updated soon to show a badge, letting other members know that you use 2FA to protect your account. This should be beneficial for everyone that uses FSFT.

PHP Site - Multilanguage Support - Best Design Practices

Joined
Mar 21, 2002
Messages
656
Hello all,

I am currently in the throws of putting together a site that is required to support multiple languages. Current requirements are English and Spanish however the design need to be flexible enough to add others in the future.

At face value this seemed easy enough, so I embarked on this task by using the following design:

Basically the default language is English, however every page has a series of flags on them, when the use clicks on the flag the language key (en for English, es for Spanish) is passed back to PHP SELF on a querystring, something like this:

mypage.php?displayLanguage=en

In the PHP code this value is picked up and validated, and then set into a session var. Based on the value of this session var the PHP code will then include a file that contains language definitions for the entire site, for example

PHP:
if ($_SESSION["displayLanguage"] == "es") 
{
  include ("LanguageData.es.php");
}
else
{
  include ("LanguageData.en.php");
}

Within these files language files I have a series of defines for each chunk of text, for example:

LanguageData.en.php
PHP:
define("LANG_INDEX_NUMBERS", "one two three")
define("LANG_INDEX_FOO", "foo")
define("LANG_INDEX_BAR", "bar")
...
...
...

LanguageData.es.php
PHP:
define("LANG_INDEX_NUMBERS", "uno dos tres")
define("LANG_INDEX_FOO", "el foo")
define("LANG_INDEX_BAR", "la bar")
...
...
...

As mentioned above these file include defines for all text used on all pages within the site (upwards of 200 defines)

Within my page code I can then do the following to create outputs

PHP:
echo '<div class="numbers">'.LANG_INDEX_NUMBERS.'</div>';
echo '<div class="foobar">'.LANG_INDEX_FOO.LANG_INDEX_BAR'</div>';

Now this all works, however as the site is expanding these language definition files are growing at an alarming rate. This is where I think there may be a issue with the design idea of using defines in a single php script. Also there is something in the back of my head that says loading all text for a specific language for the entire site on every page refresh is bordering on crazy.

As I understand the internal mechanisms of PHP, when a define is loaded this is a one time affair, i.e. for every page refresh and view the definitions are essentially reloaded. If this is the case then my code is loading/parsing/storing a full site definition file on every page view and this is going to turn into a massive and unnecessary overhead (a single page might only need 10 of the 100+ defines in the file).

If my assumption is incorrect and internally the php engine keeps a cache of defines created on other pages, then this isnt as big a problem as I think it might be. However if my assumption is correct that an alternative has to be found.

The most logical course of action would seem to be creating a unique language unique definition file for each page, however this then makes language administration considerably more difficult. As an example the site currently has 19 pages, that would be 38 language files to maintain!

So with all this in mind, does anybody have any suggestions on the best way forward for this?
 
Why not store it all in a database, pull the data by a key (language independant) then depending on the flag, display whichever language is needed from the query?
 
Are you required to write the code yourself? You might want to look into the latest version of Joomla!. It's still in RC status, but it's main focus is it's internationalization.
 
My best method IMO for a small site would be to use a path for each langauge and simply set a variable at the start that's based on the user's language selection.

e.g. include $language/index.php


Alternatively you could put all the text into a database and do an SQL query to grab it.
 
Well after much bouncing ideas around, I have come up with the following solution which, while seemingly complicated, actually makes the site easily extensible. Documented here just incase anybody is searching the forum in the future.

Basically my site has the following core infrastructure made up of two files:

  • SiteBuilder.php : This is PHP class that conditionally builds all output
  • Config.php : The primary site configuration

The class in SiteBuilder looks something like this:

PHP:
include_once("Resources/Core/Config.php");

class SiteBuilder
{
  function SiteBuilder($pageId)
  {
    // gets/set the current language from $_SESSION and/or $_GET/$_POST
    // includes the relevant language file based on $pageId.  In order to do this
    // the constant define CONST_LANGUAGE_FILEMASK (in config.php (see below) is used and
    // a simple string replace is performed to replace {0} with the language identifier (i.e. en or es)
    // and {1} is replace with the $pageId value.  For example, if page id is "Page1" and "Lang" is en
    // the the following file is included:
    //
    // "Resources/LanguageData/en/Lang.Page1.php"
  }
  
  function Render()
  {
    // conditionally creates all output that surrounds actual content (i.e. page
    // headers and footers, nav bars etc
    
    // includes the relevant content file based on $pageId, so for example if
    // the page id is "Page1" a simple string replace is performed to include:
    //
    // "Resources/Content/Fixed/Content.Page1.php
  }
  
  //various other methods to create outputs
}
?>

My config file looks something like this:

PHP:
define("CONST_PAGEID_MYPAGE1", "Page1");
define("CONST_PAGEID_MYPAGE2", "Page2");
define("CONST_PAGEID_MYPAGE3", "Page3");

define("CONST_LANGUAGE_ROOTPATH", "Resources/LanguageData/");
define("CONST_LANGUAGE_FILEMASK", CONST_LANGUAGE_ROOTPATH."{0}/Lang.{1}.php");
define("CONST_CONTENT_FILEMASK", "Resources/Content/Fixed/Content.{0}.php");

My langauge data file looks something like this:

PHP:
$pageTextData["textitem1"] = "This is text 1";
$pageTextData["textitem2"] = "This is text 2";

I also have a language file for common items (i.e. navigation bars that are on all pages etc). This is also included in the class constructor.

All language files use the same $pageTextData array name, so in my rendered content page I just make references to this array and the necessary data is extracted as the relevant file has been included when the class was instantiated. So for example my content file might look like this:

PHP:
<h1><? echo $pageTextData["textitem1"] ?><h1>
<p><? echo $pageTextData["textitem2"] ?><p>

Additionally I can now add additional languages extremely easily. Say for example I currently support English and Spanish, but I need to add German. All I need to do is download the english language directory:

Resources/LanguageData/en/

Change the directory name to "de", translate the relevant text, and upload. The sitebuilder class contains logic to parse the directory name from the language directory root and then use the available directories to display the available language options. This directory also contains the image file to display on the site as the link to the language. So something like the following:

PHP:
function GetSupportedLanguages()
{
  $root_dir = opendir (CONST_LANGUAGE_ROOTPATH); 
		
  $itemCount = 0;
  unset($this->supportedLanguages);
		
  while ($directoryEntity = readdir($root_dir))
  { 
    if ($directoryEntity != "." && $directoryEntity != ".." && $directoryEntity != "do_NOT_put_anything_else_in_this_directory" && $directoryEntity != "index.php")
    {
      $this->supportedLanguages[$itemCount] = $directoryEntity;
	  $itemCount++;
	}
  }
}

So when my "Render()" function comes to create the output for the language flags displayed at the top of the page, I simply use something like this:

PHP:
//dynamically create the displayed supported languages		
for($x = 0; $x<count($this->supportedLanguages); $x++)
{
	$lang = $this->supportedLanguages[$x];
	$flagImgAlt = $this->miscPageText["imgalt_".$lang."flag"]; //this miscPageText array is in the language include for misc/general page items
	if (strlen($flagImgAlt) == 0)
	{
		$flagImgAlt = $this->miscPageText["imgalt_unknownflag"]; //just incase the language uploaded hasnt set the alt text for the 
	}
	
	$returnData .= $this->NewLineText('<a href="'.$_SERVER['PHP_SELF'].'?'.CONST_SESSIONKEY_LANG.'='.$lang.'"><img src="'.CONST_LANGUAGE_ROOTPATH.$lang.'/flag.png" alt="'.$flagImgAlt.'"/></a>', 6);
}

So put all that together and the actual page the user browses to (regardless of the language they are using). Looks something like this:

PHP:
<?
require_once ("Resources/Core/SiteBuilder.php");
$page = new SiteBuilder(CONST_PAGEID_MYPAGE1);
$page->Render();
?>

And that, as they say, is that. Complicated, yes, however it is, I belive, a smart solution.

If you want to see all this working have a look at the following:

http://projects.4d52.net/totalmarine/Final/

Clicking on the flags in the page header, will change the language to your selection. If you hack the query string to an unknown/unsupported language the sitebuilder class takes over and defers you back to english.

BTW I don't particularly like the design, but thats what the punter wants, so thats what the punter gets
 
Back
Top