Core  3.2
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
programs/po2js.php
Go to the documentation of this file.
1 #!/usr/bin/env php
2 <?php
3 /*
4  * @author Anakeen
5  * @package FDL
6 */
7 /**
8  * translate mo file to json
9  * @author Anakeen
10  */
11 
12 $c = new Po2js($argv[1]);
13 print $c->po2json();
14 /**
15  * Convert a PO file to json string
16  */
17 class Po2js
18 {
19 
20  protected $pofile = "";
21  protected $entries = array();
22  protected $encoding = 'utf-8';
23  /**
24  * Construct the object
25  *
26  * @param string $pofile path to the po file
27  * @throws Exception
28  */
29  public function __construct($pofile)
30  {
31  if (file_exists($pofile)) {
32  $this->pofile = $pofile;
33  } else {
34  throw new Exception("PO file ($pofile) doesn't exist.");
35  }
36  }
37  /**
38  * Convert the current PO file to a json string
39  *
40  * JSON contains an object where key are po key and content po translation
41  * if there is no translation return an empty string
42  *
43  * @return string
44  */
45  public function po2json()
46  {
47  $this->po2array();
48  if (!empty($this->entries)) {
49  $js = json_encode($this->entries);
50  return $js;
51  } else {
52  return "";
53  }
54  }
55  /**
56  * Extract PO entries an store them
57  *
58  * @throws Exception
59  */
60  protected function po2array()
61  {
62  if (file_exists($this->pofile)) {
63  $pocontent = file_get_contents($this->pofile);
64  if ($this->encoding === 'iso') {
65  $pocontent = utf8_encode($pocontent);
66  }
67  if ($pocontent !== false) {
68  $pocontent.= "\n\n";
69  preg_match_all('/
70  ( ^msgctxt \s+ (?P<msgctxt> ".*" \s* (^ ".*" \s*)* ) )?
71  \s*
72  ^msgid \s+ (?P<msgid> ".*" \s* (^ ".*" \s*)* )
73  \s*
74  ^msgstr \s+ (?P<msgstr> ".*" \s* (^ ".*" \s*)* )
75  \s*
76  /xmu', $pocontent, $matches, PREG_SET_ORDER);
77  foreach ($matches as $m) {
78  $this->memoEntry($m['msgid'], $m['msgstr'], $m['msgctxt']);
79  }
80  } else {
81  throw new Exception("PO file ({$this->pofile}) is not readable.");
82  }
83  } else {
84  throw new Exception("PO file ({$this->pofile}) doesn't exist.");
85  }
86  }
87  /**
88  * Clean a key and a translation and add them to $this->entries
89  * @param string $key text to translate
90  * @param string $text translation
91  * @param string $ctxt context
92  */
93  protected function memoEntry($key, $text, $ctxt = '')
94  {
95  $key = $this->mergeMsgLines($key);
96  $text = $this->mergeMsgLines($text);
97  $ctxt = $this->mergeMsgLines($ctxt);
98 
99  if ($key && $text) {
100  if ($ctxt) {
101  $this->entries["_msgctxt_"][$ctxt][$key] = $text;
102  } else {
103  $this->entries[$key] = $text;
104  }
105  } else if ($key == "") {
106  if (stristr($text, "charset=ISO-8859") !== false) {
107  $this->encoding = 'iso';
108  }
109  }
110  }
111  /**
112  * Convert multiples quoted msg lines into a single unquoted line:
113  *
114  * ["foo..."\n"bar..."\n"baz..."] => [foo...bar...baz...]
115  *
116  * @param $lines
117  * @return string
118  */
119  protected function mergeMsgLines($lines)
120  {
121  /* Extract lines */
122  $lines = preg_split('/\s*\n+\s*/u', $lines);
123  /* Remove empty lines */
124  $lines = array_filter($lines, function ($v)
125  {
126  return ($v !== '');
127  });
128  /*
129  * Strip leading and trailing quotes from lines
130  * and unescape gettext's control sequences.
131  */
132  foreach ($lines as & $line) {
133  /* Remove leading and trailing quote */
134  $line = preg_replace('/^\s*"(.*)"\s*$/u', '\1', $line);
135  /* Unescape Getext's control sequences */
136  $line = $this->unescapeGettextControlSeq($line);
137  }
138  unset($line);
139  /*
140  * Merge back into single line
141  */
142  return implode("", $lines);
143  }
144  /**
145  * Unescape gettext's control sequence according to
146  * http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-tools/src/po-lex.c#n750
147  *
148  * @param $str
149  * @return string
150  */
151  protected function unescapeGettextControlSeq($str)
152  {
153  $str = preg_replace_callback('/\x5c([ntbrfva"\x5c]|[0-7]{1,3}|x[0-9a-fA-F][0-9a-fA-F])/u', function ($m)
154  {
155  $seq = $m[1];
156  /* Unescape control chars */
157  switch ($seq) {
158  case 'n':
159  return "\n";
160  case 't':
161  return "\t";
162  case 'b':
163  return chr(0x08);
164  case 'r':
165  return "\r";
166  case 'f':
167  return "\f";
168  case 'v':
169  return "\v";
170  case 'a':
171  return chr(0x07);
172  case '"':
173  case chr(0x5c):
174  return $seq;
175  }
176  /* Unescape hexadecimal form */
177  if (substr($seq, 0, 1) == 'x') {
178  $seq = substr($seq, 1);
179  return hex2bin($seq);
180  }
181  /* Unescape octal form */
182  return hex2bin(sprintf("%02s", dechex(octdec($seq))));
183  }
184  , $str);
185  return $str;
186  }
187 }
memoEntry($key, $text, $ctxt= '')
memoEntry($key, $text, $ctxt='')
__construct($pofile)
print
Definition: checklist.php:49
unescapeGettextControlSeq($str)
mergeMsgLines($lines)
← centre documentaire © anakeen