/**
* Class for working with PO files
*
- * @version $Id: po.php 33 2009-02-16 09:33:39Z nbachiyski $
+ * @version $Id: po.php 718 2012-10-31 00:32:02Z nbachiyski $
* @package pomo
* @subpackage po
*/
/**
* Routines for working with PO files
*/
-class PO extends Translations {
+if ( !class_exists( 'PO' ) ):
+class PO extends Gettext_Translations {
+ var $comments_before_headers = '';
/**
* Exports headers to a PO entry
$header_string.= "$header: $value\n";
}
$poified = PO::poify($header_string);
- return rtrim("msgid \"\"\nmsgstr $poified");
+ if ($this->comments_before_headers)
+ $before_headers = $this->prepend_each_line(rtrim($this->comments_before_headers)."\n", '# ');
+ else
+ $before_headers = '';
+ return rtrim("{$before_headers}msgid \"\"\nmsgstr $poified");
}
/**
return fclose($fh);
}
+ /**
+ * Text to include as a comment before the start of the PO contents
+ *
+ * Doesn't need to include # in the beginning of lines, these are added automatically
+ */
+ function set_comment_before_headers( $text ) {
+ $this->comments_before_headers = $text;
+ }
/**
* Formats a string in PO-style
$quote = '"';
$slash = '\\';
$newline = "\n";
- $tab = "\t";
$replaces = array(
"$slash" => "$slash$slash",
- "$tab" => '\t',
"$quote" => "$slash$quote",
+ "\t" => '\t',
);
+
$string = str_replace(array_keys($replaces), array_values($replaces), $string);
- $po = array();
- foreach (explode($newline, $string) as $line) {
- $po[] = wordwrap($line, PO_MAX_LINE_LEN - 2, " $quote$newline$quote");
- }
- $po = $quote.implode("${slash}n$quote$newline$quote", $po).$quote;
+ $po = $quote.implode("${slash}n$quote$newline$quote", explode($newline, $string)).$quote;
// add empty string on first line for readbility
- if (false !== strpos($po, $newline)) {
+ if (false !== strpos($string, $newline) &&
+ (substr_count($string, $newline) > 1 || !($newline === substr($string, -strlen($newline))))) {
$po = "$quote$quote$newline$po";
}
// remove empty strings
return $po;
}
+ /**
+ * Gives back the original string from a PO-formatted string
+ *
+ * @static
+ * @param string $string PO-formatted string
+ * @return string enascaped string
+ */
+ function unpoify($string) {
+ $escapes = array('t' => "\t", 'n' => "\n", '\\' => '\\');
+ $lines = array_map('trim', explode("\n", $string));
+ $lines = array_map(array('PO', 'trim_quotes'), $lines);
+ $unpoified = '';
+ $previous_is_backslash = false;
+ foreach($lines as $line) {
+ preg_match_all('/./u', $line, $chars);
+ $chars = $chars[0];
+ foreach($chars as $char) {
+ if (!$previous_is_backslash) {
+ if ('\\' == $char)
+ $previous_is_backslash = true;
+ else
+ $unpoified .= $char;
+ } else {
+ $previous_is_backslash = false;
+ $unpoified .= isset($escapes[$char])? $escapes[$char] : $char;
+ }
+ }
+ }
+ return $unpoified;
+ }
+
/**
* Inserts $with in the beginning of every new line of $string and
* returns the modified string
if (!empty($entry->translator_comments)) $po[] = PO::comment_block($entry->translator_comments);
if (!empty($entry->extracted_comments)) $po[] = PO::comment_block($entry->extracted_comments, '.');
if (!empty($entry->references)) $po[] = PO::comment_block(implode(' ', $entry->references), ':');
- if (!empty($entry->flags)) $po[] = PO::comment_block(implode("\n", $entry->flags), ',');
+ if (!empty($entry->flags)) $po[] = PO::comment_block(implode(", ", $entry->flags), ',');
if (!is_null($entry->context)) $po[] = 'msgctxt '.PO::poify($entry->context);
$po[] = 'msgid '.PO::poify($entry->singular);
if (!$entry->is_plural) {
return implode("\n", $po);
}
+ function import_from_file($filename) {
+ $f = fopen($filename, 'r');
+ if (!$f) return false;
+ $lineno = 0;
+ while (true) {
+ $res = $this->read_entry($f, $lineno);
+ if (!$res) break;
+ if ($res['entry']->singular == '') {
+ $this->set_headers($this->make_headers($res['entry']->translations[0]));
+ } else {
+ $this->add_entry($res['entry']);
+ }
+ }
+ PO::read_line($f, 'clear');
+ if ( false === $res ) {
+ return false;
+ }
+ if ( ! $this->headers && ! $this->entries ) {
+ return false;
+ }
+ return true;
+ }
+
+ function read_entry($f, $lineno = 0) {
+ $entry = new Translation_Entry();
+ // where were we in the last step
+ // can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural
+ $context = '';
+ $msgstr_index = 0;
+ $is_final = create_function('$context', 'return $context == "msgstr" || $context == "msgstr_plural";');
+ while (true) {
+ $lineno++;
+ $line = PO::read_line($f);
+ if (!$line) {
+ if (feof($f)) {
+ if ($is_final($context))
+ break;
+ elseif (!$context) // we haven't read a line and eof came
+ return null;
+ else
+ return false;
+ } else {
+ return false;
+ }
+ }
+ if ($line == "\n") continue;
+ $line = trim($line);
+ if (preg_match('/^#/', $line, $m)) {
+ // the comment is the start of a new entry
+ if ($is_final($context)) {
+ PO::read_line($f, 'put-back');
+ $lineno--;
+ break;
+ }
+ // comments have to be at the beginning
+ if ($context && $context != 'comment') {
+ return false;
+ }
+ // add comment
+ $this->add_comment_to_entry($entry, $line);
+ } elseif (preg_match('/^msgctxt\s+(".*")/', $line, $m)) {
+ if ($is_final($context)) {
+ PO::read_line($f, 'put-back');
+ $lineno--;
+ break;
+ }
+ if ($context && $context != 'comment') {
+ return false;
+ }
+ $context = 'msgctxt';
+ $entry->context .= PO::unpoify($m[1]);
+ } elseif (preg_match('/^msgid\s+(".*")/', $line, $m)) {
+ if ($is_final($context)) {
+ PO::read_line($f, 'put-back');
+ $lineno--;
+ break;
+ }
+ if ($context && $context != 'msgctxt' && $context != 'comment') {
+ return false;
+ }
+ $context = 'msgid';
+ $entry->singular .= PO::unpoify($m[1]);
+ } elseif (preg_match('/^msgid_plural\s+(".*")/', $line, $m)) {
+ if ($context != 'msgid') {
+ return false;
+ }
+ $context = 'msgid_plural';
+ $entry->is_plural = true;
+ $entry->plural .= PO::unpoify($m[1]);
+ } elseif (preg_match('/^msgstr\s+(".*")/', $line, $m)) {
+ if ($context != 'msgid') {
+ return false;
+ }
+ $context = 'msgstr';
+ $entry->translations = array(PO::unpoify($m[1]));
+ } elseif (preg_match('/^msgstr\[(\d+)\]\s+(".*")/', $line, $m)) {
+ if ($context != 'msgid_plural' && $context != 'msgstr_plural') {
+ return false;
+ }
+ $context = 'msgstr_plural';
+ $msgstr_index = $m[1];
+ $entry->translations[$m[1]] = PO::unpoify($m[2]);
+ } elseif (preg_match('/^".*"$/', $line)) {
+ $unpoified = PO::unpoify($line);
+ switch ($context) {
+ case 'msgid':
+ $entry->singular .= $unpoified; break;
+ case 'msgctxt':
+ $entry->context .= $unpoified; break;
+ case 'msgid_plural':
+ $entry->plural .= $unpoified; break;
+ case 'msgstr':
+ $entry->translations[0] .= $unpoified; break;
+ case 'msgstr_plural':
+ $entry->translations[$msgstr_index] .= $unpoified; break;
+ default:
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ if (array() == array_filter($entry->translations, create_function('$t', 'return $t || "0" === $t;'))) {
+ $entry->translations = array();
+ }
+ return array('entry' => $entry, 'lineno' => $lineno);
+ }
+
+ function read_line($f, $action = 'read') {
+ static $last_line = '';
+ static $use_last_line = false;
+ if ('clear' == $action) {
+ $last_line = '';
+ return true;
+ }
+ if ('put-back' == $action) {
+ $use_last_line = true;
+ return true;
+ }
+ $line = $use_last_line? $last_line : fgets($f);
+ $line = ( "\r\n" == substr( $line, -2 ) ) ? rtrim( $line, "\r\n" ) . "\n" : $line;
+ $last_line = $line;
+ $use_last_line = false;
+ return $line;
+ }
+
+ function add_comment_to_entry(&$entry, $po_comment_line) {
+ $first_two = substr($po_comment_line, 0, 2);
+ $comment = trim(substr($po_comment_line, 2));
+ if ('#:' == $first_two) {
+ $entry->references = array_merge($entry->references, preg_split('/\s+/', $comment));
+ } elseif ('#.' == $first_two) {
+ $entry->extracted_comments = trim($entry->extracted_comments . "\n" . $comment);
+ } elseif ('#,' == $first_two) {
+ $entry->flags = array_merge($entry->flags, preg_split('/,\s*/', $comment));
+ } else {
+ $entry->translator_comments = trim($entry->translator_comments . "\n" . $comment);
+ }
+ }
+
+ function trim_quotes($s) {
+ if ( substr($s, 0, 1) == '"') $s = substr($s, 1);
+ if ( substr($s, -1, 1) == '"') $s = substr($s, 0, -1);
+ return $s;
+ }
}
-?>
+endif;