diff --git a/components/date.inc b/components/date.inc
index dfa09a5..ba1b46c 100644
--- a/components/date.inc
+++ b/components/date.inc
@@ -130,6 +130,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']) {
@@ -324,6 +325,7 @@ function _webform_display_date($component, $value, $format = 'html') {
     '#format' => $format,
     '#value' => $value,
     '#webform_component' => $component,
+    '#translatable' => array('#title'),
   );
 }
 
diff --git a/components/email.inc b/components/email.inc
index c573ad9..4b57182 100644
--- a/components/email.inc
+++ b/components/email.inc
@@ -118,6 +118,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.
@@ -178,6 +179,7 @@ function _webform_display_email($component, $value, $format = 'html') {
     '#format' => $format,
     '#value' => isset($value[0]) ? $value[0] : '',
     '#webform_component' => $component,
+  	'#translatable' => array('#title'),
   );
 }
 
diff --git a/components/fieldset.inc b/components/fieldset.inc
index 25b051e..6d7087d 100644
--- a/components/fieldset.inc
+++ b/components/fieldset.inc
@@ -61,6 +61,7 @@ function _webform_render_fieldset($component, $value = NULL, $filter = TRUE) {
     '#attributes' => array('class' => array('webform-component-' . $component['type']), 'id' => 'webform-component-' . $component['form_key']),
     '#pre_render' => array('webform_fieldset_prerender'),
     '#webform_component' => $component,
+  	'#translatable' => array('#title', '#description'),
   );
 
   return $element;
@@ -83,6 +84,7 @@ function _webform_display_fieldset($component, $value, $format = 'html') {
       '#title' => $component['name'],
       '#weight' => $component['weight'],
       '#theme_wrappers' => array('webform_element_text'),
+      '#translatable' => array('#title'),
     );
   }
   else {
diff --git a/components/file.inc b/components/file.inc
index 652f0a9..1006274 100644
--- a/components/file.inc
+++ b/components/file.inc
@@ -315,6 +315,7 @@ function _webform_render_file($component, $value = NULL, $filter = TRUE) {
     ),
     '#webform_component' => $component,
     '#theme_wrappers' => array(),
+  	'#translatable' => array('#title', '#description'),
   );
   $element['#webform_component'] = $component;
   $element['#weight'] = $component['weight'];
@@ -514,6 +515,7 @@ function _webform_display_file($component, $value, $format = 'html') {
      '#theme_wrappers' => $format == 'text' ? array('webform_element_text') : array('webform_element'),
      '#webform_component' => $component,
      '#format' => $format,
+  	 '#translatable' => array('#title'),
   );
 }
 
diff --git a/components/grid.inc b/components/grid.inc
index 52cbb91..d6bb315 100644
--- a/components/grid.inc
+++ b/components/grid.inc
@@ -148,6 +148,7 @@ function _webform_render_grid($component, $value = NULL, $filter = TRUE) {
     '#theme' => 'webform_grid',
     '#theme_wrappers' => array('webform_element'),
     '#webform_component' => $component,
+    '#translatable' => array('#title', '#description'),
   );
 
   $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
@@ -173,6 +174,8 @@ function _webform_render_grid($component, $value = NULL, $filter = TRUE) {
         // Webform handles validation manually.
         '#validated' => TRUE,
         '#webform_validated' => FALSE,
+        
+        '#translatable' => array('#title', '#options'),
       );
     }
   }
@@ -203,6 +206,7 @@ function _webform_display_grid($component, $value, $format = 'html') {
     '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
     '#sorted' => TRUE,
     '#webform_component' => $component,
+  	'#translatable' => array('#title', '#questions', '#options'),
   );
 
   foreach ($questions as $key => $question) {
diff --git a/components/hidden.inc b/components/hidden.inc
index 343a21a..e55dc5d 100644
--- a/components/hidden.inc
+++ b/components/hidden.inc
@@ -60,6 +60,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])) {
@@ -82,6 +83,7 @@ function _webform_display_hidden($component, $value, $format = 'html') {
     '#theme' => 'webform_display_hidden',
     '#theme_wrappers' => $format == 'text' ? array('webform_element_text') : array('webform_element'),
     '#webform_component' => $component,
+  	'#translatable' => array('#title'),
   );
 
   // TODO: This check is unusual. It shows hidden fields in e-mails but not
diff --git a/components/markup.inc b/components/markup.inc
index dcb41ab..e214ecb 100644
--- a/components/markup.inc
+++ b/components/markup.inc
@@ -65,6 +65,7 @@ function _webform_render_markup($component, $value = NULL, $filter = TRUE) {
     '#input_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/pagebreak.inc b/components/pagebreak.inc
index d3c1d4c..46221d2 100644
--- a/components/pagebreak.inc
+++ b/components/pagebreak.inc
@@ -55,6 +55,7 @@ function _webform_render_pagebreak($component, $value = NULL, $filter = TRUE) {
     '#type' => 'hidden',
     '#value' => $component['name'],
     '#weight' => $component['weight'],
+  	'#translatable' => array('#value'),
   );
   return $element;
 }
@@ -69,6 +70,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 a350c7b..c94dbeb 100644
--- a/components/select.inc
+++ b/components/select.inc
@@ -269,6 +269,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.
@@ -423,6 +424,7 @@ function _webform_display_select($component, $value, $format = 'html') {
     '#format' => $format,
     '#value' => $value,
     '#webform_component' => $component,
+  	'#translatable' => array('#title'),
   );
 }
 
diff --git a/components/textarea.inc b/components/textarea.inc
index 4151137..3824dd7 100644
--- a/components/textarea.inc
+++ b/components/textarea.inc
@@ -110,6 +110,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']) {
@@ -135,6 +136,7 @@ function _webform_display_textarea($component, $value, $format = 'html') {
     '#format' => $format,
     '#value' => isset($value[0]) ? $value[0] : '',
     '#webform_component' => $component,
+    '#translatable' => array('#title'),
   );
 }
 
diff --git a/components/textfield.inc b/components/textfield.inc
index 8f24688..947a5a6 100644
--- a/components/textfield.inc
+++ b/components/textfield.inc
@@ -133,6 +133,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']) {
@@ -173,6 +174,7 @@ function _webform_display_textfield($component, $value, $format = 'html') {
     '#format' => $format,
     '#value' => isset($value[0]) ? $value[0] : '',
     '#webform_component' => $component,
+    '#translatable' => array('#title'),
   );
 }
 
diff --git a/components/time.inc b/components/time.inc
index 82f95d0..433e1c5 100644
--- a/components/time.inc
+++ b/components/time.inc
@@ -93,6 +93,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'),
   );
 
   if (drupal_strlen($component['value']) > 0) {
@@ -252,6 +253,7 @@ function _webform_display_time($component, $value, $format = 'html') {
     '#hourformat' => $component['extra']['hourformat'],
     '#value' => $value,
     '#webform_component' => $component,
+    '#translatable' => array('#title'),
   );
 }
 
diff --git a/includes/webform.components.inc b/includes/webform.components.inc
index 2ed9f48..feae800 100644
--- a/includes/webform.components.inc
+++ b/includes/webform.components.inc
@@ -703,6 +703,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);
 
@@ -723,6 +726,9 @@ function webform_component_update($component) {
     $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;
   db_update('webform_component')
@@ -781,6 +787,9 @@ function webform_component_delete($node, $component) {
     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);
 }
@@ -1023,3 +1032,112 @@ 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) {
+  // Fill in the the default values for the missing properties.
+  webform_component_defaults($component);
+  // Render 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
+  _webform_component_translation_parse($element, $component);
+}
+
+/**
+ * Remove translation source for all the translatable poperties.
+ * 
+ * @param array $component
+ */
+function webform_component_delete_translation_strings($component) {
+  // Fill in the the default values for the missing properties.
+  $component_full = webform_menu_component_load($component['cid'], $component['nid'], $component['type']);
+  // Render the component.
+  $element = webform_component_invoke($component_full['type'], 'render', $component_full, NULL, 'html');
+  // Parse the renderable array to find the translatable properties and
+  // remove translation source for them
+  _webform_component_translation_parse($element, $component_full, FALSE, TRUE);
+}
+
+/**
+ * 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.
+ * @param bool $update
+ *   Flag to signal the translation sources should be updated / created.
+ * @param bool $delete
+ *   Flag to signal the translation sources should be removed.
+ */
+function _webform_component_translation_parse($element, $component, $update = TRUE, $delete = FALSE) {
+  if (isset($element['#translatable']) && is_array($element['#translatable'])) {
+    foreach ($element['#translatable'] as $key) {
+      if (isset($element[$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) {
+            _webform_component_translation_update(array('webform', $component['nid'], $component['cid'], $key . '-' . $elem_key), $elem_value, $update, $delete);
+          }
+        }
+        else {
+          // If the translatable property is not an array, it can be treated as
+          // a string. If the #name property is set, this has been done before
+          // calling this function recursevly, therefore we prepend it to the
+          // key in order to diferentiate between the properties of more than
+          // one children of the component, when creating / using the
+          // translation source.
+          if (isset($element['#name'])) {
+            $property = $element['#name'] . $key;
+          }
+          else {
+            $property = $key;
+          }
+          _webform_component_translation_update(array('webform', $component['nid'], $component['cid'], $property), $element[$key], $update, $delete);
+        }
+      }
+    }
+  }
+  // Test properties to find posible childrens, and if found, recursevly call
+  // the function on them, after adding the children name to its renderable
+  // array.
+  foreach ($element as $key => $children) {
+    if (strpos($key, '#') !== 0) {
+      $children['#name'] = $key;
+      _webform_component_update_translation_parse($children, $component);
+    }
+  }
+}
+
+/**
+ * Update / create / remove a translation source.
+ * 
+ * @param array/string $name
+ *   The name of the translation source.
+ * @param array/string $string
+ *   The string (or array of strings) to be updated.
+ * @param bool $update
+ *   Flag to signal the translation sources should be updated / created.
+ * @param bool $delete
+ *   Flag to signal the translation sources should be removed.
+ */
+function _webform_component_translation_update($name, $string, $update = TRUE, $delete = FALSE) {
+  if ($update && function_exists('i18n_string')) {
+    $options = array(
+      'langcode' => NULL,
+      'update' => TRUE,
+      'translate' => FALSE,
+    );
+    i18n_string($name, $string, $options);
+  }
+  if ($delete && function_exists('i18n_string_remove')) {
+    i18n_string_remove($name, $string, array());
+  }
+}
+
diff --git a/webform.module b/webform.module
index 612adb0..d2bb071 100644
--- a/webform.module
+++ b/webform.module
@@ -673,6 +673,30 @@ function webform_theme() {
 }
 
 /**
+ * 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() {
+  // TODO load all components and run 
+  // webform_component_update_translation_strings() for each one. 
+  return TRUE;
+}
+
+/**
  * Implementation of hook_webform_component_info().
  */
 function webform_webform_component_info() {
@@ -1886,6 +1910,8 @@ 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'])) {
@@ -1903,6 +1929,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.
@@ -1939,6 +1967,45 @@ function _webform_client_form_add_component($node, $component, $component_value,
   }
 }
 
+function _webform_translate_component(&$element, $component) {
+  if (isset($element['#translatable']) && is_array($element['#translatable'])) {
+    foreach ($element['#translatable'] as $key) {
+      if (isset($element[$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) {
+            $element[$key][$elem_key] = webform_tt(array('webform', $component['nid'], $component['cid'], $key . '-' . $elem_key), $elem_value);
+          }
+        }
+        else {
+          // If the translatable property is not an array, it can be treated as
+          // a string. If the #name property is set, this has been done before
+          // calling this function recursevly, therefore we prepend it to the
+          // key in order to diferentiate between the properties of more than
+          // one children of the component, when creating / using the
+          // translation source.
+          if (isset($element['#name'])) {
+            $property = $element['#name'] . $key;
+          }
+          else {
+            $property = $key;
+          }
+          $element[$key] = webform_tt(array('webform', $component['nid'], $component['cid'], $property), $element[$key]);
+        }
+      }
+    }
+  }
+  // Test properties to find posible childrens, and if found, recursevly call
+  // the function on them, after adding the children name to its renderable
+  // array.
+  foreach ($element as $key => $children) {
+    if (strpos($key, '#') !== 0) {
+      $children['#name'] = $key;
+      _webform_translate_component($children, $component);
+    }
+  }
+}
+
 function webform_client_form_validate($form, &$form_state) {
   $node = node_load($form_state['values']['details']['nid']);
   $finished = $form_state['values']['details']['finished'];
@@ -3375,8 +3442,12 @@ function webform_strtotime($date) {
  * Wrapper function for tt() 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;
