Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to internationalize simple PHP website

I have to develop a pretty simple php website so I don't need framework. But it's must support multi language (EN/FR/CHINESE). I have looked for php built in system and I found two ways :

  • intl module from php5.3 (http://php.net/manual/fr/book.intl.php)
  • gettext (http://php.net/manual/fr/book.gettext.php)

I have no experience in i18n without framework, so any advices about what's the simplest way to support multi language ?

At end I just need a function that search translation into file (one file by language). EQ : trans('hello');

=> en.yaml (yaml or not, it's an example)

hello: "Hello world!"

=> fr.yaml

hello: "Bonjour tout le monde !"

And if possible I prefer Pure PHP implementations

like image 407
Kakawait Avatar asked Aug 05 '11 08:08

Kakawait


People also ask

How can I translate a PHP language?

php class Translator { private $language = 'en'; private $lang = array(); public function __construct($language){ $this->language = $language; } private function findString($str) { if (array_key_exists($str, $this->lang[$this->language])) { echo $this->lang[$this->language][$str]; return; } echo $str; } private ...

What is I18n PHP?

With I18n, all of the text strings displayed to the user from the application are replaced by function calls which can dynamically load translated strings for any language the user selects. All of the code for Meeting Planner is written in the Yii2 Framework for PHP, which has built-in support for I18n.

How do I add PHP multilingual support to my website?

Ways to add multi-language support to a website There are various ways to enable Multi-language support for an application. By having duplicate pages with the same content in the required languages. By managing language configuration (localization) with files containing texts in different languages.


2 Answers

Although ext/gettext and ext/intl are both related to i18 (internationalization), gettext deals with translation while intl deals with internationalizing things like number and date display, sorting orders and transliteration. So you'd actually need both for a complete i18-solution. Depending on your needs you may come up with an home-brew solution relying on the extensions mentioned above or your use components provided by some framework:

  • Translation
    • Symfony 2 Translation component: https://github.com/symfony/Translation
    • Zend Framework Zend_Translate
  • Internationalization
    • Zend Framework Zend_Locale

If you only need translation and the site is simple enough, perhaps your simple solution (reading a translation configuration file into an PHP array, using a simple function to retrieve a token) might be the easiest.

The most simple solution I can think of is:

$translation = array(
    'Hello world!' => array(
        'fr' => 'Bonjour tout le monde!',
        'de' => 'Hallo Welt!'
    )
);

if (!function_exists('gettext')) {
    function _($token, $lang = null) {
        global $translation;
        if (   empty($lang)
            || !array_key_exists($token, $translation)
            || !array_key_exists($lang, $translation[$token])
        ) {
            return $token;
        } else {
            return $translation[$token][$lang];
        }
    }
}

echo _('Hello World!');
like image 153
Stefan Gehrig Avatar answered Oct 22 '22 14:10

Stefan Gehrig


I know this is an old question, but I feel that the answers are lacking a more hands-on approach from start to finish. This is what I did to get translation working using PHP's gettext library and Poedit without using any additional PHP libraries on a Debian server:

Preparation step 1: Install gettext and the locales on the server

I am not sure how this is done with other operating systems, but for Debian, you do:

sudo apt-get install gettext
sudo dpkg-reconfigure locales

Edit: I assumed Ubuntu would be the same as Debian, but apparently it's slightly different. See this page for instructions for installing locales on Ubuntu.

Make sure you select all of the locales that you want to use. You should then see something like:

Generating locales (this might take a while)...
  en_US.UTF-8... done
  es_MX.UTF-8... done
  fr_FR.UTF-8... done
  zh_CN.UTF-8... done
Generation complete.

Note: Make sure you select the right variants and character encodings (most likely UTF-8) for each language. If you install es_MX.UTF-8 and try to use es_ES.UTF-8 or es_MX.ISO-8859-1 it won't work.

Preparation step 2: Install Poedit on the translators' computers

Poedit is available from the software repository for most Linux operating systems. For Debian-based, just execute:

sudo apt-get install poedit

For Windows and Mac, go to: https://poedit.net/download


Start coding:

Ok, now you're ready to get started coding. I wrote the following gettext() wrapper function to translate both singular and plurals:

function __($text, $plural=null, $number=null) {
    if (!isset($plural)) {
        return _($text);
    }
    return ngettext($text, $plural, $number);
}

Example usage:

// Singular
echo __('Hello world');

// Plural
$exp = 3;
printf(
    __(
        'Your account will expire in %d day',
        'Your account will expire in %d days',
        $exp
    ),
    $exp
);

This will work for all languages, not only languages where plural is anything where n != 1 - this includes languages with multiple plural types.

You can also add translator notes like this:

/** NOTE: The name Coconut Hotel is a brand name and shouldn't be 
 translated.
*/
echo __('Welcome to Coconut Hotel');

You can change the text from NOTE to whatever you want, but you will have to alter it in the shell script below. Important: The translators note must be part of a comment on the line immediately preceding the __() function or it won't be picked up when we scan the PHP files for translatable strings.

// Warning! THIS WILL NOT WORK!
/* NOTE: This translator's note will not be picked up because it is
not immediately preceding the __() function. */
printf(
    __(
        'Your account will expire in %d day',
        'Your account will expire in %d days',
        $exp
    ),
    $exp
);
// Warning! THIS WILL NOT WORK!

After you are ready to send the strings off to the translators, save the following as a shell script (e.g. update.sh) in your application's root directory:

#!/bin/sh
find . -iname "*.php" | xargs xgettext --add-comments=NOTE --keyword=__:1,2 --keyword=__ --from-code=UTF-8 -o i18n.pot
find . -name '*.po' | xargs -I{} msgmerge -U {} i18n.pot

To execute it, just do:

cd /path/to/script && sh update.sh

This will recursively scan for all PHP files in that directory and create a .pot file (I called it i18n.pot, but feel free to name it whatever you like) and update any existing .po files it finds with the new strings.

We then need to create the directories that all the locale files will be stored, one for each locale. They need to be of the format ./locale/{locale}/LC_MESSAGES. For example:

cd /path/to/your/project
mkdir -p ./locale/en_US.UTF-8/LC_MESSAGES
mkdir -p ./locale/es_MX.UTF-8/LC_MESSAGES
# ...etc.

You need to decide on a text domain to use. This can be anything you want, but the script will look for a file called {yourTextDomain}.mo within the LC_MESSAGES folder for that language. Put the following in your PHP script:

define('TEXT_DOMAIN', 'yourdomain');
bindtextdomain(TEXT_DOMAIN, __DIR__.'/locale');
textdomain(TEXT_DOMAIN);
bind_textdomain_codeset(TEXT_DOMAIN, 'UTF-8');

Then to actually switch to another locale, do:

$lang = 'es_MX.UTF-8'; // Change this to the language you want to use
if (setlocale(LC_ALL, $lang) === false) {
    throw new Exception("Server error: The $lang locale is not installed");
}
putenv('LC_ALL='.$lang));

Initially, you send the .pot file generated by the script above to the translators. They then open Poedit and click on File > New from POT/PO file. When they save it, they need to save it as {yourTextDomain}.po. The {yourTextDomain} needs to be exactly the same as the text domain you have in your PHP script. When they save it, it will automatically create both the .po file and the .mo file. Both of these need to be saved in that language's LC_MESSAGES directory when they are done translating.

Now when you update the strings in your PHP file, just re-execute the shell script and send the newly updated .po files to the translators. They then translate the strings and both the .po and .mo files need to be re-uploaded.

That's it. It may seem slightly difficult to get set up, but once you have it up and running, it's really easy.

like image 37
Mike Avatar answered Oct 22 '22 13:10

Mike