GetText on Windows 10 XAMPP doesn't translate my text

五迷三道 提交于 2021-02-11 14:30:23

问题


GetText drive me crazy since few hours, I can't make it work.

Here is the content of my message.po file:

msgid ""
msgstr ""
"Project-Id-Version: TEST\n"
"POT-Creation-Date: 2020-06-13 00:44+0200\n"
"PO-Revision-Date: 2020-06-13 00:53+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: nl_BE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "foo"
msgstr "bar"

msgid "fizz"
msgstr "buzz"

It is stored into

./locale/nl_BE/LC_MESSAGES/messages.po

I compile it into .MO file using msgfmt command.

And try to make it work with the following PHP code

<?php
putenv('LC_ALL=nl_BE');
setlocale(LC_ALL, 'nl_BE');
bindtextdomain("messages", "./locale");
textdomain("messages");
echo _("foo");
echo _("fizz");

Output :

>php test.php
foofizz

Do you get the same result ? any idea why it doesn't work ?

Not sure if it is related but I'm on Windows 10. and my PHP version is PHP 7.2.28 (installed with XAMPP)

Also, phpinfo() states that gettext is enabled:

gettext

GetText Support => enabled

Any help really appreciated!


回答1:


The actual "error" here is that you do not check the return value of setlocale().

I don't know enough PHP, so I explain it for C.

The function setlocale() has three different flavors:

  1. setlocale(LC_ALL, ""): Set the locale according to the current user's environment variables. That will rarely make sense for PHP because the current user is the user running the web server. In case of success it will return a string identifying the current locale setting.
  2. setlocale(LC_ALL, "nl_BE"): Try to select the locale "nl_BE". If the locale definition "nl_BE" is not valid for the server (= it is not installed), it will return NULL. In case of success, it will return a string identifying the current locale setting.
  3. setlocale(LC_ALL, NULL): This invocation queries the current locale setting for the categoy LC_ALL. And this is what the gettext runtime does, when you request a translation of a string. It first queries the currently selected locale, so that it can load the correct translation catalog.

What has happened?

You tried to switch to the locale nl_BE with flavor #2 but that actually failed, because the locale nl_BE is not installed on the server. Failure means that the locale does not get changed. Whatever locale was selected before, will still be selected.

Later, you try to retrieve a translated string with the gettext shortcut _("foobar"). The gettext runtime will use flavor #3 for querying the currently selected locale. That will not return nl_BE because the flavor #2 call had failed due to the missing locale installation, and then the original untranslated string is returned instead. More precisely: The last locale that was successfully selected gets used.

From a web application's point of view that behavior may look stupid. Why do you need locale definitions when you provide all the translations in the po/mo files?

Gettext wasn't made for that. It was made for desktop applications.

In desktop applications, it makes perfect sense to ignore the return value of setlocale() at program startup. If the desired locale is not installed, falling back to the default locale (en_US aka C aka POSIX) makes perfect sense.

But why does the gettext runtime "refuse" to use an .mo file that ships with the application just because the runtime system does not have the corresponding locale installed?

There is not just the locale category LC_MESSAGES for the translations that ship with your program. There are other categories like LC_TIME which contain translations for weekday and month names, LC_CURRENCY for currency names. And the LC_MESSAGES category is also used for picking translation for system messages like "no such file or directory".

The gettext runtime ensures that program messages are always consistent and do not mix languages and character encodings. For many web applications this seem too strict but gettext was not made for web applications.

If this strictness does not fit your use case, write your own little gettext runtime that ignores the system locale. For Perl, this already exists: https://metacpan.org/pod/Locale::gettext_dumb (yes, I am the author). Or suggest to the maintainers of the PHP gettext environment a similar solution that simply ignores whether the selected locale is actually installed on the server.




回答2:


This is a known Windows PHP problem sind 2009 and noone in the PHP teams seems to care.

I have finally after a full day found a workaround i can live with.

NONE OF THE answers here or elsewhere did help me and also not the abandoned gettext replacement script - but it helped me to find the problem.

I even changed the windows locale - did not help - And I DO NOT want to change my Windows language as i need to debug the instant language change on my website scripts and having to reboot for each translage change is no option ;-)

I have installed the other languages and locals and nothing helped.

The problem is that you can do what you want but at leat my WIN10 64pro DID never ever look into any other folder than that of my primary windows language which is

/de_DE/LC_MESSAGES 

I removed anything not relevant to the function of gettext and
these five lines remained as a minimum reproducable working example.

$path= "C:/xampp/htdocs/locale";
$domain = 'messages';
bindtextdomain($domain, $path);
textdomain($domain);
print _("The string you want to translate" );

So my idea was to change anything in these 5 lines to get different languages displayed

The path can also be relative as e.g. realpath('./locale'));

I found out that after bindtextdomain was called with a working .mp file you have to RESTART APACHE!! to return to the default values again as obviously the language strings are cached anywhere.

So the solution to change it to get different translation was not working.

So i looked into the second argument: $domain And the tests showed that this is perfectly suited for my puropse.

After changing it to something wrong and reloading the original none translated texts were displayed. After changing it back to the correct one the translated texts were immediately displayed.

I felt GREAT

Now i wanted to display more than two languages.

I renamed the serbian-cyrillic! .mo and .po files of the pig example code to message1.po and message1.mo and moved themm into the

/de_DE/LC_MESSAGES 

I had now 4 files in there

message.mo      //german translation compiled
message.po     //german translation original
message1.po    // serbian cyrillic original
message1.po    // serbian cyrillic original

and set the second line to "message1"

  $domain = 'messages1';

AND the cyrillic code was displyed!!

I have NEITHER a cyrillic language loaded into my windows no serbian local , nothing. Only English and GERMAN.

This means that at least in Windows 10 with german installed it is not be necessary to install the language or locale in order to get gettext working! (I do not knoy if this is also true für chinese... but that is a different story)

So if you have a remote Linux server and want to test your internationalization all you have to do is

1.) make a copy of all messages.po and -mo files
rename all of them to a desired name

e.g. messages_de.po /..mo or german.po .. whatever you like

put all those files into the folder with the name of your installed windows language

e.g. /de_DE/ or /en_US/

and change the message variable to the desired value

You might say "I DO NOT WANT TO CHANGE MY CODE OM DEPLOYMENT"

Then you just set a environment variable in Apache and and only if the script is run on your localhost windows machine with the environment context "dev" then run the Windows workaround. On your remote production system you run the standard gettext initialization.

if  (getenv("SERVER_CONTEXT") == "dev")  
{
    switch (GET('lang')) { 
    case "de":
    $domain="messages_de";
    break;
    case "fr": 
    $domain = "messages_fr"; 
     break;
     default:  //untranslated english texts  
     $domain = ""; 

}
        $path= "C:/xampp/htdocs/locale";
        bindtextdomain($domain, $path);
        textdomain($domain);

}
 elseif (getenv("SERVER_CONTEXT") == "dev"))  

{ the standard gettext initialization for the remote host }


        print _("The string you want to translate" );

On your windows machine you should have the following 4 files additionally in your default language directory:

messages_fr.mo     
messages_fr.po     
messages_de.po    
messages_de.po    

NOTE: For the step of automatic detection of the system you need two small config changes:

In the Apache configuration set the follwing variable in your virtualhost of the Windows machine:

SetEnv SERVER_CONTEXT "dev"

and in the Apache configuration of the virthost of your remote production system:

SetEnv SERVER_CONTEXT "prod"


来源:https://stackoverflow.com/questions/62353692/gettext-on-windows-10-xampp-doesnt-translate-my-text

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!