Drupal 7's new multilingual systems (part 3) - localization and language APIs

As promised at the end of the previous piece of my Drupal 7 multilingual post series, this part is turning to developers to spread some awareness of new features and possibilities in Drupal 7. We've talked about context support and new language selection features, and I'd like to share some tips with you to use them right. I'd also like to share an updated version of my Drupal 6 localization cheat sheet as well as its appropriate version for Drupal 7 with you and look at how can you hook into the heart of the language system.

New context support for translation

I've explained the concept and need for contexts in part 1. Now this is how you use them:

= t('May', array(), array('context' => 'Long month name'));

Simple, right? Well, as discussed, context should be used to designate logical context, to support the meaning of the short string. Drupal core does not have many contexts, and we are still to make up clear guidelines to follow. To see contexts used by developers so far, you can join the Test language group on localize.drupal.org and explore the contexts exposed on the Translate tab. You'll be able to filter for the strings using those contexts and see which modules use them (until we come up with some better solution to expose list of contexts used for developers).

So choose your context name carefully if possible and consult the list of existing contexts. If you are unsure, ask translators. Issues marked with string context is the list of items related to introducing context to modules.

Now context is not equally supported on API functions. format_plural() can take context equally well with the same syntax. When using st() and therefore when utilizing get_t(), you'll also be able to rely on context support.

The Javascript API did not get context support unfortunately in Drupal 7, so there is no way to provide string context in Javascript. You can still use the age old workaround to translate the text on the server side with t() and add to the page with drupal_add_js(..., array('type' => 'setting')) to work around this issue. It will not be as technically nice, but will work well:

// This will let you access the string as Drupal.settings.exampleModule.myMessage
'exampleModule' => array(
'myMessage' => array(
t('View', array(), array('context' => 'Example context, modify after copy-paste please')),
'type' => 'setting')

Another magic part of the localization system is the menu title and description handler code. While menus got context support for free with the long supported 'title_arguments' key where you can provide callback arguments to the title callback function (which it set to t() by default), this will not work with the translation template extractor for various reasons. So instead I'm suggesting you set a custom 'title_callback' and set your context there:

function example_menu() {
$items['item'] = array(
// ....

    // Provide 'title_callback' instead of 'title', if you need to
    // provide string context for the title.
'title_callback' => 'example_item_title_callback',

example_item_title_callback() {
t('View', array(), array('context' => 'Example context, modify after copy-paste please'));

A real stepchild in localization is watchdog() which kept the Drupal 6 API entirely, so it can only be used to translate strings with t() and does not allow for contexts to be specified. Because logs by design would store messages in English, there is no workaround for this. However, log messages are the least likely of all to encounter strings, where a context would be needed.

Project .info file strings have a similar fate. While title and description text is used and translated from .info files, context cannot be specified. Maybe in some cases, module names would be happy to use context, but its not possible in Drupal 7, unfortunately.

Verify you are doing this right

If you are not already an avid Coder module user, you either know the rules too well or you should become more familiar with this nice tool. It includes the Coder review module, which helps you review your code for mistakes in using the Drupal APIs. While it does have some built-in support for localization API checking, its best to go and use the real deal instead. Install the Translation Template Extractor module as well, which is used to parse Drupal modules for translatable strings.

If something can tell you about mistakes in using the API, the parser itself can. With tight integration into the Coder review API, it will provide all the warnings it finds when parsing your code to help you use the localization APIs better. Code review results from different review modes are displayed all together. On the picture above, the second one is a translation related warning.

Cheat sheets galore

I published a Drupal 6 translation cheat sheet in 2007 November, and for the Drupal 7 release it was about time to revise the Drupal 6 version and present a Drupal 7 version as well. The original version of the sheet was not a good fit for print and also lacked some tips and improvements made since then. We now have localize.drupal.org, more experience with how texts should be written, a slightly different API in Drupal 7 and notably, RTL theme support tips were missing from the Drupal 6 version, despite full support for those in core.

So here are the more print friendly, updated versions applicable to Drupal 6 and Drupal 7 respectively. I built two versions on the interest of being able to focus better on what's supported in each version. Take them while they are hot!

Make Drupal's language handling more versatile

Drupal 6 knew only one language per page (which was in the global $language variable) and also only offered a small set of prebaked selection algorithms for picking that language. When that language was picked, other modules relied on that value for displaying content or interface in that language.

Drupal 7 takes the language concept to a whole new level and introduces the concept of language types instead. Now the interface language is just one type with core providing two more types: content language and URL language. Even though content language and URL language cannot be configured with core, they are used by Field API and l() respectively. These built-in types are defined in locale_language_types_info() which is in itself and implementation of hook_language_types_info() that any module can use to define more language types. Drupal defines a global language variable for each type with drupal_language_initialize().

Maybe even more useful is hook_language_types_info_alter() which lets you alter language types, such as provide a configuration interface for content language or make interface language fixed to some specially coded algorithm. Stay tuned for upcoming pieces in my article series for how contributed modules take advantage of these features (you probably don't need to write the code yourself).

Beyond multiple language types, Drupal 7 also supports a configurable set of negotiation methods for each separately. While core has URL information (path, domain), session data, user setting, browser preference and default language fallback as possibilities, contributed modules can extend and alter these options. If you wanted to implement an IP address based language selector for example, you could use hook_language_negotiation_info() to add an option to the language configuration user interface, that administrators can then enable. Of course there is a corresponding alter hook as well and you can use these in conjunction with the above mentioned language type hooks to fix certain languages to your choosen negotiation methods, or to limit selection of negotiation modes per language type.

This is not all there is to the new language API, but these are the main new components. See locale.api.php for the finer details and other tidbits.

Next up: content translation

As promised earlier, we are going back to looking at features (versus APIs) and next up is content translation. We will see it is not really easy to define what is "content" in Drupal, even less so with Drupal 7. Stay tuned.

Ps. until then, you can still check out part 2 and part 1 of my series.


rfay's picture

Fantastic series of posts - thanks.

Could you elaborate on the use case for having a different interface language from the content language?


Gábor Hojtsy's picture

There are a few use cases in http://drupal.org/node/282178 and http://drupal.org/node/282191. Some examples:

- I'm an admin and want to administer *all* pages in my native language vs. pages always switching around languages I don't understand (eg. admin menu showing translated on translated pages)
- I have views, taxonomy lists, etc. showing on pages, but want them to use different language criteria compared to interface language; eg. list all posts in multiple languages but let users select a specific UI language

Jean-Luc's picture

I'm really enjoying your D7 experience in localization.
Thank you.

plach's picture

Great writings! These articles really give an idea of the awesome work made in D7!

Before the next one you might find worth reading my reply on Randy Fay's blog.

arul's picture

We are trying to build one multi-lingual site. How to enable the pdf option for multi-lingual pages, PDF is coming good for english, for other languages, its coming as boxes.

How to do. Please help.

Gábor Hojtsy's picture

Sounds like you should discuss this in the issue queue of the Drupal module you use to generate those PDF versions of pages (if you use a Drupal module for it that is).

Add new comment