3 namespace Friendica\Core\Console;
\r
6 * Read a strings.php file and create messages.po in the same directory
\r
8 * @author Hypolite Petovan <mrpetovan@gmail.com>
\r
10 class PhpToPo extends \Asika\SimpleConsole\Console
\r
13 protected $helpOptions = ['h', 'help', '?'];
\r
15 private $normBaseMsgIds = [];
\r
16 const NORM_REGEXP = "|[\\\]|";
\r
18 protected function getHelp()
\r
21 console php2po - Generate a messages.po file from a strings.php file
\r
23 bin/console php2po [-p <n>] [--base <file>] <path/to/strings.php> [-h|--help|-?] [-v]
\r
26 Read a strings.php file and create the according messages.po in the same directory
\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
37 protected function doExecute()
\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
45 if (count($this->args) == 0) {
\r
46 $this->out($this->getHelp());
\r
50 if (count($this->args) > 1) {
\r
51 throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
\r
56 $phpfile = realpath($this->getArgument(0));
\r
58 if (!file_exists($phpfile)) {
\r
59 throw new \RuntimeException('Supplied file path doesn\'t exist.');
\r
62 if (!is_writable(dirname($phpfile))) {
\r
63 throw new \RuntimeException('Supplied directory isn\'t writable.');
\r
66 $pofile = dirname($phpfile) . DIRECTORY_SEPARATOR . 'messages.po';
\r
69 include_once($phpfile);
\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
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
85 // search for plural info
\r
88 $lang_pnum = $this->getOption('p', 2);
\r
90 $infile = file($phpfile);
\r
91 foreach ($infile as $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
96 if ($this->startsWith($l, 'return')) {
\r
97 $lang_logic = str_replace('$', '', trim(str_replace('return ', '', $l), ';'));
\r
102 $this->out('Language: ' . $lang);
\r
103 $this->out('Plural forms: ' . $lang_pnum);
\r
104 $this->out('Plural forms: ' . $lang_logic);
\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
110 $base_path = $this->getOption('base', 'util' . DIRECTORY_SEPARATOR . 'messages.po');
\r
112 // load base messages.po and extract msgids
\r
114 $base_f = file($base_path);
\r
116 throw new \RuntimeException('The base ' . $base_path . ' file is missing or unavailable to read.');
\r
119 $this->out('Loading base file ' . $base_path . '...');
\r
124 foreach ($base_f as $l) {
\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
138 if ($this->startsWith($l, '"') && $_f == 2) {
\r
139 $_mids[count($_mids) - 1] .= "\n" . $l;
\r
141 if ($this->startsWith($l, 'msgid_plural ')) {
\r
143 $_mids[] = str_replace('msgid_plural ', '', $l);
\r
146 if ($this->startsWith($l, '"') && $_f == 1) {
\r
147 $_mid .= "\n" . $l;
\r
148 $_mids[count($_mids) - 1] .= "\n" . $l;
\r
150 if ($this->startsWith($l, 'msgid ')) {
\r
152 $_mid = str_replace('msgid ', '', $l);
\r
157 $this->out('Creating ' . $pofile . '...');
\r
159 // create msgid and msgstr
\r
161 foreach ($a->strings as $key => $str) {
\r
162 $msgid = $this->massageString($key);
\r
164 if (preg_match("|%[sd0-9](\$[sn])*|", $msgid)) {
\r
165 $out .= "#, php-format\n";
\r
167 $msgid = $this->findOriginalMsgId($msgid);
\r
168 $out .= 'msgid ' . $msgid . "\n";
\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
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
177 foreach ($str as $n => $msgstr) {
\r
178 $out .= 'msgstr[' . $n . '] ' . $this->massageString($msgstr) . "\n";
\r
181 $out .= 'msgstr ' . $this->massageString($str) . "\n";
\r
187 if (!file_put_contents($pofile, $out)) {
\r
188 throw new \RuntimeException('Unable to write to ' . $pofile);
\r
191 if ($warnings != '') {
\r
192 $this->out($warnings);
\r
198 private function startsWith($haystack, $needle)
\r
200 // search backwards starting from haystack length characters from the end
\r
201 return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE;
\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
210 private function massageString($str)
\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
220 $str = preg_replace("|\n([^\"])|", "\n\"$1", $str);
\r
221 return sprintf('"%s"', $str);
\r
224 private function findOriginalMsgId($str)
\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