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