Elgg  Version master
ReleaseCleaner.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\I18n;
4 
6 use Elgg\Includer;
8 
15 
19  private $codes;
20 
24  public $log = [];
25 
31  public function __construct(array $codes = []) {
32  if (empty($codes)) {
33  $codes = _elgg_services()->locale->getLanguageCodes();
34  }
35 
36  $this->codes = $codes;
37  }
38 
46  public function cleanInstallation(string $dir): void {
47  $dir = Paths::sanitize($dir, false);
48 
49  if (is_dir("{$dir}/install/languages")) {
50  $this->cleanLanguagesDir("{$dir}/install/languages");
51  }
52 
53  if (is_dir("{$dir}/languages")) {
54  $this->cleanLanguagesDir("{$dir}/languages");
55  }
56 
57  $mods = new \DirectoryIterator("{$dir}/mod");
58 
59  foreach ($mods as $mod) {
60  if ($mod->isDot() || !$mod->isDir()) {
61  continue;
62  }
63 
64  if (!in_array($mod->getFilename(), Plugins::BUNDLED_PLUGINS)) {
65  // not a core plugin
66  continue;
67  }
68 
69  if (is_dir("{$mod->getPathname()}/languages")) {
70  // only process plugins which have translations
71  $this->cleanLanguagesDir("{$mod->getPathname()}/languages");
72  }
73  }
74  }
75 
83  public function cleanLanguagesDir(string $dir): void {
84  $dir = Paths::sanitize($dir, false);
85 
86  $files = new \DirectoryIterator($dir);
87  foreach ($files as $file) {
88  if ($file->isDot() || !$file->isFile()) {
89  continue;
90  }
91 
92  if ($file->getExtension() !== 'php') {
93  continue;
94  }
95 
96  $code = $file->getBasename('.php');
97  if (!in_array($code, $this->codes)) {
99 
100  if (in_array($code, $this->codes)) {
101  // rename file to lowercase
102  rename($file->getPathname(), "{$dir}/{$code}.php");
103  $this->log[] = "Renamed {$file->getPathname()} to {$code}.php";
104  } else {
105  unlink($file->getPathname());
106  $this->log[] = "Removed {$file->getPathname()}";
107  }
108  }
109 
110  if ($code !== 'en' && file_exists("{$dir}/{$code}.php")) {
111 // $this->detectAdditionalKeys($dir, $code);
112 // $this->detectIdenticalTranslations($dir, $code);
114  $this->cleanupEmptyTranslations("{$dir}/{$code}.php");
115  }
116  }
117  }
118 
127  protected function cleanupMissingTranslationParameters(string $directory, string $language_code): void {
128  $english = Includer::includeFile("{$directory}/en.php");
129  $translation = Includer::includeFile("{$directory}/{$language_code}.php");
130 
131  foreach ($english as $key => $value) {
132  $english_matches = preg_match_all('/%[a-zA-Z]/m', $value);
133  if (!array_key_exists($key, $translation) || $english_matches === false) {
134  continue;
135  }
136 
137  $translation_matches = preg_match_all('/%[a-zA-Z]/m', $translation[$key]);
138  if ($translation_matches !== false && $english_matches === $translation_matches) {
139  continue;
140  }
141 
142  $file_contents = file_get_contents("{$directory}/{$language_code}.php");
143 
144  $pattern = '/^\s*[\'"]' . $key . '[\'"] => [\'"]' . preg_quote($translation[$key], '/') . '[\'"],{0,1}\R/m';
145  $count = 0;
146  $file_contents = preg_replace($pattern, '', $file_contents, -1, $count);
147  if ($count < 1) {
148  // try to add slashes for quotes
149  $pattern = '/^\s*[\'"]' . $key . '[\'"] => [\'"]' . preg_quote(addslashes($translation[$key]), '/') . '[\'"],{0,1}\R/m';
150  $count = 0;
151  $file_contents = preg_replace($pattern, '', $file_contents, -1, $count);
152  }
153 
154  if ($count > 0) {
155  file_put_contents("{$directory}/{$language_code}.php", $file_contents);
156  } else {
157  $this->log[] = "Unable to repair mismatch in translation argument count in {$directory}/{$language_code}.php for the key '{$key}'";
158  }
159  }
160  }
161 
169  protected function cleanupEmptyTranslations(string $translation_file): void {
170  $contents = file_get_contents($translation_file);
171  if (empty($contents)) {
172  return;
173  }
174 
175  $pattern = '/^\s*[\'"].*[\'"] => [\'"]{2},{0,1}\R/m';
176  $count = 0;
177  $contents = preg_replace($pattern, '', $contents, -1, $count);
178 
179  if ($count > 0) {
180  // something was changed
181  file_put_contents($translation_file, $contents);
182 
183  $translations = Includer::includeFile($translation_file);
184  if (!empty($translations)) {
185  $this->log[] = "Cleaned empty translations from {$translation_file}";
186  } else {
187  unlink($translation_file);
188 
189  $this->log[] = "Removed empty translation file {$translation_file}";
190  }
191  }
192  }
193 
201  protected function normalizeLanguageCode(string $code): string {
202  $code = strtolower($code);
203  return preg_replace('~[^a-z0-9]~', '_', $code);
204  }
205 
214  protected function detectAdditionalKeys(string $directory, string $language_code): void {
215  $english = Includer::includeFile("{$directory}/en.php");
216  $translation = Includer::includeFile("{$directory}/{$language_code}.php");
217 
218  foreach ($translation as $key => $value) {
219  if (array_key_exists($key, $english)) {
220  continue;
221  }
222 
223  $this->log[] = "The translation key '{$key}' exists in the '{$language_code}' translation but not in English";
224  }
225  }
226 
235  protected function detectIdenticalTranslations(string $directory, string $language_code): void {
236  $english = Includer::includeFile("{$directory}/en.php");
237  $translation = Includer::includeFile("{$directory}/{$language_code}.php");
238 
239  foreach ($translation as $key => $value) {
240  if (!array_key_exists($key, $english)) {
241  // shouldn't happen
242  continue;
243  }
244 
245  if (strlen($key) < 3) {
246  // probably a language code
247  continue;
248  }
249 
250  if ($english[$key] !== $value) {
251  continue;
252  }
253 
254  $this->log[] = "The translation key '{$key}' in the '{$language_code}' translation is identical to the English translation";
255  }
256  }
257 }
static includeFile($file)
Include a file with as little context as possible.
Definition: Includer.php:18
Removes invalid language files from an installation.
foreach(array_keys($combine_languages) as $language) $translations
detectAdditionalKeys(string $directory, string $language_code)
Detect translation keys that (still) exist in a translation but no longer in the English translation...
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition plus the scripts used to control compilation and installation of the executable as a special the source code distributed need not include anything that is normally and so on of the operating system on which the executable unless that component itself accompanies the executable If distribution of executable or object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place counts as distribution of the source even though third parties are not compelled to copy the source along with the object code You may not or distribute the Program except as expressly provided under this License Any attempt otherwise to sublicense or distribute the Program is void
Definition: LICENSE.txt:215
cleanupMissingTranslationParameters(string $directory, string $language_code)
Try to cleanup translations with a different argument count than English as this can cause failed tra...
cleanupEmptyTranslations(string $translation_file)
Remove empty translations from a translation file.
cleanLanguagesDir(string $dir)
Clean up a languages dir.
$value
Definition: generic.php:51
normalizeLanguageCode(string $code)
Normalize a language code (e.g.
__construct(array $codes=[])
Constructor.
$count
Definition: ban.php:24
if($container instanceof ElggGroup &&$container->guid!=elgg_get_page_owner_guid()) $key
Definition: summary.php:44
log($level, $message, array $context=[])
Log a message.
Definition: Loggable.php:58
cleanInstallation(string $dir)
Clean up within an installation.
detectIdenticalTranslations(string $directory, string $language_code)
Detect identical translations, this could be due to a wrong Transifex import.
static sanitize($path, $append_slash=true)
Sanitize file paths ensuring that they begin and end with slashes etc.
Definition: Paths.php:76
_elgg_services()
Get the global service provider.
Definition: elgglib.php:351
if(!$plugin instanceof\ElggPlugin) $file_contents
if(!empty($title)&&!empty($icon_name)) if(!empty($title)) if(!empty($menu)) if(!empty($header)) if(!empty($body)) $contents
Definition: message.php:73