]> git.mxchange.org Git - friendica.git/blob - src/Core/Console/PhpToPo.php
Merge branch 'master' into develop
[friendica.git] / src / Core / Console / PhpToPo.php
1 <?php\r
2 \r
3 namespace Friendica\Core\Console;\r
4 \r
5 /**\r
6  * Read a strings.php file and create messages.po in the same directory\r
7  *\r
8  * @author Hypolite Petovan <mrpetovan@gmail.com>\r
9  */\r
10 class PhpToPo extends \Asika\SimpleConsole\Console\r
11 {\r
12 \r
13         protected $helpOptions = ['h', 'help', '?'];\r
14 \r
15         private $normBaseMsgIds = [];\r
16         const NORM_REGEXP = "|[\\\]|";\r
17 \r
18         protected function getHelp()\r
19         {\r
20                 $help = <<<HELP\r
21 console php2po - Generate a messages.po file from a strings.php file\r
22 Usage\r
23         bin/console php2po [-p <n>] [--base <file>] <path/to/strings.php> [-h|--help|-?] [-v]\r
24 \r
25 Description\r
26         Read a strings.php file and create the according messages.po in the same directory\r
27 \r
28 Options\r
29         -p <n>        Number of plural forms. Default: 2\r
30         --base <file> Path to base messages.po file. Default: util/messages.po\r
31         -h|--help|-?  Show help information\r
32         -v            Show more debug information.\r
33 HELP;\r
34                 return $help;\r
35         }\r
36 \r
37         protected function doExecute()\r
38         {\r
39                 if ($this->getOption('v')) {\r
40                         $this->out('Class: ' . __CLASS__);\r
41                         $this->out('Arguments: ' . var_export($this->args, true));\r
42                         $this->out('Options: ' . var_export($this->options, true));\r
43                 }\r
44 \r
45                 if (count($this->args) == 0) {\r
46                         $this->out($this->getHelp());\r
47                         return 0;\r
48                 }\r
49 \r
50                 if (count($this->args) > 1) {\r
51                         throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');\r
52                 }\r
53 \r
54                 $a = get_app();\r
55 \r
56                 $phpfile = realpath($this->getArgument(0));\r
57 \r
58                 if (!file_exists($phpfile)) {\r
59                         throw new \RuntimeException('Supplied file path doesn\'t exist.');\r
60                 }\r
61 \r
62                 if (!is_writable(dirname($phpfile))) {\r
63                         throw new \RuntimeException('Supplied directory isn\'t writable.');\r
64                 }\r
65 \r
66                 $pofile = dirname($phpfile) . DIRECTORY_SEPARATOR . 'messages.po';\r
67 \r
68                 // start !\r
69                 include_once($phpfile);\r
70 \r
71                 $out = '';\r
72                 $out .= "# FRIENDICA Distributed Social Network\n";\r
73                 $out .= "# Copyright (C) 2010, 2011, 2012, 2013 the Friendica Project\n";\r
74                 $out .= "# This file is distributed under the same license as the Friendica package.\n";\r
75                 $out .= "# \n";\r
76                 $out .= 'msgid ""' . "\n";\r
77                 $out .= 'msgstr ""' . "\n";\r
78                 $out .= '"Project-Id-Version: friendica\n"' . "\n";\r
79                 $out .= '"Report-Msgid-Bugs-To: \n"' . "\n";\r
80                 $out .= '"POT-Creation-Date: ' . date("Y-m-d H:i:sO") . '\n"' . "\n";\r
81                 $out .= '"MIME-Version: 1.0\n"' . "\n";\r
82                 $out .= '"Content-Type: text/plain; charset=UTF-8\n"' . "\n";\r
83                 $out .= '"Content-Transfer-Encoding: 8bit\n"' . "\n";\r
84 \r
85                 // search for plural info\r
86                 $lang = "";\r
87                 $lang_logic = "";\r
88                 $lang_pnum = $this->getOption('p', 2);\r
89 \r
90                 $infile = file($phpfile);\r
91                 foreach ($infile as $l) {\r
92                         $l = trim($l);\r
93                         if ($this->startsWith($l, 'function string_plural_select_')) {\r
94                                 $lang = str_replace('function string_plural_select_', '', str_replace('($n){', '', $l));\r
95                         }\r
96                         if ($this->startsWith($l, 'return')) {\r
97                                 $lang_logic = str_replace('$', '', trim(str_replace('return ', '', $l), ';'));\r
98                                 break;\r
99                         }\r
100                 }\r
101 \r
102                 $this->out('Language: ' . $lang);\r
103                 $this->out('Plural forms: ' . $lang_pnum);\r
104                 $this->out('Plural forms: ' . $lang_logic);\r
105 \r
106                 $out .= sprintf('"Language: %s\n"', $lang) . "\n";\r
107                 $out .= sprintf('"Plural-Forms: nplurals=%s; plural=%s;\n"', $lang_pnum, $lang_logic) . "\n";\r
108                 $out .= "\n";\r
109 \r
110                 $base_path = $this->getOption('base', 'util' . DIRECTORY_SEPARATOR . 'messages.po');\r
111 \r
112                 // load base messages.po and extract msgids\r
113                 $base_msgids = [];\r
114                 $base_f = file($base_path);\r
115                 if (!$base_f) {\r
116                         throw new \RuntimeException('The base ' . $base_path . ' file is missing or unavailable to read.');\r
117                 }\r
118 \r
119                 $this->out('Loading base file ' . $base_path . '...');\r
120 \r
121                 $_f = 0;\r
122                 $_mid = "";\r
123                 $_mids = [];\r
124                 foreach ($base_f as $l) {\r
125                         $l = trim($l);\r
126 \r
127                         if ($this->startsWith($l, 'msgstr')) {\r
128                                 if ($_mid != '""') {\r
129                                         $base_msgids[$_mid] = $_mids;\r
130                                         $this->normBaseMsgIds[preg_replace(self::NORM_REGEXP, "", $_mid)] = $_mid;\r
131                                 }\r
132 \r
133                                 $_f = 0;\r
134                                 $_mid = "";\r
135                                 $_mids = [];\r
136                         }\r
137 \r
138                         if ($this->startsWith($l, '"') && $_f == 2) {\r
139                                 $_mids[count($_mids) - 1] .= "\n" . $l;\r
140                         }\r
141                         if ($this->startsWith($l, 'msgid_plural ')) {\r
142                                 $_f = 2;\r
143                                 $_mids[] = str_replace('msgid_plural ', '', $l);\r
144                         }\r
145 \r
146                         if ($this->startsWith($l, '"') && $_f == 1) {\r
147                                 $_mid .= "\n" . $l;\r
148                                 $_mids[count($_mids) - 1] .= "\n" . $l;\r
149                         }\r
150                         if ($this->startsWith($l, 'msgid ')) {\r
151                                 $_f = 1;\r
152                                 $_mid = str_replace('msgid ', '', $l);\r
153                                 $_mids = [$_mid];\r
154                         }\r
155                 }\r
156 \r
157                 $this->out('Creating ' . $pofile . '...');\r
158 \r
159                 // create msgid and msgstr\r
160                 $warnings = "";\r
161                 foreach ($a->strings as $key => $str) {\r
162                         $msgid = $this->massageString($key);\r
163 \r
164                         if (preg_match("|%[sd0-9](\$[sn])*|", $msgid)) {\r
165                                 $out .= "#, php-format\n";\r
166                         }\r
167                         $msgid = $this->findOriginalMsgId($msgid);\r
168                         $out .= 'msgid ' . $msgid . "\n";\r
169 \r
170                         if (is_array($str)) {\r
171                                 if (array_key_exists($msgid, $base_msgids) && isset($base_msgids[$msgid][1])) {\r
172                                         $out .= 'msgid_plural ' . $base_msgids[$msgid][1] . "\n";\r
173                                 } else {\r
174                                         $out .= 'msgid_plural ' . $msgid . "\n";\r
175                                         $warnings .= "[W] No source plural form for msgid:\n" . str_replace("\n", "\n\t", $msgid) . "\n\n";\r
176                                 }\r
177                                 foreach ($str as $n => $msgstr) {\r
178                                         $out .= 'msgstr[' . $n . '] ' . $this->massageString($msgstr) . "\n";\r
179                                 }\r
180                         } else {\r
181                                 $out .= 'msgstr ' . $this->massageString($str) . "\n";\r
182                         }\r
183 \r
184                         $out .= "\n";\r
185                 }\r
186 \r
187                 if (!file_put_contents($pofile, $out)) {\r
188                         throw new \RuntimeException('Unable to write to ' . $pofile);\r
189                 }\r
190 \r
191                 if ($warnings != '') {\r
192                         $this->out($warnings);\r
193                 }\r
194 \r
195                 return 0;\r
196         }\r
197 \r
198         private function startsWith($haystack, $needle)\r
199         {\r
200                 // search backwards starting from haystack length characters from the end\r
201                 return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE;\r
202         }\r
203 \r
204         /**\r
205          * Get a string and retun a message.po ready text\r
206          * - replace " with \"\r
207          * - replace tab char with \t\r
208          * - manage multiline strings\r
209          */\r
210         private function massageString($str)\r
211         {\r
212                 $str = str_replace('\\', '\\\\', $str);\r
213                 $str = str_replace('"', '\"', $str);\r
214                 $str = str_replace("\t", '\t', $str);\r
215                 $str = str_replace("\n", '\n"' . "\n" . '"', $str);\r
216                 if (strpos($str, "\n") !== false && $str[0] !== '"') {\r
217                         $str = '"' . "\n" . $str;\r
218                 }\r
219 \r
220                 $str = preg_replace("|\n([^\"])|", "\n\"$1", $str);\r
221                 return sprintf('"%s"', $str);\r
222         }\r
223 \r
224         private function findOriginalMsgId($str)\r
225         {\r
226                 $norm_str = preg_replace(self::NORM_REGEXP, "", $str);\r
227                 if (array_key_exists($norm_str, $this->normBaseMsgIds)) {\r
228                         return $this->normBaseMsgIds[$norm_str];\r
229                 }\r
230 \r
231                 return $str;\r
232         }\r
233 \r
234 }\r