3 * Class for working with PO files
5 * @version $Id: po.php 33 2009-02-16 09:33:39Z nbachiyski $
10 require_once dirname(__FILE__) . '/translations.php';
12 define('PO_MAX_LINE_LEN', 79);
14 ini_set('auto_detect_line_endings', 1);
17 * Routines for working with PO files
19 class PO extends Translations {
23 * Exports headers to a PO entry
25 * @return string msgid/msgstr PO entry for this PO file headers, doesn't contain newline at the end
27 function export_headers() {
29 foreach($this->headers as $header => $value) {
30 $header_string.= "$header: $value\n";
32 $poified = PO::poify($header_string);
33 return rtrim("msgid \"\"\nmsgstr $poified");
37 * Exports all entries to PO format
39 * @return string sequence of mgsgid/msgstr PO strings, doesn't containt newline at the end
41 function export_entries() {
43 return implode("\n\n", array_map(array('PO', 'export_entry'), $this->entries));
47 * Exports the whole PO file as a string
49 * @param bool $include_headers whether to include the headers in the export
50 * @return string ready for inclusion in PO file string for headers and all the enrtries
52 function export($include_headers = true) {
54 if ($include_headers) {
55 $res .= $this->export_headers();
58 $res .= $this->export_entries();
63 * Same as {@link export}, but writes the result to a file
65 * @param string $filename where to write the PO string
66 * @param bool $include_headers whether to include tje headers in the export
67 * @return bool true on success, false on error
69 function export_to_file($filename, $include_headers = true) {
70 $fh = fopen($filename, 'w');
71 if (false === $fh) return false;
72 $export = $this->export($include_headers);
73 $res = fwrite($fh, $export);
74 if (false === $res) return false;
80 * Formats a string in PO-style
83 * @param string $string the string to format
84 * @return string the poified string
86 function poify($string) {
93 "$slash" => "$slash$slash",
95 "$quote" => "$slash$quote",
97 $string = str_replace(array_keys($replaces), array_values($replaces), $string);
100 foreach (explode($newline, $string) as $line) {
101 $po[] = wordwrap($line, PO_MAX_LINE_LEN - 2, " $quote$newline$quote");
103 $po = $quote.implode("${slash}n$quote$newline$quote", $po).$quote;
104 // add empty string on first line for readbility
105 if (false !== strpos($po, $newline)) {
106 $po = "$quote$quote$newline$po";
108 // remove empty strings
109 $po = str_replace("$newline$quote$quote", '', $po);
114 * Inserts $with in the beginning of every new line of $string and
115 * returns the modified string
118 * @param string $string prepend lines in this string
119 * @param string $with prepend lines with this string
121 function prepend_each_line($string, $with) {
122 $php_with = var_export($with, true);
123 $lines = explode("\n", $string);
124 // do not prepend the string on the last empty line, artefact by explode
125 if ("\n" == substr($string, -1)) unset($lines[count($lines) - 1]);
126 $res = implode("\n", array_map(create_function('$x', "return $php_with.\$x;"), $lines));
127 // give back the empty line, we ignored above
128 if ("\n" == substr($string, -1)) $res .= "\n";
133 * Prepare a text as a comment -- wraps the lines and prepends #
134 * and a special character to each line
137 * @param string $text the comment text
138 * @param string $char character to denote a special PO comment,
139 * like :, default is a space
141 function comment_block($text, $char=' ') {
142 $text = wordwrap($text, PO_MAX_LINE_LEN - 3);
143 return PO::prepend_each_line($text, "#$char ");
147 * Builds a string from the entry for inclusion in PO file
150 * @param object &$entry the entry to convert to po string
151 * @return string|bool PO-style formatted string for the entry or
152 * false if the entry is empty
154 function export_entry(&$entry) {
155 if (is_null($entry->singular)) return false;
157 if (!empty($entry->translator_comments)) $po[] = PO::comment_block($entry->translator_comments);
158 if (!empty($entry->extracted_comments)) $po[] = PO::comment_block($entry->extracted_comments, '.');
159 if (!empty($entry->references)) $po[] = PO::comment_block(implode(' ', $entry->references), ':');
160 if (!empty($entry->flags)) $po[] = PO::comment_block(implode("\n", $entry->flags), ',');
161 if (!is_null($entry->context)) $po[] = 'msgctxt '.PO::poify($entry->context);
162 $po[] = 'msgid '.PO::poify($entry->singular);
163 if (!$entry->is_plural) {
164 $translation = empty($entry->translations)? '' : $entry->translations[0];
165 $po[] = 'msgstr '.PO::poify($translation);
167 $po[] = 'msgid_plural '.PO::poify($entry->plural);
168 $translations = empty($entry->translations)? array('', '') : $entry->translations;
169 foreach($translations as $i => $translation) {
170 $po[] = "msgstr[$i] ".PO::poify($translation);
173 return implode("\n", $po);