Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.641
diff -u -r1.641 common.inc
--- includes/common.inc	15 May 2007 20:19:47 -0000	1.641
+++ includes/common.inc	20 May 2007 01:00:35 -0000
@@ -747,6 +747,87 @@
 }
 
 /**
+ * Text replacement with deferred localization
+ *   
+ * It can be used for the same base text to be localized to multiple languages
+ * or when the language is not yet known when composing the text.
+ * 
+ * Example
+ * - This will print the same message in all available languages
+ * 
+ * @code  
+ *   // This first line will be localized with the username parameter
+ *   $text[] = array('Hello !username,', array('!username' => $user->name)); 
+ *   // This will be also localized, but the !content_type string has to be replaced later
+ *   $text[] = array('A new !content_type has been posted to !site_name:');
+ *   // Simple string, won't be localized
+ *   $text[] = $node->title 
+ * 
+ *   // Variables for replacement
+ *   $args['!site_name'] => variable_get('site_name', 'Drupal'); // Simple text, won't be localized
+ *   $args['!content_type'] => array($node->type); // Array, will be localized
+ * 
+ *   // We have composed the base text, now it will be localized and printed for each language
+ *   foreach (language_list() as $language) {
+ *     $output .= '<h3>'. $language->name.'</h3>';
+ *     $output .= tt($text, $args, $language->language);
+ *    }
+ * @endcode
+ *
+ * @param $text
+ *    Plain text or array of text lines
+ * @param $args
+ *    Optional associative array of replacements to make after translation.
+ *    * Array keys are placeholders, like in t() function
+ *    * Array values may be simple strings or an array with string and arguments when it must be localized
+ * @param $langcode
+ *   Language code for localization
+ * @param $wrap
+ *   Optional, TRUE for wrapping each text line
+ * @param $endline
+ *   Optional string for end of each line
+ * @return
+ *   Text localized and formatted with variable replacement
+ */
+function tt($text, $args = array(), $langcode = NULL, $wrap = FALSE, $endline = "\n") {
+  // If plain text, convert to array
+  $text = is_array($text) ? $text : array($text);
+  
+  // Prepare arguments for replacement
+  // This replacement also supports localization
+  foreach ($args as $key => $value) {
+      // If value is an array, it means it must go through localization
+      $value = is_array($value) ? t(array_shift($value), array_shift($value), $langcode) : $value;
+      switch ($key[0]) {
+        // Escaped only
+        case '@':
+          $args[$key] = check_plain($value);
+        break;
+        // Escaped and placeholder
+        case '%':
+        default:
+          $args[$key] = theme('placeholder', $value);
+          break;
+        // Pass-through
+        case '!':
+          $args[$key] = $value;
+      }    
+  }
+  // Arguments have been parsed and localized
+  // Now we go with the text.
+  // Each line can be localized with its own parameters
+  foreach ($text as $key => $value) {
+    // It is an array, run through localization with optional parameters
+    $text[$key] = is_array($value) ? t(array_shift($value), array_shift($value), $langcode) : $value;
+    if ($wrap) {
+      $text[$key] = wordwrap($text[$key]);
+    }
+  }
+  // Put the text together and final variable replacement
+  return strtr(implode($endline, $text), $args);
+}
+
+/**
  * @defgroup validation Input validation
  * @{
  * Functions to validate user input.
Index: modules/contact/contact.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact/contact.module,v
retrieving revision 1.84
diff -u -r1.84 contact.module
--- modules/contact/contact.module	14 May 2007 13:43:35 -0000	1.84
+++ modules/contact/contact.module	20 May 2007 01:00:36 -0000
@@ -364,33 +364,36 @@
 
   $account = user_load(array('uid' => arg(1), 'status' => 1));
   // Compose the body:
-  $message[] = "$account->name,";
-  $message[] = t("!name (!name-url) has sent you a message via your contact form (!form-url) at !site.", array('!name' => $user->name, '!name-url' => url("user/$user->uid", array('absolute' => TRUE)), '!form-url' => url($_GET['q'], array('absolute' => TRUE)), '!site' => variable_get('site_name', 'Drupal')));
-  $message[] = t("If you don't want to receive such e-mails, you can change your settings at !url.", array('!url' => url("user/$account->uid", array('absolute' => TRUE))));
-  $message[] = t('Message:');
+  $message[] = "!name-from,";
+  $message[] = array("!name-from (!name-from-url) has sent you a message via your contact form (!form-url) at !site.");
+  $message[] = array("If you don't want to receive such e-mails, you can change your settings at !url.");
+  $message[] = array('Message:');
   $message[] = $form_values['message'];
 
-  // Tidy up the body:
-  foreach ($message as $key => $value) {
-    $message[$key] = wordwrap($value);
-  }
-
   // Prepare all fields:
   $to = $account->mail;
   $from = $user->mail;
 
   // Format the subject:
-  $subject = '['. variable_get('site_name', 'Drupal') .'] '. $form_values['subject'];
-
-  // Prepare the body:
-  $body = implode("\n\n", $message);
-
+  $subject = '[!site-name] @subject';
+  
+  // Prepare variables for replacement
+  $variables = array(
+    '!name-from' => $user->name,
+    '!name-to' => $account->name,
+    '!name-from-url' => url("user/$user->uid", array('absolute' => TRUE)),
+    '!form-url' => url($_GET['q'], array('absolute' => TRUE)),
+    '!site-name' => variable_get('site_name', 'Drupal'),
+    '!url' => url("user/$account->uid", array('absolute' => TRUE)),
+    '@subject' => $form_values['subject']
+  );
+  
   // Send the e-mail:
-  drupal_mail('contact-user-mail', $to, $subject, $body, $from);
+  user_mail('contact-mail', $account, $variables, $subject, $message, $from);
 
   // Send a copy if requested:
   if ($form_values['copy']) {
-    drupal_mail('contact-user-copy', $from, $subject, $body, $from);
+    user_mail('contact-copy', $user, $variables, $subject, $message, $from);
   }
 
   // Log the operation:
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.172
diff -u -r1.172 locale.module
--- modules/locale/locale.module	15 May 2007 20:19:47 -0000	1.172
+++ modules/locale/locale.module	20 May 2007 01:00:37 -0000
@@ -186,10 +186,11 @@
  * Implementation of hook_user().
  */
 function locale_user($type, $edit, &$user, $category = NULL) {
-  if ($type == 'form' && $category == 'account' && variable_get('language_count', 1) > 1 && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) == LANGUAGE_NEGOTIATION_PATH) {
+  // Show language field when user is editing own account and administrator is creating account
+  if (variable_get('language_count', 1) > 1 && ($type == 'register' && user_access('administer users') || $type == 'form' && $category == 'account' )) {
     $languages = language_list('enabled');
     $languages = $languages['1'];
-    if ($user->language == '') {
+    if (!$user || $user->language == '') {
       $default = language_default();
       $user->language = $default->language;
     }
@@ -203,9 +204,9 @@
     );
     $form['locale']['language'] = array('#type' => 'radios',
       '#title' => t('Language'),
-      '#default_value' => $user->language,
+      '#default_value' => $user ? $user->language : $default->language,
       '#options' => $names,
-      '#description' => t('Selecting a different locale will change the interface language of the site.'),
+      '#description' => t('Selecting a locale will set the default site interface and mail language for this account.'),
     );
     return $form;
   }
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.782
diff -u -r1.782 user.module
--- modules/user/user.module	17 May 2007 21:33:59 -0000	1.782
+++ modules/user/user.module	20 May 2007 01:00:40 -0000
@@ -1192,16 +1192,13 @@
 }
 
 function user_pass_submit($form_values, $form, &$form_state) {
-  global $base_url;
 
   $account = $form_values['account'];
-  $from = variable_get('site_mail', ini_get('sendmail_from'));
 
   // Mail one time login URL and instructions.
-  $variables = array('!username' => $account->name, '!site' => variable_get('site_name', 'Drupal'), '!login_url' => user_pass_reset_url($account), '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $account->mail, '!date' => format_date(time()), '!login_uri' => url('user', array('absolute' => TRUE)), '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE)));
-  $subject = _user_mail_text('pass_subject', $variables);
-  $body = _user_mail_text('pass_body', $variables);
-  $mail_success = drupal_mail('user-pass', $account->mail, $subject, $body, $from);
+  $variables = array('!login_url' => user_pass_reset_url($account));
+
+  $mail_success = user_mail('pass', $account, $variables);
 
   if ($mail_success) {
     watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail));
@@ -1216,6 +1213,63 @@
 }
 
 /**
+ * Send an e-mail message to a user, using Drupal variables and default settings.
+ * This function automatically provides subject and body for known user e-mails
+ *
+ * @param $key
+ *   A key to identify the mail sent. The mailkey for altering will be 'user-' + $key
+ * @param $account
+ *   User account to which the e-mail will be sent.
+ * @param $subject
+ *   Optional subject of the e-mail to be sent. This must not contain any newline
+ *   characters, or the mail may not be sent properly.
+ * @param $body
+ *   Optional message to be sent. Drupal will format the correct line endings for you.
+ * @param $from
+ *   Optional mail to set From, Reply-To, Return-Path and Error-To to this value.
+ * @param $headers
+ *   Optional associative array containing the headers to add. This is typically
+ *   used to add extra headers (From, Cc, and Bcc).
+ *   <em>When sending mail, the mail must contain a From header.</em>
+ * @return
+ *   Returns TRUE if the mail was successfully accepted for delivery, FALSE otherwise.
+ */
+function user_mail($key, $account, $variables = array(), $subject = '', $body = '', $from = NULL, $headers = array()) {
+  global $base_url;
+  
+  // Add standard variables
+  $variables += array('!username' => $account->name, '!site' => variable_get('site_name', 'Drupal'), '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $account->mail, '!date' => format_date(time()), '!login_uri' => url('user', array('absolute' => TRUE)), '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE)));
+  
+  // Get the language code for this user account
+  $langcode = user_language($account);
+  
+  // Build the e-mail if no subject and body has been passed
+  $subject = $subject ? tt($subject, $variables, $langcode) : _user_mail_text($key.'_subject', $variables, $langcode);
+  $body = $body ? tt($body, $variables, $langcode, TRUE, "\n\n") : _user_mail_text($key.'_body', $variables, $langcode);
+  $from = $from ? $from : variable_get('site_mail', ini_get('sendmail_from'));
+  
+  return drupal_mail('user-'.$key, $account->mail, $subject, $body, $from, $headers);
+}
+
+function _user_mail_localize($text) {
+  
+}
+/**
+ * Returns user language code for notifications
+ * 
+ * @param $account
+ *   Optional user account
+ */
+function user_language($account = NULL) {
+  global $user;
+
+  $account = isset($account) ? $account : $user;
+  $default = language_default();
+  // The user language will be the site default if no language is set for the account.
+  return (isset($account->language) && $account->language) ? $account->language : $default->language;  
+}
+
+/**
  * Menu callback; process one time login link and redirects to the user page on success.
  */
 function user_pass_reset($uid, $timestamp, $hashed_pass, $action = NULL) {
@@ -1341,7 +1395,6 @@
 }
 
 function user_register_submit($form_values, $form, &$form_state) {
-  global $base_url;
   $admin = user_access('administer users');
 
   $mail = $form_values['mail'];
@@ -1353,7 +1406,7 @@
     $pass = user_password();
   };
   $notify = isset($form_values['notify']) ? $form_values['notify'] : NULL;
-  $from = variable_get('site_mail', ini_get('sendmail_from'));
+
   if (isset($form_values['roles'])) {
     $roles = array_filter($form_values['roles']);     // Remove unset roles
   }
@@ -1379,7 +1432,7 @@
 
   watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
 
-  $variables = array('!username' => $name, '!site' => variable_get('site_name', 'Drupal'), '!password' => $pass, '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $mail, '!date' => format_date(time()), '!login_uri' => url('user', array('absolute' => TRUE)), '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE)), '!login_url' => user_pass_reset_url($account));
+  $variables = array('!password' => $pass, '!mailto' => $mail, '!login_url' => user_pass_reset_url($account));
 
   // The first user may login immediately, and receives a customized welcome e-mail.
   if ($account->uid == 1) {
@@ -1399,19 +1452,14 @@
     }
     else if (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) {
       // No e-mail verification is required, create new user account, and login user immediately.
-      $subject = _user_mail_text('welcome_subject', $variables);
-      $body = _user_mail_text('welcome_body', $variables);
-      drupal_mail('user-register-welcome', $mail, $subject, $body, $from);
+      user_mail('welcome', $account);
       user_authenticate($account->name, trim($pass));
       $form_state['redirect'] = '';
       return;
     }
     else if ($account->status || $notify) {
       // Create new user account, no administrator approval required.
-      $subject = $notify ? _user_mail_text('admin_subject', $variables) : _user_mail_text('welcome_subject', $variables);
-      $body = $notify ? _user_mail_text('admin_body', $variables) : _user_mail_text('welcome_body', $variables);
-
-      drupal_mail(($notify ? 'user-register-notify' : 'user-register-welcome'), $mail, $subject, $body, $from);
+      user_mail(($notify ? 'admin' : 'welcome'), $account);
 
       if ($notify) {
         drupal_set_message(t('Password and further instructions have been e-mailed to the new user %user.', array('%user' => $name)));
@@ -1424,11 +1472,8 @@
     }
     else {
       // Create new user account, administrator approval required.
-      $subject = _user_mail_text('approval_subject', $variables);
-      $body = _user_mail_text('approval_body', $variables);
-
-      drupal_mail('user-register-approval-user', $mail, $subject, $body, $from);
-      drupal_mail('user-register-approval-admin', $from, $subject, t("!username has applied for an account.\n\n!edit_uri", $variables), $from);
+      user_mail('approval', $account, $variables);
+      user_mail('approval-admin', user_load(1), $variables);
       drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.<br />In the meantime, your password and further instructions have been sent to your e-mail address.'));
 
     }
@@ -1679,7 +1724,7 @@
 
 /*** Administrative features ***********************************************/
 
-function _user_mail_text($messageid, $variables = array()) {
+function _user_mail_text($messageid, $variables = array(), $langcode = NULL) {
 
   // Check if an admin setting overrides the default string.
   if ($admin_setting = variable_get('user_mail_'. $messageid, FALSE)) {
@@ -1689,21 +1734,23 @@
   else {
     switch ($messageid) {
       case 'welcome_subject':
-        return t('Account details for !username at !site', $variables);
+        return t('Account details for !username at !site', $variables, $langcode);
       case 'welcome_body':
-        return t("!username,\n\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n--  !site team", $variables);
+        return t("!username,\n\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n--  !site team", $variables, $langcode);
       case 'admin_subject':
-        return t('An administrator created an account for you at !site', $variables);
+        return t('An administrator created an account for you at !site', $variables, $langcode);
       case 'admin_body':
-        return t("!username,\n\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n--  !site team", $variables);
+        return t("!username,\n\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n--  !site team", $variables, $langcode);
       case 'approval_subject':
-        return t('Account details for !username at !site (pending admin approval)', $variables);
+        return t('Account details for !username at !site (pending admin approval)', $variables, $langcode);
       case 'approval_body':
-        return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been granted, you may log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you may wish to change your password at !edit_uri\n\n\n--  !site team", $variables);
+        return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been granted, you may log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you may wish to change your password at !edit_uri\n\n\n--  !site team", $variables, $langcode);
       case 'pass_subject':
-        return t('Replacement login information for !username at !site', $variables);
+        return t('Replacement login information for !username at !site', $variables, $langcode);
       case 'pass_body':
-        return t("!username,\n\nA request to reset the password for your account has been made at !site.\n\nYou may now log in to !uri_brief clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.", $variables);
+        return t("!username,\n\nA request to reset the password for your account has been made at !site.\n\nYou may now log in to !uri_brief clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.", $variables, $langcode);
+      case 'approval-admin_subject':
+        return t("!username has applied for an account.\n\n!edit_uri", $variables, $langcode);
     }
   }
 }
