diff --git a/uc_cart/uc_cart.module b/uc_cart/uc_cart.module
index e1622f6..452aee8 100644
--- a/uc_cart/uc_cart.module
+++ b/uc_cart/uc_cart.module
@@ -743,7 +743,7 @@ function uc_cart_uc_message() {
 
   $messages['checkout_instructions'] = '';
   $messages['review_instructions'] = t("Your order is almost complete. Please review the details below and click 'Submit order' if all the information is correct.  You may use the 'Back' button to make changes to your order if necessary.");
-  $messages['completion_message'] = t('Your order is complete! Your order number is [uc_order:order-id].');
+  $messages['completion_message'] = t('Your order is complete! Your order number is [uc_order:number].');
   $messages['completion_logged_in'] = t('Thank you for shopping at [store:name]. While logged in, you may continue shopping or <a href="[uc_order:url]">view your current order status</a> and order history.');
   $messages['completion_existing_user'] = t("Thank you for shopping at [store:name]. Your current order has been attached to the account we found matching your e-mail address.\n\n<a href=\"!user_url\">Login</a> to view your current order status and order history. Remember to login when you make your next purchase for a faster checkout experience!", array('!user_url' => url('user')));
   $messages['completion_new_user'] = t("Thank you for shopping at [store:name]. A new account has been created for you here that you may use to view your current order status.\n\n<a href=\"!user_url\">Login</a> to your new account using the following information:\n\n<strong>Username:</strong> !new_username\n<strong>Password:</strong> !new_password", array('!user_url' => url('user')));
diff --git a/uc_file/uc_file.tokens.inc b/uc_file/uc_file.tokens.inc
index 36ec6f7..6c6ad84 100644
--- a/uc_file/uc_file.tokens.inc
+++ b/uc_file/uc_file.tokens.inc
@@ -30,17 +30,23 @@ function uc_file_token_info() {
  * Implements hook_tokens().
  */
 function uc_file_tokens($type, $tokens, $data = array(), $options = array()) {
-  $values = array();
+  $language_code = NULL;
+  if (isset($options['language'])) {
+    $language_code = $options['language']->language;
+  }
+  $sanitize = !empty($options['sanitize']);
+
+  $replacements = array();
 
   if ($type == 'uc_file' && !empty($data['uc_file'])) {
     $files = $data['uc_file'];
 
     if (isset($tokens['downloads'])) {
-      $values[$tokens['downloads']] = theme('uc_file_downloads_token', array('file_downloads' => $files));
+      $replacements[$tokens['downloads']] = theme('uc_file_downloads_token', array('file_downloads' => $files));
     }
   }
 
-  return $values;
+  return $replacements;
 }
 
 /**
diff --git a/uc_order/uc_order.rules_defaults.inc b/uc_order/uc_order.rules_defaults.inc
index 686ae59..2d37030 100644
--- a/uc_order/uc_order.rules_defaults.inc
+++ b/uc_order/uc_order.rules_defaults.inc
@@ -18,7 +18,7 @@ function uc_order_default_rules_configuration() {
       'order:select' => 'order',
       'from' => uc_store_email_from(),
       'addresses' => '[uc_order:email]',
-      'subject' => t('Order #[uc_order:order-id] Update'),
+      'subject' => t('Order #[uc_order:number] Update'),
       'message' => uc_get_message('order_update_email'),
       'format' => filter_default_format(),
     ));
diff --git a/uc_order/uc_order.tokens.inc b/uc_order/uc_order.tokens.inc
index edb60db..96e738d 100644
--- a/uc_order/uc_order.tokens.inc
+++ b/uc_order/uc_order.tokens.inc
@@ -9,112 +9,116 @@
  * Implements hook_token_info().
  */
 function uc_order_token_info() {
-  $type = array(
+  $types = array(
     'name' => t('Orders'),
     'description' => t('Tokens related to Ubercart orders.'),
     'needs-data' => 'uc_order',
   );
 
-  $order['new-username'] = array(
+  $tokens = array();
+
+  $tokens['new-username'] = array(
     'name' => t('New username'),
     'description' => t('New username associated with an order if applicable.'),
   );
-  $order['new-password'] = array(
+  $tokens['new-password'] = array(
     'name' => t('New password'),
     'description' => t('New password associated with an order if applicable.'),
   );
-  $order['order-id'] = array(
-    'name' => t('Order ID'),
+  $tokens['number'] = array(
+    'name' => t('Order number'),
     'description' => t('The unique identifier of the order.'),
   );
-  $order['url'] = array(
+  $tokens['url'] = array(
     'name' => t('URL'),
     'description' => t('The URL to the order'),
+    'type' => 'url',
   );
-  $order['link'] = array(
+  $tokens['link'] = array(
     'name' => t('Link'),
     'description' => t('A link to the order using the order ID.'),
   );
-  $order['admin-url'] = array(
+  $tokens['admin-url'] = array(
     'name' => t('Admin URL'),
     'description' => t('The URL to the admin view page using the order ID.'),
+    'type' => 'url',
   );
-  $order['admin-link'] = array(
+  $tokens['admin-link'] = array(
     'name' => t('Admin link'),
     'description' => t('A link to the order admin view page using the order ID.'),
   );
-  $order['subtotal'] = array(
+  $tokens['subtotal'] = array(
     'name' => t('Subtotal'),
     'description' => t('The subtotal of products on an order.'),
   );
-  $order['total'] = array(
+  $tokens['total'] = array(
     'name' => t('Total'),
     'description' => t('The order total.'),
   );
-  $order['email'] = array(
+  $tokens['email'] = array(
     'name' => t('Email'),
     'description' => t('The primary e-mail address of the order.'),
   );
-  $order['shipping-method'] = array(
+  $tokens['shipping-method'] = array(
     'name' => t('Shipping method'),
     'description' => t('The title of the first shipping line item.'),
   );
-  $order['shipping-address'] = array(
+  $tokens['shipping-address'] = array(
     'name' => t('Shipping address'),
     'description' => t('The order shipping address.'),
   );
-  $order['shipping-phone'] = array(
+  $tokens['shipping-phone'] = array(
     'name' => t('Shipping phone number'),
     'description' => t('The phone number for the shipping address.'),
   );
-  $order['billing-address'] = array(
+  $tokens['billing-address'] = array(
     'name' => t('Billing address'),
     'description' => t('The order billing address.'),
   );
-  $order['billing-phone'] = array(
+  $tokens['billing-phone'] = array(
     'name' => t('Billing phone number'),
     'description' => t('The phone number for the billing address.'),
   );
-  $order['first-name'] = array(
+  $tokens['first-name'] = array(
     'name' => t("Customer's first name"),
     'description' => t('The first name associated with the order.'),
   );
-  $order['last-name'] = array(
+  $tokens['last-name'] = array(
     'name' => t("Customer's last name"),
     'description' => t('The last name associated with the order.'),
   );
-  $order['comments'] = array(
+  $tokens['comments'] = array(
     'name' => t('Comments'),
     'description' => t('Comments left by the customer.'),
   );
-  $order['last-comment'] = array(
+  $tokens['last-comment'] = array(
     'name' => t('Last comment'),
     'description' => t('Last order comment left by an administrator (not counting the order admin comments).'),
   );
-  $order['order-status'] = array(
+  $tokens['order-status'] = array(
     'name' => t('Order status'),
     'description' => t('The current order status.'),
   );
 
-  $order['customer'] = array(
+  $tokens['customer'] = array(
     'name' => t('Customer'),
     'description' => t('The user associated with the order.'),
     'type' => 'user',
   );
-  $order['created'] = array(
+  $tokens['created'] = array(
     'name' => t('Created'),
     'description' => t('The date and time when the order was created.'),
     'type' => 'date',
   );
-  $order['modified'] = array(
+  $tokens['modified'] = array(
     'name' => t('Modified'),
     'description' => t('The date and time when the order was last modified.'),
     'type' => 'date',
   );
 
   return array(
-    'types' => array('uc_order' => $type),
-    'tokens' => array('uc_order' => $order),
+    'types' => array('uc_order' => $types),
+    'tokens' => array('uc_order' => $tokens),
   );
 }
 
@@ -122,63 +126,52 @@ function uc_order_token_info() {
  * Implements hook_tokens().
  */
 function uc_order_tokens($type, $tokens, $data = array(), $options = array()) {
-  $values = array();
-
+  $language_code = NULL;
+  if (isset($options['language'])) {
+    $language_code = $options['language']->language;
+  }
   $sanitize = !empty($options['sanitize']);
 
+  $replacements = array();
+
   if ($type == 'uc_order' && !empty($data['uc_order'])) {
+
     $order = $data['uc_order'];
+    $path = 'user/' . $order->uid . '/orders/' . $order->order_id;
+    $admin_path = 'admin/store/orders/' . $order->order_id;
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'new-username':
           if (isset($_SESSION['new_user']) && is_array($_SESSION['new_user'])) {
-            $values[$original] = $sanitize ? check_plain($_SESSION['new_user']['name']) : $_SESSION['new_user']['name'];
+            $replacements[$original] = $sanitize ? check_plain($_SESSION['new_user']['name']) : $_SESSION['new_user']['name'];
           }
           break;
 
         case 'new-password':
           if (isset($_SESSION['new_user']) && is_array($_SESSION['new_user'])) {
-            $values[$original] = $sanitize ? check_plain($_SESSION['new_user']['pass']) : $_SESSION['new_user']['pass'];
+            $replacements[$original] = $sanitize ? check_plain($_SESSION['new_user']['pass']) : $_SESSION['new_user']['pass'];
           }
           break;
 
-        case 'order-id':
-          $values[$original] = $order->order_id;
+        case 'number':
+          $replacements[$original] = $order->order_id;
           break;
 
         case 'url':
-        case 'link':
-          if (!isset($url)) {
-            $url = url('user/' . $order->uid . '/orders/' . $order->order_id, array('absolute' => TRUE));
-          }
-          else {
-            break;
-          }
+          $replacements[$original] = url($path, array('absolute' => TRUE));
+          break;
 
-          if (isset($tokens['url'])) {
-            $values[$tokens['url']] = $url;
-          }
-          if (isset($tokens['link'])) {
-            $values[$tokens['link']] = l($order->order_id, $url);
-          }
+        case 'link':
+          $replacements[$original] = l($order->order_id, url($path, array('absolute' => TRUE)));
           break;
 
         case 'admin-url':
-        case 'admin-link':
-          if (!isset($admin_url)) {
-            $admin_url = url('admin/store/orders/' . $order->order_id, array('absolute' => TRUE));
-          }
-          else {
-            break;
-          }
+          $replacements[$original] = url($admin_path, array('absolute' => TRUE));
+          break;
 
-          if (isset($tokens['admin-url'])) {
-            $values[$tokens['admin-url']] = $admin_url;
-          }
-          if (isset($tokens['admin-link'])) {
-            $values[$tokens['admin-link']] = l($order->order_id, $admin_url);
-          }
+        case 'admin-link':
+          $replacements[$original] = l($order->order_id, url($admin_path, array('absolute' => TRUE)));
           break;
 
         case 'subtotal':
@@ -192,15 +185,15 @@ function uc_order_tokens($type, $tokens, $data = array(), $options = array()) {
               }
             }
           }
-          $values[$original] = $subtotal;
+          $replacements[$original] = $subtotal;
           break;
 
         case 'total':
-          $values[$original] = uc_currency_format($order->order_total);
+          $replacements[$original] = uc_currency_format($order->order_total);
           break;
 
         case 'email':
-          $values[$original] = $sanitize ? check_plain($order->primary_email) : $order->primary_email;
+          $replacements[$original] = $sanitize ? check_plain($order->primary_email) : $order->primary_email;
           break;
 
         case 'shipping-method':
@@ -212,34 +205,34 @@ function uc_order_tokens($type, $tokens, $data = array(), $options = array()) {
               }
             }
           }
-          $values[$original] = !isset($ship_method) ? t('Standard delivery') : $ship_method;
+          $replacements[$original] = !isset($ship_method) ? t('Standard delivery') : $ship_method;
           break;
 
         case 'shipping-address':
-          $values[$original] = uc_order_address($order, 'delivery');
+          $replacements[$original] = uc_order_address($order, 'delivery');
           break;
 
         case 'shipping-phone':
-          $values[$original] = check_plain($order->delivery_phone);
+          $replacements[$original] = check_plain($order->delivery_phone);
           break;
 
         case 'billing-address':
-          $values[$original] = uc_order_address($order, 'billing');
+          $replacements[$original] = uc_order_address($order, 'billing');
           break;
 
         case 'billing-phone':
-          $values[$original] = check_plain($order->billing_phone);
+          $replacements[$original] = check_plain($order->billing_phone);
           break;
 
         case 'first-name':
         case 'last-name':
           if (variable_get('uc_customer_list_address', 'billing') == 'shipping') {
-            $values[$tokens['first-name']] = $sanitize ? check_plain($order->delivery_first_name) : $order->delivery_first_name;
-            $values[$tokens['last-name']] = $sanitize ? check_plain($order->delivery_last_name) : $order->delivery_last_name;
+            $replacements[$tokens['first-name']] = $sanitize ? check_plain($order->delivery_first_name) : $order->delivery_first_name;
+            $replacements[$tokens['last-name']] = $sanitize ? check_plain($order->delivery_last_name) : $order->delivery_last_name;
           }
           else {
-            $values[$tokens['first-name']] = $sanitize ? check_plain($order->billing_first_name) : $order->billing_first_name;
-            $values[$tokens['last-name']] = $sanitize ? check_plain($order->billing_last_name) : $order->billing_last_name;
+            $replacements[$tokens['first-name']] = $sanitize ? check_plain($order->billing_first_name) : $order->billing_first_name;
+            $replacements[$tokens['last-name']] = $sanitize ? check_plain($order->billing_last_name) : $order->billing_last_name;
           }
           break;
 
@@ -248,7 +241,7 @@ function uc_order_tokens($type, $tokens, $data = array(), $options = array()) {
           if ($sanitize) {
             $result = check_plain($result);
           }
-          $values[$original] = empty($result) ? t('<i>No comments left.</i>') : $result;
+          $replacements[$original] = empty($result) ? t('<i>No comments left.</i>') : $result;
           break;
 
         case 'last-comment':
@@ -256,40 +249,49 @@ function uc_order_tokens($type, $tokens, $data = array(), $options = array()) {
           if ($sanitize) {
             $result = check_plain($result);
           }
-          $values[$original] = empty($result) ? t('<i>No comment found.</i>') : $result;
+          $replacements[$original] = empty($result) ? t('<i>No comment found.</i>') : $result;
           break;
 
         case 'order-status':
-          $values[$original] = uc_order_status_data($order->order_status, 'title');
+          $replacements[$original] = uc_order_status_data($order->order_status, 'title');
           break;
 
         case 'customer':
-          $values[$original] = $order->uid;
+          $replacements[$original] = $order->uid;
           break;
 
         case 'created':
-          $values[$original] = format_date($order->created, 'short');
+          $replacements[$original] = format_date($order->created, 'short');
           break;
 
         case 'modified':
-          $values[$original] = format_date($order->modified, 'short');
+          $replacements[$original] = format_date($order->modified, 'short');
           break;
       }
     }
 
+    // Handle chaining for tokens that have 'type' defined in hook_token_info()
+    if ($link_tokens = token_find_with_prefix($tokens, 'url')) {
+      $replacements += token_generate('url', $link_tokens, array('path' => $path), $options);
+    }
+
+    if ($link_tokens = token_find_with_prefix($tokens, 'admin-url')) {
+      $replacements += token_generate('url', $link_tokens, array('path' => $admin_path), $options);
+    }
+
     if ($customer_tokens = token_find_with_prefix($tokens, 'customer')) {
       $customer = user_load($order->uid);
-      $values += token_generate('user', $customer_tokens, array('user' => $customer), $options);
+      $replacements += token_generate('user', $customer_tokens, array('user' => $customer), $options);
     }
 
     if ($created_tokens = token_find_with_prefix($tokens, 'created')) {
-      $values += token_generate('date', $created_tokens, array('date' => $order->created), $options);
+      $replacements += token_generate('date', $created_tokens, array('date' => $order->created), $options);
     }
 
     if ($changed_tokens = token_find_with_prefix($tokens, 'modified')) {
-      $values += token_generate('date', $changed_tokens, array('date' => $order->modified), $options);
+      $replacements += token_generate('date', $changed_tokens, array('date' => $order->modified), $options);
     }
   }
 
-  return $values;
+  return $replacements;
 }
diff --git a/uc_product/uc_product.tokens.inc b/uc_product/uc_product.tokens.inc
index 22e6dfb..52d455f 100644
--- a/uc_product/uc_product.tokens.inc
+++ b/uc_product/uc_product.tokens.inc
@@ -9,70 +9,82 @@
  * Implements hook_token_info().
  */
 function uc_product_token_info() {
-  $product['model'] = array(
+  $types = array(
+    'name' => t('Products'),
+    'description' => t('Tokens for Ubercart products.'),
+    'needs-data' => 'product',
+  );
+
+  $tokens = array();
+
+  $tokens['model'] = array(
     'name' => t('Model number'),
     'description' => t("The product's model number."),
   );
 
-  $product['list_price'] = array(
+  $tokens['list_price'] = array(
     'name' => t('List price'),
     'description' => t("The product's list price."),
   );
-  $product['cost'] = array(
+  $tokens['cost'] = array(
     'name' => t('Cost'),
     'description' => t("The product's cost."),
   );
-  $product['sell_price'] = array(
+  $tokens['sell_price'] = array(
     'name' => t('Sell price'),
     'description' => t("The product's sell price."),
   );
 
-  $product['weight'] = array(
+  $tokens['weight'] = array(
     'name' => t('Weight'),
     'description' => t("The product's formatted weight."),
   );
 
-  $product['weight-value'] = array(
+  $tokens['weight-value'] = array(
     'name' => t('Weight value'),
     'description' => t("The numerical value of the product's weight."),
   );
-  $product['weight_units'] = array(
+  $tokens['weight_units'] = array(
     'name' => t('Weight units'),
     'description' => t("The unit of measurement for the product's weight."),
   );
 
-  $product['length'] = array(
+  $tokens['length'] = array(
     'name' => t('Length'),
     'description' => t("The product's formatted length."),
   );
-  $product['width'] = array(
+  $tokens['width'] = array(
     'name' => t('Width'),
     'description' => t("The product's formatted width."),
   );
-  $product['height'] = array(
+  $tokens['height'] = array(
     'name' => t('Height'),
     'description' => t("The product's formatted height."),
   );
 
-  $product['length-value'] = array(
+  $tokens['length-value'] = array(
     'name' => t('Length value'),
     'description' => t("The numerical value of the product's length."),
   );
-  $product['width-value'] = array(
+  $tokens['width-value'] = array(
     'name' => t('Width value'),
     'description' => t("The numerical value of the product's width."),
   );
-  $product['height-value'] = array(
+  $tokens['height-value'] = array(
     'name' => t('Height value'),
     'description' => t("The numerical value of the product's height."),
   );
-  $product['length_units'] = array(
+  $tokens['length_units'] = array(
     'name' => t('Length units'),
     'description' => t("The unit of measurement for the product's length, width, and height."),
   );
 
+  // Inherit all 'node' type tokens
+  $nodetokens = node_token_info();
+
   return array(
-    'tokens' => array('node' => $product),
+    'types' => array('product' => $types),
+    'tokens' => array('product' => $tokens + $nodetokens['tokens']['node']),
   );
 }
 
@@ -80,72 +92,84 @@ function uc_product_token_info() {
  * Implements hook_tokens().
  */
 function uc_product_tokens($type, $tokens, $data = array(), $options = array()) {
-  $values = array();
+  $language_code = NULL;
+  if (isset($options['language'])) {
+    $language_code = $options['language']->language;
+  }
   $sanitize = !empty($options['sanitize']);
 
-  if ($type == 'node' && !empty($data['node']) && uc_product_is_product($data['node'])) {
-    $node = $data['node'];
+  $replacements = array();
+
+  if ($type == 'product'                      &&
+      !empty($data['product'])                &&
+      uc_product_is_product($data['product'])    ) {
+
+    $product = $data['product'];
+    // We want to inherit all 'node' tokens since products are nodes,
+    // so treat all tokens as node tokens first, then process them
+    // as product tokens so we can add or override what node does.
+    $replacements += token_generate('node', $tokens, array('node' => $product), $options);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'model':
-          $values[$original] = $sanitize ? check_plain($node->model) : $node->model;
+          $replacements[$original] = $sanitize ? check_plain($product->model) : $product->model;
           break;
 
         case 'list_price':
-          $values[$original] = $object->list_price;
+          $replacements[$original] = uc_currency_format($product->list_price);
           break;
 
         case 'cost':
-          $values[$original] = $object->cost;
+          $replacements[$original] = uc_currency_format($product->cost);
           break;
 
         case 'sell_price':
-          $values[$original] = $object->sell_price;
+          $replacements[$original] = uc_currency_format($product->sell_price);
           break;
 
         case 'weight':
-          $values[$original] = uc_weight_format($object->weight, $object->weight_units);
+          $replacements[$original] = uc_weight_format($product->weight, $product->weight_units);
           break;
 
         case 'weight-value':
-          $values[$original] = $object->weight;
+          $replacements[$original] = $product->weight;
           break;
 
         case 'weight_units':
-          $values[$original] = $object->weight_units;
+          $replacements[$original] = $product->weight_units;
           break;
 
         case 'length':
-          $values[$original] = uc_length_format($object->length, $object->length_units);
+          $replacements[$original] = uc_length_format($product->length, $product->length_units);
           break;
 
         case 'width':
-          $values[$original] = uc_length_format($object->width, $object->length_units);
+          $replacements[$original] = uc_length_format($product->width, $product->length_units);
           break;
 
         case 'height':
-          $values[$original] = uc_length_format($object->height, $object->length_units);
+          $replacements[$original] = uc_length_format($product->height, $product->length_units);
           break;
 
-        case 'length-raw':
-          $values[$original] = $object->length;
+        case 'length-value':
+          $replacements[$original] = $product->length;
           break;
 
-        case 'width-raw':
-          $values[$original] = $object->width;
+        case 'width-value':
+          $replacements[$original] = $product->width;
           break;
 
-        case 'height-raw':
-          $values[$original] = $object->height;
+        case 'height-value':
+          $replacements[$original] = $product->height;
           break;
 
         case 'length_units':
-          $values[$original] = $object->length_units;
+          $replacements[$original] = $product->length_units;
           break;
       }
     }
 
-    return $values;
+    return $replacements;
   }
 }
diff --git a/uc_roles/uc_roles.tokens.inc b/uc_roles/uc_roles.tokens.inc
index c32d1ff..d50e8bb 100644
--- a/uc_roles/uc_roles.tokens.inc
+++ b/uc_roles/uc_roles.tokens.inc
@@ -35,28 +35,32 @@ function uc_roles_token_info() {
  * Implements hook_tokens().
  */
 function uc_roles_tokens($type, $tokens, $data = array(), $options = array()) {
-  $values = array();
-
+  $language_code = NULL;
+  if (isset($options['language'])) {
+    $language_code = $options['language']->language;
+  }
   $sanitize = !empty($options['sanitize']);
 
+  $replacements = array();
+
   if ($type == 'uc_role' && !empty($data['uc_role'])) {
     $object = $data['uc_role'];
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'expiration':
-          $values[$original] = format_date($object->expiration, 'medium');
+          $replacements[$original] = format_date($object->expiration, 'medium');
           break;
         case 'name':
-          $values[$original] = $sanitize ? check_plain(_uc_roles_get_name($object->rid)) : _uc_roles_get_name($object->rid);
+          $replacements[$original] = $sanitize ? check_plain(_uc_roles_get_name($object->rid)) : _uc_roles_get_name($object->rid);
           break;
       }
     }
 
     if ($expiration_tokens = token_find_with_prefix($tokens, 'expiration')) {
-      $values += token_generate('date', $expiration_tokens, array('date' => $object->expiration), $options);
+      $replacements += token_generate('date', $expiration_tokens, array('date' => $object->expiration), $options);
     }
   }
 
-  return $values;
+  return $replacements;
 }
diff --git a/uc_stock/uc_stock.tokens.inc b/uc_stock/uc_stock.tokens.inc
index ee0f300..fb96702 100644
--- a/uc_stock/uc_stock.tokens.inc
+++ b/uc_stock/uc_stock.tokens.inc
@@ -17,15 +17,15 @@ function uc_stock_token_info() {
 
   $stock['level'] = array(
     'name' => t('Level'),
-    'description' => t('The current stock level'),
+    'description' => t('The current stock level.'),
   );
   $stock['model'] = array(
     'name' => t('Model'),
-    'description' => t('The model or SKU of the stock level'),
+    'description' => t('The model or SKU of the stock level.'),
   );
   $stock['threshold'] = array(
     'name' => t('Threshold'),
-    'description' => t('The threshold or warning limit of the stock level'),
+    'description' => t('The threshold or warning limit of the stock level.'),
   );
 
   return array(
diff --git a/uc_store/uc_store.tokens.inc b/uc_store/uc_store.tokens.inc
index 14c2603..864c087 100644
--- a/uc_store/uc_store.tokens.inc
+++ b/uc_store/uc_store.tokens.inc
@@ -30,6 +30,7 @@ function uc_store_token_info() {
   $store['url'] = array(
     'name' => t('URL'),
     'description' => t('The Ubercart store URL.'),
+    'type' => 'url',
   );
   $store['link'] = array(
     'name' => t('Store link'),
@@ -58,6 +59,7 @@ function uc_store_token_info() {
   $store['help-url'] = array(
     'name' => t('Help page URL'),
     'description' => t('The URL to the store help page.'),
+    'type' => 'url',
   );
 
   return array(
@@ -75,20 +77,20 @@ function uc_store_token_info() {
 function uc_store_tokens($type, $tokens, $data = array(), $options = array()) {
   global $base_url;
 
-  $values = array();
+  $replacements = array();
 
   if ($type == 'site') {
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'login-link':
           $login_link = url('user', array('absolute' => TRUE));
-          $values[$original] = l($login_link, $login_link);
+          $replacements[$original] = l($login_link, $login_link);
           break;
 
         case 'logo':
           // Use a logo; but only if we have one to use.
           if ($uri = theme_get_setting('logo')) {
-            $values[$original] = '<img src="' . $uri . '" />';
+            $replacements[$original] = '<img src="' . $uri . '" />';
           }
           break;
       }
@@ -99,43 +101,51 @@ function uc_store_tokens($type, $tokens, $data = array(), $options = array()) {
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'name':
-          $values[$original] = variable_get('uc_store_name', t('Our store'));
+          $replacements[$original] = variable_get('uc_store_name', t('Our store'));
           break;
 
         case 'url':
-          $values[$original] = $base_url;
+          $replacements[$original] = $base_url;
           break;
 
         case 'link':
-          $values[$original] = l(variable_get('uc_store_name', t('Our store')), $base_url);
+          $replacements[$original] = l(variable_get('uc_store_name', t('Our store')), $base_url);
           break;
 
         case 'owner':
-          $values[$original] = variable_get('uc_store_owner', '');
+          $replacements[$original] = variable_get('uc_store_owner', '');
           break;
 
         case 'email':
-          $values[$original] = variable_get('uc_store_email', '');
+          $replacements[$original] = variable_get('uc_store_email', '');
           break;
 
         case 'phone':
-          $values[$original] = variable_get('uc_store_phone', '');
+          $replacements[$original] = variable_get('uc_store_phone', '');
           break;
 
         case 'fax':
-          $values[$original] = variable_get('uc_store_fax', '');
+          $replacements[$original] = variable_get('uc_store_fax', '');
           break;
 
         case 'address':
-          $values[$original] = uc_store_address();
+          $replacements[$original] = uc_store_address();
           break;
 
         case 'help-url':
-          $values[$original] = url(variable_get('uc_store_help_page', ''), array('absolute' => TRUE));
+          $replacements[$original] = url(variable_get('uc_store_help_page', ''), array('absolute' => TRUE));
           break;
       }
     }
+
+    // Handle chaining for tokens that have 'type' defined in hook_token_info()
+    if ($link_tokens = token_find_with_prefix($tokens, 'url')) {
+      $replacements += token_generate('url', $link_tokens, array('path' => $base_url), $options);
+    }
+    if ($link_tokens = token_find_with_prefix($tokens, 'help-url')) {
+      $replacements += token_generate('url', $link_tokens, array('path' => $variable_get('uc_store_help_page', '')), $options);
+    }
   }
 
-  return $values;
+  return $replacements;
 }
