PHP Conference Nagoya 2025

Gettext

add a note

User Contributed Notes 11 notes

up
56
marioandrea dot petruccelli at gmail dot com
6 years ago
How to use gettext on Windows.

If you use Linux start from the step 2 and consider cmd as linux shell.

1] First you have to download and install this
https://mlocati.github.io/articles/gettext-iconv-windows.html

Check all options during the installation and go on.


2] Create a index.php file into your website directory with this code inside:

<?php
echo _("Good Morning");
?>

3] Open cmd and move into your website folder using cd

4] Now create the .mo files directly from the php files using this command
xgettext -n index.php

Use xgettext -help to see how to include more php files.

5] Once finished will be generate a file called messages.mo. Now you have to set the language and the charset. Open messages.mo with notepad and edit the lines:

- "Language: \n" BECOMES "Language fr\n"
- "Content-Type: text/plain; charset=CHARSET\n" BECOMES "Content-Type: text/plain; charset=UTF-8\n"

6] Remember to translate every line where is present msgstr "", translating the instance msgid into language you have chosen. For instance:

#: index.php:2
msgid "Good Morning"
msgstr "Bonjour"

7] Once finished open cmd and move into your website folder using cd and then type
msgfmt messages.po

This will make a file called messages.mo . Is a binary version of the messages.po file

8] Now you have to create a folder structure like this into your website folder. Do this for each language you want to add.

website/locale/fr_FR/LC_MESSAGES

9] Move messages.mo and messages.po in locale/fr_FR/LC_MESSAGES

10] Now edit the index.php as follows

<?php

$locale
= "fr_FR";

if (
defined('LC_MESSAGES')) {
setlocale(LC_MESSAGES, $locale); // Linux
bindtextdomain("messages", "./locale");
} else {
putenv("LC_ALL={$locale}"); // windows
bindtextdomain("messages", ".\locale");
}


textdomain("messages");

echo
_("Good Morning");
?>

11] Open index.php in your browser and if you will see "Bonjour" it means everything is okay. If not, start from the step 2 again.

If it was usefull for you vote up!

Visit my github profile https://github.com/TheoRelativity/
up
23
jpatokal at iki dot fi
15 years ago
Warning for Linux (Ubuntu) users! Your system will *only* support the locales installed on your OS, in the *exact* format given by your OS. (See also the PHP setlocale man page.) To get a list of them, enter locale -a, which will give you something like this:

C
en_US.utf8
ja_JP.utf8
POSIX

So this machine only has English and Japanese! To add eg. Finnish, install the package:

sudo apt-get install language-pack-fi-base

Rerun locale -a, and "fi_FI.utf8" should appear. Make sure you're using the same name in your PHP code:

setlocale(LC_ALL, "fi_FI.utf8");

Adjust your po paths so that they match, e.g. "./locale/fi_FI.utf8/LC_MESSAGES/messages.po".

Now restart Apache, and it should finally work. Figuring this out took quite a while...
up
18
sasq1 at go2 dot pl
15 years ago
And what about pgettext and npgettext? They are there in the gettext documentation, but there aren't in PHP. They're very useful if you have the same messages for translation, but in different contexts, so they should be translated separately and probably differently.

Fortunately, there is a simple work-around, which may help:
From the gettext.h header one can find out that pgettext() is only a macro calling dcgettext() internally with properly mangled parameters - msgctxt and msgid are glued together with use of ASCII char 4 [EOT, End Of Text]. The same way they're written in .mo files, so it's possible to refer them this way.

Here's my "emulated" pgettext() function:

<?php
if (!function_exists('pgettext')) {

function
pgettext($context, $msgid)
{
$contextString = "{$context}\004{$msgid}";
$translation = dcgettext('messages', contextString,LC_MESSAGES);
if (
$translation == $contextString) return $msgid;
else return
$translation;
}

}
?>

By default, xgettext doesn't support pgettext function for PHP source files. But there is a parameter which can work-around it. Here's how I call xgettext:

xgettext --force-po --keyword="pgettext:1c,2" -c -o messages.po sourceFile.php

In sourceFile.php I use the following test code:

pgettext('menu', 'Open'); //Substitute "Otwórz"
pgettext('forum', 'Open'); //Substitute "Otwarty", different context

Generated .po file fragment:

msgctxt "menu"
msgid "Open"
msgstr "Otwórz"

msgctxt "forum"
msgctxt "Open"
msgstr "Otwarty"

I've tested it out and everything works fine :-) If anyone have some further suggestions or fixes, please write ;-)
up
5
rainwalker at seznam dot cz
15 years ago
My PHP app was made for UTF-8 but i had a problem that gettext always returned all texts in ISO-8859-2 instead of UTF-8.

Then i found out that you have to set locale in PHP exactly to encoding you request. So when i wanted czech UTF-8 i used:

setlocale(LC_ALL, "cs_CZ.UTF-8");

Now it works...
up
12
yuricardenas at gmail dot com
15 years ago
*An important thing to keep in mind*:
Do not forget to set the charset in .po file!
For example:

"Content-Type: text/plain; charset=UTF-8\n"

Then PHP will be able to find the .mo file you generated, using msgfmt, from the .po file WITH CHARSET SET.

Because of this I've wasted a lot of time debugging my code, testing every single little changes people suggested over this manual and Internet:

<?php

//this:
setlocale( LC_MESSAGES, 'pt_BR')
//or this:
setlocale( LC_MESSAGES, 'pt_BR.utf8')
//or this:
setlocale( LC_MESSAGES, '')

//this:
putenv("LANG=pt_BR.utf8");
//or this:
putenv("LANGUAGE=pt_BR.utf8");

//this:
bindtextdomain('mydomain', dirname(__FILE__).'/locale');
//or this:
bindtextdomain("*", dirname(__FILE__).'/locale');
//or this:
bindtextdomain('*', dirname(__FILE__).'/locale');

//setting or not "bind_textdomain_codeset()":
bind_textdomain_codeset("mydomain", 'UTF-8');
?>

As well as what locale directory name to set:
./locale/pt_BR.UTF8/LC_MESSAGES/mydomain.mo
or
./locale/pt_BR/LC_MESSAGES/mydomain.mo
or
./locale/pt/LC_MESSAGES/mydomain.mo

Finally, the code which brought the right translated strings (also with the correct charset) was:

<?php

$directory
= dirname(__FILE__).'/locale';
$domain = 'mydomain';
$locale ="pt_BR.utf8";

//putenv("LANG=".$locale); //not needed for my tests, but people say it's useful for windows

setlocale( LC_MESSAGES, $locale);
bindtextdomain($domain, $directory);
textdomain($domain);
bind_textdomain_codeset($domain, 'UTF-8');
?>

And the three directory's names worked out, using the pt_BR.utf8 locale. (My tests were made restarting Apache then trying each directory).

I hope to help someone else not to waste as much time as I've wasted... =P

Using:
Ubuntu 8.04 (hardy)
Apache 2.2.8
PHP 5.2.4-2ubuntu5.6
up
3
Johnny
10 years ago
Please also be noticed that the server will cache the .mo file on first loading and then "seldom" reload it, so your update to .mo file will not be revealed. Some solution to clear the cache from web is to restart the apache server, or to use another name for the textdomain everytime. These are tedious. I cannot find better solution yet.
up
1
php at devicenull dot org
16 years ago
To get this working properly on debian, install the locales-all package. I just spent a few hours finding a bug where it wouldn't work because that package is missing
up
0
maximran800 at gmail dot com
6 years ago
I have some problems setting geettext.
I ahve tried all the above example in noted, but none work.
However, i found a nice trick here and it work!!:
My platform is freebsd.

below is the post:
http://php.net/manual/en/function.gettext.php

Gettext translations are cached. If you change *.mo files your page may not be translated as expected. Here's simple workaround which does not require restarting webserver (I know, this is just a dirty hack):

<?php
function initialize_i18n($locale) {
putenv('LANG='.$locale);
setlocale(LC_ALL,"");
setlocale(LC_MESSAGES,$locale);
setlocale(LC_CTYPE,$locale);
$domains = glob($locales_root.'/'.$locale.'/LC_MESSAGES/messages-*.mo');
$current = basename($domains[0],'.mo');
$timestamp = preg_replace('{messages-}i','',$current);
bindtextdomain($current,$locales_root);
textdomain($current);
}
?>

to make this work you have to put your locale inside file messages-[unix_time].mo and use this name (without .mo) as your domain to fool caching mechanism (domain names differ)

msgfmt messages.po -o messages-`date +%s`.mo

for me this works fine (although this is not very elegant solution)
up
0
afrimuchkov at yandex dot ru
7 years ago
Soo.... It was pretty hard)
Finaly:
1)Working if dir is "en", not "en_GB" or "en_GB.utf8". Only "en".
2)But use setlocale(LC_MESSAGES, "en_GB.utf8");
up
-2
Anonymous
9 years ago
for all they try to use a non-alpha-char in a domain name: DONT TRY THIS !!! we have search some hours to realized that "foo-bar" is not a good idea for a domain name. sometimes we got a correct translation, and sometimes not. so just use only the characters A-Z for a domain name!
up
-1
cucurella at gmail dot com
4 years ago
<?php

// read directly .po files

function T_($contenido) {

global
$language;

if (
$language == "en") { $translation_file = "langs/en.po"; }
if (
$language == "es") { $translation_file = "langs/es.po"; }

if (
file_exists("$translation_file")) {
$IDIOMA_CONTENT = file("$translation_file");
$num_lineas = count($IDIOMA_CONTENT);
} else {
return
$contenido;
}

for (
$i = 0; $i <= $num_lineas; $i++) {
$linea1 = $IDIOMA_CONTENT[$i];
$linea1 = rtrim($linea1);
$string6 = substr($linea1, 0, 6);

if (
$string6 == "msgid ") {
$orig = str_replace($string6, "", $linea1);
$orig = str_replace("\"", "", $orig);

if (
"$orig" == "$contenido") {
$linea2 = $IDIOMA_CONTENT[$i + 1];
$linea2 = rtrim($linea2);
$string7 = substr($linea2, 0, 7);

if (
$string7 == "msgstr ") {
$trad = str_replace($string7, "", $linea2);
$trad = str_replace("\"", "", $trad);
return(
"$trad");
}
} else {
$i = $i + 3;
}
}
}

return(
"$contenido");
}

$language = "es";

print
T_("I on puc comprar el meu domini ?");
print
T_("Aquí tens alguns links...");

?>
To Top