Subversion Repositories oidplus

Rev

Rev 1127 | Rev 1133 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1127 Rev 1130
1
#!/usr/bin/env php
1
#!/usr/bin/env php
2
<?php
2
<?php
3
 
3
 
4
/*
4
/*
5
 * OIDplus 2.0
5
 * OIDplus 2.0
6
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
6
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
7
 *
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
10
 * You may obtain a copy of the License at
11
 *
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
18
 * limitations under the License.
19
 */
19
 */
20
 
20
 
21
// This script updates the language message files (adding new entries and
21
// This script updates the language message files (adding new entries and
22
// removing entries that are not existing anymore).
22
// removing entries that are not existing anymore).
23
// It requires that PHP scripts are using following syntax for translations:
23
// It requires that PHP scripts are using following syntax for translations:
24
//             _L('hello world',optionalParams) <recommended>
24
//             _L('hello world',optionalParams) <recommended>
25
//             _L("hello world",optionalParams)
25
//             _L("hello world",optionalParams)
26
// and JS files:
26
// and JS files:
27
//             _L('hello world',optionalParams)
27
//             _L('hello world',optionalParams)
28
//             _L("hello world",optionalParams) <recommended>
28
//             _L("hello world",optionalParams) <recommended>
29
 
29
 
30
$dir = __DIR__ . '/../../';
30
$dir = __DIR__ . '/../../';
31
 
31
 
32
// TODO: Support the case messages*.xml (for than 1 message file per language)
32
// TODO: Support the case messages*.xml (for than 1 message file per language)
33
//       Not sure how to solve it, because if there is a string we do not have,
33
//       Not sure how to solve it, because if there is a string we do not have,
34
//       to which file do we add it?
34
//       to which file do we add it?
35
 
35
 
36
// ---
36
// ---
37
 
37
 
38
$langs = array();
38
$langs = array();
39
// TODO: Instead of hard-coding messages.xml, read it from manifest.xml
39
// TODO: Instead of hard-coding messages.xml, read it from manifest.xml
40
$tmp = glob($dir.'/plugins/'.'*'.'/language/'.'*'.'/messages.xml');
40
$tmp = glob($dir.'/plugins/'.'*'.'/language/'.'*'.'/messages.xml');
41
foreach ($tmp as $tmp2) {
41
foreach ($tmp as $tmp2) {
42
        $tmp3 = explode('/', $tmp2);
42
        $tmp3 = explode('/', $tmp2);
43
        $lang = $tmp3[count($tmp3)-2];
43
        $lang = $tmp3[count($tmp3)-2];
44
        if ($lang == 'enus') continue; // ignore base lang
44
        if ($lang == 'enus') continue; // ignore base lang
45
        $langs[] = $lang;
45
        $langs[] = $lang;
46
}
46
}
47
 
47
 
48
// ---
48
// ---
49
 
49
 
50
$all_strings = array();
50
$all_strings = array();
51
 
51
 
52
$it = new RecursiveDirectoryIterator($dir);
52
$it = new RecursiveDirectoryIterator($dir);
53
$it->setFlags(RecursiveDirectoryIterator::SKIP_DOTS); // DOES NOT WORK! Folders with . prefix still get evaluated!
53
$it->setFlags(RecursiveDirectoryIterator::SKIP_DOTS); // DOES NOT WORK! Folders with . prefix still get evaluated!
54
foreach(new RecursiveIteratorIterator($it) as $file) {
54
foreach(new RecursiveIteratorIterator($it) as $file) {
55
        if ((strpos(str_replace('\\','/',realpath($file)),'/vendor/') !== false) && (strpos(str_replace('\\','/',realpath($file)),'/vendor/danielmarschall/') === false)) continue; // ignore third-party-code
55
        if ((strpos(str_replace('\\','/',realpath($file)),'/vendor/') !== false) && (strpos(str_replace('\\','/',realpath($file)),'/vendor/danielmarschall/') === false)) continue; // ignore third-party-code
56
        if (strpos(str_replace('\\','/',realpath($file)),'/dev/') !== false) continue; // ignore development utilities
56
        if (strpos(str_replace('\\','/',realpath($file)),'/dev/') !== false) continue; // ignore development utilities
57
 
57
 
58
        if (preg_match('@[/\\\\]\\.[^\\.]@',$file,$m)) continue; // Alternative to SKIP_DOTS
58
        if (preg_match('@[/\\\\]\\.[^\\.]@',$file,$m)) continue; // Alternative to SKIP_DOTS
59
 
59
 
60
        if ($file->getExtension() == 'php') {
60
        if ($file->getExtension() == 'php') {
61
                $cont = file_get_contents($file);
61
                $cont = file_get_contents($file);
62
                $cont = phpRemoveComments($cont);
62
                $cont = phpRemoveComments($cont);
63
#               $cont = str_replace('function _L($str, ...$sprintfArgs) {', '', $cont);
63
#               $cont = str_replace('function _L($str, ...$sprintfArgs) {', '', $cont);
64
                $cont = str_replace('_L($', '', $cont);
64
                $cont = str_replace('_L($', '', $cont);
65
                $strings = get_php_L_strings($cont);
65
                $strings = get_php_L_strings($cont);
66
                $strings_test = get_js_L_strings($cont);
66
                $strings_test = get_js_L_strings($cont);
67
 
67
 
68
                if (serialize($strings) != serialize($strings_test)) {
68
                if (serialize($strings) != serialize($strings_test)) {
69
                        echo "Attention: File ".realpath($file)." ambiguous _L() functions\n";
69
                        echo "Attention: File ".realpath($file)." ambiguous _L() functions\n";
70
                }
70
                }
71
 
71
 
72
                $all_strings = array_merge($all_strings, $strings);
72
                $all_strings = array_merge($all_strings, $strings);
73
        }
73
        }
74
        if ($file->getExtension() == 'js') {
74
        if ($file->getExtension() == 'js') {
75
                $cont = file_get_contents($file);
75
                $cont = file_get_contents($file);
76
                $cont = str_replace('function _L()', '', $cont);
76
                $cont = str_replace('function _L()', '', $cont);
77
                $strings = get_js_L_strings($cont);
77
                $strings = get_js_L_strings($cont);
78
                $all_strings = array_merge($all_strings, $strings);
78
                $all_strings = array_merge($all_strings, $strings);
79
        }
79
        }
80
}
80
}
81
 
81
 
82
foreach ($all_strings as $str) {
82
foreach ($all_strings as $str) {
83
        test_missing_placeholder($str);
83
        test_missing_placeholder($str);
84
}
84
}
85
 
85
 
86
$all_strings = array_unique($all_strings);
86
$all_strings = array_unique($all_strings);
87
sort($all_strings);
87
sort($all_strings);
88
 
88
 
89
// ---
89
// ---
90
 
90
 
91
foreach ($langs as $lang) {
91
foreach ($langs as $lang) {
92
        // TODO: Instead of hard-coding messages.xml, read it from manifest.xml
92
        // TODO: Instead of hard-coding messages.xml, read it from manifest.xml
93
        $translation_files = glob($dir.'/plugins/'.'*'.'/language/'.$lang.'/messages.xml');
93
        $translation_files = glob($dir.'/plugins/'.'*'.'/language/'.$lang.'/messages.xml');
94
        $translation_file = count($translation_files) > 0 ? $translation_files[0] : null;
94
        $translation_file = count($translation_files) > 0 ? $translation_files[0] : null;
95
        if (file_exists($translation_file)) {
95
        if (file_exists($translation_file)) {
96
                $xml = simplexml_load_string(file_get_contents($translation_file));
96
                $xml = simplexml_load_string(file_get_contents($translation_file));
97
                if (!$xml) {
97
                if (!$xml) {
98
                        echo "STOP: Cannot load $translation_file\n";
98
                        echo "STOP: Cannot load $translation_file\n";
99
                        continue;
99
                        continue;
100
                }
100
                }
101
                foreach ($xml->message as $msg) {
101
                foreach ($xml->message as $msg) {
102
                        $src = trim($msg->source->__toString());
102
                        $src = trim($msg->source->__toString());
103
                        $dst = trim($msg->target->__toString());
103
                        $dst = trim($msg->target->__toString());
104
                        $translation_array[$src] = $dst;
104
                        $translation_array[$src] = $dst;
105
                }
105
                }
106
        }
106
        }
107
 
107
 
108
        // ---
108
        // ---
109
 
109
 
110
        echo "Processing ".realpath($translation_file)." ...\n";
110
        echo "Processing ".realpath($translation_file)." ...\n";
111
 
111
 
112
        $stats_total = 0;
112
        $stats_total = 0;
113
        $stats_translated = 0;
113
        $stats_translated = 0;
114
        $stats_not_translated = 0;
114
        $stats_not_translated = 0;
115
 
115
 
116
        $cont  = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'."\n";
116
        $cont  = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'."\n";
117
        $cont .= '<translation'."\n";
117
        $cont .= '<translation'."\n";
118
        $cont .= '      xmlns="urn:oid:1.3.6.1.4.1.37476.2.5.2.5.4.1"'."\n";
118
        $cont .= '      xmlns="urn:oid:1.3.6.1.4.1.37476.2.5.2.5.4.1"'."\n";
119
        $cont .= '      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'."\n";
119
        $cont .= '      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'."\n";
120
        $cont .= '      xsi:schemaLocation="urn:oid:1.3.6.1.4.1.37476.2.5.2.5.4.1 https://oidplus.viathinksoft.com/oidplus/plugins/messages.xsd">'."\n";
120
        $cont .= '      xsi:schemaLocation="urn:oid:1.3.6.1.4.1.37476.2.5.2.5.4.1 https://oidplus.viathinksoft.com/oidplus/plugins/messages.xsd">'."\n";
121
        $cont .= "\n";
121
        $cont .= "\n";
122
        foreach ($all_strings as $string) {
122
        foreach ($all_strings as $string) {
123
                $stats_total++;
123
                $stats_total++;
124
                $string = trim($string);
124
                $string = trim($string);
125
                $cont .= "      <message>\n";
125
                $cont .= "      <message>\n";
126
                $cont .= "              <source><![CDATA[\n";
126
                $cont .= "              <source><![CDATA[\n";
127
                $cont .= "              $string\n";
127
                $cont .= "              $string\n";
128
                $cont .= "              ]]></source>\n";
128
                $cont .= "              ]]></source>\n";
129
                if (isset($translation_array[$string]) && !empty($translation_array[$string])) {
129
                if (isset($translation_array[$string]) && !empty($translation_array[$string])) {
130
                        $translation_array[$string] = trim($translation_array[$string]);
130
                        $translation_array[$string] = trim($translation_array[$string]);
131
                        $stats_translated++;
131
                        $stats_translated++;
132
                        if (substr_count($string,'%') != substr_count($translation_array[$string],'%')) {
132
                        if (substr_count($string,'%') != substr_count($translation_array[$string],'%')) {
133
                                echo "\tAttention: Number of %-Replacements differs at translation of message '$string'\n";
133
                                echo "\tAttention: Number of %-Replacements differs at translation of message '$string'\n";
134
                        }
134
                        }
135
                        $cont .= "              <target><![CDATA[\n";
135
                        $cont .= "              <target><![CDATA[\n";
136
                        $cont .= "              ".$translation_array[$string]."\n";
136
                        $cont .= "              ".$translation_array[$string]."\n";
137
                        $cont .= "              ]]></target>\n";
137
                        $cont .= "              ]]></target>\n";
138
                        test_missing_placeholder($translation_array[$string]);
138
                        test_missing_placeholder($translation_array[$string]);
139
                } else {
139
                } else {
140
                        $stats_not_translated++;
140
                        $stats_not_translated++;
141
                        $cont .= "              <target><![CDATA[\n";
141
                        $cont .= "              <target><![CDATA[\n";
142
                        $cont .= "              ]]></target><!-- TODO: TRANSLATE -->\n";
142
                        $cont .= "              ]]></target><!-- TODO: TRANSLATE -->\n";
143
                }
143
                }
144
                $cont .= "      </message>\n";
144
                $cont .= "      </message>\n";
145
        }
145
        }
146
        $cont .= "</translation>\n";
146
        $cont .= "</translation>\n";
147
        file_put_contents($translation_file, $cont);
147
        file_put_contents($translation_file, $cont);
148
 
148
 
149
        echo "\t$stats_total total messages, $stats_translated already translated (".round($stats_translated/$stats_total*100,2)."%), $stats_not_translated not translated (".round($stats_not_translated/$stats_total*100,2)."%)\n";
149
        echo "\t$stats_total total messages, $stats_translated already translated (".round($stats_translated/$stats_total*100,2)."%), $stats_not_translated not translated (".round($stats_not_translated/$stats_total*100,2)."%)\n";
150
        echo "\tDone...\n";
150
        echo "\tDone...\n";
151
}
151
}
152
 
152
 
153
if (count($langs) > 0) {
153
if (count($langs) > 0) {
154
        echo "All done!\n";
154
        echo "All done!\n";
155
} else {
155
} else {
156
        echo "Attention: No language plugins found!\n";
156
        echo "Attention: No language plugins found!\n";
157
}
157
}
158
 
158
 
159
# ---
159
# ---
160
 
160
 
-
 
161
/**
-
 
162
 * @param string $cont
-
 
163
 * @return string[]
-
 
164
 */
161
function get_js_L_strings($cont) {
165
function get_js_L_strings(string $cont): array {
162
        // Works with JavaScript and PHP
166
        // Works with JavaScript and PHP
163
        $cont = preg_replace('@/\\*.+\\*/@ismU', '', $cont);
167
        $cont = preg_replace('@/\\*.+\\*/@ismU', '', $cont);
164
        $cont = str_replace('\\"', chr(1), $cont);
168
        $cont = str_replace('\\"', chr(1), $cont);
165
        $cont = str_replace("\\'", chr(2), $cont);
169
        $cont = str_replace("\\'", chr(2), $cont);
166
        $cont = str_replace("\\\\", "\\", $cont);
170
        $cont = str_replace("\\\\", "\\", $cont);
167
        $m = array();
171
        $m = array();
168
        preg_match_all('@[^_A-Za-z0-9]_L\\s*\\(([^\\)]*)(["\'])(.+)\\2@ismU', $cont, $m);
172
        preg_match_all('@[^_A-Za-z0-9]_L\\s*\\(([^\\)]*)(["\'])(.+)\\2@ismU', $cont, $m);
169
        foreach ($m[3] as &$x) {
173
        foreach ($m[3] as &$x) {
170
                $x = str_replace(chr(1), '"', $x);
174
                $x = str_replace(chr(1), '"', $x);
171
                $x = str_replace(chr(2), "'", $x);
175
                $x = str_replace(chr(2), "'", $x);
172
        }
176
        }
173
        return $m[3];
177
        return $m[3];
174
}
178
}
175
 
179
 
-
 
180
/**
-
 
181
 * @param string $cont
-
 
182
 * @return string[]
-
 
183
 */
176
function get_php_L_strings($cont) {
184
function get_php_L_strings(string $cont): array {
177
        // Works only with PHP
185
        // Works only with PHP
178
        $out = array();
186
        $out = array();
179
        $tokens = token_get_all($cont);
187
        $tokens = token_get_all($cont);
180
        $activated = 0;
188
        $activated = 0;
181
        foreach ($tokens as $token) {
189
        foreach ($tokens as $token) {
182
                if (is_array($token)) {
190
                if (is_array($token)) {
183
                        if (($token[0] == T_STRING) && ($token[1] == '_L')) {
191
                        if (($token[0] == T_STRING) && ($token[1] == '_L')) {
184
                                $activated = 1;
192
                                $activated = 1;
185
                        } else if (($activated == 1) && ($token[0] == T_CONSTANT_ENCAPSED_STRING)) {
193
                        } else if (($activated == 1) && ($token[0] == T_CONSTANT_ENCAPSED_STRING)) {
186
                                $tmp = stripcslashes($token[1]);
194
                                $tmp = stripcslashes($token[1]);
187
                                $out[] = substr($tmp,1,strlen($tmp)-2);
195
                                $out[] = substr($tmp,1,strlen($tmp)-2);
188
                                $activated = 0;
196
                                $activated = 0;
189
                        }
197
                        }
190
                }
198
                }
191
        }
199
        }
192
        return $out;
200
        return $out;
193
}
201
}
194
 
202
 
-
 
203
/**
-
 
204
 * @param string $test
-
 
205
 * @return void
-
 
206
 */
195
function test_missing_placeholder($test) {
207
function test_missing_placeholder(string $test) {
196
        $max = -1;
208
        $max = -1;
197
        for ($i=99; $i>=1; $i--) {
209
        for ($i=99; $i>=1; $i--) {
198
                if (strpos($test, '%'.$i) !== false) {
210
                if (strpos($test, '%'.$i) !== false) {
199
                        $max = $i;
211
                        $max = $i;
200
                        break;
212
                        break;
201
                }
213
                }
202
        }
214
        }
203
 
215
 
204
        for ($i=1; $i<=$max; $i++) {
216
        for ($i=1; $i<=$max; $i++) {
205
                if (strpos($test, '%'.$i) === false) {
217
                if (strpos($test, '%'.$i) === false) {
206
                        echo "Attention: %$i is missing in string '$test'!\n";
218
                        echo "Attention: %$i is missing in string '$test'!\n";
207
                        $max = $i;
219
                        $max = $i;
208
                        break;
220
                        break;
209
                }
221
                }
210
        }
222
        }
211
 
223
 
212
        $test = preg_replace('@%([1-9][0-9]|%)*@ism', '', $test);
224
        $test = preg_replace('@%([1-9][0-9]|%)*@ism', '', $test);
213
        if (strpos($test,'%') !== false) {
225
        if (strpos($test,'%') !== false) {
214
                echo "Attention: Wrong percentage sign in '$test'!\n";
226
                echo "Attention: Wrong percentage sign in '$test'!\n";
215
        }
227
        }
216
}
228
}
217
 
229
 
218
# ---
230
# ---
219
 
231
 
-
 
232
/**
-
 
233
 * @param string $fileStr
-
 
234
 * @return string
-
 
235
 */
220
function phpRemoveComments($fileStr) {
236
function phpRemoveComments(string $fileStr): string {
221
 
237
 
222
        // https://stackoverflow.com/questions/503871/best-way-to-automatically-remove-comments-from-php-code
238
        // https://stackoverflow.com/questions/503871/best-way-to-automatically-remove-comments-from-php-code
223
 
239
 
224
        $newStr  = '';
240
        $newStr  = '';
225
 
241
 
226
        $commentTokens = array(T_COMMENT);
242
        $commentTokens = array(T_COMMENT);
227
 
243
 
228
        if (defined('T_DOC_COMMENT')) $commentTokens[] = T_DOC_COMMENT; // PHP 5
244
        if (defined('T_DOC_COMMENT')) $commentTokens[] = T_DOC_COMMENT; // PHP 5
229
        if (defined('T_ML_COMMENT'))  $commentTokens[] = T_ML_COMMENT;  // PHP 4
245
        if (defined('T_ML_COMMENT'))  $commentTokens[] = T_ML_COMMENT;  // PHP 4
230
 
246
 
231
        $tokens = token_get_all($fileStr);
247
        $tokens = token_get_all($fileStr);
232
 
248
 
233
        foreach ($tokens as $token) {
249
        foreach ($tokens as $token) {
234
                if (is_array($token)) {
250
                if (is_array($token)) {
235
                        if (in_array($token[0], $commentTokens)) continue;
251
                        if (in_array($token[0], $commentTokens)) continue;
236
                        $token = $token[1];
252
                        $token = $token[1];
237
                }
253
                }
238
                $newStr .= $token;
254
                $newStr .= $token;
239
        }
255
        }
240
 
256
 
241
        return $newStr;
257
        return $newStr;
242
 
258
 
243
}
259
}
244
 
260