Drupal – Ubercart Localization

Localization with Ubercart is somehow a bit difficult. Assume we have 2 languages, English and Traditional Chinese, installed. For a particular product, there will be one node representing the English one and the other node representing the Traditional Chinese one. If you add the English node into the cart, it will not be translated in the cart and checkout page. In other words, they are treated as 2 different products.

The following example can localize the Product content type even in the cart and checkout page. and it is based on the work by Stewart Adam. Please refer to for the post – Translating or internationalizing an Ubercart store: Common problems & solutions more information.

1. Create a custom module and add the following hook implementation in the .module file.

  * Implementation of hook_i18nsync_fields_alter().
function custom_i18nsync_fields_alter($fields, $type) {
  if(in_array($type, uc_product_types())) {
    $fields['uc_products']['#title'] = 'Products';
    // These values were found by doing a print_r($node) in node-product.tpl.php
    $fields['uc_products']['#options'] = array (
      'model' => 'SKU',
      'list_price' => 'List Price',
      'cost' => 'Cost',
      'sell_price' => 'Sell Price',
      'weight' => 'Weight',
      'weight_units' => 'Weight Units',
      'dim_length' => 'Length',
      'dim_width' => 'Width',
      'dim_height' => 'Height',
      'length_units' => 'Length Units',
      'pkg_qty' => 'Quantity',
      'default_qty' => 'Default quantity to add to cart'

  * Implementation of hook_add_to_cart().
function custom_add_to_cart($nid, $qty, $data) {
  /* Due to Drupal's use of multiple nodes for product translations, the same
   * product in a different language is treated as a different product entirely.
   * This is problematic as the same product in different languages can be added
   * to the cart simultaneously. This function works around that problem by
   * always using the tnid/original node. As a result, the cart must be
   * localized as it is displayed.
  $node = node_load($nid);
  // Determine if this node is the source node or a translated one
  // Remember: tnid is 0 if there are no translations
  $is_source = ($node->nid == $node->tnid || $node->tnid == 0) ? 1 : 0;
  if ($is_source) {
    // If it is the source, then all is well…
    $result[] =  array('success' => TRUE);
  } else {
    /* If we are not the source node, then fail to add this product silently and
     * call uc_cart_add_item() to add the source node's product instead. It will
     * be localized later - see custom_cart_item()
    uc_cart_add_item($node->tnid, $qty, $data);
    $result[] = array('success' => FALSE, 'silent' => TRUE);
  // Remember: We need an array in an array here
  return $result;

  * Implementation of hook_cart_item().
function custom_cart_item($op, &$item) {
  /* hook_cart_display() isn't really a hook, it's mostly for internal use.
   * However, we do need to access later. Setting $item->module forces
   * a module_invoke() call in uc_cart.module to call custom_cart_display()
   * instead of the default uc_product_cart_display(). We will call
   * uc_product_cart_display() inside our function to ensure things work as
   * usual in future versions.
  $item->module = "custom";
  /* Note that although it is possible to use check for case 'load' in $op and
   * then override the $item->nid and $item->title values, this will cause bugs
   * when attempting to add or remove products in different languages. To
   * resolve these bugs, we are forcing the use of custom_cart_display() and
   * rewriting the code for the title, img, and anchors to localize the cart.

  * Implementation of hook_cart_display().
function custom_cart_display($item) {
  /* Call uc_product_cart_display() to get things setup as usual and to ensure
   * this hack still works even if uc_product_cart_display changes at some point
   * in the future.
  $display_item = uc_product_cart_display($item);
  // Get the translations, if any.
  $node = node_load($item->nid);
  global $language;
  $translations = translation_node_get_translations($node->tnid);
  if ($translations[$language->language]) {
    // Reminder: NEVER override the nid. That is what causes the bugs!
    $tnode = node_load($translations[$language->language]->nid);
    $display_item["title"]["#value"] = node_access('view', $tnode) ? l($tnode->title, 'node/'. $tnode->nid) : check_plain($tnode->title);
    $display_item["image"]["#value"] = uc_product_get_picture($tnode->nid, 'cart');
  return $display_item;


2. The above code will show the translated content in the cart page. Next, we need to localize the checkout page by editing the them template.php.

 * Fix for ubercart translation problem in the checkout page
 * Reference: http://www.firewing1.com/node/27
function phptemplate_cart_review_table($show_subtotal = TRUE) {
  $subtotal = 0;

  // Set up table header.
  $header = array(
    array('data' => t('Qty'), 'class' => 'qty'),
    array('data' => t('Products'), 'class' => 'products'),
    array('data' => t('Price'), 'class' => 'price'),

  $context = array();

  // Set up table rows.
  $contents = uc_cart_get_contents();
  global $language;
  foreach ($contents as $item) {
	// Get the translation node if any
	$node = node_load($item->nid);
    $translations = translation_node_get_translations($node->tnid);
    if ($translations[$language->language]) {
      $tnode = node_load($translations[$language->language]->nid);
    } else {
      $tnode = $node;
    $price_info = array(
      'price' => $item->price,
      'qty' => $item->qty,

    $context['revision'] = 'altered';
    $context['type'] = 'cart_item';
    $context['subject'] = array(
      'cart' => $contents,
      'cart_item' => $item,
      /* Get the translated node */
      //'node' => node_load($item->nid),
      'node' => node_load($tnode),

    $total = uc_price($price_info, $context);
    $subtotal += $total;

    /* Get the translated node title and description */
    //$description = check_plain($item->title) . uc_product_get_description($item);
    $description = check_plain($tnode->title) . uc_product_get_description($item);

    // Remove node from context to prevent the price from being altered.
    $context['revision'] = 'themed-original';
    $context['type'] = 'amount';
    $rows[] = array(
      array('data' => t('@qty×', array('@qty' => $item->qty)), 'class' => 'qty'),
      array('data' => $description, 'class' => 'products'),
      array('data' => uc_price($total, $context), 'class' => 'price'),

  // Add the subtotal as the final row.
  if ($show_subtotal) {
    $context = array(
      'revision' => 'themed-original',
      'type' => 'amount',
    $rows[] = array(
      'data' => array(array('data' => '<span id="subtotal-title">' . t('Subtotal:') . '</span> ' . uc_price($subtotal, $context), 'colspan' => 4, 'class' => 'subtotal')),
      'class' => 'subtotal',

  return theme('table', $header, $rows, array('class' => 'cart-review'));


3. Enable the Synchronize Translations in i18n module and edit the Product content type and activate the Multilingual support with translation.


4. With the same page under the Multilanguage options. Synchronize all fields except the Taxonomy. I will show you how to localize the Taxonomy in another article.
Drupal – Taxonomy Translation


5. Try it now @ http://<drupal_root>/cart and http://<drupal_root>/cart/checkout

Done =)


2 thoughts on “Drupal – Ubercart Localization”

    1. This solution only applies to Drupal 6. i guess you have to refer to the Ubercart project page if you want to translate the string in D7 Ubercart.

      Sorry that i can’t help.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.