1 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,200 +0,0 @@ |
1 |
-<?php |
|
2 |
- |
|
3 |
-/* |
|
4 |
- * This file is part of the Symfony package. |
|
5 |
- * |
|
6 |
- * (c) Fabien Potencier <fabien@symfony.com> |
|
7 |
- * |
|
8 |
- * For the full copyright and license information, please view the LICENSE |
|
9 |
- * file that was distributed with this source code. |
|
10 |
- */ |
|
11 |
- |
|
12 |
-$usageInstructions = <<<END |
|
13 |
- |
|
14 |
- Usage instructions |
|
15 |
- ------------------------------------------------------------------------------- |
|
16 |
- |
|
17 |
- $ cd symfony-code-root-directory/ |
|
18 |
- |
|
19 |
- # show the translation status of all locales |
|
20 |
- $ php translation-status.php |
|
21 |
- |
|
22 |
- # show the translation status of all locales and all their missing translations |
|
23 |
- $ php translation-status.php -v |
|
24 |
- |
|
25 |
- # show the status of a single locale |
|
26 |
- $ php translation-status.php fr |
|
27 |
- |
|
28 |
- # show the status of a single locale and all its missing translations |
|
29 |
- $ php translation-status.php fr -v |
|
30 |
- |
|
31 |
-END; |
|
32 |
- |
|
33 |
-$config = [ |
|
34 |
- // if TRUE, the full list of missing translations is displayed |
|
35 |
- 'verbose_output' => false, |
|
36 |
- // NULL = analyze all locales |
|
37 |
- 'locale_to_analyze' => null, |
|
38 |
- // the reference files all the other translations are compared to |
|
39 |
- 'original_files' => [ |
|
40 |
- 'src/Symfony/Component/Form/Resources/translations/validators.en.xlf', |
|
41 |
- 'src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf', |
|
42 |
- 'src/Symfony/Component/Validator/Resources/translations/validators.en.xlf', |
|
43 |
- ], |
|
44 |
-]; |
|
45 |
- |
|
46 |
-$argc = $_SERVER['argc']; |
|
47 |
-$argv = $_SERVER['argv']; |
|
48 |
- |
|
49 |
-if ($argc > 3) { |
|
50 |
- echo str_replace('translation-status.php', $argv[0], $usageInstructions); |
|
51 |
- exit(1); |
|
52 |
-} |
|
53 |
- |
|
54 |
-foreach (array_slice($argv, 1) as $argumentOrOption) { |
|
55 |
- if (0 === strpos($argumentOrOption, '-')) { |
|
56 |
- $config['verbose_output'] = true; |
|
57 |
- } else { |
|
58 |
- $config['locale_to_analyze'] = $argumentOrOption; |
|
59 |
- } |
|
60 |
-} |
|
61 |
- |
|
62 |
-foreach ($config['original_files'] as $originalFilePath) { |
|
63 |
- if (!file_exists($originalFilePath)) { |
|
64 |
- echo sprintf('The following file does not exist. Make sure that you execute this command at the root dir of the Symfony code repository.%s %s', PHP_EOL, $originalFilePath); |
|
65 |
- exit(1); |
|
66 |
- } |
|
67 |
-} |
|
68 |
- |
|
69 |
-$totalMissingTranslations = 0; |
|
70 |
- |
|
71 |
-foreach ($config['original_files'] as $originalFilePath) { |
|
72 |
- $translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']); |
|
73 |
- $translationStatus = calculateTranslationStatus($originalFilePath, $translationFilePaths); |
|
74 |
- |
|
75 |
- $totalMissingTranslations += array_sum(array_map(function ($translation) { |
|
76 |
- return count($translation['missingKeys']); |
|
77 |
- }, array_values($translationStatus))); |
|
78 |
- |
|
79 |
- printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output']); |
|
80 |
-} |
|
81 |
- |
|
82 |
-exit($totalMissingTranslations > 0 ? 1 : 0); |
|
83 |
- |
|
84 |
-function findTranslationFiles($originalFilePath, $localeToAnalyze) |
|
85 |
-{ |
|
86 |
- $translations = []; |
|
87 |
- |
|
88 |
- $translationsDir = dirname($originalFilePath); |
|
89 |
- $originalFileName = basename($originalFilePath); |
|
90 |
- $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName); |
|
91 |
- |
|
92 |
- $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern); |
|
93 |
- foreach ($translationFiles as $filePath) { |
|
94 |
- $locale = extractLocaleFromFilePath($filePath); |
|
95 |
- |
|
96 |
- if (null !== $localeToAnalyze && $locale !== $localeToAnalyze) { |
|
97 |
- continue; |
|
98 |
- } |
|
99 |
- |
|
100 |
- $translations[$locale] = $filePath; |
|
101 |
- } |
|
102 |
- |
|
103 |
- return $translations; |
|
104 |
-} |
|
105 |
- |
|
106 |
-function calculateTranslationStatus($originalFilePath, $translationFilePaths) |
|
107 |
-{ |
|
108 |
- $translationStatus = []; |
|
109 |
- $allTranslationKeys = extractTranslationKeys($originalFilePath); |
|
110 |
- |
|
111 |
- foreach ($translationFilePaths as $locale => $translationPath) { |
|
112 |
- $translatedKeys = extractTranslationKeys($translationPath); |
|
113 |
- $missingKeys = array_diff_key($allTranslationKeys, $translatedKeys); |
|
114 |
- |
|
115 |
- $translationStatus[$locale] = [ |
|
116 |
- 'total' => count($allTranslationKeys), |
|
117 |
- 'translated' => count($translatedKeys), |
|
118 |
- 'missingKeys' => $missingKeys, |
|
119 |
- ]; |
|
120 |
- } |
|
121 |
- |
|
122 |
- return $translationStatus; |
|
123 |
-} |
|
124 |
- |
|
125 |
-function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput) |
|
126 |
-{ |
|
127 |
- printTitle($originalFilePath); |
|
128 |
- printTable($translationStatus, $verboseOutput); |
|
129 |
- echo PHP_EOL.PHP_EOL; |
|
130 |
-} |
|
131 |
- |
|
132 |
-function extractLocaleFromFilePath($filePath) |
|
133 |
-{ |
|
134 |
- $parts = explode('.', $filePath); |
|
135 |
- |
|
136 |
- return $parts[count($parts) - 2]; |
|
137 |
-} |
|
138 |
- |
|
139 |
-function extractTranslationKeys($filePath) |
|
140 |
-{ |
|
141 |
- $translationKeys = []; |
|
142 |
- $contents = new \SimpleXMLElement(file_get_contents($filePath)); |
|
143 |
- |
|
144 |
- foreach ($contents->file->body->{'trans-unit'} as $translationKey) { |
|
145 |
- $translationId = (string) $translationKey['id']; |
|
146 |
- $translationKey = (string) $translationKey->source; |
|
147 |
- |
|
148 |
- $translationKeys[$translationId] = $translationKey; |
|
149 |
- } |
|
150 |
- |
|
151 |
- return $translationKeys; |
|
152 |
-} |
|
153 |
- |
|
154 |
-function printTitle($title) |
|
155 |
-{ |
|
156 |
- echo $title.PHP_EOL; |
|
157 |
- echo str_repeat('=', strlen($title)).PHP_EOL.PHP_EOL; |
|
158 |
-} |
|
159 |
- |
|
160 |
-function printTable($translations, $verboseOutput) |
|
161 |
-{ |
|
162 |
- if (0 === count($translations)) { |
|
163 |
- echo 'No translations found'; |
|
164 |
- |
|
165 |
- return; |
|
166 |
- } |
|
167 |
- $longestLocaleNameLength = max(array_map('strlen', array_keys($translations))); |
|
168 |
- |
|
169 |
- foreach ($translations as $locale => $translation) { |
|
170 |
- $isTranslationCompleted = $translation['translated'] === $translation['total']; |
|
171 |
- if ($isTranslationCompleted) { |
|
172 |
- textColorGreen(); |
|
173 |
- } |
|
174 |
- |
|
175 |
- echo sprintf('| Locale: %-'.$longestLocaleNameLength.'s | Translated: %d/%d', $locale, $translation['translated'], $translation['total']).PHP_EOL; |
|
176 |
- |
|
177 |
- textColorNormal(); |
|
178 |
- |
|
179 |
- if (true === $verboseOutput && count($translation['missingKeys']) > 0) { |
|
180 |
- echo str_repeat('-', 80).PHP_EOL; |
|
181 |
- echo '| Missing Translations:'.PHP_EOL; |
|
182 |
- |
|
183 |
- foreach ($translation['missingKeys'] as $id => $content) { |
|
184 |
- echo sprintf('| (id=%s) %s', $id, $content).PHP_EOL; |
|
185 |
- } |
|
186 |
- |
|
187 |
- echo str_repeat('-', 80).PHP_EOL; |
|
188 |
- } |
|
189 |
- } |
|
190 |
-} |
|
191 |
- |
|
192 |
-function textColorGreen() |
|
193 |
-{ |
|
194 |
- echo "\033[32m"; |
|
195 |
-} |
|
196 |
- |
|
197 |
-function textColorNormal() |
|
198 |
-{ |
|
199 |
- echo "\033[0m"; |
|
200 |
-} |
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,200 @@ |
1 |
+<?php |
|
2 |
+ |
|
3 |
+/* |
|
4 |
+ * This file is part of the Symfony package. |
|
5 |
+ * |
|
6 |
+ * (c) Fabien Potencier <fabien@symfony.com> |
|
7 |
+ * |
|
8 |
+ * For the full copyright and license information, please view the LICENSE |
|
9 |
+ * file that was distributed with this source code. |
|
10 |
+ */ |
|
11 |
+ |
|
12 |
+$usageInstructions = <<<END |
|
13 |
+ |
|
14 |
+ Usage instructions |
|
15 |
+ ------------------------------------------------------------------------------- |
|
16 |
+ |
|
17 |
+ $ cd symfony-code-root-directory/ |
|
18 |
+ |
|
19 |
+ # show the translation status of all locales |
|
20 |
+ $ php translation-status.php |
|
21 |
+ |
|
22 |
+ # show the translation status of all locales and all their missing translations |
|
23 |
+ $ php translation-status.php -v |
|
24 |
+ |
|
25 |
+ # show the status of a single locale |
|
26 |
+ $ php translation-status.php fr |
|
27 |
+ |
|
28 |
+ # show the status of a single locale and all its missing translations |
|
29 |
+ $ php translation-status.php fr -v |
|
30 |
+ |
|
31 |
+END; |
|
32 |
+ |
|
33 |
+$config = [ |
|
34 |
+ // if TRUE, the full list of missing translations is displayed |
|
35 |
+ 'verbose_output' => false, |
|
36 |
+ // NULL = analyze all locales |
|
37 |
+ 'locale_to_analyze' => null, |
|
38 |
+ // the reference files all the other translations are compared to |
|
39 |
+ 'original_files' => [ |
|
40 |
+ 'src/Symfony/Component/Form/Resources/translations/validators.en.xlf', |
|
41 |
+ 'src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf', |
|
42 |
+ 'src/Symfony/Component/Validator/Resources/translations/validators.en.xlf', |
|
43 |
+ ], |
|
44 |
+]; |
|
45 |
+ |
|
46 |
+$argc = $_SERVER['argc']; |
|
47 |
+$argv = $_SERVER['argv']; |
|
48 |
+ |
|
49 |
+if ($argc > 3) { |
|
50 |
+ echo str_replace('translation-status.php', $argv[0], $usageInstructions); |
|
51 |
+ exit(1); |
|
52 |
+} |
|
53 |
+ |
|
54 |
+foreach (array_slice($argv, 1) as $argumentOrOption) { |
|
55 |
+ if (0 === strpos($argumentOrOption, '-')) { |
|
56 |
+ $config['verbose_output'] = true; |
|
57 |
+ } else { |
|
58 |
+ $config['locale_to_analyze'] = $argumentOrOption; |
|
59 |
+ } |
|
60 |
+} |
|
61 |
+ |
|
62 |
+foreach ($config['original_files'] as $originalFilePath) { |
|
63 |
+ if (!file_exists($originalFilePath)) { |
|
64 |
+ echo sprintf('The following file does not exist. Make sure that you execute this command at the root dir of the Symfony code repository.%s %s', PHP_EOL, $originalFilePath); |
|
65 |
+ exit(1); |
|
66 |
+ } |
|
67 |
+} |
|
68 |
+ |
|
69 |
+$totalMissingTranslations = 0; |
|
70 |
+ |
|
71 |
+foreach ($config['original_files'] as $originalFilePath) { |
|
72 |
+ $translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']); |
|
73 |
+ $translationStatus = calculateTranslationStatus($originalFilePath, $translationFilePaths); |
|
74 |
+ |
|
75 |
+ $totalMissingTranslations += array_sum(array_map(function ($translation) { |
|
76 |
+ return count($translation['missingKeys']); |
|
77 |
+ }, array_values($translationStatus))); |
|
78 |
+ |
|
79 |
+ printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output']); |
|
80 |
+} |
|
81 |
+ |
|
82 |
+exit($totalMissingTranslations > 0 ? 1 : 0); |
|
83 |
+ |
|
84 |
+function findTranslationFiles($originalFilePath, $localeToAnalyze) |
|
85 |
+{ |
|
86 |
+ $translations = []; |
|
87 |
+ |
|
88 |
+ $translationsDir = dirname($originalFilePath); |
|
89 |
+ $originalFileName = basename($originalFilePath); |
|
90 |
+ $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName); |
|
91 |
+ |
|
92 |
+ $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern); |
|
93 |
+ foreach ($translationFiles as $filePath) { |
|
94 |
+ $locale = extractLocaleFromFilePath($filePath); |
|
95 |
+ |
|
96 |
+ if (null !== $localeToAnalyze && $locale !== $localeToAnalyze) { |
|
97 |
+ continue; |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ $translations[$locale] = $filePath; |
|
101 |
+ } |
|
102 |
+ |
|
103 |
+ return $translations; |
|
104 |
+} |
|
105 |
+ |
|
106 |
+function calculateTranslationStatus($originalFilePath, $translationFilePaths) |
|
107 |
+{ |
|
108 |
+ $translationStatus = []; |
|
109 |
+ $allTranslationKeys = extractTranslationKeys($originalFilePath); |
|
110 |
+ |
|
111 |
+ foreach ($translationFilePaths as $locale => $translationPath) { |
|
112 |
+ $translatedKeys = extractTranslationKeys($translationPath); |
|
113 |
+ $missingKeys = array_diff_key($allTranslationKeys, $translatedKeys); |
|
114 |
+ |
|
115 |
+ $translationStatus[$locale] = [ |
|
116 |
+ 'total' => count($allTranslationKeys), |
|
117 |
+ 'translated' => count($translatedKeys), |
|
118 |
+ 'missingKeys' => $missingKeys, |
|
119 |
+ ]; |
|
120 |
+ } |
|
121 |
+ |
|
122 |
+ return $translationStatus; |
|
123 |
+} |
|
124 |
+ |
|
125 |
+function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput) |
|
126 |
+{ |
|
127 |
+ printTitle($originalFilePath); |
|
128 |
+ printTable($translationStatus, $verboseOutput); |
|
129 |
+ echo PHP_EOL.PHP_EOL; |
|
130 |
+} |
|
131 |
+ |
|
132 |
+function extractLocaleFromFilePath($filePath) |
|
133 |
+{ |
|
134 |
+ $parts = explode('.', $filePath); |
|
135 |
+ |
|
136 |
+ return $parts[count($parts) - 2]; |
|
137 |
+} |
|
138 |
+ |
|
139 |
+function extractTranslationKeys($filePath) |
|
140 |
+{ |
|
141 |
+ $translationKeys = []; |
|
142 |
+ $contents = new \SimpleXMLElement(file_get_contents($filePath)); |
|
143 |
+ |
|
144 |
+ foreach ($contents->file->body->{'trans-unit'} as $translationKey) { |
|
145 |
+ $translationId = (string) $translationKey['id']; |
|
146 |
+ $translationKey = (string) $translationKey->source; |
|
147 |
+ |
|
148 |
+ $translationKeys[$translationId] = $translationKey; |
|
149 |
+ } |
|
150 |
+ |
|
151 |
+ return $translationKeys; |
|
152 |
+} |
|
153 |
+ |
|
154 |
+function printTitle($title) |
|
155 |
+{ |
|
156 |
+ echo $title.PHP_EOL; |
|
157 |
+ echo str_repeat('=', strlen($title)).PHP_EOL.PHP_EOL; |
|
158 |
+} |
|
159 |
+ |
|
160 |
+function printTable($translations, $verboseOutput) |
|
161 |
+{ |
|
162 |
+ if (0 === count($translations)) { |
|
163 |
+ echo 'No translations found'; |
|
164 |
+ |
|
165 |
+ return; |
|
166 |
+ } |
|
167 |
+ $longestLocaleNameLength = max(array_map('strlen', array_keys($translations))); |
|
168 |
+ |
|
169 |
+ foreach ($translations as $locale => $translation) { |
|
170 |
+ $isTranslationCompleted = $translation['translated'] === $translation['total']; |
|
171 |
+ if ($isTranslationCompleted) { |
|
172 |
+ textColorGreen(); |
|
173 |
+ } |
|
174 |
+ |
|
175 |
+ echo sprintf('| Locale: %-'.$longestLocaleNameLength.'s | Translated: %d/%d', $locale, $translation['translated'], $translation['total']).PHP_EOL; |
|
176 |
+ |
|
177 |
+ textColorNormal(); |
|
178 |
+ |
|
179 |
+ if (true === $verboseOutput && count($translation['missingKeys']) > 0) { |
|
180 |
+ echo str_repeat('-', 80).PHP_EOL; |
|
181 |
+ echo '| Missing Translations:'.PHP_EOL; |
|
182 |
+ |
|
183 |
+ foreach ($translation['missingKeys'] as $id => $content) { |
|
184 |
+ echo sprintf('| (id=%s) %s', $id, $content).PHP_EOL; |
|
185 |
+ } |
|
186 |
+ |
|
187 |
+ echo str_repeat('-', 80).PHP_EOL; |
|
188 |
+ } |
|
189 |
+ } |
|
190 |
+} |
|
191 |
+ |
|
192 |
+function textColorGreen() |
|
193 |
+{ |
|
194 |
+ echo "\033[32m"; |
|
195 |
+} |
|
196 |
+ |
|
197 |
+function textColorNormal() |
|
198 |
+{ |
|
199 |
+ echo "\033[0m"; |
|
200 |
+} |