? sites/default/settings.php
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.130
diff -U3 -r1.130 locale.inc
--- includes/locale.inc	28 May 2007 06:08:38 -0000	1.130
+++ includes/locale.inc	30 May 2007 07:29:48 -0000
@@ -604,6 +629,11 @@
     '#default_value' => 'overwrite',
     '#options' => array('overwrite' => t('Strings in the uploaded file replace existing ones, new ones are added'), 'keep' => t('Existing strings are kept, only new strings are added')),
   );
+  $form['import']['importer'] = array(
+    '#type' => 'select',
+    '#title' => t('File type'),
+    '#options' => module_invoke_all("localeapi", "importer"),
+  );
   $form['import']['submit'] = array('#type' => 'submit', '#value' => t('Import'));
   $form['#attributes']['enctype'] = 'multipart/form-data';

@@ -614,31 +644,27 @@
  * Process the locale import form submission.
  */
 function locale_translate_import_form_submit($form, &$form_state, $form_values) {
-  // Ensure we have the file uploaded
-  if ($file = file_check_upload('file')) {
-
-    // Add language, if not yet supported
-    $languages = language_list('language', TRUE);
-    $langcode = $form_values['langcode'];
-    if (!isset($languages[$langcode])) {
-      $predefined = _locale_get_predefined_list();
-      locale_add_language($langcode);
-      drupal_set_message(t('The language %language has been created.', array('%language' => t($predefined[$langcode][0]))));
-    }
-
-    // Now import strings into the language
-    if ($ret = _locale_import_po($file, $langcode, $form_values['mode'], $form_values['group']) == FALSE) {
-      $variables = array('%filename' => $file->filename);
-      drupal_set_message(t('The translation import of %filename failed.', $variables), 'error');
-      watchdog('locale', 'The translation import of %filename failed.', $variables, WATCHDOG_ERROR);
+  // Add language, if not yet supported
+  $languages = language_list('language', TRUE);
+  $langcode = $form_values['langcode'];
+  if (!isset($languages[$langcode])) {
+    $predefined = _locale_get_predefined_list();
+    locale_add_language($langcode);
+    drupal_set_message(t('The language %language has been created.', array('%language' => t($predefined[$langcode][0]))));
+  }
+  if ($file = file_check_upload('file')) { // Get the file content
+    $fd = fopen($file->filepath, "rb");    // File will get closed by PHP on return
+    if (empty($fd)) {
+      drupal_set_message(t('The translation import failed, because the file %filename could not be read.', array('%filename' => $file->filepath)), 'error');
+      return FALSE;
     }
+    _locale_import($fd, $form_values['mode'], $form_values['langcode'], $form_values['group'], $form_values['importer']);
   }
   else {
     drupal_set_message(t('File to import not found.'), 'error');
-    return 'admin/build/translate/import';
   }
-
-  $form_state['redirect'] = 'admin/build/translate';
+  $form_state['redirect'] = !empty($_GET["destination"]) ? $_GET["destination"] :
+    !empty($_POST["destination"]) ? $_POST["destination"] : 'admin/build/translate/import';
   return;
 }
 /**
@@ -680,13 +706,18 @@
   $form['export']['langcode'] = array('#type' => 'select',
     '#title' => t('Language name'),
     '#options' => $names,
-    '#description' => t('Select the language you would like to export in gettext Portable Object (.po) format.'),
+    '#description' => t('Select the language you would like to export.'),
   );
   $form['export']['group'] = array('#type' => 'radios',
     '#title' => t('Text group'),
     '#default_value' => 'default',
     '#options' => module_invoke_all('locale', 'groups'),
   );
+  $form['export']['exporter'] = array(
+    '#type' => 'select',
+    '#title' => t('File type'),
+    '#options' => module_invoke_all("localeapi", "exporter_language"),
+  );
   $form['export']['submit'] = array('#type' => 'submit', '#value' => t('Export'));
   return $form;
 }
@@ -699,13 +730,18 @@
   $form['export'] = array('#type' => 'fieldset',
     '#title' => t('Export template'),
     '#collapsible' => TRUE,
-    '#description' => t('Generate a gettext Portable Object Template (.pot) file with all strings from the Drupal locale database.'),
+    '#description' => t('Generate a template file with all strings from the Drupal locale database.'),
   );
   $form['export']['group'] = array('#type' => 'radios',
     '#title' => t('Text group'),
     '#default_value' => 'default',
     '#options' => module_invoke_all('locale', 'groups'),
   );
+  $form['export']['exporter'] = array(
+    '#type' => 'select',
+    '#title' => t('File type'),
+    '#options' => module_invoke_all("localeapi", "exporter_template"),
+  );
   $form['export']['submit'] = array('#type' => 'submit', '#value' => t('Export'));
   // Reuse PO export submission callback.
   $form['#submit'][] = 'locale_translate_export_po_form_submit';
@@ -717,8 +753,9 @@
  * Process a translation (or template) export form submission.
  */
 function locale_translate_export_po_form_submit($form, &$form_state, $form_values) {
-  // If template is required, language code is not given.
-  _locale_export_po(isset($form_values['langcode']) ? $form_values['langcode'] : NULL, $form_values['group']);
+  $langcode = NULL;
+  if (!empty($form_values['langcode'])) {$langcode = $form_values['langcode'];}
+  _locale_translate_export($form_values['exporter'], $form_values['group'], $langcode);
 }
 /**
  * @} End of "locale-translate-export"
@@ -896,52 +933,50 @@
  */

 /**
- * Parses Gettext Portable Object file information and inserts into database
+ * Coordenates all exporting process
  *
- * @param $file
- *   Drupal file object corresponding to the PO file to import
- * @param $lang
- *   Language code
+ * @param $data
+ *   string, all file content
  * @param $mode
- *   Should existing translations be replaced ('overwrite' or 'keep')
+ *   string, "overwrite" or "keep" modes
+ * @param $importer
+ *   string, function name that will write the translation file format
+ * @param $language
+ *   string, Language code to generate the output for, or NULL if
+ *   generating translation template.
+ * @param $tables
+ *   string, function name that will save on database all translated strings
  * @param $group
- *   Text group to import PO file into (eg. 'default' for interface translations)
+ *   string, Text group to export PO file from (eg. 'default' for
+ *   interface translations)
  */
-function _locale_import_po($file, $lang, $mode, $group = NULL) {
-  // If not in 'safe mode', increase the maximum execution time:
-  if (!ini_get('safe_mode')) {
-    set_time_limit(240);
-  }
-
-  // Check if we have the language already in the database
-  if (!db_fetch_object(db_query("SELECT language FROM {languages} WHERE language = '%s'", $lang))) {
-    drupal_set_message(t('The language selected for import is not supported.'), 'error');
-    return FALSE;
+function _locale_import($data, $mode, $language = NULL, $group = NULL, $importer = "locale_import_po", $tables = "_locale_import_locale_table") {
+  // get all strings from the file
+  $strings = call_user_func_array($importer, array($data, $language, $mode, $group));
+  if (empty($strings)) { // no strings found
+    $message = t('The translation import failed.');
+    drupal_set_message($message, 'error');
+    watchdog('locale', $message, WATCHDOG_ERROR);
   }
-
-  // Get strings from file (returns on failure after a partial import, or on success)
-  $status = _locale_import_read_po('db-store', $file, $mode, $lang, $group);
-  if ($status === FALSE) {
-    // error messages are set in _locale_import_read_po
-    return FALSE;
-  }
-
-  // Get status information on import process
-  list($headerdone, $additions, $updates) = _locale_import_one_string('db-report');
-
-  if (!$headerdone) {
-    drupal_set_message(t('The translation file %filename appears to have a missing or malformed header.', array('%filename' => $file->filename)), 'error');
+  else {
+    while (list(, $string) = each($strings)) { // save on database each string
+      call_user_func_array($tables, array("db-store", $string, $language, $mode, $group));
+    }
+    // Get status information on import process
+    list($headerdone, $additions, $updates) = call_user_func_array($tables, array('db-report'));
+    if (!$headerdone) {
+      drupal_set_message(t('The translation appears to have a missing or malformed header.'), 'error');
+    }
+    cache_clear_all("locale:$language", 'cache'); // rebuild locale cache
+    menu_rebuild(); // rebuild the menu, strings may have changed
+    drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings and %update strings were updated.',
+      array('%number' => $additions,
+      '%update' => $updates)));
+    watchdog('locale', t('Importedinto %locale: %number new strings added and %update updated.',
+      array('%locale' => $language,
+      '%number' => $additions,
+      '%update' => $updates)));
   }
-
-  // rebuild locale cache
-  cache_clear_all("locale:$lang", 'cache');
-
-  // rebuild the menu, strings may have changed
-  menu_rebuild();
-
-  drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings and %update strings were updated.', array('%number' => $additions, '%update' => $updates)));
-  watchdog('locale', 'Imported %file into %locale: %number new strings added and %update updated.', array('%file' => $file->filename, '%locale' => $lang, '%number' => $additions, '%update' => $updates));
-  return TRUE;
 }

 /**
@@ -949,8 +984,6 @@
  *
  * @param $op
  *   Storage operation type: db-store or mem-store
- * @param $file
- *   Drupal file object corresponding to the PO file to import
  * @param $mode
  *   Should existing translations be replaced ('overwrite' or 'keep')
  * @param $lang
@@ -958,21 +991,19 @@
  * @param $group
  *   Text group to import PO file into (eg. 'default' for interface translations)
  */
-function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL, $group = NULL) {
-
-  $fd = fopen($file->filepath, "rb"); // File will get closed by PHP on return
-  if (!$fd) {
-    _locale_import_message('The translation import failed, because the file %filename could not be read.', $file);
-    return FALSE;
+function _locale_import_po($file, $lang = NULL, $mode = NULL, $group = NULL) {
+  // If not in 'safe mode', increase the maximum execution time:
+  if (!ini_get('safe_mode')) {
+    set_time_limit(240);
   }
-
+  $strings = array();
   $context = "COMMENT"; // Parser context: COMMENT, MSGID, MSGID_PLURAL, MSGSTR and MSGSTR_ARR
   $current = array();   // Current entry being read
   $plural = 0;          // Current plural form
   $lineno = 0;          // Current line

-  while (!feof($fd)) {
-    $line = fgets($fd, 10*1024); // A line should not be this long
+  while (!feof($file)) {
+    $line = fgets($file, 10*1024); // A line should not be this long
     $lineno++;
     $line = trim(strtr($line, array("\\\n" => "")));

@@ -981,25 +1012,25 @@
         $current["#"][] = substr($line, 1);
       }
       elseif (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) { // End current entry, start a new one
-        _locale_import_one_string($op, $current, $mode, $lang, $file);
+        $strings[] = $current;
         $current = array();
         $current["#"][] = substr($line, 1);
         $context = "COMMENT";
       }
       else { // Parse error
-        _locale_import_message('The translation file %filename contains an error: "msgstr" was expected but not found on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains an error: "msgstr" was expected but not found on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
     }
     elseif (!strncmp("msgid_plural", $line, 12)) {
       if ($context != "MSGID") { // Must be plural form for current entry
-        _locale_import_message('The translation file %filename contains an error: "msgid_plural" was expected but not found on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains an error: "msgid_plural" was expected but not found on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $line = trim(substr($line, 12));
       $quoted = _locale_import_parse_quoted($line);
       if ($quoted === FALSE) {
-        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains a syntax error on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $current["msgid"] = $current["msgid"] ."\0". $quoted;
@@ -1007,17 +1038,17 @@
     }
     elseif (!strncmp("msgid", $line, 5)) {
       if ($context == "MSGSTR") {   // End current entry, start a new one
-        _locale_import_one_string($op, $current, $mode, $lang, $file);
+        $strings[] = $current;
         $current = array();
       }
       elseif ($context == "MSGID") { // Already in this context? Parse error
-        _locale_import_message('The translation file %filename contains an error: "msgid" is unexpected on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains an error: "msgid" is unexpected on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $line = trim(substr($line, 5));
       $quoted = _locale_import_parse_quoted($line);
       if ($quoted === FALSE) {
-        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file,  $lineno);
+        drupal_set_message(t('The translation contains a syntax error on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $current["msgid"] = $quoted;
@@ -1025,11 +1056,11 @@
     }
     elseif (!strncmp("msgstr[", $line, 7)) {
       if (($context != "MSGID") && ($context != "MSGID_PLURAL") && ($context != "MSGSTR_ARR")) { // Must come after msgid, msgid_plural, or msgstr[]
-        _locale_import_message('The translation file %filename contains an error: "msgstr[]" is unexpected on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains an error: "msgstr[]" is unexpected on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       if (strpos($line, "]") === FALSE) {
-        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains a syntax error on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $frombracket = strstr($line, "[");
@@ -1037,7 +1068,7 @@
       $line = trim(strstr($line, " "));
       $quoted = _locale_import_parse_quoted($line);
       if ($quoted === FALSE) {
-        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains a syntax error on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $current["msgstr"][$plural] = $quoted;
@@ -1045,13 +1076,13 @@
     }
     elseif (!strncmp("msgstr", $line, 6)) {
       if ($context != "MSGID") {   // Should come just after a msgid block
-        _locale_import_message('The translation file %filename contains an error: "msgstr" is unexpected on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains an error: "msgstr" is unexpected on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $line = trim(substr($line, 6));
       $quoted = _locale_import_parse_quoted($line);
       if ($quoted === FALSE) {
-        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains a syntax error on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       $current["msgstr"] = $quoted;
@@ -1060,7 +1091,7 @@
     elseif ($line != "") {
       $quoted = _locale_import_parse_quoted($line);
       if ($quoted === FALSE) {
-        _locale_import_message('The translation file %filename contains a syntax error on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains a syntax error on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
       if (($context == "MSGID") || ($context == "MSGID_PLURAL")) {
@@ -1073,7 +1104,7 @@
         $current["msgstr"][$plural] .= $quoted;
       }
       else {
-        _locale_import_message('The translation file %filename contains an error: there is an unexpected string on line %line.', $file, $lineno);
+        drupal_set_message(t('The translation contains an error: there is an unexpected string on line %line.', array('%line' => $lineno)), 'error');
         return FALSE;
       }
     }
@@ -1081,13 +1112,14 @@

   // End of PO file, flush last entry
   if (($context == "MSGSTR") || ($context == "MSGSTR_ARR")) {
-    _locale_import_one_string($op, $current, $mode, $lang, $file);
+    $strings[] = $current;
+//     _locale_import_one_string($op, $current, $mode, $lang, $file);
   }
   elseif ($context != "COMMENT") {
-    _locale_import_message('The translation file %filename ended unexpectedly at line %line.', $file, $lineno);
+    drupal_set_message(t('The translation ended unexpectedly at line %line.', array('%line' => $lineno)), 'error');
     return FALSE;
   }
-
+  return $strings;
 }

 /**
@@ -1101,7 +1133,7 @@
  *   An optional line number argument
  */
 function _locale_import_message($message, $file, $lineno = NULL) {
-  $vars = array('%filename' => $file->filename);
+//   $vars = array('%filename' => $file->filename);
   if (isset($lineno)) {
     $vars['%line'] = $lineno;
   }
@@ -1125,7 +1157,7 @@
  * @param $group
  *   Text group to import PO file into (eg. 'default' for interface translations)
  */
-function _locale_import_one_string($op, $value = NULL, $mode = NULL, $lang = NULL, $file = NULL, $group = 'default') {
+function _locale_import_locale_table($op, $value = NULL, $lang = NULL, $mode = NULL, $group = 'default') {
   static $additions = 0;
   static $updates = 0;
   static $headerdone = FALSE;
@@ -1152,7 +1184,7 @@
         $hdr = _locale_import_parse_header($value['msgstr']);

         // Get the plural formula
-        if (isset($hdr["Plural-Forms"]) && $p = _locale_import_parse_plural_forms($hdr["Plural-Forms"], $file->filename)) {
+        if (isset($hdr["Plural-Forms"]) && $p = _locale_import_parse_plural_forms($hdr["Plural-Forms"])) {
           list($nplurals, $plural) = $p;
           db_query("UPDATE {languages} SET plurals = %d, formula = '%s' WHERE language = '%s'", $nplurals, $plural, $lang);
         }
@@ -1288,7 +1320,7 @@
  *   An array containing the number of plurals and a
  *   formula in PHP for computing the plural form
  */
-function _locale_import_parse_plural_forms($pluralforms, $filename) {
+function _locale_import_parse_plural_forms($pluralforms) {
   // First, delete all whitespace
   $pluralforms = strtr($pluralforms, array(" " => "", "\t" => ""));

@@ -1315,7 +1347,7 @@
     return array($nplurals, $plural);
   }
   else {
-    drupal_set_message(t('The translation file %filename contains an error: the plural formula could not be parsed.', array('%filename' => $filename)), 'error');
+    drupal_set_message(t('The translation file contains an error: the plural formula could not be parsed.'), 'error');
     return FALSE;
   }
 }
@@ -1561,138 +1593,164 @@
  */

 /**
- * Exports a Portable Object (Template) file for a language
+ * Coordenates all exporting process
  *
+ * @param $exporter
+ *   string, function name that will write the translation file format
  * @param $language
- *   Language code to generate the output for, or NULL if generating
- *   translation template.
+ *   string, Language code to generate the output for, or NULL if
+ *   generating translation template.
  * @param $group
- *   Text group to export PO file from (eg. 'default' for interface translations)
+ *   string, Text group to export PO file from (eg. 'default' for
+ *   interface translations)
  */
-function _locale_export_po($language = NULL, $group = 'default') {
+function _locale_translate_export($exporter, $group, $language = NULL) {
   global $user;
-  $header = '';
+  $strings = _locale_export_get_strings($language, $group);
+  if (!empty($language)) {
+    $language = db_fetch_object(db_query("SELECT * FROM {languages} WHERE language = '%s'", $language));
+  }
+  $po_content =  call_user_func_array($exporter, array($strings, $user, $language));
+  _locale_export_write($po_content, $language);
+}
+
+/**
+ * Search on tables for translatable strings and their translation
+ *
+ * @param $language
+ *   string, Language code to generate the output for, or NULL if
+ *   generating translation template.
+ * @param $group
+ *   string, Text group to export PO file from (eg. 'default' for
+ *   interface translations)
+ */
+function _locale_export_get_strings($language = NULL, $group = 'default') {
   // Get language specific strings, or all strings
   if (isset($language)) {
-    $meta = db_fetch_object(db_query("SELECT * FROM {languages} WHERE language = '%s'", $language));
-    $result = db_query("SELECT s.lid, s.source, s.location, t.translation, t.plid, t.plural FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE t.language = '%s' AND s.textgroup = '%s' ORDER BY t.plid, t.plural", $language, $group);
+    $result = db_query("SELECT s.lid, s.source, s.location, t.translation, t.plid, t.plural
+      FROM {locales_source} s
+      INNER JOIN {locales_target} t ON s.lid = t.lid
+      WHERE t.language = '%s' AND s.textgroup = '%s'
+      ORDER BY t.plid, t.plural", $language, $group);
   }
   else {
-    $result = db_query("SELECT s.lid, s.source, s.location, t.plid, t.plural FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE s.textgroup = '%s' ORDER BY t.plid, t.plural", $group);
+    $result = db_query("SELECT s.lid, s.source, s.location, t.plid, t.plural
+      FROM {locales_source} s
+      INNER JOIN {locales_target} t ON s.lid = t.lid
+      WHERE s.textgroup = '%s'
+      ORDER BY t.plid, t.plural", $group);
   }

   // Build array out of the database results
   $parent = array();
-  while ($child = db_fetch_object($result)) {
-    if ($child->source != '') {
-      $parent[$child->lid]['comment'] = $child->location;
-      $parent[$child->lid]['msgid'] = $child->source;
-      $parent[$child->lid]['translation'] = isset($child->translation) ? $child->translation : '';
-      if ($child->plid) {
-        $parent[$child->lid]['child'] = 1;
-        $parent[$child->plid]['plural'] = $child->lid;
+  while ($string = db_fetch_object($result)) {
+    if ($string->source != '') {
+      $parent[$string->lid]['comment'] = $string->location;
+      $parent[$string->lid]['source'] = $string->source;
+      $parent[$string->lid]['translated'] = $string->translation;
+      if (!empty($string->plid)) {
+        $parent[$string->plid]['haschild'] = TRUE;
+        $parent[$string->lid]['plural'] = $string->plural;
+        $parent[$string->lid]['child'] = $string->plid;
       }
     }
   }
+  return $parent;
+}

-  // Generating Portable Object file for a language
-  if (isset($language)) {
-    $filename = $language .'.po';
-    $header = "# $meta->name translation of ". variable_get('site_name', 'Drupal') ."\n";
-    $header .= '# Copyright (c) '. date('Y') .' '. $user->name .' <'. $user->mail .">\n";
-    $header .= "#\n";
-    $header .= "msgid \"\"\n";
-    $header .= "msgstr \"\"\n";
-    $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
-    $header .= "\"POT-Creation-Date: ". date("Y-m-d H:iO") ."\\n\"\n";
-    $header .= "\"PO-Revision-Date: ". date("Y-m-d H:iO") ."\\n\"\n";
-    $header .= "\"Last-Translator: ". $user->name .' <'. $user->mail .">\\n\"\n";
-    $header .= "\"Language-Team: ". $meta->name .' <'. $user->mail .">\\n\"\n";
-    $header .= "\"MIME-Version: 1.0\\n\"\n";
-    $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
-    $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
-    if ($meta->formula && $meta->plurals) {
-      $header .= "\"Plural-Forms: nplurals=". $meta->plurals ."; plural=". strtr($meta->formula, array('$' => '')) .";\\n\"\n";
-    }
-    $header .= "\n";
-    watchdog('locale', 'Exported %locale translation file: %filename.', array('%locale' => $meta->name, '%filename' => $filename));
-  }
-
-  // Generating Portable Object Template
-  else {
-    $filename = 'drupal.pot';
-    $header = "# LANGUAGE translation of PROJECT\n";
-    $header .= "# Copyright (c) YEAR NAME <EMAIL@ADDRESS>\n";
-    $header .= "#\n";
-    $header .= "msgid \"\"\n";
-    $header .= "msgstr \"\"\n";
-    $header .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
-    $header .= "\"POT-Creation-Date: ". date("Y-m-d H:iO") ."\\n\"\n";
-    $header .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n";
-    $header .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
-    $header .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
-    $header .= "\"MIME-Version: 1.0\\n\"\n";
-    $header .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
-    $header .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
-    $header .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n";
-    $header .= "\n";
-    watchdog('locale', 'Exported translation file: %filename.', array('%filename' => $filename));
-  }
-
-  // Start download process
-  header("Content-Disposition: attachment; filename=$filename");
-  header("Content-Type: text/plain; charset=utf-8");
-
-  print $header;
-
-  foreach ($parent as $lid => $message) {
-    if (!isset($message['child'])) {
-      if ($message['comment']) {
-        print '#: '. $message['comment'] ."\n";
-      }
-      print 'msgid '. _locale_export_print($message['msgid']);
-      if (!empty($message['plural'])) {
-        $plural = $message['plural'];
-        print 'msgid_plural '. _locale_export_print($parent[$plural]['msgid']);
-        if (isset($language)) {
-          $translation = $message['translation'];
-          for ($i = 0; $i < $meta->plurals; $i++) {
-            print 'msgstr['. $i .'] '. _locale_export_print($translation);
-            if ($plural) {
-              $translation = $parent[$plural]['translation'];
-              if ($i > 1) {
-                $translation = _locale_export_remove_plural($translation);
-              }
-              $plural = $parent[$plural]['plural'];
-            }
-            else {
-              $translation = '';
-            }
-          }
+/**
+ * Exports a Portable Object (Template) file for a language
+ *
+ * @param $language
+ *   array, meta information about the language: code, english name, plural
+ * @param $strings
+ *  array, all strings and their translation
+ * @param $user
+ *  object, the user object with name and mail attributes
+ * @return
+ *  string, all PO/POT content
+ */
+function _locale_export_po($strings, $user = NULL, $language = NULL) {
+  if (isset($language)) { // Generating Portable Object file for a language
+    $output = "# ". $language->name ." translation of ". variable_get('site_name', 'Drupal') ."\n";
+    $output .= '# Copyright (c) '. date('Y') .' '. $user->name .' <'. $user->mail .">\n";
+    $output .= "#\n";
+    $output .= "msgid \"\"\n";
+    $output .= "msgstr \"\"\n";
+    $output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
+    $output .= "\"POT-Creation-Date: ". date("Y-m-d H:iO") ."\\n\"\n";
+    $output .= "\"PO-Revision-Date: ". date("Y-m-d H:iO") ."\\n\"\n";
+    $output .= "\"Last-Translator: ". $user->name .' <'. $user->mail .">\\n\"\n";
+    $output .= "\"Language-Team: ". $language->name .' <'. $user->mail .">\\n\"\n";
+    $output .= "\"MIME-Version: 1.0\\n\"\n";
+    $output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
+    $output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
+    if ($language->formula && $language->plurals) {
+      $output .= "\"Plural-Forms: nplurals=". $language->plurals ."; plural=". strtr($language->formula, array('$' => '')) .";\\n\"\n";
+    }
+    $output .= "\n";
+  }
+  else { // Generating Portable Object Template
+    $output = "# LANGUAGE translation of PROJECT\n";
+    $output .= "# Copyright (c) YEAR NAME <EMAIL@ADDRESS>\n";
+    $output .= "#\n";
+    $output .= "msgid \"\"\n";
+    $output .= "msgstr \"\"\n";
+    $output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
+    $output .= "\"POT-Creation-Date: ". date("Y-m-d H:iO") ."\\n\"\n";
+    $output .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n";
+    $output .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
+    $output .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
+    $output .= "\"MIME-Version: 1.0\\n\"\n";
+    $output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
+    $output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
+    $output .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n";
+    $output .= "\n";
+  }
+
+  while (list($lid, $message) = each($strings)) {
+    if (!isset($message['haschild'])) {
+      if (empty($message['plural'])) {
+        if ($message['comment']) {
+          $output .= "\n#: ". $message['comment'] ."\n";
+        }
+        $output .= 'msgid '. _locale_export_string($message['source']);
+        if (!empty($language)) {
+          $output .= 'msgstr '. _locale_export_string($message['translated']);
         }
         else {
-          print 'msgstr[0] ""'."\n";
-          print 'msgstr[1] ""'."\n";
+          $output .= 'msgstr ""'. "\n";
         }
       }
       else {
-        if (isset($language)) {
-          print 'msgstr '. _locale_export_print($message['translation']);
+        if ($message['plural'] == 1) {
+          if ($message['comment']) {
+            $output .= "\n#: ". $message['comment'] ."\n";
+          }
+          $output .= 'msgid '. _locale_export_string($strings[$message['plural']]['source']);
+          $output .= 'msgid_plural '. _locale_export_string($message['source']);
+          if (!empty($language)) {
+            $output .= 'msgstr[0] '. _locale_export_string($strings[$message['plural']]['translated']);;
+            $output .= 'msgstr[1] '. _locale_export_string($message['translated']);
+          }
+          else {
+            $output .= 'msgstr[0] ""'. "\n";
+            $output .= 'msgstr[1] ""'. "\n";
+          }
         }
         else {
-          print 'msgstr ""'."\n";
+          $output .= 'msgstr['. $message['plural'] .'] '. _locale_export_string($message['translated']);
         }
       }
-      print "\n";
     }
   }
-  die();
+  return $output;
 }

 /**
  * Print out a string on multiple lines
  */
-function _locale_export_print($str) {
+function _locale_export_string($str) {
   $stri = addcslashes($str, "\0..\37\\\"");
   $parts = array();

@@ -1754,6 +1812,30 @@
 }

 /**
+ * Exports a Portable Object (Template) file for a language
+ *
+ * @param $output
+ *   string, PO content
+ * @param $language
+ *   array, meta information about the language: code, english name, plural
+ */
+function _locale_export_write($output, $language) {
+  if (!empty($language)) { // Generating Portable Object file for a language
+    $filename = $language->language .'.po';
+    watchdog('locale', t('Exported %locale translation file: %filename.', array('%locale' => $language->name, '%filename' => $filename)));
+  }
+  else { // Generating Portable Object Template
+    $filename = 'drupal.pot';
+    watchdog('locale', t('Exported translation file: %filename.', array('%filename' => $filename)));
+  }
+  // Start download process
+  header("Content-Disposition: attachment; filename=$filename");
+  header("Content-Type: text/plain; charset=utf-8");
+  print $output;
+  die();
+}
+
+/**
  * Removes plural index information from a string
  */
 function _locale_export_remove_plural($entry) {
