Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.417
diff -u -r1.417 theme.inc
--- includes/theme.inc	20 Feb 2008 13:39:29 -0000	1.417
+++ includes/theme.inc	29 Feb 2008 19:15:29 -0000
@@ -1545,7 +1545,7 @@
   if ($object->uid && $object->name) {
     // Shorten the name when it is too long or it will break many tables.
     if (drupal_strlen($object->name) > 20) {
-      $name = drupal_substr($object->name, 0, 15) .'...';
+      $name = drupal_truncate_chars($object->name, 15, FALSE, TRUE);
     }
     else {
       $name = $object->name;
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.174
diff -u -r1.174 locale.inc
--- includes/locale.inc	9 Jan 2008 21:36:13 -0000	1.174
+++ includes/locale.inc	29 Feb 2008 19:15:28 -0000
@@ -1979,7 +1979,7 @@
     foreach ($arr as $lid => $value) {
       $rows[] = array(
         $value['group'],
-        array('data' => check_plain(truncate_utf8($value['source'], 150, FALSE, TRUE)) .'<br /><small>'. $value['location'] .'</small>'),
+        array('data' => check_plain(drupal_truncate_chars($value['source'], 150, FALSE, TRUE)) .'<br /><small>'. $value['location'] .'</small>'),
         array('data' => _locale_translate_language_list($value['languages'], $limit_language), 'align' => 'center'),
         array('data' => l(t('edit'), "admin/build/translate/edit/$lid"), 'class' => 'nowrap'),
         array('data' => l(t('delete'), "admin/build/translate/delete/$lid"), 'class' => 'nowrap'),
@@ -2234,7 +2234,7 @@
 function _locale_get_predefined_list() {
   return array(
     "aa" => array("Afar"),
-    "ab" => array("Abkhazian", "Ð°Ò§Ñ?ÑƒÐ° Ð±Ñ‹Ð·ÑˆÓ™Ð°"),
+    "ab" => array("Abkhazian", "Ð°Ò§Ñ?ÑƒÐ° Ð±Ñ‹Ð·ÑˆÓ™Ð°"),
     "ae" => array("Avestan"),
     "af" => array("Afrikaans"),
     "ak" => array("Akan"),
@@ -2245,8 +2245,8 @@
     "ay" => array("Aymara"),
     "az" => array("Azerbaijani", "azÉ™rbaycan"),
     "ba" => array("Bashkir"),
-    "be" => array("Belarusian", "Ð‘ÐµÐ»Ð°Ñ€ÑƒÑ?ÐºÐ°Ñ?"),
-    "bg" => array("Bulgarian", "Ð‘ÑŠÐ»Ð³Ð°Ñ€Ñ?ÐºÐ¸"),
+    "be" => array("Belarusian", "Ð‘ÐµÐ»Ð°Ñ€ÑƒÑ?ÐºÐ°Ñ?"),
+    "bg" => array("Bulgarian", "Ð‘ÑŠÐ»Ð³Ð°Ñ€Ñ?ÐºÐ¸"),
     "bh" => array("Bihari"),
     "bi" => array("Bislama"),
     "bm" => array("Bambara", "Bamanankan"),
@@ -2267,14 +2267,14 @@
     "de" => array("German", "Deutsch"),
     "dv" => array("Maldivian"),
     "dz" => array("Bhutani"),
-    "ee" => array("Ewe", "Æ?Ê‹É›"),
+    "ee" => array("Ewe", "Æ?Ê‹É›"),
     "el" => array("Greek", "Î•Î»Î»Î·Î½Î¹ÎºÎ¬"),
     "en" => array("English"),
     "eo" => array("Esperanto"),
     "es" => array("Spanish", "EspaÃ±ol"),
     "et" => array("Estonian", "Eesti"),
     "eu" => array("Basque", "Euskera"),
-    "fa" => array("Persian", /* Left-to-right marker "â€­" */ "Ù?Ø§Ø±Ø³ÛŒ", LANGUAGE_RTL),
+    "fa" => array("Persian", /* Left-to-right marker "â€­" */ "Ù?Ø§Ø±Ø³ÛŒ", LANGUAGE_RTL),
     "ff" => array("Fulah", "Fulfulde"),
     "fi" => array("Finnish", "Suomi"),
     "fj" => array("Fiji"),
@@ -2289,7 +2289,7 @@
     "gv" => array("Manx"),
     "ha" => array("Hausa"),
     "he" => array("Hebrew", /* Left-to-right marker "â€­" */ "×¢×‘×¨×™×ª", LANGUAGE_RTL),
-    "hi" => array("Hindi", "à¤¹à¤¿à¤¨à¥?à¤¦à¥€"),
+    "hi" => array("Hindi", "à¤¹à¤¿à¤¨à¥?à¤¦à¥€"),
     "ho" => array("Hiri Motu"),
     "hr" => array("Croatian", "Hrvatski"),
     "hu" => array("Hungarian", "Magyar"),
@@ -2300,7 +2300,7 @@
     "ie" => array("Interlingue"),
     "ig" => array("Igbo"),
     "ik" => array("Inupiak"),
-    "is" => array("Icelandic", "Ã?slenska"),
+    "is" => array("Icelandic", "Ã?slenska"),
     "it" => array("Italian", "Italiano"),
     "iu" => array("Inuktitut"),
     "ja" => array("Japanese", "æ—¥æœ¬èªž"),
@@ -2312,7 +2312,7 @@
     "kk" => array("Kazakh", "ÒšÐ°Ð·Ð°Ò›"),
     "kl" => array("Greenlandic"),
     "km" => array("Cambodian"),
-    "kn" => array("Kannada", "à²•à²¨à³?à²¨à²¡"),
+    "kn" => array("Kannada", "à²•à²¨à³?à²¨à²¡"),
     "ko" => array("Korean", "í•œêµ­ì–´"),
     "kr" => array("Kanuri"),
     "ks" => array("Kashmiri"),
@@ -2330,7 +2330,7 @@
     "mg" => array("Malagasy"),
     "mh" => array("Marshallese"),
     "mi" => array("Maori"),
-    "mk" => array("Macedonian", "ÐœÐ°ÐºÐµÐ´Ð¾Ð½Ñ?ÐºÐ¸"),
+    "mk" => array("Macedonian", "ÐœÐ°ÐºÐµÐ´Ð¾Ð½Ñ?ÐºÐ¸"),
     "ml" => array("Malayalam", "à´®à´²à´¯à´¾à´³à´‚"),
     "mn" => array("Mongolian"),
     "mo" => array("Moldavian"),
@@ -2362,7 +2362,7 @@
     "rm" => array("Rhaeto-Romance"),
     "rn" => array("Kirundi"),
     "ro" => array("Romanian", "RomÃ¢nÄƒ"),
-    "ru" => array("Russian", "Ð ÑƒÑ?Ñ?ÐºÐ¸Ð¹"),
+    "ru" => array("Russian", "Ð ÑƒÑ?Ñ?ÐºÐ¸Ð¹"),
     "rw" => array("Kinyarwanda"),
     "sa" => array("Sanskrit"),
     "sc" => array("Sardinian"),
@@ -2371,20 +2371,20 @@
     "sg" => array("Sango"),
     "sh" => array("Serbo-Croatian"),
     "si" => array("Singhalese"),
-    "sk" => array("Slovak", "SlovenÄ?ina"),
-    "sl" => array("Slovenian", "SlovenÅ¡Ä?ina"),
+    "sk" => array("Slovak", "SlovenÄ?ina"),
+    "sl" => array("Slovenian", "SlovenÅ¡Ä?ina"),
     "sm" => array("Samoan"),
     "sn" => array("Shona"),
     "so" => array("Somali"),
     "sq" => array("Albanian", "Shqip"),
-    "sr" => array("Serbian", "Ð¡Ñ€Ð¿Ñ?ÐºÐ¸"),
+    "sr" => array("Serbian", "Ð¡Ñ€Ð¿Ñ?ÐºÐ¸"),
     "ss" => array("Siswati"),
     "st" => array("Sesotho"),
     "su" => array("Sudanese"),
     "sv" => array("Swedish", "Svenska"),
     "sw" => array("Swahili", "Kiswahili"),
-    "ta" => array("Tamil", "à®¤à®®à®¿à®´à¯?"),
-    "te" => array("Telugu", "à°¤à±†à°²à±?à°—à±?"),
+    "ta" => array("Tamil", "à®¤à®®à®¿à®´à¯?"),
+    "te" => array("Telugu", "à°¤à±†à°²à±?à°—à±?"),
     "tg" => array("Tajik"),
     "th" => array("Thai", "à¸ à¸²à¸©à¸²à¹„à¸—à¸¢"),
     "ti" => array("Tigrinya"),
@@ -2398,7 +2398,7 @@
     "tw" => array("Twi"),
     "ty" => array("Tahitian"),
     "ug" => array("Uighur"),
-    "uk" => array("Ukrainian", "Ð£ÐºÑ€Ð°Ñ—Ð½Ñ?ÑŒÐºÐ°"),
+    "uk" => array("Ukrainian", "Ð£ÐºÑ€Ð°Ñ—Ð½Ñ?ÑŒÐºÐ°"),
     "ur" => array("Urdu", /* Left-to-right marker "â€­" */ "Ø§Ø±Ø¯Ùˆ", LANGUAGE_RTL),
     "uz" => array("Uzbek", "o'zbek"),
     "ve" => array("Venda"),
@@ -2409,7 +2409,7 @@
     "yo" => array("Yoruba", "YorÃ¹bÃ¡"),
     "za" => array("Zhuang"),
     "zh-hans" => array("Chinese, Simplified", "ç®€ä½“ä¸­æ–‡"),
-    "zh-hant" => array("Chinese, Traditional", "ç¹?é«”ä¸­æ–‡"),
+    "zh-hant" => array("Chinese, Traditional", "ç¹?é«”ä¸­æ–‡"),
     "zu" => array("Zulu", "isiZulu"),
   );
 }
Index: includes/unicode.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/unicode.inc,v
retrieving revision 1.29
diff -u -r1.29 unicode.inc
--- includes/unicode.inc	28 Dec 2007 12:02:50 -0000	1.29
+++ includes/unicode.inc	29 Feb 2008 19:15:29 -0000
@@ -196,8 +196,8 @@
  *
  * Use this function whenever you want to chop off a string at an unsure
  * location. On the other hand, if you're sure that you're splitting on a
- * character boundary (e.g. after using strpos() or similar), you can safely use
- * substr() instead.
+ * character boundary (e.g. after using drupal_strlen() or similar), you can safely 
+ * use drupal_truncate_chars() instead.
  *
  * @param $string
  *   The string to truncate.
@@ -231,14 +231,13 @@
  * @return
  *   The truncated string.
  */
-function truncate_utf8($string, $len, $wordsafe = FALSE, $dots = FALSE) {
-
+function drupal_truncate_chars($string, $len, $wordsafe = FALSE, $dots = FALSE) {
   if (drupal_strlen($string) <= $len) {
     return $string;
   }
 
   if ($dots) {
-    $len -= 4;
+    $len -= 3;
   }
 
   if ($wordsafe) {
@@ -255,7 +254,7 @@
   }
 
   if ($dots) {
-    $string .= ' ...';
+    $string .= '...';
   }
 
   return $string;
@@ -271,8 +270,8 @@
  *
  * Notes:
  * - Only encode strings that contain non-ASCII characters.
- * - We progressively cut-off a chunk with truncate_utf8(). This is to ensure
- *   each chunk starts and ends on a character boundary.
+ * - We progressively cut-off a chunk with truncate_truncate_bytes(). This is to ensure
+ *   each chunk starts and ends on a byte boundary.
  * - Using \n as the chunk separator may cause problems on some systems and may
  *   have to be changed to \r\n or \r.
  */
Index: modules/menu/menu.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v
retrieving revision 1.159
diff -u -r1.159 menu.module
--- modules/menu/menu.module	20 Feb 2008 13:46:40 -0000	1.159
+++ modules/menu/menu.module	29 Feb 2008 19:15:30 -0000
@@ -232,7 +232,7 @@
       break;
     }
     if ($data['link']['mlid'] != $exclude && $data['link']['hidden'] >= 0) {
-      $title = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, FALSE);
+      $title = $indent .' '. drupal_truncate_chars($data['link']['title'], 30, TRUE);
       if ($data['link']['hidden']) {
         $title .= ' ('. t('disabled') .')';
       }
Index: modules/statistics/statistics.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/statistics/statistics.module,v
retrieving revision 1.273
diff -u -r1.273 statistics.module
--- modules/statistics/statistics.module	20 Feb 2008 13:46:40 -0000	1.273
+++ modules/statistics/statistics.module	29 Feb 2008 19:15:31 -0000
@@ -301,7 +301,7 @@
  */
 function _statistics_link($path, $width = 35) {
   $title = drupal_get_path_alias($path);
-  $title = truncate_utf8($title, $width, FALSE, TRUE);
+  $title = drupal_truncate_chars($title, $width, FALSE, TRUE);
   return l($title, $path);
 }
 
Index: modules/book/book.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.module,v
retrieving revision 1.457
diff -u -r1.457 book.module
--- modules/book/book.module	20 Feb 2008 13:46:39 -0000	1.457
+++ modules/book/book.module	29 Feb 2008 19:15:29 -0000
@@ -844,7 +844,7 @@
       break;
     }
     if (!in_array($data['link']['mlid'], $exclude)) {
-      $toc[$data['link']['mlid']] = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, TRUE);
+      $toc[$data['link']['mlid']] = $indent .' '. drupal_truncate_chars($data['link']['title'], 30, TRUE, TRUE);
       if ($data['below']) {
         _book_toc_recurse($data['below'], $indent .'--', $toc, $exclude, $depth_limit);
       }
Index: modules/comment/comment.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.admin.inc,v
retrieving revision 1.4
diff -u -r1.4 comment.admin.inc
--- modules/comment/comment.admin.inc	8 Jan 2008 10:35:41 -0000	1.4
+++ modules/comment/comment.admin.inc	29 Feb 2008 19:15:29 -0000
@@ -64,7 +64,7 @@
   while ($comment = db_fetch_object($result)) {
     $comments[$comment->cid] = '';
     $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
-    $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('title' => truncate_utf8($comment->comment, 128), 'fragment' => 'comment-'. $comment->cid)));
+    $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('title' => drupal_truncate_chars($comment->comment, 128), 'fragment' => 'comment-'. $comment->cid)));
     $form['username'][$comment->cid] = array('#value' => theme('username', $comment));
     $form['node_title'][$comment->cid] = array('#value' => l($comment->node_title, 'node/'. $comment->nid));
     $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small'));
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.621
diff -u -r1.621 comment.module
--- modules/comment/comment.module	23 Feb 2008 08:02:48 -0000	1.621
+++ modules/comment/comment.module	29 Feb 2008 19:15:30 -0000
@@ -1543,7 +1543,7 @@
     // 2) Strip out all HTML tags
     // 3) Convert entities back to plain-text.
     // Note: format is checked by check_markup().
-    $comment_values['subject'] = trim(truncate_utf8(decode_entities(strip_tags(check_markup($comment_values['comment'], $comment_values['format']))), 29, TRUE));
+    $comment_values['subject'] = trim(drupal_truncate_chars(decode_entities(strip_tags(check_markup($comment_values['comment'], $comment_values['format']))), 29, TRUE));
     // Edge cases where the comment body is populated only by HTML tags will
     // require a default subject.
     if ($comment_values['subject'] == '') {
Index: modules/filter/filter.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v
retrieving revision 1.206
diff -u -r1.206 filter.module
--- modules/filter/filter.module	20 Feb 2008 13:46:39 -0000	1.206
+++ modules/filter/filter.module	29 Feb 2008 19:15:30 -0000
@@ -854,8 +854,8 @@
   }
 
   // Use +3 for '...' string length.
-  if (strlen($text) > $_length + 3) {
-    $text = substr($text, 0, $_length) .'...';
+  if (drupal_strlen($text) > $_length + 3) {
+    $text = drupal_truncate_chars($text, $_length);
   }
 
   return $text;
Index: modules/aggregator/aggregator.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.module,v
retrieving revision 1.375
diff -u -r1.375 aggregator.module
--- modules/aggregator/aggregator.module	20 Feb 2008 13:46:37 -0000	1.375
+++ modules/aggregator/aggregator.module	29 Feb 2008 19:15:29 -0000
@@ -740,7 +740,7 @@
       $title = $item['TITLE'];
     }
     elseif (!empty($item['DESCRIPTION'])) {
-      $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", truncate_utf8($item['DESCRIPTION'], 40));
+      $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", drupal_truncate_chars($item['DESCRIPTION'], 40));
     }
     else {
       $title = '';
Index: modules/search/search.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.module,v
retrieving revision 1.251
diff -u -r1.251 search.module
--- modules/search/search.module	20 Feb 2008 13:46:40 -0000	1.251
+++ modules/search/search.module	29 Feb 2008 19:15:31 -0000
@@ -394,7 +394,7 @@
  * Helper function for array_walk in search_index_split.
  */
 function _search_index_truncate(&$text) {
-  $text = truncate_utf8($text, 50);
+  $text = drupal_truncate_chars($text, 50);
 }
 
 /**
@@ -1228,7 +1228,7 @@
 
   // If we didn't find anything, return the beginning.
   if (count($ranges) == 0) {
-    return truncate_utf8($text, 256) .' ...';
+    return drupal_truncate_chars($text, 256, FALSE, TRUE);
   }
 
   // Sort the text ranges by starting position.
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.950
diff -u -r1.950 node.module
--- modules/node/node.module	20 Feb 2008 13:46:40 -0000	1.950
+++ modules/node/node.module	29 Feb 2008 19:15:31 -0000
@@ -336,7 +336,7 @@
   // sentence boundaries.
 
   // The teaser may not be longer than maximum length specified. Initial slice.
-  $teaser = truncate_utf8($body, $size);
+  $teaser = drupal_truncate_chars($body, $size);
 
   // Store the actual length of the UTF8 string -- which might not be the same
   // as $size.
Index: modules/dblog/dblog.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/dblog/dblog.admin.inc,v
retrieving revision 1.6
diff -u -r1.6 dblog.admin.inc
--- modules/dblog/dblog.admin.inc	8 Jan 2008 10:35:41 -0000	1.6
+++ modules/dblog/dblog.admin.inc	29 Feb 2008 19:15:30 -0000
@@ -68,7 +68,7 @@
         $icons[$dblog->severity],
         t($dblog->type),
         format_date($dblog->timestamp, 'small'),
-        l(truncate_utf8(_dblog_format_message($dblog), 56, TRUE, TRUE), 'admin/reports/event/'. $dblog->wid, array('html' => TRUE)),
+        l(drupal_truncate_chars(_dblog_format_message($dblog), 56, TRUE, TRUE), 'admin/reports/event/'. $dblog->wid, array('html' => TRUE)),
         theme('username', $dblog),
         $dblog->link,
       ),
@@ -102,7 +102,7 @@
 
   $rows = array();
   while ($dblog = db_fetch_object($result)) {
-    $rows[] = array($dblog->count, truncate_utf8(_dblog_format_message($dblog), 56, TRUE, TRUE));
+    $rows[] = array($dblog->count, drupal_truncate_chars(_dblog_format_message($dblog), 56, TRUE, TRUE));
   }
 
   if (empty($rows)) {
