Currently in the localization systems we are missing the support for managing more than one language on a single page request. This is needed for:

  • Modules that produce email need to localize that emails for each user's language, not for the page request language. These emails are usually triggered on cron.
  • When an administrator creates/approves an user account and the user gets a notification it is currently in the administrator's language. It should be in the user's language.
  • When a user registers and the administrator is notified, the email is currently in the language the user has used for registering, that the administrator may understand or not...

This patch has several parts.

  • It adds the ability to translate into several languages in the same page request to locale module and a 'locale_switch_language' function that will temporarily change the current language, while it stores the original one to reset it later
  • Adds a 'user_context' function to user module, which will be the one to switch language and maybe other settings for a specific user. This is meant to be a simple api function in user module. To switch language back and forth for an user account, the code would look like
user_context($account); // Will switch language to user account
// operations here
// ........
user_context() // Will reset the language to the original one in the request
  • Adds a new operation for user hook, $type = 'context' so other modules can act when switching the context of the user. Currently it is only used by locale module to switch language. Other uses may be, i.e. some module changing the 'from' address for emails depending on the user being emailed, or customizing the emails for groups of users...
  • Uses all the above functionality for emails sent to users from the user module. Also, when a user registers, it uses the administrator's language when emailing him.
  • Adds the language selection for user accounts for some cases when it didn't make sense before, because now, besides changing the interface language, it is also used for notifications.
  • Hope I've been able to explain right this language mess.. :-)

    There are some tricky issues in this patch, like switching global variables, and this 'context' hook that needs to be documented also. But this is just a starting point. There's been some talk in the past about global variables, user variables, etc... This new hook operation at least would allow modules to switch global settings -in the $conf array- on a per user basis, which is something to start with.

    Also, I've implemented the whole thing to show the complete picture, but I could post some smaller patch just for the locale module.

    Support from Acquia helps fund testing for Drupal Acquia logo

    Comments

    Gábor Hojtsy’s picture

    Status: Active » Needs work

    Have you looked into somehow passing on the required language instead (which is only what we need to know there)? Having a global environment switching around sounds rather dangerous. If you have other parts of code, or overlapping reused strings, you get mixed language output. Like in your patch when user #1 gets a mail, he gets the $subject set previously, so it will be in some other language then what t() will produce there.

    Jose Reyero’s picture

    Gabor

    Have you looked into somehow passing on the required language instead (which is only what we need to know there)?

    Yes, but then you need the modules using it to mess with languages, like checking language for the user, then switching to default language if the user doesn't have one. This way we isolate everything into locale module and this looked to me like the simpler api to do it, just 'user_context()'....
    Also, just a possibility, other modules may use language for some query rewriting.

    Having a global environment switching around sounds rather dangerous.

    Well, we just switch global language in core for now. We are just allowing that possibility for other modules to play.
    Also, the url rewriting code will need to know about language to get the work done.

    If you have other parts of code, or overlapping reused strings, you get mixed language output. Like in your patch when user #1 gets a mail, he gets the $subject set previously, so it will be in some other language then what t() will produce there.

    That we should avoid. That subject setting needs to be moved after the 'user_context()'.

    However, the option of just adding a language argument to locale also makes sense. I just think this is a more powerful option but also, I agree with you, somehow more risky.

    moshe weitzman’s picture

    suscribe

    Gábor Hojtsy’s picture

    OK, I put some more though to this yesterday. As we are really only dealing with emails here, what about introducing a user_mail() function which would get a user object to send email to and some kind of parameters to indicate what to send in the email. I mean maybe we can separate this language swtching into one function for this purpose, where we might as well be able to pass on the required language. So the outer code will not need to deal with the language at all.

    Jose Reyero’s picture

    Status: Needs work » Needs review
    FileSize
    6.17 KB

    Ok Gabor, sure we can find some other solution.

    In the meanwhile, here's a patch, only for locale module and 't()' function to support multilingual requests. It just adds a language parameter to 't' and 'locale' functions. Nothing here about user emails or global language switching.

    I think this functionality alone makes sense, as in the worst case it will allow contributed modules to workout some solution for emailing users in different languages while still being able to use the localization system.

    Btw, in the previous patch slipped some php file intended just for testing (/languageswitch.php). Here's the code for testing this new patch. Copy and paste it into a php file in the root folder and run it.

    <?php
    require_once './includes/bootstrap.inc';
    drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
    
    $output = '<h2>Testing multilingual requests</h2>';
    foreach (language_list() as $tmplang) {
      $output .= '<h3>'.$tmplang->name.'</h3>';
      $output .= '<p>'.t('Create content', 0, $tmplang).'</p>';
      $output .= '<p>'.t('One, two, three', 0, $tmplang).'</p>';
    }
    $output .= '<h3>Back to default request language</h3>';
    $output .= '<p>'.t('One, two, three').'</p>';
    print theme('page', $output);
    
    drupal_page_footer();
    
    Gábor Hojtsy’s picture

    Status: Needs review » Needs work

    Well, this gets much more reasonable. :) I have two remarks:

    - What about having a $langcode parameter instead of passing on a full fledged $t_language? The $user only has a langcode anyway. That would simplify the code greatly, so you would not need to reference $t_language->language at every places.
    - Use isset() when checking for whether the parameter was passed, not simply the value itself, so we are E_ALL compatible.

    Gábor Hojtsy’s picture

    Status: Needs work » Needs review

    Hm, db_set_active() might be an interesting prior practice to look at too. Hm... Maybe we can get some independent reviewers (Moshe, Dries, Steven?) to help decide whether such global environment switches are good. I would not say I like db_set_active() either, but it is definitely in Drupal, so a language_set_active() might as well be possible after all. Hm... We should think about it. (I am not decided yet).

    Gábor Hojtsy’s picture

    BTW I would envision language_set_active() to be much simpler then the user_context stuff, just switching between one language to another (from among the list of defined languages).

    Dries’s picture

    The db_set_active() function has always been a pain. We've had other issues with global swithces (i.e. the global $user object getting switched). So, I'd prefer not to use a global switch, but to pass a language parameter to drupal_mail().

    Jose Reyero’s picture

    Reworked the patch to use $langcode parameter instead of $language object

    Gabor, as long as we have a default value for the parameter (NULL), it should be ok about E_ALL notices

    We can work on the user emails in a different patch.

    Jose Reyero’s picture

    Please, see also #82499 which is the related patch for user emails, relying on this one

    Gábor Hojtsy’s picture

    Title: Locale improvement: multilingual requests » Add optional language parameter to t()
    Status: Needs review » Fixed

    OK, updated the patch:

    - Documentation still said $t_language, not $langcode.
    - As far as I know, we use isset($value) for $value = NULL parameters, so used that

    Applied the patch to my testing environment, and because I have some Czech translations around, I quickly tested how it works. Turned PHP module on, created a block with this content: return t('Home') . t('Home', 0, 'cs') . t('Nosuchstuff', 0, 'cs'); And checked whether it works. Well, it does, "Home" is translated, and Nosuchstuff gets into the DB. I also checked the cache refresh function, and it does refresh the cache for all languages, so it does not need modification.

    This is a simple and straightforward change, so committed it right away.

    Anonymous’s picture

    Status: Fixed » Closed (fixed)