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