--- i18ncomment.info
+++ i18ncomment.info
@@ -0,0 +1,7 @@
+; $Id$
+name = Joined Comments
+description = Joins comments from each content translations to single comments listings.
+dependencies[] = comment
+dependencies[] = translation
+package = Multilanguage
+core = 6.x

--- i18ncomment.module
+++ i18ncomment.module
@@ -0,0 +1,499 @@
+<?php
+// $Id$
+
+/*
+ * @file
+ * Includes comments from node's translations to the comments list.
+ *
+ * This module contains parts of standard comments module with changed
+ * queries, because it's merely impossible to hook-in into standard
+ * comments module in other way.
+ */
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function i18ncomment_form_alter(&$form, $form_state, $form_id) {
+  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
+    $form['comment']['i18ncomment'] = array(
+      '#type' => 'radios',
+      '#title' => t("Comments in translations"),
+      '#description' => t('Set this option to "Join" if you want to list all comments without respect to their language.'),
+      '#default_value' => variable_get('i18ncomment_'. $form['#node_type']->type, TRUE),
+      '#options' => array(
+        0 => t("Separate"),
+        1 => t("Joined"),
+      )
+    );
+  }
+}
+
+/**
+ * Implementation of hook_comment().
+ */
+function i18ncomment_comment(&$edit, $op) {
+  if ($op == 'insert') {
+    $nids = i18ncomment_translation_node_get_nids($edit['nid']);
+    // Add the comment to database.
+    // Here we are building the thread field. See the documentation for
+    // comment_render().
+    if ($edit['pid'] == 0) {
+      // This is a comment with no parent comment (depth 0): we start
+      // by retrieving the maximum thread level.
+      $args = $nids;
+      $args[] = $edit['cid'];
+      $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND cid <> %d', $args));
+
+      // Strip the "/" from the end of the thread.
+      $max = rtrim($max, '/');
+
+      // Finally, build the thread field for this new comment.
+      $thread = int2vancode(vancode2int($max) + 1) .'/';
+    }
+    else {
+      // This is comment with a parent comment: we increase
+      // the part of the thread value at the proper depth.
+
+      // Get the parent comment:
+      $parent = _comment_load($edit['pid']);
+
+      // Strip the "/" from the end of the parent thread.
+      $parent->thread = (string) rtrim((string) $parent->thread, '/');
+
+      // Get the max value in _this_ thread.
+      $args = $nids;
+      $args[] = $parent->thread;
+      $args[] = $edit['cid'];
+      $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND thread LIKE '%s.%%' AND cid <> %d", $args));
+
+      if ($max == '') {
+        // First child of this parent.
+        $thread = $parent->thread .'.'. int2vancode(0) .'/';
+      }
+      else {
+        // Strip the "/" at the end of the thread.
+        $max = rtrim($max, '/');
+
+        // We need to get the value at the correct depth.
+        $parts = explode('.', $max);
+        $parent_depth = count(explode('.', $parent->thread));
+        $last = $parts[$parent_depth];
+
+        // Finally, build the thread field for this new comment.
+        $thread = $parent->thread .'.'. int2vancode(vancode2int($last) + 1) .'/';
+      }
+    }
+    db_query('UPDATE {comments} SET thread = "%s" WHERE cid = %d', $thread, $edit['cid']);
+  }
+}
+
+/**
+ * Enchanced comment_render().
+ */
+function i18ncomment_render_multilang($node, $cid = 0) {
+  global $user;
+  
+  $output = '';
+
+  if (user_access('access comments')) {
+    // Pre-process variables.
+    $nid = $node->nid;
+    if (empty($nid)) {
+      $nid = 0;
+    }
+
+    $mode = _comment_get_display_setting('mode', $node);
+    $order = _comment_get_display_setting('sort', $node);
+    $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+
+    if ($cid && is_numeric($cid)) {
+      // Single comment view.
+      $query = 'SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d';
+      $query_args = array($cid);
+      if (!user_access('administer comments')) {
+        $query .= ' AND c.status = %d';
+        $query_args[] = COMMENT_PUBLISHED;
+      }
+
+      $query = db_rewrite_sql($query, 'c', 'cid');
+      $result = db_query($query, $query_args);
+
+      if ($comment = db_fetch_object($result)) {
+        $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+        $links = module_invoke_all('link', 'comment', $comment, 1);
+        drupal_alter('link', $links, $node);
+
+        $output .= theme('comment_view', $comment, $node, $links);
+      }
+    }
+    else {
+      /////////////////////////////////////////
+      $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+      // Multiple comment view
+      $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid in ('. db_placeholders($nids) .')';
+      $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid in ('. db_placeholders($nids) .')';
+
+      $query_args = $nids;
+      ////////////////////////////////////////////
+
+      if (!user_access('administer comments')) {
+        $query .= ' AND c.status = %d';
+        $query_count .= ' AND c.status = %d';
+        $query_args[] = COMMENT_PUBLISHED;
+      }
+
+      if ($order == COMMENT_ORDER_NEWEST_FIRST) {
+        if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+          $query .= ' ORDER BY c.cid DESC';
+        }
+        else {
+          $query .= ' ORDER BY c.thread DESC';
+        }
+      }
+      else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
+        if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+          $query .= ' ORDER BY c.cid';
+        }
+        else {
+          // See comment above. Analysis reveals that this doesn't cost too
+          // much. It scales much much better than having the whole comment
+          // structure.
+          $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
+        }
+      }
+      $query = db_rewrite_sql($query, 'c', 'cid');
+      $query_count = db_rewrite_sql($query_count, 'c', 'cid');
+
+      // Start a form, for use with comment control.
+      $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
+
+      $divs = 0;
+      $num_rows = FALSE;
+      $comments = '';
+      drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
+
+     while ($comment = db_fetch_object($result)) {
+        $comment = drupal_unpack($comment);
+        $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+        $comment->depth = count(explode('.', $comment->thread)) - 1;
+
+        if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
+          if ($comment->depth > $divs) {
+            $divs++;
+            $comments .= '<div class="indented">';
+          }
+          else {
+            while ($comment->depth < $divs) {
+              $divs--;
+              $comments .= '</div>';
+            }
+          }
+        }
+
+        if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
+          $comments .= theme('comment_flat_collapsed', $comment, $node);
+        }
+        else if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
+          $comments .= theme('comment_flat_expanded', $comment, $node);
+        }
+        else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
+          $comments .= theme('comment_thread_collapsed', $comment, $node);
+        }
+        else if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
+          $comments .= theme('comment_thread_expanded', $comment, $node);
+        }
+
+        $num_rows = TRUE;
+      }
+      while ($divs-- > 0) {
+        $comments .= '</div>';
+      }
+
+      $comment_controls = variable_get('comment_controls_'. $node->type, COMMENT_CONTROLS_HIDDEN);
+      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_ABOVE || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+      }
+
+      $output .= $comments;
+      $output .= theme('pager', NULL, $comments_per_page, 0);
+
+      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_BELOW || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+      }
+    }
+
+    // If enabled, show new comment form if it's not already being displayed.
+    $reply = arg(0) == 'comment' && arg(1) == 'reply';
+    if (user_access('post comments') && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW) && !$reply) {
+      $output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
+    }
+
+    $output = theme('comment_wrapper', $output, $node);
+  }
+
+  return $output;
+}
+
+/**
+ * Preprocessing node template.
+ */
+function i18ncomment_preprocess_node(&$vars) {
+  $node = $vars['node'];
+
+  $translations_enabled = variable_get('language_content_type_'. $node->type, 0);
+  $join_multilang_comments = variable_get('i18ncomment_'. $node->type, TRUE);
+
+  if ($translations_enabled && $join_multilang_comments) {
+    if (!$vars['teaser'] && $node->comment) {
+      $vars['comments'] = i18ncomment_render_multilang($node);
+      $node->comment = NULL;
+    }
+  }
+}
+
+/**
+ * Implementation of hook_link_alter().
+ */
+function i18ncomment_link_alter(&$links, $node) {
+  if (isset($links['comment_comments']) || isset($links['comment_new_comments']) || isset($links['comment_add'])) {
+    $all = i18ncomment_num_all($node->nid);
+    if ($all) {
+      if (isset($links['comment_add']) && variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) != COMMENT_FORM_SEPARATE_PAGE) {
+        unset($links['comment_add']);
+      }
+      $links['comment_comments'] = array(
+        'title' => format_plural($all, '1 comment', '@count comments'),
+        'href' => "node/$node->nid",
+        'attributes' => array('title' => t('Jump to the first comment of this posting.')),
+        'fragment' => 'comments'
+      );
+
+      $new = i18ncomment_num_new($node->nid);
+      if ($new) {
+        $links['comment_new_comments'] = array(
+          'title' => format_plural($new, '1 new comment', '@count new comments'),
+          'href' => "node/$node->nid",
+          'query' => i18ncomment_new_page_count($all, $new, $node),
+          'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
+          'fragment' => 'new'
+        );
+      }
+      else {
+        if (isset($links['comment_new_comments'])) {
+          unset($links['comment_new_comments']);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Get comment count for a node plus it's translations.
+ */
+function i18ncomment_num_all($nid) {
+  static $cache;
+
+  $nids = i18ncomment_translation_node_get_nids($nid);
+
+  if (!isset($cache[$nid])) {
+    $cache[$nid] = db_result(db_query('SELECT SUM(comment_count) FROM {node_comment_statistics} WHERE nid IN ('. db_placeholders($nids) .')', $nids));
+  }
+  return $cache[$nid];
+}
+
+/**
+ * Get number of new comments for current user and specified node plus it's translations.
+ */
+function i18ncomment_num_new($nid, $timestamp = 0) {
+  global $user;
+
+  if ($user->uid) {
+    // Retrieve the timestamp at which the current user last viewed the
+    // specified node.
+    if (!$timestamp) {
+      $timestamp = i18ncomment_node_last_viewed($nid);
+    }
+    $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
+
+    $nids = i18ncomment_translation_node_get_nids($nid);
+
+    // Use the timestamp to retrieve the number of new comments.
+    $args = $nids;
+    $args[] = $timestamp;
+    $args[] = COMMENT_PUBLISHED;
+    $result = db_result(db_query('SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid IN ('. db_placeholders($nids) .') AND timestamp > %d AND c.status = %d', $args));
+
+    return $result;
+  }
+  else {
+    return 0;
+  }
+}
+
+/**
+ * Retrieves the latest timestamp at which the current user last viewed the
+ * specified node or it's translations.
+ */
+function i18ncomment_node_last_viewed($nid) {
+  global $user;
+  static $history;
+
+  $nids = i18ncomment_translation_node_get_nids($nid);
+  $args = $nids;
+  $args[] = $user->uid;
+
+  if (!isset($history[$nid])) {
+    $history[$nid] = db_fetch_object(db_query("SELECT timestamp FROM {history} WHERE nid IN (". db_placeholders($nids) .") AND uid = %d ORDER BY timestamp DESC", $args));
+  }
+
+  return (isset($history[$nid]->timestamp) ? $history[$nid]->timestamp : 0);
+}
+
+/**
+ * Decide on the type of marker to be displayed for a given node or it's translations.
+ */
+function i18ncomment_node_mark($nid, $timestamp) {
+  global $user;
+  static $cache;
+
+  if (!$user->uid) {
+    return MARK_READ;
+  }
+  if (!isset($cache[$nid])) {
+    $cache[$nid] = i18ncomment_node_last_viewed($nid);
+  }
+  if ($cache[$nid] == 0 && $timestamp > NODE_NEW_LIMIT) {
+    return MARK_NEW;
+  }
+  elseif ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT) {
+    return MARK_UPDATED;
+  }
+  return MARK_READ;
+}
+
+/**
+ * Calculate page number for first new comment. Handle node translations.
+ */
+function i18ncomment_new_page_count($num_comments, $new_replies, $node) {
+  $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+  $mode = _comment_get_display_setting('mode', $node);
+  $order = _comment_get_display_setting('sort', $node);
+  $pagenum = NULL;
+  $flat = in_array($mode, array(COMMENT_MODE_FLAT_COLLAPSED, COMMENT_MODE_FLAT_EXPANDED));
+  if ($num_comments <= $comments_per_page || ($flat && $order == COMMENT_ORDER_NEWEST_FIRST)) {
+    // Only one page of comments or flat forum and newest first.
+    // First new comment will always be on first page.
+    $pageno = 0;
+  }
+  else {
+    if ($flat) {
+      // Flat comments and oldest first.
+      $count = $num_comments - $new_replies;
+    }
+    else {
+      $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+      $args = $nids;
+      $args[] = $new_replies;
+      // Threaded comments. See the documentation for comment_render().
+      if ($order == COMMENT_ORDER_NEWEST_FIRST) {
+        // Newest first: find the last thread with new comment
+        $result = db_query('(SELECT thread FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY thread DESC LIMIT 1', $args);
+        $thread = db_result($result);
+        $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND thread > '". $thread ."'", $nids);
+      }
+      else {
+        // Oldest first: find the first thread with new comment
+        $result = db_query('(SELECT thread FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY SUBSTRING(thread, 1, (LENGTH(thread) - 1)) LIMIT 1', $args);
+        $thread = substr(db_result($result), 0, -1);
+        $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < '". $thread ."'", $nids);
+      }
+      $count = db_result($result_count);
+    }
+    $pageno =  $count / $comments_per_page;
+  }
+  if ($pageno >= 1) {
+    $pagenum = "page=". intval($pageno);
+  }
+  return $pagenum;
+}
+
+/**
+ * Implementation of hook_theme_registry_alter().
+ */
+function i18ncomment_theme_registry_alter(&$theme_registry) {
+  if ($theme_registry['comment_view']['function'] == 'theme_comment_view') {
+    $theme_registry['comment_view']['function'] = 'i18ncomment_comment_view';
+  }
+}
+
+/**
+ * Replacement for theme_comment_view(). Handle node's translations when placing the "new" flag on comment.
+ */
+function i18ncomment_comment_view($comment, $node, $links = array(), $visible = TRUE) {
+  static $first_new = TRUE;
+
+  $output = '';
+  $comment->new = i18ncomment_node_mark($comment->nid, $comment->timestamp);
+  if ($first_new && $comment->new != MARK_READ) {
+    // Assign the anchor only for the first new comment. This avoids duplicate
+    // id attributes on a page.
+    $first_new = FALSE;
+    $output .= "<a id=\"new\"></a>\n";
+  }
+
+  $output .= "<a id=\"comment-$comment->cid\"></a>\n";
+
+  // Switch to folded/unfolded view of the comment
+  if ($visible) {
+    $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
+
+    // Comment API hook
+    comment_invoke_comment($comment, 'view');
+
+    $output .= theme('comment', $comment, $node, $links);
+  }
+  else {
+    $output .= theme('comment_folded', $comment);
+  }
+
+  return $output;
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function i18ncomment_nodeapi(&$node, $op, $arg = 0) {
+  switch ($op) {
+    case 'load':
+      if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+        return db_fetch_array(db_query("SELECT ns.last_comment_timestamp, ns.last_comment_name, SUM(ns.comment_count) as 'comment_count' FROM {node_comment_statistics} ns INNER JOIN {node} n ON ns.nid = n.nid WHERE n.tnid = 10 GROUP BY n.tnid ORDER BY ns.last_comment_timestamp DESC", $node->tnid));
+      }
+      break;
+  }
+}
+
+/**
+ * Get nids of all translations of the node plus it's own nid.
+ */
+function i18ncomment_translation_node_get_nids($nid) {
+  static $translations = array();
+
+  if (isset($translations[$nid])) {
+    return $translations[$nid];
+  }
+  else {
+    $node = node_load($nid);
+    if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+      $translations[$nid] = translation_node_get_translations($node->tnid);
+      foreach ($translations[$nid] as $key => $translation) {
+        $translations[$nid][$key] = $translation->nid;
+      }
+    }
+    else {
+      $translations[$nid][] = $node->nid;
+    }
+  }
+  return $translations[$nid];
+}

--- i18ncomment/README.txt
+++ i18ncomment/README.txt
@@ -0,0 +1,8 @@
+// $Id$
+
+INSTALLATION
+
+1. Enable module at admin/build/modules.
+2. Make sure that in Comment settings of your content type, "Comments in node translations" option is set to "Joined".
+3. Add this code to the very end of your theme's node.tpl.php (or node-{content-type}.tpl.php) file:
+<?php print($comments); ?>

--- i18ncomment/i18ncomment.info
+++ i18ncomment/i18ncomment.info
@@ -0,0 +1,7 @@
+; $Id$
+name = Joined Comments
+description = Joins comments from each content translations to single comments listings.
+dependencies[] = comment
+dependencies[] = translation
+package = Multilanguage
+core = 6.x

--- i18ncomment/i18ncomment.install
+++ i18ncomment/i18ncomment.install
@@ -0,0 +1,16 @@
+<?php
+// $Id$
+
+/*
+ * @file
+ * Install i18ncomments module.
+ */
+
+/*
+ * Implementation of hook_install(). We need to execute our module after
+ * others to prevent some glitches caused by $node->comment = NULL set in
+ * hook_preprocess_node().
+ */
+function i18ncomment_install() {
+  db_query("UPDATE {system} SET weight = 100 WHERE name = 'i18ncomment'");
+}

--- i18ncomment/i18ncomment.module
+++ i18ncomment/i18ncomment.module
@@ -0,0 +1,499 @@
+<?php
+// $Id$
+
+/*
+ * @file
+ * Includes comments from node's translations to the comments list.
+ *
+ * This module contains parts of standard comments module with changed
+ * queries, because it's merely impossible to hook-in into standard
+ * comments module in other way.
+ */
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function i18ncomment_form_alter(&$form, $form_state, $form_id) {
+  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
+    $form['comment']['i18ncomment'] = array(
+      '#type' => 'radios',
+      '#title' => t("Comments in translations"),
+      '#description' => t('Set this option to "Join" if you want to list all comments without respect to their language.'),
+      '#default_value' => variable_get('i18ncomment_'. $form['#node_type']->type, TRUE),
+      '#options' => array(
+        0 => t("Separate"),
+        1 => t("Joined"),
+      )
+    );
+  }
+}
+
+/**
+ * Implementation of hook_comment().
+ */
+function i18ncomment_comment(&$edit, $op) {
+  if ($op == 'insert') {
+    $nids = i18ncomment_translation_node_get_nids($edit['nid']);
+    // Add the comment to database.
+    // Here we are building the thread field. See the documentation for
+    // comment_render().
+    if ($edit['pid'] == 0) {
+      // This is a comment with no parent comment (depth 0): we start
+      // by retrieving the maximum thread level.
+      $args = $nids;
+      $args[] = $edit['cid'];
+      $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND cid <> %d', $args));
+
+      // Strip the "/" from the end of the thread.
+      $max = rtrim($max, '/');
+
+      // Finally, build the thread field for this new comment.
+      $thread = int2vancode(vancode2int($max) + 1) .'/';
+    }
+    else {
+      // This is comment with a parent comment: we increase
+      // the part of the thread value at the proper depth.
+
+      // Get the parent comment:
+      $parent = _comment_load($edit['pid']);
+
+      // Strip the "/" from the end of the parent thread.
+      $parent->thread = (string) rtrim((string) $parent->thread, '/');
+
+      // Get the max value in _this_ thread.
+      $args = $nids;
+      $args[] = $parent->thread;
+      $args[] = $edit['cid'];
+      $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND thread LIKE '%s.%%' AND cid <> %d", $args));
+
+      if ($max == '') {
+        // First child of this parent.
+        $thread = $parent->thread .'.'. int2vancode(0) .'/';
+      }
+      else {
+        // Strip the "/" at the end of the thread.
+        $max = rtrim($max, '/');
+
+        // We need to get the value at the correct depth.
+        $parts = explode('.', $max);
+        $parent_depth = count(explode('.', $parent->thread));
+        $last = $parts[$parent_depth];
+
+        // Finally, build the thread field for this new comment.
+        $thread = $parent->thread .'.'. int2vancode(vancode2int($last) + 1) .'/';
+      }
+    }
+    db_query('UPDATE {comments} SET thread = "%s" WHERE cid = %d', $thread, $edit['cid']);
+  }
+}
+
+/**
+ * Enchanced comment_render().
+ */
+function i18ncomment_render_multilang($node, $cid = 0) {
+  global $user;
+  
+  $output = '';
+
+  if (user_access('access comments')) {
+    // Pre-process variables.
+    $nid = $node->nid;
+    if (empty($nid)) {
+      $nid = 0;
+    }
+
+    $mode = _comment_get_display_setting('mode', $node);
+    $order = _comment_get_display_setting('sort', $node);
+    $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+
+    if ($cid && is_numeric($cid)) {
+      // Single comment view.
+      $query = 'SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d';
+      $query_args = array($cid);
+      if (!user_access('administer comments')) {
+        $query .= ' AND c.status = %d';
+        $query_args[] = COMMENT_PUBLISHED;
+      }
+
+      $query = db_rewrite_sql($query, 'c', 'cid');
+      $result = db_query($query, $query_args);
+
+      if ($comment = db_fetch_object($result)) {
+        $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+        $links = module_invoke_all('link', 'comment', $comment, 1);
+        drupal_alter('link', $links, $node);
+
+        $output .= theme('comment_view', $comment, $node, $links);
+      }
+    }
+    else {
+      /////////////////////////////////////////
+      $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+      // Multiple comment view
+      $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid in ('. db_placeholders($nids) .')';
+      $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid in ('. db_placeholders($nids) .')';
+
+      $query_args = $nids;
+      ////////////////////////////////////////////
+
+      if (!user_access('administer comments')) {
+        $query .= ' AND c.status = %d';
+        $query_count .= ' AND c.status = %d';
+        $query_args[] = COMMENT_PUBLISHED;
+      }
+
+      if ($order == COMMENT_ORDER_NEWEST_FIRST) {
+        if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+          $query .= ' ORDER BY c.cid DESC';
+        }
+        else {
+          $query .= ' ORDER BY c.thread DESC';
+        }
+      }
+      else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
+        if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+          $query .= ' ORDER BY c.cid';
+        }
+        else {
+          // See comment above. Analysis reveals that this doesn't cost too
+          // much. It scales much much better than having the whole comment
+          // structure.
+          $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
+        }
+      }
+      $query = db_rewrite_sql($query, 'c', 'cid');
+      $query_count = db_rewrite_sql($query_count, 'c', 'cid');
+
+      // Start a form, for use with comment control.
+      $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
+
+      $divs = 0;
+      $num_rows = FALSE;
+      $comments = '';
+      drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
+
+     while ($comment = db_fetch_object($result)) {
+        $comment = drupal_unpack($comment);
+        $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+        $comment->depth = count(explode('.', $comment->thread)) - 1;
+
+        if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
+          if ($comment->depth > $divs) {
+            $divs++;
+            $comments .= '<div class="indented">';
+          }
+          else {
+            while ($comment->depth < $divs) {
+              $divs--;
+              $comments .= '</div>';
+            }
+          }
+        }
+
+        if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
+          $comments .= theme('comment_flat_collapsed', $comment, $node);
+        }
+        else if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
+          $comments .= theme('comment_flat_expanded', $comment, $node);
+        }
+        else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
+          $comments .= theme('comment_thread_collapsed', $comment, $node);
+        }
+        else if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
+          $comments .= theme('comment_thread_expanded', $comment, $node);
+        }
+
+        $num_rows = TRUE;
+      }
+      while ($divs-- > 0) {
+        $comments .= '</div>';
+      }
+
+      $comment_controls = variable_get('comment_controls_'. $node->type, COMMENT_CONTROLS_HIDDEN);
+      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_ABOVE || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+      }
+
+      $output .= $comments;
+      $output .= theme('pager', NULL, $comments_per_page, 0);
+
+      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_BELOW || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+      }
+    }
+
+    // If enabled, show new comment form if it's not already being displayed.
+    $reply = arg(0) == 'comment' && arg(1) == 'reply';
+    if (user_access('post comments') && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW) && !$reply) {
+      $output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
+    }
+
+    $output = theme('comment_wrapper', $output, $node);
+  }
+
+  return $output;
+}
+
+/**
+ * Preprocessing node template.
+ */
+function i18ncomment_preprocess_node(&$vars) {
+  $node = $vars['node'];
+
+  $translations_enabled = variable_get('language_content_type_'. $node->type, 0);
+  $join_multilang_comments = variable_get('i18ncomment_'. $node->type, TRUE);
+
+  if ($translations_enabled && $join_multilang_comments) {
+    if (!$vars['teaser'] && $node->comment) {
+      $vars['comments'] = i18ncomment_render_multilang($node);
+      $node->comment = NULL;
+    }
+  }
+}
+
+/**
+ * Implementation of hook_link_alter().
+ */
+function i18ncomment_link_alter(&$links, $node) {
+  if (isset($links['comment_comments']) || isset($links['comment_new_comments']) || isset($links['comment_add'])) {
+    $all = i18ncomment_num_all($node->nid);
+    if ($all) {
+      if (isset($links['comment_add']) && variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) != COMMENT_FORM_SEPARATE_PAGE) {
+        unset($links['comment_add']);
+      }
+      $links['comment_comments'] = array(
+        'title' => format_plural($all, '1 comment', '@count comments'),
+        'href' => "node/$node->nid",
+        'attributes' => array('title' => t('Jump to the first comment of this posting.')),
+        'fragment' => 'comments'
+      );
+
+      $new = i18ncomment_num_new($node->nid);
+      if ($new) {
+        $links['comment_new_comments'] = array(
+          'title' => format_plural($new, '1 new comment', '@count new comments'),
+          'href' => "node/$node->nid",
+          'query' => i18ncomment_new_page_count($all, $new, $node),
+          'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
+          'fragment' => 'new'
+        );
+      }
+      else {
+        if (isset($links['comment_new_comments'])) {
+          unset($links['comment_new_comments']);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Get comment count for a node plus it's translations.
+ */
+function i18ncomment_num_all($nid) {
+  static $cache;
+
+  $nids = i18ncomment_translation_node_get_nids($nid);
+
+  if (!isset($cache[$nid])) {
+    $cache[$nid] = db_result(db_query('SELECT SUM(comment_count) FROM {node_comment_statistics} WHERE nid IN ('. db_placeholders($nids) .')', $nids));
+  }
+  return $cache[$nid];
+}
+
+/**
+ * Get number of new comments for current user and specified node plus it's translations.
+ */
+function i18ncomment_num_new($nid, $timestamp = 0) {
+  global $user;
+
+  if ($user->uid) {
+    // Retrieve the timestamp at which the current user last viewed the
+    // specified node.
+    if (!$timestamp) {
+      $timestamp = i18ncomment_node_last_viewed($nid);
+    }
+    $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
+
+    $nids = i18ncomment_translation_node_get_nids($nid);
+
+    // Use the timestamp to retrieve the number of new comments.
+    $args = $nids;
+    $args[] = $timestamp;
+    $args[] = COMMENT_PUBLISHED;
+    $result = db_result(db_query('SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid IN ('. db_placeholders($nids) .') AND timestamp > %d AND c.status = %d', $args));
+
+    return $result;
+  }
+  else {
+    return 0;
+  }
+}
+
+/**
+ * Retrieves the latest timestamp at which the current user last viewed the
+ * specified node or it's translations.
+ */
+function i18ncomment_node_last_viewed($nid) {
+  global $user;
+  static $history;
+
+  $nids = i18ncomment_translation_node_get_nids($nid);
+  $args = $nids;
+  $args[] = $user->uid;
+
+  if (!isset($history[$nid])) {
+    $history[$nid] = db_fetch_object(db_query("SELECT timestamp FROM {history} WHERE nid IN (". db_placeholders($nids) .") AND uid = %d ORDER BY timestamp DESC", $args));
+  }
+
+  return (isset($history[$nid]->timestamp) ? $history[$nid]->timestamp : 0);
+}
+
+/**
+ * Decide on the type of marker to be displayed for a given node or it's translations.
+ */
+function i18ncomment_node_mark($nid, $timestamp) {
+  global $user;
+  static $cache;
+
+  if (!$user->uid) {
+    return MARK_READ;
+  }
+  if (!isset($cache[$nid])) {
+    $cache[$nid] = i18ncomment_node_last_viewed($nid);
+  }
+  if ($cache[$nid] == 0 && $timestamp > NODE_NEW_LIMIT) {
+    return MARK_NEW;
+  }
+  elseif ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT) {
+    return MARK_UPDATED;
+  }
+  return MARK_READ;
+}
+
+/**
+ * Calculate page number for first new comment. Handle node translations.
+ */
+function i18ncomment_new_page_count($num_comments, $new_replies, $node) {
+  $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+  $mode = _comment_get_display_setting('mode', $node);
+  $order = _comment_get_display_setting('sort', $node);
+  $pagenum = NULL;
+  $flat = in_array($mode, array(COMMENT_MODE_FLAT_COLLAPSED, COMMENT_MODE_FLAT_EXPANDED));
+  if ($num_comments <= $comments_per_page || ($flat && $order == COMMENT_ORDER_NEWEST_FIRST)) {
+    // Only one page of comments or flat forum and newest first.
+    // First new comment will always be on first page.
+    $pageno = 0;
+  }
+  else {
+    if ($flat) {
+      // Flat comments and oldest first.
+      $count = $num_comments - $new_replies;
+    }
+    else {
+      $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+      $args = $nids;
+      $args[] = $new_replies;
+      // Threaded comments. See the documentation for comment_render().
+      if ($order == COMMENT_ORDER_NEWEST_FIRST) {
+        // Newest first: find the last thread with new comment
+        $result = db_query('(SELECT thread FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY thread DESC LIMIT 1', $args);
+        $thread = db_result($result);
+        $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND thread > '". $thread ."'", $nids);
+      }
+      else {
+        // Oldest first: find the first thread with new comment
+        $result = db_query('(SELECT thread FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY SUBSTRING(thread, 1, (LENGTH(thread) - 1)) LIMIT 1', $args);
+        $thread = substr(db_result($result), 0, -1);
+        $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < '". $thread ."'", $nids);
+      }
+      $count = db_result($result_count);
+    }
+    $pageno =  $count / $comments_per_page;
+  }
+  if ($pageno >= 1) {
+    $pagenum = "page=". intval($pageno);
+  }
+  return $pagenum;
+}
+
+/**
+ * Implementation of hook_theme_registry_alter().
+ */
+function i18ncomment_theme_registry_alter(&$theme_registry) {
+  if ($theme_registry['comment_view']['function'] == 'theme_comment_view') {
+    $theme_registry['comment_view']['function'] = 'i18ncomment_comment_view';
+  }
+}
+
+/**
+ * Replacement for theme_comment_view(). Handle node's translations when placing the "new" flag on comment.
+ */
+function i18ncomment_comment_view($comment, $node, $links = array(), $visible = TRUE) {
+  static $first_new = TRUE;
+
+  $output = '';
+  $comment->new = i18ncomment_node_mark($comment->nid, $comment->timestamp);
+  if ($first_new && $comment->new != MARK_READ) {
+    // Assign the anchor only for the first new comment. This avoids duplicate
+    // id attributes on a page.
+    $first_new = FALSE;
+    $output .= "<a id=\"new\"></a>\n";
+  }
+
+  $output .= "<a id=\"comment-$comment->cid\"></a>\n";
+
+  // Switch to folded/unfolded view of the comment
+  if ($visible) {
+    $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
+
+    // Comment API hook
+    comment_invoke_comment($comment, 'view');
+
+    $output .= theme('comment', $comment, $node, $links);
+  }
+  else {
+    $output .= theme('comment_folded', $comment);
+  }
+
+  return $output;
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function i18ncomment_nodeapi(&$node, $op, $arg = 0) {
+  switch ($op) {
+    case 'load':
+      if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+        return db_fetch_array(db_query("SELECT ns.last_comment_timestamp, ns.last_comment_name, SUM(ns.comment_count) as 'comment_count' FROM {node_comment_statistics} ns INNER JOIN {node} n ON ns.nid = n.nid WHERE n.tnid = 10 GROUP BY n.tnid ORDER BY ns.last_comment_timestamp DESC", $node->tnid));
+      }
+      break;
+  }
+}
+
+/**
+ * Get nids of all translations of the node plus it's own nid.
+ */
+function i18ncomment_translation_node_get_nids($nid) {
+  static $translations = array();
+
+  if (isset($translations[$nid])) {
+    return $translations[$nid];
+  }
+  else {
+    $node = node_load($nid);
+    if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+      $translations[$nid] = translation_node_get_translations($node->tnid);
+      foreach ($translations[$nid] as $key => $translation) {
+        $translations[$nid][$key] = $translation->nid;
+      }
+    }
+    else {
+      $translations[$nid][] = $node->nid;
+    }
+  }
+  return $translations[$nid];
+}

--- i18ncomment/translations/i18ncomment.pot
+++ i18ncomment/translations/i18ncomment.pot
@@ -0,0 +1,40 @@
+# $Id$
+#
+# LANGUAGE translation of Drupal (general)
+# Copyright YEAR NAME <EMAIL@ADDRESS>
+# Generated from files:
+#  i18ncomment.module: n/a
+#  i18ncomment.info: n/a
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"POT-Creation-Date: 2009-07-22 03:56+0300\n"
+"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
+"Last-Translator: NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: i18ncomment.module:20
+msgid "Comments in translations"
+msgstr ""
+
+#: i18ncomment.module:20
+msgid "Set this option to \"Join\" if you want to list all comments without respect to their language."
+msgstr ""
+
+#: i18ncomment.module:23
+msgid "Separate"
+msgstr ""
+
+#: i18ncomment.module:24
+msgid "Joined"
+msgstr ""
+
+#: i18ncomment.info:0
+msgid "Joins comments from each content translations to single comments listings."
+msgstr ""

--- i18ncomment/translations/ru.po
+++ i18ncomment/translations/ru.po
@@ -0,0 +1,40 @@
+# $Id$
+#
+# Russian translation of Drupal (general)
+# Copyright YEAR NAME <EMAIL@ADDRESS>
+# Generated from files:
+#  i18ncomment.module: n/a
+#  i18ncomment.info: n/a
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"POT-Creation-Date: 2009-07-22 04:17+0300\n"
+"PO-Revision-Date: 2009-07-22 04:21+0200\n"
+"Last-Translator: \n"
+"Language-Team: Russian <EMAIL@ADDRESS>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));\n"
+
+#: i18ncomment.module:20
+msgid "Comments in translations"
+msgstr "Комментарии в переводах"
+
+#: i18ncomment.module:21
+msgid "Set this option to \"Join\" if you want to list all comments without respect to their language."
+msgstr "Установите эту опцию в \"Объединенные\", чтобы показывать в материалах все комментарии вне зависимости от их языка."
+
+#: i18ncomment.module:24
+msgid "Separate"
+msgstr "Отдельные"
+
+#: i18ncomment.module:25
+msgid "Joined"
+msgstr "Объединенные"
+
+#: i18ncomment.info:0
+msgid "Joins comments from each content translations to single comments listings."
+msgstr "Объединяет комментарии в материалах без привязки к языку материала."
+

--- i18ncomment/translations/uk.po
+++ i18ncomment/translations/uk.po
@@ -0,0 +1,40 @@
+# $Id$
+#
+# Ukrainian translation of Drupal (general)
+# Copyright YEAR NAME <EMAIL@ADDRESS>
+# Generated from files:
+#  i18ncomment.module: n/a
+#  i18ncomment.info: n/a
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"POT-Creation-Date: 2009-07-22 03:58+0300\n"
+"PO-Revision-Date: 2009-07-22 04:21+0200\n"
+"Last-Translator: \n"
+"Language-Team: Ukrainian <EMAIL@ADDRESS>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));\n"
+
+#: i18ncomment.module:20
+msgid "Comments in translations"
+msgstr "Коментарі у перекладах"
+
+#: i18ncomment.module:20
+msgid "Set this option to \"Join\" if you want to list all comments without respect to their language."
+msgstr "Виставте цю опцію в \"Об'єднані\", якщо ви хочете показувати у матеріалах усі коментарі без залежності від їх мови."
+
+#: i18ncomment.module:23
+msgid "Separate"
+msgstr "Окремі"
+
+#: i18ncomment.module:24
+msgid "Joined"
+msgstr "Об'єднані"
+
+#: i18ncomment.info:0
+msgid "Joins comments from each content translations to single comments listings."
+msgstr "Об'єднує коментарі у матеріалах без прив'язки до мови матеріалу."
+


