There has been a lot of controversy over a number of changes introduced in Joomla! 1.6. One of the top ones (in my humble opinion, the least significant one nonetheless) is about the changes in the language files in Joomla! 1.6. People argue that the new scheme provides less flexibility and doesn't allow using a single extension installation package for Joomla! 1.5 and 1.6. To cut a long story short, this is utter bullocks and I'm going to tell you why. If you are a Joomla! developer, you'd better read this post. After all, I do offer unified Joomla! 1.5/1.6 packages for all of my extensions for the last nine months.

Update, May 2012: This information can now be found in the Joomla! documentation wiki (and this page).

Natural language keys vs. namespaced keys

The first debated choice is the lack of using "natural language keys" as in this example:

Are you feeling lucky?=Are you feeling lucky?
Foobar=Foobar

It's true that Joomla! 1.6, since it's using the PHP INI parsing features, doesn't support this kind of strings. Here's a newsflash for you: the keys on these strings are actually illegal based on the INI standard - yes, there is a standard, it's a Microsoft one. We shouldn't see such keys in the first place!

Moreover they are very bad, especially when we reach the point where the Joomla! Framework starts supporting HMVC (e.g. a component displaying a view of another component inside a view of its own). Why would natural language strings be bad in this case? Start thinking beyond English, thank you very much. Some languages have genders. In Greek, an article is a neutral noun whereas a category is a female noun. The English word "Published" would be translated as "Δημοσιευμένο" for the article and "Δημοσιευμένη" for a category. Without prefixing each and every language string with the component name, we end up with broken Greek (and equally broken German, French, ...) when strings from multiple components are loaded.

So, just bite the bullet and do a search & replace over your code. Ideally, your translation strings should look like COM_MYCOMPONENT_A_DESCRIPTIVE_TITLE. If you can't code a search & replace script to process your code and language files in under 60 minutes you should consider giving up development altogether ;)

But I don't want my users to see untranslated keys!

That's the most common and outright lamest excuse for using "natural language" translation keys that I hear. Let's see what that means. Usually, your definitive language file will be the English one. Let's say that a volunteer contributes a German translation file. Everything's fine, until you add more translation keys and -ultimately- the translation becomes out of date. This means that, all of a sudden, your German users will start seeing untranslated strings (the language keys) instead of the translation. This is because Joomla!, by default, only loads the current user's preferred language files.

Putting aside the actual usefulness (or lack thereof) of publishing outdated translations with your component, the above issue can be remedied with just 3 lines of PHP on the very top of your component's dispatcher file, that is the file in the root directory of your component. Oh, yeah, the trick below also works with modules and plugins!

$jlang =& JFactory::getLanguage();
$jlang->load('com_foobar', JPATH_SITE, 'en-GB', true);
$jlang->load('com_foobar', JPATH_SITE, $jlang->getDefault(), true);
$jlang->load('com_foobar', JPATH_SITE, null, true);

Here is the logic if reading the code doesn't work for you:

  1. We load the JLanguage object instance by calling the JFactory factory class
  2. We load the English (definitive) translation which contains all of our keys
  3. We then load the site's default language file, as it may not be British English on that particular site.
  4. Finally, we do what Joomla! normally does, loading the files for the current user's preferred language

The trick is the final parameter in JLanguage::load() which is true and means "overwrite existing keys". Do you see my point? If you want to load the back-end language files, exchange JPATH_SITE with JPATH_COMPONENT and you're set.

Do you want one more reason to do that? By not using "natural language" translation keys it's too easy spotting translation strings you forgot to put in your translation files. You know, if you don't put something in the translation file, neither will your translators do. This results in guaranteed incomplete translations and disgruntled users who want their sites in a language other than English. Been there, done that; trust me when I say so!

Surrounding values with quotes

In Joomla! 1.5 you could write this:

COM_FOOBAR_TITLE=Foobar Component for Joomla!

In Joomla! 1.6 this will not work. Instead, you have to write it this way:

COM_FOOBAR_TITLE="Foobar Component for Joomla!"

That is, you must enclose the value of the translation strings in double quotes. Again, this is actually required by the INI format when you intend to include non-alphanumeric characters in your value. Joomla! 1.5 had it wrong by not enforcing the double quotes. The bonus is that double quotes DO WORK with Joomla! 1.5 as well, so it's not a big deal.

Again, you should be able to hack up a PHP script to do that in under 20 minutes.

Double quotes inside language strings

Now we get to serious business! Here's the problem: different version of PHP need a different way to "escape" double quotes inside the value (right hand) part of translation files.

This form of slash-escape works in PHP 5.3 but not PHP 5.2:

COM_FOOBAR_EXAMPLE=""Come quickly", the girl said"

The ugly _QQ_ escape works on both PHP 5.3 and 5.2:

COM_FOOBAR_EXAMPLE=""_QQ_"Come quickly"_QQ_", the girl said"

The major PITA is that while the PHP 5.3 way works in Joomla! 1.5 and 1.6 alike, most live hosts still use PHP 5.2. The PHP 5.2 way doesn't work in Joomla! 1.5 and result to the "_QQ_" being output verbatim. This seems to be an unsurmountable obstacle for unified packages. Guess what? It is not. You just have to play it clever instead of moan, groan or bitch about it. Here's the deal, for each type of extension.

With components, Joomla! 1.6 allows for per-extension language files. For example, you can create a file in administrator/components/com_foobar/language/en-GB/en-GB.com_foobar.ini to override the system-wide language file with the same name for the back-end. What does this mean? The system-wide language file can contain the Joomla! 1.5 style translation strings, whereas the file inside the private language directory of your component contains the Joomla! 1.6 translation strings. Effectively, you can provide two sets of translation strings in the same package.

With modules and plugins, Joomla! 1.6 allows two sets of translation files: those ending in .ini and those ending in .sys.ini. The .sys.ini are only loaded when parameters are being edited in the com_modules and com_plugins respectively. The straight .ini file is only loaded when the module/plugin gets to run. That said, using the $jlang trick above you can also load the .sys.ini file when your module/plugin runs under Joomla! 1.6 (it's a small if block). OK, you can now connect the dots, can't you? Two sets of files, one with Joomla! 1.5 style language strings, one with Joomla! 1.6 style language strings... and four lines of PHP to match everything to taste.

I have no idea about templates, I haven't made one of my own since the Joomla! 1.0 era. IIRC, the .ini/.sys.ini trick also works there but YMMV.

The bottom line is...

...that if you are a developer who is complaining that it's impossible to create a unified Joomla! 1.5/1.6 package for your extension, one or more of the following are true:

  • You never tested Joomla! 1.6 while it was still in beta; in this case you should not complain now because you had your chance to propose solutions or think of workarounds like I did for six months before Joomla! 1.6 was published. Frankly, I don't consider myself some sort of coding genius; I just read the framework's code and try to understand how it works and how I can make my code work with it. It's not that hard and six months is more than enough to do that,
  • You didn't bother to read other people's code (for example, my code) who had solved those problems before you even started thinking about them. I regularly read other people's code and steal blocks of code borrow ideas from it. That's why our code is GPL, folks. The core value of the GPL is having your code open for other people to study it and learn from it.
  • You are too lazy to search & replace a few strings in your code. Granted, developers are lazy but that's not just being lazy, it's actually as lame as it gets. I wrote my replacing scripts in an hour or so. It took me another 2 hours to make sure there were no odd translation strings generated dynamically by my code and I was set. Don't be lazy!

So, stop complaining and get busy getting your unified Joomla! 1.5/1.6 installation packages ready! Just stick to the code!

No comments