diff --git a/components/date.inc b/components/date.inc
index b79f31f..b9db4e4 100644
--- a/components/date.inc
+++ b/components/date.inc
@@ -134,6 +134,7 @@ function _webform_render_date($component, $value = NULL, $filter = TRUE) {
     '#theme_wrappers' => array('webform_element'),
     '#element_validate' => array('webform_validate_date'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   if ($component['extra']['datepicker']) {
diff --git a/components/email.inc b/components/email.inc
index f8fb148..38229e8 100644
--- a/components/email.inc
+++ b/components/email.inc
@@ -120,6 +120,7 @@ function _webform_render_email($component, $value = NULL, $filter = TRUE) {
     '#element_validate'  => array('_webform_validate_email'),
     '#theme_wrappers' => array('webform_element'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   // Add an e-mail class for identifying the difference from normal textfields.
diff --git a/components/fieldset.inc b/components/fieldset.inc
index 92875b4..e4e9fdc 100644
--- a/components/fieldset.inc
+++ b/components/fieldset.inc
@@ -62,6 +62,7 @@ function _webform_render_fieldset($component, $value = NULL, $filter = TRUE) {
     '#attributes' => array('class' => array('webform-component-fieldset'), 'id' => 'webform-component-' . $component['form_key']),
     '#pre_render' => array('webform_fieldset_prerender'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   // Hide the fieldset title if #title_display is 'none'.
diff --git a/components/file.inc b/components/file.inc
index 6df31cb..f6dadb3 100644
--- a/components/file.inc
+++ b/components/file.inc
@@ -367,6 +367,7 @@ function _webform_render_file($component, $value = NULL, $filter = TRUE) {
     '#weight' => $component['weight'],
     '#theme_wrappers' => array('webform_element'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   return $element;
diff --git a/components/grid.inc b/components/grid.inc
index a53192b..7a6ce14 100644
--- a/components/grid.inc
+++ b/components/grid.inc
@@ -157,6 +157,7 @@ function _webform_render_grid($component, $value = NULL, $filter = TRUE) {
     '#theme_wrappers' => array('webform_element'),
     '#process' => array('webform_expand_grid'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description', 'options'),
   );
 
   if ($value) {
@@ -180,6 +181,8 @@ function webform_expand_grid($element) {
   if (!empty($element['#qrand'])) {
     _webform_shuffle_options($questions);
   }
+  
+  $element['#options'] = $options;
 
   foreach ($questions as $key => $question) {
     if ($question != '') {
@@ -193,6 +196,9 @@ function webform_expand_grid($element) {
         // Webform handles validation manually.
         '#validated' => TRUE,
         '#webform_validated' => FALSE,
+        
+        '#translatable' => array('title'),
+
       );
     }
   }
@@ -231,7 +237,7 @@ function _webform_display_grid($component, $value, $format = 'html') {
 
   foreach ($questions as $key => $question) {
     if ($question !== '') {
-      $element[$question] = array(
+        $element[$key] = array(
         '#title' => $question,
         '#value' => isset($value[$key]) ? $value[$key] : NULL,
       );
@@ -399,17 +405,13 @@ function theme_webform_grid($variables) {
 
   $rows = array();
   $header = array(array('data' => '', 'class' => array('webform-grid-question')));
-  $first = TRUE;
+  // Set the header for the table.
+  foreach ($element['#options'] as $option) {
+    $header[] = array('data' => _webform_filter_xss($option), 'class' => array('checkbox', 'webform-grid-option'));
+  }
   foreach (element_children($element) as $key) {
     $question_element = $element[$key];
 
-    // Set the header for the table.
-    if ($first) {
-      foreach ($question_element['#options'] as $option) {
-        $header[] = array('data' => _webform_filter_xss($option), 'class' => array('checkbox', 'webform-grid-option'));
-      }
-      $first = FALSE;
-    }
 
     // Create a row with the question title.
     $row = array(array('data' => _webform_filter_xss($question_element['#title']), 'class' => array('webform-grid-question')));
diff --git a/components/hidden.inc b/components/hidden.inc
index 5930da6..4ac823e 100644
--- a/components/hidden.inc
+++ b/components/hidden.inc
@@ -62,6 +62,7 @@ function _webform_render_hidden($component, $value = NULL, $filter = TRUE) {
     '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
     '#default_value' => $filter ? _webform_filter_values($component['value']) : $component['value'],
     '#weight' => $component['weight'],
+    '#translatable' => array('title'),
   );
 
   if (isset($value[0])) {
diff --git a/components/markup.inc b/components/markup.inc
index 064abf5..bf9c2bd 100644
--- a/components/markup.inc
+++ b/components/markup.inc
@@ -65,6 +65,7 @@ function _webform_render_markup($component, $value = NULL, $filter = TRUE) {
     '#format' => $component['extra']['format'],
     '#theme_wrappers' => array('webform_element'),
     '#webform_component' => $component,
+    '#translatable' => array('title'),
   );
 
   // TODO: Remove when #markup becomes available in D7.
diff --git a/components/number.inc b/components/number.inc
index 13ddbcc..3b67278 100644
--- a/components/number.inc
+++ b/components/number.inc
@@ -256,6 +256,7 @@ function _webform_render_number($component, $value = NULL, $filter = TRUE) {
     '#step' => abs($component['extra']['step']),
     '#integer' => $component['extra']['integer'],
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   // Flip the min and max properties to make min less than max if needed.
diff --git a/components/pagebreak.inc b/components/pagebreak.inc
index 95c838c..d832c7b 100644
--- a/components/pagebreak.inc
+++ b/components/pagebreak.inc
@@ -71,6 +71,7 @@ function _webform_display_pagebreak($component, $value = NULL, $format = 'html')
     '#weight' => $component['weight'],
     '#format' => $format,
     '#webform_component' => $component,
+    '#translatable' => array('title'),
   );
   return $element;
 }
diff --git a/components/select.inc b/components/select.inc
index f3ffafe..837b7b0 100644
--- a/components/select.inc
+++ b/components/select.inc
@@ -271,6 +271,7 @@ function _webform_render_select($component, $value = NULL, $filter = TRUE) {
     '#theme_wrappers' => array('webform_element'),
     '#pre_render' => array(), // Needed to disable double-wrapping of radios and checkboxes.
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description', 'options'),
   );
 
   // Convert the user-entered options list into an array.
@@ -451,6 +452,7 @@ function _webform_display_select($component, $value, $format = 'html') {
   return array(
     '#title' => $component['name'],
     '#weight' => $component['weight'],
+    '#options' => _webform_select_options($component),
     '#theme' => 'webform_display_select',
     '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
     '#format' => $format,
@@ -505,7 +507,8 @@ function theme_webform_display_select($variables) {
   $component = $element['#webform_component'];
 
   // Convert submitted 'safe' values to un-edited, original form.
-  $options = _webform_select_options($component, TRUE);
+  $options = $element['#options'];
+
 
   $items = array();
   if ($component['extra']['multiple']) {
diff --git a/components/textarea.inc b/components/textarea.inc
index 7523cd9..019e5a4 100644
--- a/components/textarea.inc
+++ b/components/textarea.inc
@@ -112,6 +112,7 @@ function _webform_render_textarea($component, $value = NULL, $filter = TRUE) {
     '#resizable' => (bool) $component['extra']['resizable'], // MUST be FALSE to disable.
     '#theme_wrappers' => array('webform_element'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   if ($component['extra']['disabled']) {
diff --git a/components/textfield.inc b/components/textfield.inc
index 7c45881..8a2bff1 100644
--- a/components/textfield.inc
+++ b/components/textfield.inc
@@ -135,6 +135,7 @@ function _webform_render_textfield($component, $value = NULL, $filter = TRUE) {
     '#attributes' => $component['extra']['attributes'],
     '#theme_wrappers' => array('webform_element'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   if ($component['extra']['disabled']) {
diff --git a/components/time.inc b/components/time.inc
index f417020..e69cba0 100644
--- a/components/time.inc
+++ b/components/time.inc
@@ -99,6 +99,7 @@ function _webform_render_time($component, $value = NULL, $filter = TRUE) {
     '#theme' => 'webform_time',
     '#theme_wrappers' => array('webform_element'),
     '#webform_component' => $component,
+    '#translatable' => array('title', 'description'),
   );
 
   // Set the value from Webform if available.
diff --git a/includes/webform.components.inc b/includes/webform.components.inc
index 18e74bd..11f61a3 100644
--- a/includes/webform.components.inc
+++ b/includes/webform.components.inc
@@ -756,6 +756,9 @@ function webform_component_insert(&$component) {
     ))
     ->execute();
 
+  // Create translation source for all the translatable poperties.
+  webform_component_update_translation_strings($component);
+
   // Post-insert actions.
   module_invoke_all('webform_component_insert', $component);
 
@@ -775,6 +778,8 @@ function webform_component_update($component) {
     $function = $module . '_webform_component_presave';
     $function($component);
   }
+  // Update / create translation source for all the translatable poperties.
+  webform_component_update_translation_strings($component);
 
   $component['value'] = isset($component['value']) ? $component['value'] : NULL;
   $component['mandatory'] = isset($component['mandatory']) ? $component['mandatory'] : 0;
@@ -834,6 +839,9 @@ function webform_component_delete($node, $component) {
     $child_component = $node->webform['components'][$row->cid];
     webform_component_delete($node, $child_component);
   }
+  
+  // Remove translation source for all the translatable poperties.
+  webform_component_delete_translation_strings($component);
 
   // Post-delete actions.
   module_invoke_all('webform_component_delete', $component);
@@ -1071,3 +1079,97 @@ function webform_validate_unique($element, $form_state) {
     }
   }
 }
+
+
+/**
+ * Update / create translation source for all the translatable poperties.
+ *
+ * @param array $component
+ */
+function webform_component_update_translation_strings(&$component) {
+  if (module_exists('i18n_string')) {
+    // Fill in the the default values for the missing properties.
+    webform_component_defaults($component);
+    // Render the 'render' FAPI array for the component.
+    $element = webform_component_invoke($component['type'], 'render', $component, NULL, 'html');
+    // Parse the renderable array to find the translatable properties and
+    // update / create translation source for them
+    $component['extra']['translated_strings'] = _webform_component_translation_parse($element, $component);
+    // Render the 'display' FAPI array for the component.
+    $element = webform_component_invoke($component['type'], 'display', $component, NULL, 'html');
+    // Parse the renderable array to find the translatable properties and
+    // update / create translation source for them
+    $component['extra']['translated_strings'] = array_merge($component['extra']['translated_strings'], _webform_component_translation_parse($element, $component));
+  }
+}
+
+/**
+ * Remove translation source for all the translatable poperties.
+ *
+ * @param array $component
+ */
+function webform_component_delete_translation_strings($component) {
+  if (module_exists('i18n_string')) {
+    if (isset($component['extra']['translated_strings'])) {
+      foreach ($component['extra']['translated_strings'] as $name) {
+        i18n_string_remove($name);
+      }
+    }
+  }
+}
+
+/**
+ * Parse a component renderable array to find the translatable properties and
+ * create / update or remove translation source for them.
+ *
+ * @param array $element
+ *   The renderable array to be parsed.
+ * @param array $component
+ *   The component which was rendered.
+ */
+function _webform_component_translation_parse($element, $component) {
+  $translated_properies = array();
+
+  if (!isset($element['#parents'])) {
+    $element['#parents'] = array();
+  }
+
+  if (isset($element['#translatable']) && is_array($element['#translatable'])) {
+    foreach ($element['#translatable'] as $key) {
+      if (isset($element['#' . $key]) && $element['#' . $key] != '') {
+        if (isset($element['#parents']) && count($element['#parents'])) {
+          $property = '[' . implode('][', $element['#parents']) . ']#' . $key;
+        }
+        else {
+          $property = '#' . $key;
+        }
+        if (is_array($element['#' . $key])) {
+          // If the translatable property is an array, we translate the children.
+          foreach ($element['#' . $key] as $elem_key => $elem_value) {
+            $name = implode(':', array('webform', $component['nid'], $component['cid'], $property . '-' . $elem_key));
+            $translated_properies[] = $name;
+            webform_tt($name, $elem_value, NULL, TRUE);
+          }
+        }
+        else {
+          // If the translatable property is not an array, it can be treated as
+          // a string.
+          $name = implode(':', array('webform', $component['nid'], $component['cid'], $property));
+          $translated_properies[] = $name;
+          webform_tt($name, $element['#' . $key], NULL, TRUE);
+        }
+      }
+    }
+  }
+
+  // Recursevly call the function on the children, after adding the children
+  // name to its #parents array.
+  foreach (element_children($element) as $child) {
+    $element[$child]['#parents'] = $element['#parents'];
+    $element[$child]['#parents'][] = $child;
+    // Add the translated propertied to the list.
+    $translated_properies = array_merge($translated_properies, _webform_component_translation_parse($element[$child], $component));
+  }
+
+  return $translated_properies;
+}
diff --git a/webform.module b/webform.module
index ea75f0a..c1d34f4 100644
--- a/webform.module
+++ b/webform.module
@@ -720,6 +720,47 @@ function webform_element_info() {
   return $elements;
 }
 
+
+ /**
+ * Implements hook_i18n_string_info().
+ */
+function webform_i18n_string_info() {
+  $groups['webform'] = array(
+    'title' => t('Webform'),
+    'description' => t('Localizable properties of webforms, like title, select options, and others.'),
+    'format' => FALSE, // This group doesn't have strings with format
+    'list' => FALSE, // This group cannot list all strings
+    //'refresh callback' => 'webform_i18n_string_refresh',
+  );
+  return $groups;
+}
+
+/**
+ * Update / create translation source for all the translatable poperties of
+ * all the components
+ */
+function webform_i18n_string_refresh() {
+  $components = db_select('webform_component', 'wc')
+                  ->fields('wc')
+                  ->execute()
+                  ->fetchAllAssoc('cid');
+
+  foreach ($components as $component) {
+    $component = (array) $component;
+    $component['extra'] = unserialize($component['extra']);
+
+    module_load_include('inc', 'webform', 'includes/webform.components');
+    webform_component_update_translation_strings($component);
+
+    $component['extra'] = serialize($component['extra']);
+    drupal_write_record('webform_component', $component, array('nid', 'cid'));
+  }
+
+  return TRUE;
+}
+
+
+
 /**
  * Implements hook_webform_component_info().
  */
@@ -2094,6 +2135,9 @@ function _webform_client_form_add_component($node, $component, $component_value,
     // This component is display only.
     $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value'];
     if ($display_element = webform_component_invoke($component['type'], 'display', $component, $data, $format)) {
+      
+     // Translate the translatable properties.
+     _webform_translate_component($display_element, $component);
       // The form_builder() function usually adds #parents and #id for us, but
       // because these are not marked for #input, we need to add them manually.
       if (!isset($display_element['#parents'])) {
@@ -2111,6 +2155,8 @@ function _webform_client_form_add_component($node, $component, $component_value,
     // Add this user-defined field to the form (with all the values that are always available).
     $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
     if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter)) {
+      // Translate the translatable properties.
+      _webform_translate_component($element, $component);
       $parent_fieldset[$component['form_key']] = $element;
 
       // Override the value if one already exists in the form state.
@@ -2147,6 +2193,62 @@ function _webform_client_form_add_component($node, $component, $component_value,
   }
 }
 
+/**
+ * Translates the component properties that are translatable. These are found
+ * in under 'translated_strigs' in the 'extra' array of the component, which
+ * is build when the component is inserted / updated, or when all webform
+ * strings are updated from admin/config/regional/translate/i18n_string.
+ * @param array $element
+ *   The FAPI renderable array of the component instance.
+ * @param array $component
+ *   The component.
+ */
+function _webform_translate_component(&$element, $component) {
+  if (isset($component['extra']['translated_strings']) && is_array($component['extra']['translated_strings'])) {
+    foreach ($component['extra']['translated_strings'] as $name) {
+      $name_list = explode(':', $name);
+      $current_element = &$element;
+      if (strpos($name_list[3], '[') !== FALSE) {
+        // The property is deeper in the renderable array, we must extract the
+        // the place where it is.
+        list ($children, $property)  = explode(']#', $name_list[3]);
+        // Remove the '[' from the begining of the string.
+        $children = drupal_substr($children, 1);
+        $children_array = explode('][', $children);
+        foreach ($children_array as $child) {
+          if (isset($current_element[$child])) {
+            $current_element = &$current_element[$child];
+          }
+          else {
+            continue;
+          }
+        }
+      }
+      else {
+        // Remove the '#' from the begining of the property, for consistency.
+        $property = drupal_substr($name_list[3], 1);
+      }
+
+      if (strpos($property, '-') !== FALSE) {
+        // If property is array, we extract the key from the property.
+        list ($property, $key) = explode('-', $property);
+        if (isset($current_element['#' . $property][$key])) {
+          $current_element['#' . $property][$key] = webform_tt($name, $current_element['#' . $property][$key]);
+        }
+      }
+      else {
+        // Else we can treat the property as string.
+        if (isset($current_element['#' . $property])) {
+          $current_element['#' . $property] = webform_tt($name, $current_element['#' . $property]);
+        }
+      }
+    }
+  }
+}
+
+
+
+
 function webform_client_form_validate($form, &$form_state) {
   $node = node_load($form_state['values']['details']['nid']);
   $finished = $form_state['values']['details']['finished'];
@@ -3652,11 +3754,16 @@ function webform_strtotime($date) {
 }
 
 /**
- * Wrapper function for tt() if i18nstrings enabled.
+ * Wrapper function for i18n_string() if i18nstrings enabled.
  */
 function webform_tt($name, $string, $langcode = NULL, $update = FALSE) {
-  if (function_exists('tt')) {
-    return tt($name, $string, $langcode, $update);
+
+if (function_exists('i18n_string')) {
+    $options = array(
+      'langcode' => $langcode,
+      'update' => $update,
+    );
+    return i18n_string($name, $string, $options);
   }
   else {
     return $string;
