PHP localization architecture with global $lang array - php

I'm localizing my site and using an array to store language strings. This is my architecture:
One file per language, for example: language_en.php for English, language_fr.php for French that gets included in the beginning of the script.
In each language file I have an array (consisting of about 2000 elements):
static $phrases = array();
$phrases['del'] = 'Remove';
...
Then I have a function:
function phrase($key) {
global $phrases;
return $phrases[$key]
}
Where-ever I need a localized string further in my app, I use:
phrase('del');
Couple of questions around this architecture:
$language is global, does this have negative performance
effects when the array gets larger and larger?
Would it be better to add the array right into the phrase() function and avoid it to be global?

There's nothing wrong regarding that approach.
Even though arrays grow up sometime, it shouldn't cause performance penalty. Also, it's okay using global in this case.
If you've "global paranoid" you can use closures to create a copy of global variable, like this:
$phrase = function() use ($phrases)
{
return $phrases[$key];
};
I advise you to use symfony/translation component in order to manage the translation catalogs. Symfony trabslations is a great component.
Maybe you want take a look on tis answer (mine): Best way to organize your localized translation file

Related

PHP 101: variable vs function

I'm creating a global file to hold items that will be re-used throughout my website. What are the differences between these two lines of code? Is one "better" than the other?
This:
$logo = "img/mainlogo.jpg";
vs this:
function logo() {
echo "img/mainlogo.jpg";
}
You should code clear and readable and split in the html and php. The performance profit is not significant...
<?php
...
$logo = "img/mainlogo.jpg";
...
?>
...
<img src="<?= $logo ?>" alt="logo">
...
Of the two options you posted, the function is the better choice. But, to be brutally honest, this sort of thing is exactly what constants are for:
defined('MAIN_LOGO') || define('MAIN_LOGO','img/mainlogo.jpg');
suppose you're working on a site that has to support multiple languages, then you can simply use the same trick:
defined('CLIENT_LOCALE') || define('CLIENT_LOCATE',$whereverYouGetThisFrom);
defined('MAIN_LOGO') || define('MAIN_LOGO','img/mainlogo_'.CLIENT_LOCALE.'.jpg');
//if language is EN, mainlogo_EN.jpg will be used, if lang is ES, mainlogo_ES.jpg, etc...
Besides, a constant, once defined cannot be redefined (clue is in the name, of course). Also: since PHP still has a lot of C-stuff going under the bonnet, and you've tagged this question performance, it might interest you that constants are much like C's macro's, which are a lot faster than regular function calls, or even C++ inline functions (even if they were indeed compiled as inline functions).
Anyway, if you have a ton of these things you want to centralize, either think of creating a couple of ini files for your project, and parse them into some sort of global object
Functions are good.
I see that function logo() is better than $logo. echo doesn't take much memory, but $logo does. Even though, function logo() takes something, it will be handled by PHP's very own garbage collector. You can also use these functions to ensure that you are not misusing the memory allocated.
memory_get_peak_usage();
memory_get_usage();
Explanation:
Upon the ending of an in use function PHP clears the memory it was using, at least more efficiently than if not using a function. If you are using recursive code or something similar that is memory intensive try putting the code into a function or method, upon closing of the function/method the memory used for the function will be garbaged much more efficiently than that of unsetting variables within the loop itself.
Source: 7 tips to prevent PHP running out of memory
The main purpose of a function is to avoid code repetition and perform a specific task. Based on that definition, using a function to only return a value is a bad design.
In that context I think is better a good readability in the code than to save several bytes of memory. We are in 2012, optimization is good but this type of micro-optimization is simply ridiculous. I prefer assigning a variable, it's clear and do what you expect.
$logo = "img/mainlogo.jpg"; can be redefined naturally later without changing code by doing this $logo="img/newmainlogo.jpg"; whereas the function would have to be modified itself, in its first definition.

php global variables with variable names

I am trying to have a function that among other things declares global variables based on a variable that i give it.
the part that fails is making the variables global
function setGlobalVariable($name) {
global $name, $arrayname_{$name};
}
any idea?
thanks :)
Really, stop messing with global variables that way.
Anywaym here's your solution if you really want to do that:
function setGlobalVariable($name) {
$GLOBALS['arrayname_' . $name] = 'yourvalue';
}
You should not do that. Global variables are in general a sign of poor design. What is it that you are trying to achieve? I am sure that there is a better solution. Besides that, global does not work like that. global makes other variables outside your function locally available. Use $_GLOBAL to create globals.
Take a look at the Registry Pattern (http://martinfowler.com/eaaCatalog/registry.html).
A well-known object that other objects
can use to find common objects and
services.
There are various PHP implementations, for example Zend_Registry: http://framework.zend.com/manual/en/zend.registry.html
You're almost right, but not quite; a variable variable takes the form of ${"name"}, so what you're looking for is something like global ${"arrayname_$name"};.
http://www.reddit.com/r/programming/comments/dst56/today_i_learned_about_php_variable_variables/c12np38 is fascinating reading on the topic, if you feel so inclined.
It's likely a terrible idea, though, and if you're resorting to that sort of thing, it's a good indication that your code may be poorly designed. Consider refactoring it (for example, to keep a single known array that your other arrays are kept in, and may be referenced by key.)

PHP - Function inside a Function. Good or bad?

I would like to know if it is a good thing to define a function inside another function in PHP. Isn't it better to define it before the function (and not inside) in terms of performances.
I think you should care more about maintenability, and less about performance, especially in that kind of situation, where the difference in performances is probably not that big between the two solutions, while the difference in maintenability seems important.
Like Donald Knuth said :
We should forget about small
efficiencies, say about 97% of the
time: premature optimization is the
root of all evil.
This is quite true, in this situation ;-)
There are multiple reasons against it:
The documentation of the inner function will not be parsed.
The inner function only exists after the outer function has been called (but even outside the scope of the outer function afterwards)
It is hard to read (because it is not seen commonly)
The only advantage I could think of is defining a callback, but this is better done with create_function() (<PHP 5.3) or closures (>=PHP5.3)
If you're concerned about performance on this level, you should really be using another language
It depends on the situation, as it may be more desirable than using create_function(). However you should know that the function which is created within the function is global in scope.
function creator() {
function inside() {
echo "hi.";
}
}
creator();
inside();
This will print "hi." even though the inside() function was created "inside" of the creator function. So if you have a function in a loop which is creating a function, you need to check to see if the function exists, otherwise it will cause a function exists error after the first loop.
That's a bad practice. Not only all weird things can happen and you'll lose too much time trying to debug it, but also the code becomes more confusing.
In terms of performance I'm not completely sure about it. All I know is that if you define a function inside another, that last function will only exist if the outer one is called. That may relief some memory. But I believe the payoff is not significant.
A very common way is to define the function outside of that function and call it inside.

PHP language engine

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.

Programming Multi-Language PHP applications

I'm developing a PHP application and I'm wondering about the best way to include multi-language support for users in other countries.
I'm proficient with PHP but have never developed anything with support for other languages.
I was thinking of putting the language into a PHP file with constants, example:
en.php could contain:
define('HZ_DB_CONN_ERR', 'There was an error connecting to the database.');
and fr.php could contain:
define('HZ_DB_CONN_ERR', 'whatever the french is for the above...');
I could then call a function and automatically have the correct language passed in.
hz_die('HZ_DB_CONN_ERR', $this);
Is this a good way of going about it?
-- morristhebear.
Weird. People seem to be ignoring the obvious solution. Your idea of locale-specific files is fine. Have en.php:
define('LOGIN_INCORRECT','Your login details are incorrect.');
...
Then, assuming you have a global config/constants file (which I'd suggest for many reasons), have code like this:
if ($language == 'en') {
reqire_once 'en.php';
} else if ($language == 'de') {
require_once 'de.php';
}
You can define functions for number and currency display, comparison/sorting (ie German, French and English all have different collation methods), etc.
People often forget PHP is a dynamic language so things like this:
if ($language == 'en') {
function cmp($a, $b) { ... }
} else if ($language == 'de') {
function cmp($a, $b) { ... }
}
are in fact perfectly legal. Use them.
You can use gettext or something which supports gettext as well as more such as Zend_Translate.
Edit:
Just for precision, Zend_Translate supports gettext without the gettext module. You can see here the many different types of input it supports.
If you use arrays as also suggested you can also use this with Zend_Translate. The point being, if you use arrays today and gettext, xml or something else tomorrow you only have to change your config for Zend_Translate.
I really love the following approach:
one file is the translator itself:
class Translator{
private static $strs = array();
private static $currlang = 'en';
public static function loadTranslation($lang, $strs){
if (empty(self::$strs[$lang]))
self::$strs[$lang] = array();
self::$strs[$lang] = array_merge(self::$strs[$lang], $strs);
}
public static function setDefaultLang($lang){
self::$currlang = $lang;
}
public static function translate($key, $lang=""){
if ($lang == "") $lang = self::$currlang;
$str = self::$strs[$lang][$key];
if (empty($str)){
$str = "$lang.$key";
}
return $str;
}
public static function freeUnused(){
foreach(self::$strs as $lang => $data){
if ($lang != self::$currlang){
$lstr = self::$strs[$lang]['langname'];
self::$strs[$lang] = array();
self::$strs[$lang]['langname'] = $lstr;
}
}
}
public static function getLangList(){
$list = array();
foreach(self::$strs as $lang => $data){
$h['name'] = $lang;
$h['desc'] = self::$strs[$lang]['langname'];
$h['current'] = $lang == self::$currlang;
$list[] = $h;
}
return $list;
}
public static function &getAllStrings($lang){
return self::$strs[$lang];
}
}
function generateTemplateStrings($arr){
$trans = array();
foreach($arr as $totrans){
$trans[$totrans] = Translator::translate($totrans);
}
return $trans;
}
the language-files can be simply include()d and look like this:
en.php:
Translator::loadTranslation('en', array(
'textfield_1' => 'This is some Textfield',
'another_textfield ' => 'This is a longer Text showing how this is used',
));
de.php:
Translator::loadTranslation('de', array(
'textfield_1' => 'Dies ist ein Textfeld',
'another_textfield ' => 'Dies ist ein längerer Text, welcher aufzeigt, wie das hier funktioniert.',
));
in you app you can do either translate one string like this:
$string = Translator::translate('textfield_1')
or even a bunch of strings:
$strings = generateTemplateStrings(array('textfield_1', 'another_textfield'));
Because the language-files can just be included, you can stack the very easily and, say, include a global file first and then include files from submodules which can either add new strings or replace already defined ones (which doesn't work with the define()-method).
Because it's pure PHP, it can even be opcode-cached very easily.
I even have scripts around which generate CSV-files containing untranslated strings from a file and even better: convert the translated CSV-file back to the language file.
I have this solution in productive use since 2004 and I'm so very happy with this.
Of course, you can even extend it with conventions for pluralizing for example. Localizing number formats is something you'd have to do using other means though - the intl-extension comes to mind.
Thanks to all the people for your approaches and solutions. I came here with the same beggining doubt, and i can conlude, that to use the "define method" is more realistic, as, even with the odd problem that doesn't use very stylish methods with other libraries or some, being realistic and in a productive thinking, is so easy to mantain, and understand, so, you can get other languages trough non programing skills dudes.
I consider better that the arrays method, as you don't need to distribute that big multilanguage to get some traductions, besides, you are using less memory as you are loading only the needed traducted strings.
About the thing that, those can't be redefined; well, generrally if you are defining constants is because they don't need or must not be changed, so if needed that you can use a section for defined strings and variable ones.
I addmint that i don't like the way those define files looks but, take in count that your visitors are not worried at all about the way idiom is implemented, is a perfect valid implementations and you'll can easily get some traductions.
You might want to look at a framework like CakePHP or CodeIgniter that make writing internationalized applications much easier. It's not just strings you have to consider -- things like number formats and date formats also have to be accounted for
Your solution should work fine as long as it is solely intended for translating. As others have mentioned, there are many other locale-based variables, such as currency and date formats.
A solid approach to making your application locale-proof would be to use Zend_Locale combined with Zend_Translate.
Zend_Locale allows you to easily detect the user's locale, or set it if you wish. This class is useful for automatically setting the correct currency format, for example.
Zend_Translate allows you to easily translate text using several different formats:
Array
CSV
Gettext
Ini
Tbx
Tmx
Qt
Xliff
XmlTm
Your approach is workable, in the case where you include different constantfiles depending on which language the user has choosen.
You could also skip the constant-part and just define a big hashtable with constant => translation to prevent crashes in namespace.
file: en.php
$constants = Array(
'CONSTANT_KEY' => 'Translated string'
);
file: functions.php
function getConstant($key, $fallback) {
// ... return constant or fallback here.
}
However, when it comes to large amount of data, this approach will be hard to maintain. There are a few other approaches that might serve your purpose better, for instance, an ideal solution is where all your constants are stored in a global memoryspace for your entire site, to avoid having each and every reguest/thread keeping all this translated data in memory. This requires some sort php module-approach. Gettext as someone here suggested might use that approach.
Google for PHP localization to find useful resources.
One tip for you, is to create a function that returns the translated string, and if it is not in the hashtable, then return the hash key you requested with something like a * behind it to notify you it needs translation.
Here is how I do it:
parent_script.php:
$lang_pick = "EN"; //use your own method to set language choice
require_once('trans_index.php');
echo $txt['hello'];
trans_index.php :
$text = array();
$text['hello'] = array (
"EN"=> "Hello",
"FR"=> "Bonjour",
"DE"=> "Guten Tag",
"IT"=> "Ciao"
); //as many as needed
foreach($text as $key => $val) {
$txt[$key] = $text[$key][$lang_pick];
}
That may be too simple for your needs, but I find it very workable. It also makes it very easy to maintain the multiple versions of the text.

Categories