These days I'm working on two projects based on laravel and I have to implement the localization. So I found out that the most used method is using Gettext.
I have some difficulties translating strings with only gettext() and ngettext(). I'd build a wrapper able to translate strings like so:
fantasticFunction('Hi my name is :name and I have {n, one friend, :n friends}', [ 'name' => 'Luca', 'n' => 1 ])
Instead of writing:
sprintf('Hi my name is %s and I have', name) . ' ' . $n . ngettext('friend', 'friends', $n)
so the problem is: if I'd make such as wrapper, then tools like poedit would not recognize that strings. How can I solve?
You need to add fantasticFunction as a keyword for extraction — this is well documented in xgettext docs and Poedit has properties UI for it.
But nothing will help you with that {n, one friend, :n friends} thingie. If you decided to use gettext, you need to actually follow its model and this isn't compatible with it. Do have a look at how ngettext works and read up on plural forms in different languages in gettext before inventing your own approach. You're vague about your "difficulties" with using gettext's plurals support (which is generally speaking competently done), are you sure it isn't just a case of NIH syndrome?
Related
In Magento, performing a translation in modules involves invoking a helper and calling its translation function, E.G.
Mage::helper("core")->__("This is a string to translate");
However, I haven't found any resources online regarding translating strings that contain variables. From past experience, I know that this is usually handled by using a token inside the string, with token replacements defined using additional arguments.
For example, GNU gettext's manual recommends using format strings for translation:
sprintf (gettext ("Hello %s!"), username ());
While Yii Framework has a similar, but subtly different format:
Yii::t("default", "Hello {username}!", array("username"=>username()));
From a quick grep of Magento's core files, it looks like Magento uses C-style format strings, for example in Mage_Adminhtml_Block_Api_User_Edit::getHeaderText the following can be found:
return Mage::helper('adminhtml')->__("Edit User '%s'", $this->escapeHtml(Mage::registry('api_user')->getUsername()));
But I would like further confirmation or advice since online documentation is quite sparse.
Magento uses following syntax for translation:
Mage::helper('catalog')->__("This is %s text %s", "First String", "Second String");
As suggested by Markus Harrison, I am adding these documentation for format strings:
Formatting link 1
Formatting link 2 - wiki
I am building a multilingual application in PHP + CodeIgniter. I have settled upon using gettext for UI text translation, and so far it has proven efficient and easy to work with.
But now I am facing something really annoying: the gettext() function only accepts one parametre, while I'd like a printf-like behaviour that I get from Zend Framework's gettext adapter, where I can use %1$s, %2$s etc. as placeholders and then specify the replacement strings as additional parametres to Zend view's translate() function.
I do not wish to ditch gettext due to the easy translation management with .po files and poEdit (I can get it updated with a single click, after all). What are my options?
I have already tried writing a helper to interact with gettext: run the first argument through gettext and then run strtr on the resulting string. Are there any other/better approaches you would recommend?
It's quite simple actually, you define a variadic function like this:
function myGettext($id)
{
return vsprintf(gettext($id), array_slice(func_get_args(), 1));
}
Now doing myGettext('%u %s in a %s', 3, 'monkeys', 'tree') will return the expected string with the placeholders replaced by the remaining arguments. You obviously also need to implement a plural aware function that calls ngettext() instead.
Regarding poEdit, you have to modify the keywords it searches for, it's been a while since I last used it but it was quite simple, the only problem I faced was identifying keywords for plural support (see this).
Hope it helps!
Reading about Kohana templates and saw something I've never seen before:
$this->template->title = __('Welcome To Acme Widgets');
What does __('Text') mean? What is it? What does it do?
In Kohana (version 3) the function is defined in system/base.php and is a convenience function to aid (as the other answers have mentioned) internationalization. You provide a string (with, optionally, some placeholders to substitute values into the finished text) which is then interpreted and, if required, a translation is returned.
Contrary to assumptions in other answers, this does not use gettext.
A very basic example would be (this particular string is already translated into English, Spanish and French in Kohana):
// 1. In your bootstrap.php somewhere below the Kohana::init line
I18n::lang('fr');
// 2. In a view
echo __("Hello, world!"); // Bonjour, monde!
The double '__' is used for Localization in CakePHP (and possible other frameworks)
http://book.cakephp.org/view/163/Localization-in-CakePHP
It means someone created a function named __ (That's two underscores next to one another.)
My guess is it defined somewhere in the Kohana documentation.
It's string gettext ( string $message ): http://php.net/manual/en/function.gettext.php
Returns a translated string if one is
found in the translation table, or the
submitted message if not found.
The __() is just an alias for it. So __("some text") is equivalent to gettext("some text")
edit: Actually if it's two underscores than it isn't gettext(). The alias for gettext() is one underscore.
Second edit: It looks like __() might be another alias for gettext(). With a slightly different meaning from _(). See here: http://groups.google.com/group/cake-php/browse_thread/thread/9f501e31a4d4130d?pli=1
Third and final edit: Here's an article explaining it in more detail. Looks like it isn't a built in function, but rather something that is commonly added in a lot of frameworks. It is essentially an alias of gettext - it performs the same function. However, it isn't a direct alias (I don't think). It is implemented in and is specific to the framework. It searches for and returns a localization or translation of the string it is given. For more, see this blog post: http://www.eatmybusiness.com/food/2007/04/13/what-on-earth-does-a-double-underscore-then-parenthesis-mean-in-php-__/7/
// Display a translated message
echo __('Hello, world');
// With parameter replacement
echo __('Hello, :user', array(':user' => $username));
See http://kohanaframework.org/3.2/guide/api/I18n for details.
I'm writing Content Management software in PHP (which should not be bigger then 3kb when minified), but what engine should I use for languages (english, dutch, german, chinese, etc...)? I was thinking of creating a function called
function _(){}
that reads strings from a file (a .ini file or similar). But does somebody has an (preferably one with as less code as possible) engine that might be smaller or faster?
I'm not sure if these engines exist already, if not, please say and I will use the _() function.
If I were you I would make my translation function like such (which I believe is very similar to gettext): make it into an sprintf()-like function and translate based on the format string, like so:
function __() {
$a = func_get_args();
$a[0] = lookup_translation($a[0]);
return call_user_func_array("sprintf", $a);
}
Now, you can use the function simply like this:
echo __("Thanks for logging in, %s!", $username);
And in a data file somewhere you have:
"Thanks for logging in, %s!"="Merci pour enlogger, %s!" (*)
The advantages of this are:
You don't have to think up identifiers for every single message: __("login_message", $username), __("logout_message", $username), etc...
You don't immediately have to write a translation for the string, which you would have to if you just used an identifier. You can defer the translation until later, once you're done coding and everything works in English.
(Similarly) You don't have to translate all strings for all languages at once, but you can do it in chunks
For maximum convenience, I would make the __ function log untranslated messages somewhere, so you don't have to go hunting for untranslated strings. Let the system tell you what needs to be translated!
(*) Disclaimer: I don't speak French ;)
You can't use _() because this is a build-in function for internationalization. You are free to roll your own function (call it __()) or use the build-in one which uses the widespread gettext system.
Drupal, for example, uses function t() for this purposes.
I am working on a small parser that should accept input in a number of languages. I am going to have maybe 50 strings that will serve as keywords/anchors in parsing the input. In PHP, what would be the best way to organize these translated keywords?
One possible solution that comes to my mind is to use an associative array. Like this:
$lang = array('us' => array('totalDebt' => 'Total Debt', 'color' => 'Color'),
'gb' => array('totalDebt' => 'Total Debt', 'color' => 'Colour'))
which I could then access using the following:
$langCode = 'en';
$debtPos = strpos($lang[$langCode]['totalDebt']);
Are there any better, proven methods for dealing with a bunch of short strings translated into a bunch of languages?
For a complete translation solution, you can look on a solution like gettext.
you solution is good enough (fast, cheap on resources) for small dictionaries.
I didn't understand what you tried to do using the strpos() function.
Don't forget to use some kind of fallback if the term you want to translate doesn't exists in the language, usually the fallback is to the English.
Generally people use l10n (or something similar) in their application because it allows for additional languages by adding a localization file which means the programmers do not have to change code. But if it is a simple script, then full on localization would be overkill.
I have seen your solution used in other OS projects, however the $lang array is usually constructed in different files. For example:
<?php // lang.us.php
$LANG['us'] = array(
'totalDebt' => 'Total Debt',
'color' => 'Color',
);
and so on for lang.gb.php, lang.de.php, etc.
As Peter said, you've got the right idea, but separate your languages into different files. It means that PHP won't have to store the array of every single word in every single language. Because you only are going to be loading one language at a time, you can skip the $lang['us'] level of nesting too.
You could even "extend" languages:
<?php // lang.en-gb.php
$lang = array(
'color' => "Colour",
'totalDebt' => "Total Debt",
...
);
?>
<?php // lang.en-us.php
include('lang.en-gb.php');
$lang['color'] = "Color";
// don't need to redefine "totalDebt"
?>
You might even consider creating a basic class which handles all this for you: detect the locale, choose the correct file, have "error" handling, if a term isn't defined in a particular language, etc.
You probably don't want 'totaldebt' => 'Total Debt' as that will ultimately obfuscate your code, and is slower than just storing 'Total Debt' as the hash key. The gettext way of doing things is to just wrap the strings in a function.
so rather than:
echo "Color";
You do:
echo t("Color");
the t() function will look at the globally defined language, and make the substitution if possible. At some point, you may want to use gettext and doing it this way will be compatible.