diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 2121619..d013732 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -652,7 +652,8 @@ function _menu_check_access(&$item, $map) {
  *   (link title attribute) matches the description, it is translated as well.
  */
 function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
-  $callback = $item['title_callback'];
+  $title_callback = $item['title_callback'];
+  $description_callback = $item['description_callback'];
   $item['localized_options'] = $item['options'];
   // All 'class' attributes are assumed to be an array during rendering, but
   // links stored in the database may use an old string value.
@@ -671,7 +672,7 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
   if (!$link_translate || ($item['title'] == $item['link_title'])) {
     // t() is a special case. Since it is used very close to all the time,
     // we handle it directly instead of using indirect, slower methods.
-    if ($callback == 't') {
+    if ($title_callback == 't') {
       if (empty($item['title_arguments'])) {
         $item['title'] = t($item['title']);
       }
@@ -679,15 +680,15 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
         $item['title'] = t($item['title'], menu_unserialize($item['title_arguments'], $map));
       }
     }
-    elseif ($callback) {
+    elseif ($title_callback) {
       if (empty($item['title_arguments'])) {
-        $item['title'] = $callback($item['title']);
+        $item['title'] = $title_callback($item['title']);
       }
       else {
-        $item['title'] = call_user_func_array($callback, menu_unserialize($item['title_arguments'], $map));
+        $item['title'] = call_user_func_array($title_callback, menu_unserialize($item['title_arguments'], $map));
       }
       // Avoid calling check_plain again on l() function.
-      if ($callback == 'check_plain') {
+      if ($title_callback == 'check_plain') {
         $item['localized_options']['html'] = TRUE;
       }
     }
@@ -697,12 +698,27 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
   }
 
   // Translate description, see the motivation above.
-  if (!empty($item['description'])) {
-    $original_description = $item['description'];
-    $item['description'] = t($item['description']);
-    if ($link_translate && isset($item['options']['attributes']['title']) && $item['options']['attributes']['title'] == $original_description) {
-      $item['localized_options']['attributes']['title'] = $item['description'];
+  $original_description = $item['description'];
+  if (!empty($item['description_arguments']) || !empty($item['description'])) {
+    if ($description_callback == 't') {
+       if (empty($item['description_arguments'])) {
+         $item['description'] = t($item['description']);
+       }
+       else {
+         $item['description'] = t($item['description'], menu_unserialize($item['description_arguments'], $map));
+       }
     }
+    elseif ($description_callback) {
+       if (empty($item['description_arguments'])) {
+         $item['description'] = $description_callback($item['description']);
+       }
+       else {
+         $item['description'] = call_user_func_array($description_callback, menu_unserialize($item['description_arguments'], $map));
+       }
+    }
+  }
+  if ($link_translate && isset($item['options']['attributes']['title']) && $item['options']['attributes']['title'] == $original_description) {
+    $item['localized_options']['attributes']['title'] = $item['description'];
   }
 }
 
@@ -1393,6 +1409,8 @@ function _menu_build_tree($menu_name, array $parameters = array()) {
       'theme_arguments',
       'type',
       'description',
+      'description_callback',
+      'description_arguments',
     ));
     for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) {
       $query->orderBy('p' . $i, 'ASC');
@@ -3669,6 +3687,8 @@ function _menu_router_build($callbacks) {
       'theme arguments' => array(),
       'theme callback' => '',
       'description' => '',
+      'description arguments' => array(),
+      'description callback' => 't',
       'position' => '',
       'context' => 0,
       'tab_parent' => '',
@@ -3723,6 +3743,8 @@ function _menu_router_save($menu, $masks) {
       'theme_arguments',
       'type',
       'description',
+      'description_callback',
+      'description_arguments',
       'position',
       'weight',
       'include_file',
@@ -3753,6 +3775,8 @@ function _menu_router_save($menu, $masks) {
       'theme_arguments' => serialize($item['theme arguments']),
       'type' => $item['type'],
       'description' => $item['description'],
+      'description_callback' => $item['description callback'],
+      'description_arguments' => ($item['description arguments'] ? serialize($item['description arguments']) : ''),
       'position' => $item['position'],
       'weight' => $item['weight'],
       'include_file' => $item['include file'],
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 2cbb615..ecd0784 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1109,6 +1109,20 @@ function system_schema() {
         'type' => 'text',
         'not null' => TRUE,
       ),
+      'description_callback' => array(
+        'description' => 'A function which will alter the description. Defaults to t()',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'description_arguments' => array(
+        'description' => 'A serialized array of arguments for the description callback. If empty, the description will be used as the sole argument for the description callback.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
       'position' => array(
         'description' => 'The position of the block (left or right) on the system administration page for this item.',
         'type' => 'varchar',
