What ProcessWire 2.2 focuses on
There are lots of components to supporting multiple languages in a CMS, and the area we're focused on with this version is supporting multiple languages for PW's admin, modules and 3rd party modules. This is so that your clients can make edits to their site without having to know English. While PW hasn't had specific multi-language tools for your front end, it can support multiple languages any number of ways there (as many users have done, with language trees, multiple fields, etc). But the back-end tools have always been in English without an obvious way around that. So the biggest need has been a way to support multiple languages for PW's admin tools, core modules, 3rd party modules, etc. Basically, the non-dynamic side. And that's what PW 2.2 tackles.
The direction being used
We originally looked at PHP's gettext tools (http://www.php.net/m...ook.gettext.php), as they've been successfully used to provide this capability for CMSs like WordPress and Drupal, and they are a ready-to-go solution that is built-in to PHP and adds multi language capability with little overhead. I like gettext() from the PHP side as it's a really easy solution (for coding at least), and it's really efficient from the PHP side. But I felt that it puts a lot of burden on the translators with a lot of technical jargon, file formats and other tools. I want the translation tools to have the same simplicity as the rest of PW, and it looked like that was going to be a hard sell with gettext.
Instead, we're using a home-brewed solution that essentially does the same thing as gettext, but is a whole lot simpler for translation. It's simple enough that if you find something that needs translation, you can just go and edit it in PW like you would edit anything else. It also means that these tools will be very simple for 3rd party module developers to use. They will also have use in your own sites and templates for your non-dynamic content.
The tradeoff is that it takes longer to code this way (for me) and that it can't possibly be as efficient as gettext (given that gettext is built-in to PHP). Translations take up memory. However, PW, doesn't have files with tens of thousands of lines of code and doesn't need to keep thousands of translations in memory at any given time. When it gets down to the needs of PW and the needs of those using it, I don't believe we'd ever benefit from the efficiency of gettext. So the home-brewed solution won out.
The actual solution is coded as a module. So if you don't need anything other than English in the admin, then you'll just leave the 'Languages' module uninstalled.
How it works from the admin side
The Languages module will be provided with the core, ready for a 1-click install. As soon as you click 'install', a 'Languages' tool is added to your Setup menu, a 'language' template is added to your templates, and a 'language' field is added to your fields and appended to the 'user' template with the default language (English) selected.
When you go to Setup > Languages, you'll see a list of languages that are installed. From here you can click to edit a language or click 'Add New' to add a new language. Each language is technically a page (just like with users), so you can add additional fields to the 'language' template should it suit your needs. By default, each language template has a name (ISO-639 code, i.e. 'en'), a title (i.e. 'English') and a 'translations' file field. Each file in this field contains translations for a file in ProcessWire (whether a module, core, template, etc.). These files are created by another module in ProcessWire (to be discussed below) but a files field is provided here so that you can easily share your translations with other people. Likewise, you'll be able to download new language packs from the ProcessWire site and just upload them here, ready to use.
How to make a translation
ProcessWire keeps track of language translations on a per-file basis. So if you want to translate a file in ProcessWire, you have to tell it which one. ProcessWire will then load the file into memory and look for function calls that indicate translatable text. Then it presents you with a screen of all the phrases it found for translation, along with inputs for providing a translation for each one. See the attached screenshot for an example of this. When you hit 'save', it saves those translations in a JSON file that PW's languages module uses for runtime translation. This JSON file can also be shared with others, distributed with a module you've created, or zipped up with others as part of a language pack.
As soon as the English version native to the translated file changes, the translated versions are considered out of date. We don't want to make guesses about the scope of the text change. So your translation screen will show you any existing translations that are considered 'out of date' along with new entry fields to provide new translations.
Any fields left blank on the translation screen are considered untranslated and thus the original language (English) is substituted for any untranslated phrases.
How it works from the code side
I mentioned earlier that I like how gettext works from the developer side, and ProcessWire works in a very similar manner in this regard. Meaning, indicating text as translatable involves a "_" function call followed by the text. ProcessWire also needs a context, so you call this function with '$this'. Here's an example:
$value = $this->_('Add New Page Here');
So $this->_('text') identifies the text as translatable. You have to use $this->_('text') rather than the gettext format of _('text') for two reasons: first is that the _('...') function is native to PHP and actually calls gettext, so that's already taken! Second is that ProcessWire needs a context to the function call… it needs to know what class or file it was called from, otherwise all translations would be a global namespace. So ProcessWire figures that out behind the scenes with a context of the calling class and uses the Reflection API to determine the file. If you didn't understand that last sentence, then don't worry because you don't need to–ProcessWire is taking care of the technical details for you.
In addition to the $this->_('text') you can use a function format that would most likely be more suitable for translations performed in your template files. In this case, because "_" is already taken by gettext, we use "__" like WordPress does. So you can do this:
echo __('Submit Form');
ProcessWire figures out the context of that call automatically and groups the translation with others from your template file. This type of call can also be used in 3rd party modules, but you'll have to tell PW the context, i.e.
<?php
echo __($this, 'Submit Form'); // these two lines do exactly the same thing
echo $this->_('Submit Form');
Best practices for pre-translated text
Just like with gettext, when entering text that's ultimately going to be translated, you need to avoid putting in any dynamic things in it. For instance, a string like "Found $count products" is not good for translation because it contains a variable in it. Instead, you'd want to enter such a string as: sprintf("Found %d products", $count). For more details, see the 'Marking Strings for Translation' and 'Best Practices' sections in WordPress's i18n page. They use gettext, but the same applies to us:
Marking strings for translation
http://codex.wordpre...for_Translation
Best practices
http://codex.wordpre...#Best_Practices
New API variables
The Languages module installs a new API variable called $languages. It's interface is identical to that of $users in that it can be iterated as a PageArray or you can use get() and find() calls to pull individual languages from it, i.e.
<?php
foreach($languages as $language) {
echo $language->title . "<br />";
}
…and…
<?php
$french = $languages->get('fr');
The Languages module also adds a $language variable to every $user. So you can check what the current language is like this:
<?php if($user->language->name == 'es') echo "Hola!";
Of course, $language is just a Page object, so it will also contain any other fields you have added to your 'language' template.
Because ProcessWire's native language is English, the language is assumed to be English by default. So if you want it to assume a different language by default for your site, then you would just edit the 'guest' user and select a different language.
Next Steps
So that's the current state of ProcessWire 2.2 and how it's multilanguage support works. But it's not the only part of it. Because ProcessWire's admin is itself a site developed in PW, and because PW's output is not all non-dynamic, we need multilanguage support in our dynamic fieldtypes and inputfields in order to be truly multilingual. This is what I'll be focusing on in November, so will have more updates on that side of it then. Of course, this side may also be useful for the front-end (your sites) too, though my feeling is that a multi-tree approach is better for accessibility and SEO even if you do have multilingual fields. But even with a multi-tree approach, having multilingual fields will no doubt be useful with shared assets and more.
I'm also hoping to have a beta version ready for those that are interested in testing within a month (or in the next few days, if interested in testing before multilingual fields are in place).
Please post your questions, suggestions, feedback, etc. This is a work in progress and nothing is set in stone.
Edit: added 'New API variables' section.













