diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index d37a98e..20c5000 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -1260,55 +1260,128 @@ function drupal_unpack($obj, $field = 'data') {
  * @ingroup sanitization
  */
 function t($string, array $args = array(), array $options = array()) {
-  global $language;
-  static $custom_strings;
-
-  // Merge in default.
-  if (empty($options['langcode'])) {
-    $options['langcode'] = isset($language->language) ? $language->language : 'en';
+  $t = new t($string);
+  // @todo Convert into methods.
+  $t->args = $args;
+  if (isset($options['langcode'])) {
+    $t->langcode = $options['langcode'];
   }
-  if (empty($options['context'])) {
-    $options['context'] = '';
+  if (isset($options['context'])) {
+    $t->context = $options['context'];
   }
+  return $t;
+}
 
-  // First, check for an array of customized strings. If present, use the array
-  // *instead of* database lookups. This is a high performance way to provide a
-  // handful of string replacements. See settings.php for examples.
-  // Cache the $custom_strings variable to improve performance.
-  if (!isset($custom_strings[$options['langcode']])) {
-    $custom_strings[$options['langcode']] = variable_get('locale_custom_strings_' . $options['langcode'], array());
-  }
-  // Custom strings work for English too, even if locale module is disabled.
-  if (isset($custom_strings[$options['langcode']][$options['context']][$string])) {
-    $string = $custom_strings[$options['langcode']][$options['context']][$string];
-  }
-  // Translate with locale module if enabled.
-  elseif ($options['langcode'] != 'en' && function_exists('locale')) {
-    $string = locale($string, $options['context'], $options['langcode']);
-  }
-  if (empty($args)) {
-    return $string;
+class t {
+  /**
+   * The string to format.
+   *
+   * @var string
+   */
+  public $string;
+
+  /**
+   * Associative array of tokens as keys and replacement values.
+   *
+   * @var array
+   */
+  public $args = array();
+
+  /**
+   * Flag denoting whether $this->args have been sanitized already.
+   */
+  private $sanitized = FALSE;
+
+  /**
+   * Language code to translate $string into.
+   *
+   * @var string
+   */
+  public $langcode = 'en';
+
+  /**
+   * Translatable string context.
+   *
+   * @var string
+   */
+  public $context = '';
+
+  /**
+   * Custom string overrides; statically shared across class instances.
+   *
+   * @var array
+   */
+  private static $custom_strings = array();
+
+  public function __construct($string) {
+    $this->string = $string;
+
+    // Set default language.
+    if (isset($GLOBALS['language']->language)) {
+      $this->langcode = $GLOBALS['language']->language;
+    }
   }
-  else {
+
+  /**
+   * Sanitizes string token replacement arguments.
+   */
+  public function sanitize() {
     // Transform arguments before inserting them.
-    foreach ($args as $key => $value) {
+    foreach ($this->args as $key => $value) {
       switch ($key[0]) {
         case '@':
           // Escaped only.
-          $args[$key] = check_plain($value);
+          $this->args[$key] = check_plain($value);
           break;
 
         case '%':
         default:
           // Escaped and placeholder.
-          $args[$key] = drupal_placeholder($value);
+          $this->args[$key] = drupal_placeholder($value);
           break;
 
         case '!':
           // Pass-through.
       }
     }
-    return strtr($string, $args);
+    $this->sanitized = TRUE;
+  }
+
+  public function translate() {
+    // First, check for an array of customized strings. If present, use the array
+    // *instead of* database lookups. This is a high performance way to provide a
+    // handful of string replacements. See settings.php for examples.
+    // Cache the $custom_strings variable to improve performance.
+    if (!isset($this->custom_strings[$this->langcode])) {
+      $this->custom_strings[$this->langcode] = variable_get('locale_custom_strings_' . $this->langcode, array());
+    }
+    // Custom strings work for English too, even if locale module is disabled.
+    if (isset($this->custom_strings[$this->langcode][$this->context][$this->string])) {
+      $this->string = $this->custom_strings[$this->langcode][$this->context][$this->string];
+    }
+    // Translate with locale module if enabled.
+    elseif ($this->langcode != 'en' && function_exists('locale')) {
+      $this->string = locale($this->string, $this->context, $this->langcode);
+    }
+  }
+
+  /**
+   * Returns the string representation of subject with sanitized token replacements.
+   */
+  public function __toString() {
+    // Translate the string.
+    // @todo Add ->noTranslate() or ->English() method to prevent translation?
+    $this->translate();
+    // If there are no token replacements, return the string.
+    if (empty($this->args)) {
+      return $this->string;
+    }
+    // Sanitize token replacement values.
+    if (!$this->sanitized) {
+      $this->sanitize();
+    }
+    // Perform token replacements and return the string.
+    return strtr($this->string, $this->args);
   }
 }
 
diff --git a/includes/common.inc b/includes/common.inc
index d0649d7..ab0ca98 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2262,7 +2262,9 @@ function drupal_http_header_attributes(array $attributes = array()) {
  */
 function drupal_attributes(array $attributes = array()) {
   foreach ($attributes as $attribute => &$data) {
-    $data = implode(' ', (array) $data);
+    if (is_array($data)) {
+      $data = implode(' ', $data);
+    }
     $data = $attribute . '="' . check_plain($data) . '"';
   }
   return $attributes ? ' ' . implode(' ', $attributes) : '';
