I follow the cakePHP book to config the i18n and l10n of my site. Suppose I have English, Spanish, German.
Browsers usually have a list of languages ordered by priority. If at the first position of the list appears a language that is configured on my site all works fine. But if, for example, the list is this: French, German, Spanish, English; localization fails and shows the i18n identifiers that I used in my code.
I would like that in this case the page appears translated in German, because in the list is the first language that my site can provide. This would be the perfect solution but at least I would like that I can configure a default language (for example, English) if the first language of the browser list is unknown by my page.
What I can do to achieve this and where should put the code?
Thank you in advance.
Well, I did the next in app/Config/bootstrap.php:
$browserLangs = explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']);
$find = 'eng';
foreach($browserLangs as $browserLang) {
$lang = substr($browserLang,0,2);
var_dump($lang);
if ($lang == 'en') {
$find = 'eng';
break;
} else if ($lang == 'es') {
$find = 'spa';
break;
} else if ($lang == 'ca') {
$find = 'cat';
break;
}
}
Configure::write('Config.language', $find);
In Accept Language header we have a string with all the list of languages of the browser. Then I define English as default, and try to find other language in the list, breaking the bucle when I get one.
I put this as an answer because it works, but its dirty because:
I don't know if this is the best way and the best place,
this is very static, you have to put the languages one by one and when you add another language you have to update the algorithm. The best solution will check automatically for the locales defined on the cake application, which I don't know how to ask for.
Perhaps this code should be defined as a plugin, component or something like that.
Perhaps someone can improve this or post another better solution as a new answer.
You will have to open the created .po file, with an editor (for example: http://poedit.net) there you can create multiple language versions.
Dont forget to change the plural forms for every language, for german it's nplurals=2; plural=(n != 1), here a list: http://translate.sourceforge.net/wiki/l10n/pluralforms.
After that, you'll have to save the language file in app/Locale/deu/LC_MESSAGES/...
You should have a default.mo and default.po file in that folder at the end.
Related
I have a non-Symfony PHP project in which I want to use Symfony Translate component with MoFileLoader. I have setup PoEdit so it recognizes trans("string") function when parsing file looking for translations.
This part of code <?php echo $translator->trans("Welcome"); ?> is parsed as it should be.
Problem is I can't make PoEdit to recognize this as plural string <?php echo $translator->trans('Found {result} search result', ['{result}' => 1]); ?>
1 is clearly number of results and with that data I want to be able to use correct plural string.
Big issue is that my main language is Croatian which plural form nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); is way more complex than English one nplurals=2; plural=(n!=1)
I have added this Sources keywords in PoEdit
trans
trans:1,2
I already tried to use PHP and gettext to accomplish this but it is just not working for me due to server restrictions and other problems with gettex, Apache and PHP.
This is the way I'm using Symfony Translate component:
<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\MoFileLoader;
$lang = "hr";
$translator = new Translator($lang, new MessageSelector());
$translator->setFallbackLocales(['en']);
$translator->addLoader('mo', new MoFileLoader());
$translator->addResource('mo', "locale/$lang/LC_MESSAGES/messages.mo", $lang);
echo $translator->trans("Welcome");
echo $translator->trans('Found {result} search result', ['{result}' => 1]);
?>
Is there a way to make PoEdit recognize this as plural string:
<?php echo $translator->trans('Found {result} search result', ['{result}' => 1]); ?>
trans
trans:1,2
You gave two conflicting instructions there - one to treat trans as singular, one as plural. Additionally, the plural one, trans:1,2 doesn't make sense: you're asking to extract 1st parameter as singular and 2nd as plural, but you don't even have two string parameters. See documentation for the syntax.
$translator->trans('Found {result} search result', ['{result}' => 1]);
There's no way trans() could work like this — how would it know what number to use to determine the plural version to choose? And how would it know what to use for plural version in English if it's nowhere to be found in the code?
You need to use transChoice() instead, as documented in Symfony docs. Plurals are always a moderately complicated subject, you absolutely must read the documentation to understand the concepts before writing any code.
I installed gettext on a server and want to use for a php script..
I use with 3 languages. The original website is written in english..
Other are french and Italian.
I can without problem change language to French or Italian.. But when I want load the en_EN.mo file to replace the origine text of the php file, the translation are not loaded.
I try many things.. Remove file, replace name.. restart apache..
Nothing.. it work perfectly with all other langage, but impossible to use a english file.
I create new language.. it work too.. but never the en_EN..
All my files langage are in the same tree style
locale
en_EN
LC_MESSAGES
en_EN.mo
fr_FR
LC_MESSAGES
fr_FR.mo
...
if I force to use..
$directory = './locale';
$domain ='en_EN';
$locale ='en_EN';
putenv('LC_ALL='.$locale);
setlocale(LC_ALL, $locale);
bindtextdomain($domain, $directory);
textdomain($domain);
bind_textdomain_codeset($domain, 'UTF-8');
no result..
My problem is that I have no idea how to debug it ? Where can I find a log or something to help ? Where see errors ?
It's strange that it work with all lang, but not english.. I use Poedit to create my file, I have create the file many time.. in the same way that other..
Please share your idea :)
I create new language.. it work too.. but never the en_EN..
You misunderstand what the locale string means. It is not “2 language code letters followed by the same letters in uppercase” (that wouldn’t make any sense, would it), it is “language code followed by country code” per ISO 3166. And “EN” is not only not a valid country code for any country that speaks English, it’s not a valid country at all. You’re asking WordPress to run under the English locale (presumably), but only providing it translation files for “English in a made-up country” locale that WP is never going to look for.
You’re thinking of en_US.
I have my php gettext default language in English let's say
I would like in one of my controller, to translate some words in 2 other languages and put them all in an array.
ideally I could do
$word_sv = gettext($word, 'sv_SV');
$word_fi = gettext($word, 'fi_FI');
but it doesn't exist.
Is the only way to change the overall gettext settings each time?
function setLang($lang){
putenv("LC_ALL=$lang");
setlocale(LC_ALL, $lang);
bindtextdomain("myPHPApp", "./locale");
textdomain("myPHPApp");
}
setLang('sv_SV');
$word_sv = gettext($word);
setLang('fi_FI');
$word_fi = gettext($word);
related: saw it on Google after : i18n with gettext but without the locale hassle?
Edit
here are the proposed answered solutions:
https://github.com/Philipp15b/php-i18n (seems best solution)
http://glotpress.trac.wordpress.org/browser/trunk/pomo (could use it if I find a good doc or tuto ;))
change locale on the fly, probably not good
I know the pains of using gettext, but its performance is what keeps me with it !
In your case, you might want to look at this little project ? i'm pretty sure this might help you !
this simply uses .ini files with translations, you can freely switch between files and echo the different languages for the same word.
https://github.com/Philipp15b/php-i18n
If you are bound to gettext here, Let the computer do the work for you.
You either have a list of words you then want to check against all languages, the do the wordlist for each language first. That will spare you some overhead to call the setlanguage function between each word and language.
If you want to go each language, each word, write the functions that way:
function gettext_by_lang($lang, $word) {
putenv("LC_ALL=$lang");
setlocale(LC_ALL, $lang);
bindtextdomain("myPHPApp", "./locale");
textdomain("myPHPApp");
return gettext($word);
}
$word_sv = gettext_by_lang('sv_SV', $word);
$word_fi = gettext_by_lang('fi_FI', $word);
This would at least make your code more compact. Another idea that comes to mind is using a a parser for PO and MO files so that you can check for the data.
In PHP one of that is shipping with Wordpress / Glotpress:
http://glotpress.trac.wordpress.org/browser/trunk/pomo
Maybe this helps. That library is being maintained.
I guess an obvious answer is to roll your own global function:
function getLocalText($string, $lang)
{
putenv("LC_ALL=$lang");
setlocale(LC_ALL, $lang);
bindtextdomain("myPHPApp", "./locale");
textdomain("myPHPApp");
return gettext($string);
}
$word_fi = getLocalText($word, 'fi_FI');
I am working on a project and there are going to be 3 different languages: English, French and Spanish. This will be defined when the user signs up.
Now in my config file I have the following:
define("DEFAULT_SLOGAN", "The default slogan will go here.");
Until I started realizing that I needed to accept different languages.
The user has an assigned language code (EN, FR, SP). How would I go about having different language strings for each page? Would I need to have something like this:
define("DEFAULT_SLOGAN_EN", "Slogan in english");
define("DEFAULT_SLOGAN_FR", "Slogan in french");
define("DEFAULT_SLOGAN_SP", "Slogan in spanish");
And for each string just have 3 different versions of it? Not too sure the best way to approach this.
Thanks!
A simple strategy that is often used are arrays:
$lang['en'] = array(
'DEFAULT_SLOGAN' => 'The default slogan will go here.',
);
// Same for other languages
Then, in your actual code, make sure that $lang is available (you can use the global keyword for this) and use these arrays.
A better approach would be to create
locale_definitions_EN.php containing
define("DEFAULT_SLOGAN", "The english default slogan will go here.");
local_definitions_FR.php containing
define("DEFAULT_SLOGAN", "The french default slogan will go here.");
etc. and then do something like
include "locale_definitions_$userLocale.php";
This has the advantage, that you don't need the memory to hold the unused other language constants.
In php (or maybe gettext in general), what does gettext do when it sees a variable to dynamic content?
I have 2 cases in mind.
1) Let's say I have <?=$user1?> poked John <?=$user2?>. Maybe in some language the order of the words is different. How does gettext handle that? (no, I'm not building facebook, that was just an example)
2) Let's say I store some categories in a database. They rarely, but they are store in a database. What would happen if I do <?php echo gettext($data['name']); ?> ? I would like the translators to translate those category names too, but does it have to be done in the database itself?
Thanks
Your best option is to use sprintf() function. Then you would use printf notation to handle dynamic content in your strings. Here is a function I found on here a while ago to handle this easily for you:
function translate()
{
$args = func_get_args();
$num = func_num_args();
$args[0] = gettext($args[0]);
if($num <= 1)
return $args[0];
return call_user_func_array('sprintf', $args);
}
Now for example 1, you would want to change the string to:
%s poked %s
Which you would input into the translate() function like this:
<?php echo translate('%s poked %s', $user1, $user2); ?>
You would parse out all translate() functions with poEdit. and then translate the string "%s poked %s" into whatever language you wanted, without modifying the %s string placeholders. Those would get replace upon output by the translate() function with user1 and user2 respectively. You can read more on sprintf() in the PHP Manual for more advanced usages.
For issue #2. You would need to create a static file which poEdit could parse containing the category names. For example misctranslations.php:
<?php
_('Cars');
_('Trains');
_('Airplanes');
Then have poEdit parse misctranslations.php. You would then be able to output the category name translation using <?php echo gettext($data['name']); ?>
To build a little on what Mark said... the only problem with the above solution is that the static list must be always maintained by hand and if you add a new string before all the others or you completely change an existing one, the soft you use for translating might confuse the new strings and you could lose some translations.
I'm actually writing an article about this (too little time to finish it anytime soon!) but my proposed answer goes something like this:
Gettext allows you to store the line number that the string appears in the code inside the .po file. If you change the string entirely, the .po editor will know that the string is not new but it is an old one (thanks to the line number).
My solution to this is to write a script that reads the database and creates a static file with all the gettext strings. The big difference to Mark's solution is to have the primary key (let's call it ID) on the database match the line number in the new file. In that case, if you completely change one original translation, the lines are still the same and your translator soft will recognize the strings.
Of course there might be newer and more intelligent .po editors out there but at least if yours is giving you trouble with newer strings then this will solve them.
My 2 cents.
If you have somewhere in your code:
<?=sprintf(_('%s poked %s'), $user1, $user2)?>
and one of your languages needs to swap the arguments it is very simple. Simply translate your code like this:
msgid "%s poked %s"
msgstr "%2$s translation_of_poked %1$s"