Best Way to Internationalize Simple PHP Website

Best way to internationalize simple PHP website

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!');

best practice for internationalizing white-labeled PHP code

There are two tried and true methods for PHP internationalization.

The most prevalent is to create huge language arrays like so:

define('MSG__GREETING', 1);

$lang['en_US'] = array(MSG__GREETING => 'Hello, and welcome to ');
$lang['fr_FR'] = array(MSG__GREETING => 'Bonjour, et bienvenue à ');

$selectedLang = 'fr_FR';
echo $lang[$selectedLang][MSG__GREETING] . 'Fhloston Paradise';

Unfortunately, this gets very tedious very quickly.

The ideal method, which I've used numerous times, is the accepted method of internationalization for Linux apps: il8n, via PHP's gettext extension.

With this method, you basically end up doing stuff like this:

setenv('LANG=fr_FR');
echo _('Hello, and welcome to ') . 'Fhloston Paradise';

and then in il8n files (called .po) you have each translation. It's actually much easier to maintain and extend over the long run, especially since you can just email your .po files to various translators and they just fill in the blanks, as it were... no coding skills necessary.

Here's a tutorial to get you started: http://kunststube.net/frontback/

Whats the best & fastest method to support multi language in PHP Application

This is a complicated problem and its not always as obvious as you might think. You may in some cases, with right to left languages or particular cultural reasons, need to develop separate layouts for a particular region.

Regardless of which method you choose, you will want to cache all of or parts of your pages and use a cached version if available before regenerating the page again.

I would probably avoid 3 and 4. You don't want to be reading from the disk more than you have to. If you can cache translation arrays in memcached, you can save yourself disk access in loading translation tables.

Best way to achieve multilingual URLs?

I have something similar in an application I'm developing.

Each page has a unique ID which is matched to a slug, page title, meta description and such for each language in a table.

As for the people saying it's not standard practice and not to bother with it I disagree as having nicely translated URLs can help your SEO in different languages.

What is the best way of i18n in PHP

I would go for gettext. As it is a de-facto standard and is used in many applications in different languages. Many people use it means bigger community and good support.

If you search for gettext in stackoverflow, you'll get good resources and examples.

Getting started with it in PHP:
http://www.onlamp.com/pub/a/php/2002/06/13/php.html

Best practice multi language website

Topic's premise

There are three distinct aspects in a multilingual site:

  • interface translation
  • content
  • url routing

While they all interconnected in different ways, from CMS point of view they are managed using different UI elements and stored differently. You seem to be confident in your implementation and understanding of the first two. The question was about the latter aspect - "URL Translation? Should we do this or not? and in what way?"

What the URL can be made of?

A very important thing is, don't get fancy with IDN. Instead favor transliteration (also: transcription and romanization). While at first glance IDN seems viable option for international URLs, it actually does not work as advertised for two reasons:

  • some browsers will turn the non-ASCII chars like 'ч' or 'ž' into '%D1%87' and '%C5%BE'
  • if user has custom themes, the theme's font is very likely to not have symbols for those letters

I actually tried to IDN approach few years ago in a Yii based project (horrible framework, IMHO). I encountered both of the above mentioned problems before scraping that solution. Also, I suspect that it might be an attack vector.

Available options ... as I see them.

Basically you have two choices, that could be abstracted as:

  • http://site.tld/[:query]: where [:query] determines both language and content choice

  • http://site.tld/[:language]/[:query]: where [:language] part of URL defines the choice of language and [:query] is used only to identify the content

Query is Α and Ω ..

Lets say you pick http://site.tld/[:query].

In that case you have one primary source of language: the content of [:query] segment; and two additional sources:

  • value $_COOKIE['lang'] for that particular browser
  • list of languages in HTTP Accept-Language (1), (2) header

First, you need to match the query to one of defined routing patterns (if your pick is Laravel, then read here). On successful match of pattern you then need to find the language.

You would have to go through all the segments of the pattern. Find the potential translations for all of those segments and determine which language was used. The two additional sources (cookie and header) would be used to resolve routing conflicts, when (not "if") they arise.

Take for example: http://site.tld/blog/novinka.

That's transliteration of "блог, новинка", that in English means approximately "blog", "latest".

As you can already notice, in Russian "блог" will be transliterated as "blog". Which means that for the first part of [:query] you (in the best case scenario) will end up with ['en', 'ru'] list of possible languages. Then you take next segment - "novinka". That might have only one language on the list of possibilities: ['ru'].

When the list has one item, you have successfully found the language.

But if you end up with 2 (example: Russian and Ukrainian) or more possibilities .. or 0 possibilities, as a case might be. You will have to use cookie and/or header to find the correct option.

And if all else fails, you pick the site's default language.

Language as parameter

The alternative is to use URL, that can be defined as http://site.tld/[:language]/[:query]. In this case, when translating query, you do not need to guess the language, because at that point you already know which to use.

There is also a secondary source of language: the cookie value. But here there is no point in messing with Accept-Language header, because you are not dealing with unknown amount of possible languages in case of "cold start" (when user first time opens site with custom query).

Instead you have 3 simple, prioritized options:

  1. if [:language] segment is set, use it
  2. if $_COOKIE['lang'] is set, use it
  3. use default language

When you have the language, you simply attempt to translate the query, and if translation fails, use the "default value" for that particular segment (based on routing results).

Isn't here a third option?

Yes, technically you can combine both approaches, but that would complicate the process and only accommodate people who want to manually change URL of http://site.tld/en/news to http://site.tld/de/news and expect the news page to change to German.

But even this case could probable be mitigated using cookie value (which would contain information about previous choice of language), to implement with less magic and hope.

Which approach to use?

As you might already guessed, I would recommend http://site.tld/[:language]/[:query] as the more sensible option.

Also in real word situation you would have 3rd major part in URL: "title". As in name of the product in online shop or headline of article in news site.

Example: http://site.tld/en/news/article/121415/EU-as-global-reserve-currency

In this case '/news/article/121415' would be the query, and the 'EU-as-global-reserve-currency' is title. Purely for SEO purposes.

Can it be done in Laravel?

Kinda, but not by default.

I am not too familiar with it, but from what I have seen, Laravel uses simple pattern-based routing mechanism. To implement multilingual URLs you will probably have to extend core class(es), because multilingual routing need access to different forms of storage (database, cache and/or configuration files).

It's routed. What now?

As a result of all you would end up with two valuable pieces of information: current language and translated segments of query. These values then can be used to dispatch to the class(es) which will produce the result.

Basically, the following URL: http://site.tld/ru/blog/novinka (or the version without '/ru') gets turned into something like

$parameters = [
'language' => 'ru',
'classname' => 'blog',
'method' => 'latest',
];

Which you just use for dispatching:

$instance = new {$parameter['classname']};
$instance->{'get'.$parameters['method']}( $parameters );

.. or some variation of it, depending on the particular implementation.

what is the most efficient way to add multiple languages to a PHP website

I am a CakePHP developer. All the strings are inside the translate function __('some expression').

https://book.cakephp.org/4/en/core-libraries/internationalization-and-localization.html

Using a console command all the strings are parsed from PHP/view files and a .po file is generated. Using a PO Editor I translate the expressions.

For the strings stored in the database, for example books in a library, you should have a separate column for each language: book_name_en, book_name_fr.
After checking $_GET['lang'] for example, you select the column for that language. The .po files should be only for static strings.



Related Topics



Leave a reply



Submit