MediaWiki REL1_34
Updater.php
Go to the documentation of this file.
1<?php
8namespace LocalisationUpdate;
9
13class Updater {
21 public function isDirectory( $path ) {
22 $filename = basename( $path );
23 return strpos( $filename, '*' ) !== false;
24 }
25
35 public function expandRemotePath( $info, $repos ) {
36 $pattern = $repos[$info['repo']];
37 unset( $info['repo'], $info['orig'] );
38
39 // This assumes all other keys are used as variables
40 // in the pattern. For example name -> %NAME%.
41 $keys = [];
42 foreach ( array_keys( $info ) as $key ) {
43 $keys[] = '%' . strtoupper( $key ) . '%';
44 }
45
46 $values = array_values( $info );
47 return str_replace( $keys, $values, $pattern );
48 }
49
57 public function readMessages( ReaderFactory $readerFactory, array $files ) {
58 $messages = [];
59
60 foreach ( $files as $filename => $contents ) {
61 $reader = $readerFactory->getReader( $filename );
62 try {
63 $parsed = $reader->parse( $contents );
64 } catch ( \Exception $e ) {
65 trigger_error( __METHOD__ . ": Unable to parse messages from $filename", E_USER_WARNING );
66 continue;
67 }
68
69 foreach ( $parsed as $code => $langMessages ) {
70 if ( !isset( $messages[$code] ) ) {
71 $messages[$code] = [];
72 }
73 $messages[$code] = array_merge( $messages[$code], $langMessages );
74 }
75
76 $c = array_sum( array_map( 'count', $parsed ) );
77 // Useful for debugging, maybe create interface to pass this to the script?
78 # echo "$filename with " . get_class( $reader ) . " and $c\n";
79 }
80
81 return $messages;
82 }
83
92 public function findChangedTranslations( $origin, $remote, $blacklist = [] ) {
93 $changed = [];
94 foreach ( $remote as $key => $value ) {
95 if ( isset( $blacklist[$key] ) ) {
96 continue;
97 }
98
99 if ( !isset( $origin[$key] ) || $value !== $origin[$key] ) {
100 $changed[$key] = $value;
101 }
102 }
103 return $changed;
104 }
105
113 public function fetchFiles( FetcherFactory $factory, $path ) {
114 $fetcher = $factory->getFetcher( $path );
115
116 if ( $this->isDirectory( $path ) ) {
117 $files = $fetcher->fetchDirectory( $path );
118 } else {
119 $files = [ $path => $fetcher->fetchFile( $path ) ];
120 }
121
122 // Remove files which were not found
123 return array_filter( $files );
124 }
125
126 public function execute(
127 Finder $finder,
128 ReaderFactory $readerFactory,
129 FetcherFactory $fetcherFactory,
130 array $repos,
131 $logger
132 ) {
133 $components = $finder->getComponents();
134
135 $updatedMessages = [];
136
137 foreach ( $components as $key => $info ) {
138 $logger->logInfo( "Updating component $key" );
139
140 $originFiles = $this->fetchFiles( $fetcherFactory, $info['orig'] );
141 $remotePath = $this->expandRemotePath( $info, $repos );
142 try {
143 $remoteFiles = $this->fetchFiles( $fetcherFactory, $remotePath );
144 } catch ( \Exception $e ) {
145 $logger->logError( __METHOD__ . ": Unable to fetch messages from $remotePath" );
146 continue;
147 }
148
149 if ( $remoteFiles === [] ) {
150 // Small optimization: if nothing to compare with, skip
151 continue;
152 }
153
154 $originMessages = $this->readMessages( $readerFactory, $originFiles );
155 $remoteMessages = $this->readMessages( $readerFactory, $remoteFiles );
156
157 if ( !isset( $remoteMessages['en'] ) ) {
158 // Could not find remote messages
159 continue;
160 }
161
162 // If remote translation in English is not present or differs, we do not want
163 // translations for other languages for those messages, as they are either not
164 // used in this version of code or can be incompatible.
165 $forbiddenKeys = $this->findChangedTranslations(
166 $originMessages['en'],
167 $remoteMessages['en']
168 );
169
170 // We never accept updates for English strings
171 unset( $originMessages['en'], $remoteMessages['en'] );
172
173 // message: string in all languages; translation: string in one language.
174 foreach ( $remoteMessages as $language => $remoteTranslations ) {
175 // Check for completely new languages
176 $originTranslations = [];
177 if ( isset( $originMessages[$language] ) ) {
178 $originTranslations = $originMessages[$language];
179 }
180
181 $updatedTranslations = $this->findChangedTranslations(
182 $originTranslations,
183 $remoteTranslations,
184 $forbiddenKeys
185 );
186
187 // Avoid empty arrays
188 if ( $updatedTranslations === [] ) {
189 continue;
190 }
191
192 if ( !isset( $updatedMessages[$language] ) ) {
193 $updatedMessages[$language] = [];
194 }
195
196 // In case of conflicts, which should not exist, this prefers the
197 // first translation seen.
198 $updatedMessages[$language] += $updatedTranslations;
199 }
200 }
201
202 return $updatedMessages;
203 }
204}
Constructs fetchers based on the repository urls.
Interface for classes which provide list of components, which should be included for l10n updates.
Definition Finder.php:14
Constructs readers for files based on the names.
getReader( $filename)
Constructs a suitable reader for a given path.
Executes the localisation update.
Definition Updater.php:13
fetchFiles(FetcherFactory $factory, $path)
Fetches files from given Url pattern.
Definition Updater.php:113
execute(Finder $finder, ReaderFactory $readerFactory, FetcherFactory $fetcherFactory, array $repos, $logger)
Definition Updater.php:126
expandRemotePath( $info, $repos)
Expands repository relative path to full url with the given repository patterns.
Definition Updater.php:35
findChangedTranslations( $origin, $remote, $blacklist=[])
Find new and changed translations in $remote and returns them.
Definition Updater.php:92
readMessages(ReaderFactory $readerFactory, array $files)
Parses translations from given list of files.
Definition Updater.php:57
isDirectory( $path)
Whether the path is a pattern and thus we need to use appropriate code for fetching directories.
Definition Updater.php:21