diff -urp flag/flag.inc flag.new/flag.inc
--- flag/flag.inc	2008-11-13 16:00:04.000000000 -0500
+++ flag.new/flag.inc	2008-11-14 11:31:50.000000000 -0500
@@ -742,12 +742,25 @@ class flag_node extends flag_flag {
       'show_on_page' => TRUE,
       'show_on_teaser' => TRUE,
       'show_on_form' => FALSE,
+      'i18n' => 0,
     );
     return $options;
   }
   
   function options_form(&$form) {
     parent::options_form($form);
+    $form['i18n'] = array(
+      '#type' => 'radios',
+      '#title' => t('Internationalization'),
+      '#options' => array(
+        '1' => t('Flag translations of content as a group'),
+        '0' => t('Flag each translation of content separately'),
+      ),
+      '#default_value' => $this->i18n,
+      '#description' => t('Flagging translations as a group effectively allows users to flag the original piece of content regardless of the translation they are viewing. Changing this setting will <strong>not</strong> update content that has been flagged already.'),
+      '#access' => module_exists('translation_helpers'),
+      '#weight' => 5,
+    );
     $form['display']['show_on_teaser'] = array(
       '#type' => 'checkbox',
       '#title' => t('Display link on node teaser'),
@@ -781,6 +794,24 @@ class flag_node extends flag_flag {
     return $node->nid;
   }
 
+  /**
+   * Adjust the Content ID to find the translation parent if i18n-enabled.
+   *
+   * @param $content_id
+   *   The nid for the content.
+   * @return
+   *   The tnid if available, the nid otherwise.
+   */
+  function get_translation_id($content_id) {
+    if ($this->i18n) {
+      $node = $this->fetch_content($content_id);
+      if (!empty($node->tnid)) {
+        $content_id = $node->tnid;
+      }
+    }
+    return $content_id;
+  }
+
   function uses_hook_link($teaser) {
     if ($teaser && $this->show_on_teaser || !$teaser && $this->show_on_page) {
       return TRUE;
@@ -788,6 +819,17 @@ class flag_node extends flag_flag {
     return FALSE;
   }
 
+  function flag($action, $content_id, $account = NULL, $skip_permission_check = FALSE) {
+    $content_id = $this->get_translation_id($content_id);
+    return parent::flag($action, $content_id, $account, $skip_permission_check);
+  }
+
+
+  function is_flagged($content_id, $uid = NULL) {
+    $content_id = $this->get_translation_id($content_id);
+    return parent::is_flagged($content_id, $uid);
+  }
+
   function get_labels_token_types() {
     return array('node');
   }
diff -urp flag/flag.install flag.new/flag.install
--- flag/flag.install	2008-11-13 16:13:23.000000000 -0500
+++ flag.new/flag.install	2008-11-14 11:31:50.000000000 -0500
@@ -80,6 +80,12 @@ function flag_requirements($phase) {
       $requirements['flag_content_clash']['description'] = _flag_flag_content_message();
     }
   }
+
+  if (module_exists('translation') && !module_exists('translation_helpers')) {
+    $requirements['flag_translation']['title'] = $t('Flag');
+    $requirements['flag_translation']['severity'] = REQUIREMENT_ERROR;
+    $requirements['flag_translation']['description'] = $t('To have the flag module work with translations, you need to install and enable the <a href="http://drupal.org/project/translation_helpers">Translation helpers</a> module.');
+  }
   return $requirements;
 }
 
diff -urp flag/flag.module flag.new/flag.module
--- flag/flag.module	2008-11-09 03:40:00.000000000 -0500
+++ flag.new/flag.module	2008-11-14 12:32:41.000000000 -0500
@@ -138,7 +138,7 @@ function flag_link($type, $object = NULL
 
   // Get all possible flags for this content-type.
   $flags = flag_get_flags($type);
-  
+
   foreach ($flags as $flag) {
     if (!$flag->user_access($user)) {
       // User has no permission to use this flag.
@@ -264,8 +264,26 @@ function flag_nodeapi(&$node, $op, $teas
       }
       break;
     case 'delete':
-      db_query("DELETE FROM {flag_content} WHERE content_type = 'node' AND content_id = %d", $node->nid);
-      db_query("DELETE FROM {flag_counts} WHERE content_type = 'node' AND content_id = %d", $node->nid);
+      foreach (flag_get_flags('node') as $flag) {
+        // If the flag is being tracked by translation set and the node is part
+        // of a translation set, don't delete the flagging record.
+        // Instead, data will be updated in the 'translation_change' op, below.
+        db_query("DELETE FROM {flag_content} WHERE fid = %d AND content_id = %d", $flag->fid, $node->nid);
+        db_query("DELETE FROM {flag_counts} WHERE fid = %d AND content_id = %d", $flag->fid, $node->nid);
+      }
+      break;
+    case 'translation_change':
+      if (isset($node->translation_change)) {
+        // If there is only one node remaining, track by nid rather than tnid.
+        // Otherwise, use the new tnid.
+        $content_id = $node->translation_change['new_tnid'] == 0 ? $node->translation_change['remaining_nid'] : $node->translation_change['new_tnid'];
+        foreach (flag_get_flags('node') as $flag) {
+          if ($flag->applies_to_content_id($node->nid) && ($flag->i18n)) {
+            db_query("UPDATE {flag_content} SET content_id = %d WHERE fid = %d AND content_id = %d", $content_id, $flag->fid, $node->translation_change['old_tnid']);
+            db_query("UPDATE {flag_counts} SET content_id = %d WHERE fid = %d AND content_id = %d", $content_id, $flag->fid, $node->translation_change['old_tnid']);
+          }
+        }
+      }
       break;
   }
 }
@@ -532,6 +550,14 @@ function flag_form(&$form_state, $name, 
     );
   }
 
+  $form['global'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Global flag'),
+    '#default_value' => $flag->global,
+    '#description' => t('If checked, flag is considered "global" and each node is either flagged or not. If unchecked, each user has individual flags on content.'),
+    '#weight' => 1,
+  );
+
   $form['roles'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Roles that may use this flag'),
@@ -539,13 +565,7 @@ function flag_form(&$form_state, $name, 
     '#default_value' => $flag->roles,
     '#required' => TRUE,
     '#description' => t('Checking <em>authenticated user</em> will allow all logged-in users to flag content with this flag. Anonymous users may not flag content.'),
-  );
-
-  $form['global'] = array(
-    '#type' => 'checkbox',
-    '#title' => t("Global flag"),
-    '#default_value' => $flag->global,
-    '#description' => t('If checked, flag is considered "global" and each node is either flagged or not. If unchecked, each user has individual flags on content.'),
+    '#weight' => 5,
   );
 
   $form['types'] = array(
@@ -554,7 +574,8 @@ function flag_form(&$form_state, $name, 
     '#options' => node_get_types('names'),
     '#default_value' => $flag->types,
     '#description' => t('Check any node types that this flag may be used on. You must check at least one node type.'),
-    '#required' => TRUE
+    '#required' => TRUE,
+    '#weight' => 10,
   );
 
   $form['display'] = array(
@@ -562,6 +583,7 @@ function flag_form(&$form_state, $name, 
     '#title' => t('Display options'),
     '#description' => t('Flags are usually controlled through links that allow users to toggle their behavior. You can choose how users interact with flags by changing options here. It is legitimate to have none of the following checkboxes ticked, if, for some reason, you wish <a href="@placement-url">to place the the links on the page yourself</a>.', array('@placement-url' => 'http://drupal.org/node/295383')),
     '#tree' => FALSE,
+    '#weight' => 20,
   );
 
   $form['submit'] = array(
@@ -571,7 +593,7 @@ function flag_form(&$form_state, $name, 
     // to give the flag handler a chance to remove it (e.g. flag_broken).
     '#weight' => 999,
   );
-  
+
   $flag->options_form($form);
 
   return $form;
