Gyong Ju - South Korea

A simple approach to Localization in PHP

Posted: February 22, 2010 at 10:31 am

There’s quite a number of ways to approach localization (L10N) in PHP. Typing in “simple localization php” in Google yields an impressive amount of results. I won’t go into a heavy theoretical approach in what the best way is or that you need to use gettext or any such approach. I just want to present the function(s) I’m using and leave it up to you to decide whether you like the approach.

First some constants that I use. You can rip them out if you don’t like them, they’re not essential, I just like using constants for this type of work.

/**
* Define the url path for the resources
*/
defined('INCLUDE_PATH') or define('INCLUDE_PATH', '/include');

/**
* Define the language using language code based on BCP 47 + RFC 4644,
* http://www.rfc-editor.org/rfc/bcp/bcp47.txt
*
* The language files can be found in directory 'lang'
*/
defined('LANGUAGE') or define('LANGUAGE', 'en-us');

The constants are used in the code below, replace them with your own values or approach where necessary. What happens is that a language file is loaded based on the configured language. A static array $translations is used to ensure that the language file is loaded once and every other subsequent call is handled through the $translations array in memory rather than reloading the language file. The language file is constructed in JSON format and when the language file is loaded into the $translations array the PHP json_decode function is used to convert from JSON to PHP associative array format. When a call is made to the function a language phrase is passed to the function and the matching value is found in the $translations array by using the language phrase as a key for the associative array.

Please ensure the .txt files are utf-8 encoded (without BOM), otherwise the json PHP functions will not operate correctly (see comment DanyBoy below).

/**
* Load the proper language file and return the translated phrase
*
* The language file is JSON encoded and returns an associative array
* Language filename is determined by BCP 47 + RFC 4646
* http://www.rfc-editor.org/rfc/bcp/bcp47.txt
*
* @param string $phrase The phrase that needs to be translated
* @return string
*/
function localize($phrase) {
    /* Static keyword is used to ensure the file is loaded only once */
    static $translations = NULL;
    /* If no instance of $translations has occured load the language file */
    if (is_null($translations)) {
        $lang_file = INCLUDE_PATH . '/lang/' . LANGUAGE . '.txt';
        if (!file_exists($lang_file)) {
            $lang_file = INCLUDE_PATH . '/lang/' . 'en-us.txt';
        }
        $lang_file_content = file_get_contents($lang_file);
        /* Load the language file as a JSON object and transform it into an associative array */
        $translations = json_decode($lang_file_content, true);
    }
    return $translations[$phrase];
}

An excerpt of the US English language file (in JSON format):

{
    "lang":"en-us",
    "No":"No",
    "Yes":"Yes",
    "or":"or",
    "Do you require help":"Do you require help"
}

An excerpt of the German language file:

{
    "lang":"de",
    "No":"Nein",
    "Yes":"Ja",
    "or":"oder",
    "Do you require help":"Brauchen Sie Hilfe"
}

Edit : Please make sure the language files are saved in UTF-8 format as this is the default encoding for the json_decode function used in the code above.

An example of it’s usage:

print localize('Do you require help') . localize('Yes') . localize('or') . localize('No');

An example of usage in some of my own code:

$create_page_array = array(
    'status_message' => $status_message,
    'table_caption' => localize('Delete Operation'),
    'table_explanation' => localize('This command deletes an operation'),
    'table_content' => $table_content,
    'checkbox' => 'operation_name',
    'table_sort' => '[[1,0]]'
);

The advantages to me are:

  1. Very easy integration into your code
  2. The language phrases remain recognizable in your own code
  3. The language files can be easily customised because the original phrase is part of the translation
  4. Language file needs to be loaded only once
  5. The language file is coded in an open standard (JSON) and can be processed in other ways that you currently don’t foresee

A Simple Front End Controller in PHP

Posted: January 18, 2010 at 10:21 am

A Front End Controller is part of an MVC pattern.

The controller receives input and initiates a response by making calls on model objects. An MVC application may be a collection of model/view/controller triplets, each responsible for a different UI element. MVC is often seen in web applications where the view is the HTML or XHTML generated by the app. The controller receives GET or POST input and decides what to do with it, handing over to domain objects (i.e. the model) that contain the business rules and know how to carry out specific tasks such as processing a new subscription.

Continue Reading

NIST RBAC Data Model

Posted: January 9, 2010 at 2:50 pm

There’s an updated article for the data model

I’m nearing the end of my development work for the first version of the NIST RBAC API for PHP. Rather than trying to explain this myself I quote the Wikipedia page on this and Role Based Access Control (RBAC) in general:

Continue Reading

Serving media files from a different url with Django and NGINX

Posted: November 21, 2009 at 8:04 am

Media files in Django are served through the web server and they can be served with a different url than the Django content itself. By spreading requests across multiple urls you can speed up your site because the browser will execute requests in parallel. The rule of thumb seems to be a maximum of 2-3 hostnames otherwise the added DNS requests negate the speed up effect.

Continue Reading

Logging out of a Basic Authentication session (with PHP example)

Posted: October 4, 2009 at 8:44 am

A good friend of mine asked if it was possible to log out of a Basic Authentication session. My first knee-jerk response was that Basic Authentication has no log out function and you should close the browser to safely log out of the session. After some days silence he came back with a script he’d found on the php.net site. The script used sessions to break the Basic Authentication behavior of the browser. It wasn’t a very successful script because it only worked in a limited set of browsers but it got me thinking about a better solution.

Continue Reading

Switching to WordPress

Posted: October 3, 2009 at 10:32 pm

I finally succumbed to ease of use and switched from my bespoke PivotLog installation to WordPress. I thoroughly enjoyed Pivot but when switching from Textdrive to Amazon EC2 I had to change and migrate so many things that I settled for the easier solution; WordPress.

Continue Reading

Changes to this site

Posted: October 2, 2009 at 9:41 pm

As explained in one of the first posts on this blog this site is basically just one big Atom feed that gets transformed into this blog by using a bit of Apache content negotiation and client side XSLT. Besides some issues with browsers ignoring client side XSLT in a feed and forcing their own rendition of my feed which was fixed by inserting 512 bytes of crud to throw of the feed sniffing this approach has worked fine for the last four years.

Continue Reading

A Query Engine for PHP

Posted: October 2, 2009 at 9:40 pm

Looking at the code in the previous entry wasn’t exactly a pleasant aesthetic experience (sorry for that, bit of a botched job) so for my new project, an implementation of the NIST RBAC model in PHP, I decided to code a nice generic PHP query engine. The Query Engine takes a number of arguments like the SQL query, the arguments for the query (to be passed into prepared statements), the types of the arguments and whether the query is part of an overall transaction. The nice thing is that the QueryEngine function returns the results as an associative array using the database column names as the key value.

Continue Reading

Transactions, prepared statements and PHP mysqli

Posted: October 2, 2009 at 9:39 pm

While working with my good friend Arnold Consten on his new PHP application we came across some nice learning points for dealing with mysqli transactions and prepared statements. It turns out that the order of events is very specific for transactions and prepared statements to work correctly together:

Continue Reading

Templates, template engines and PHP

Posted: October 2, 2009 at 6:48 pm

In the process of developing Lilliput CMS I had to think about how to do templating with PHP. There’s a lot of material available regarding PHP and templating and most of it is really weird. Having had a look at the Top 25 PHP template engines I can’t for the life of me understand why I would want to use something like Smarty, Savant or phptal. Obviously a lot of love and attention has been poured into these solutions but I can’t escape the feeling that these template engines are recreating PHP and its innate templating function. This feeling was confirmed when reading the “Templates and template engines” article on the php patterns website.

Continue Reading