Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
JavaFFS.php
1<?php
2
6
16class JavaFFS extends SimpleFFS implements MetaYamlSchemaExtender {
17 public function supportsFuzzy() {
18 return 'write';
19 }
20
21 public function getFileExtensions() {
22 return [ '.properties' ];
23 }
24
25 protected $keySeparator = '=';
26
28 public function __construct( FileBasedMessageGroup $group ) {
29 parent::__construct( $group );
30
31 if ( isset( $this->extra['keySeparator'] ) ) {
32 $this->keySeparator = $this->extra['keySeparator'];
33 }
34 }
35
41 public function readFromVariable( $data ) {
42 $data = TextContent::normalizeLineEndings( $data );
43 $lines = array_map( 'ltrim', explode( "\n", $data ) );
44 $authors = $messages = [];
45 $linecontinuation = false;
46
47 $key = '';
48 $value = '';
49 foreach ( $lines as $line ) {
50 if ( $linecontinuation ) {
51 $linecontinuation = false;
52 $valuecont = $line;
53 $valuecont = str_replace( '\n', "\n", $valuecont );
54 $value .= $valuecont;
55 } else {
56 if ( $line === '' ) {
57 continue;
58 }
59
60 if ( $line[0] === '#' || $line[0] === '!' ) {
61 $match = [];
62 $ok = preg_match( '/#\s*Author:\s*(.*)/', $line, $match );
63
64 if ( $ok ) {
65 $authors[] = $match[1];
66 }
67
68 continue;
69 }
70
71 if ( strpos( $line, $this->keySeparator ) === false ) {
72 throw new MWException( "Line without separator '{$this->keySeparator}': $line." );
73 }
74
75 list( $key, $value ) = self::readRow( $line, $this->keySeparator );
76 if ( $key === '' ) {
77 throw new MWException( "Empty key in line $line." );
78 }
79 }
80
81 // @todo This doesn't handle the pathological case of even number of trailing \
82 if ( strlen( $value ) && $value[strlen( $value ) - 1] === "\\" ) {
83 $value = substr( $value, 0, strlen( $value ) - 1 );
84 $linecontinuation = true;
85 } else {
86 $messages[$key] = ltrim( $value );
87 }
88 }
89
90 $messages = $this->group->getMangler()->mangleArray( $messages );
91
92 return [
93 'AUTHORS' => $authors,
94 'MESSAGES' => $messages,
95 ];
96 }
97
102 protected function writeReal( MessageCollection $collection ) {
103 $header = $this->doHeader( $collection );
104 $header .= $this->doAuthors( $collection );
105 $header .= "\n";
106
107 $output = '';
108 $mangler = $this->group->getMangler();
109
111 foreach ( $collection as $key => $m ) {
112 $value = $m->translation();
113 $value = str_replace( TRANSLATE_FUZZY, '', $value );
114
115 if ( $value === '' ) {
116 continue;
117 }
118
119 // Just to give an overview of translation quality.
120 if ( $m->hasTag( 'fuzzy' ) ) {
121 $output .= "# Fuzzy\n";
122 }
123
124 $key = $mangler->unmangle( $key );
125 $output .= self::writeRow( $key, $this->keySeparator, $value );
126 }
127
128 if ( $output ) {
129 return $header . $output;
130 }
131
132 return '';
133 }
134
143 public static function writeRow( $key, $sep, $value ) {
144 /* Keys containing the separator need escaping. Also escape comment
145 * characters, though strictly they would only need escaping when
146 * they are the first character. Plus the escape character itself. */
147 $key = addcslashes( $key, "#!$sep\\" );
148 // Make sure we do not slip newlines trough... it would be fatal.
149 $value = str_replace( "\n", '\\n', $value );
150
151 return "$key$sep$value\n";
152 }
153
161 public static function readRow( $line, $sep ) {
162 if ( strpos( $line, '\\' ) === false ) {
163 /* Nothing appears to be escaped in this line.
164 * Just read the key and the value. */
165 list( $key, $value ) = explode( $sep, $line, 2 );
166 } else {
167 /* There might be escaped separators in the key.
168 * Using slower method to find the separator. */
169
170 /* Make the key default to empty instead of value, because
171 * empty key causes error on callers, while empty value
172 * wouldn't. */
173 $key = '';
174 $value = $line;
175
176 /* Find the first unescaped separator. Example:
177 * First line is the string being read, second line is the
178 * value of $escaped after having read the above character.
179 *
180 * ki\ts\\s\=a = koira
181 * 0010010010000
182 * ^ Not separator because $escaped was true
183 * ^ Split the string into key and value here
184 */
185
186 $len = strlen( $line );
187 $escaped = false;
188 for ( $i = 0; $i < $len; $i++ ) {
189 $char = $line[$i];
190 if ( $char === '\\' ) {
191 $escaped = !$escaped;
192 } elseif ( $escaped ) {
193 $escaped = false;
194 } elseif ( $char === $sep ) {
195 $key = substr( $line, 0, $i );
196 // Excluding the separator character from the value
197 $value = substr( $line, $i + 1 );
198 break;
199 }
200 }
201 }
202
203 /* We usually don't want to expand things like \t in values since
204 * translators cannot easily input those. But in keys we do.
205 * \n is exception we do handle in values. */
206 $key = trim( $key );
207 $key = stripcslashes( $key );
208 $value = ltrim( $value );
209 $value = str_replace( '\n', "\n", $value );
210
211 return [ $key, $value ];
212 }
213
218 protected function doHeader( MessageCollection $collection ) {
219 if ( isset( $this->extra['header'] ) ) {
220 $output = $this->extra['header'];
221 } else {
222 global $wgSitename;
223
224 $code = $collection->code;
225 $name = Utilities::getLanguageName( $code );
226 $native = Utilities::getLanguageName( $code, $code );
227 $output = "# Messages for $name ($native)\n";
228 $output .= "# Exported from $wgSitename\n";
229 }
230
231 return $output;
232 }
233
238 protected function doAuthors( MessageCollection $collection ) {
239 $output = '';
240 $authors = $collection->getAuthors();
241 $authors = $this->filterAuthors( $authors, $collection->code );
242
243 foreach ( $authors as $author ) {
244 $output .= "# Author: $author\n";
245 }
246
247 return $output;
248 }
249
250 public static function getExtraSchema(): array {
251 $schema = [
252 'root' => [
253 '_type' => 'array',
254 '_children' => [
255 'FILES' => [
256 '_type' => 'array',
257 '_children' => [
258 'header' => [
259 '_type' => 'text',
260 ],
261 'keySeparator' => [
262 '_type' => 'text',
263 ],
264 ]
265 ]
266 ]
267 ]
268 ];
269
270 return $schema;
271 }
272}
This class implements default behavior for file based message groups.
JavaFFS class implements support for Java properties files.
Definition JavaFFS.php:16
__construct(FileBasedMessageGroup $group)
Definition JavaFFS.php:28
writeReal(MessageCollection $collection)
Definition JavaFFS.php:102
supportsFuzzy()
Query the capabilities of this FFS.
Definition JavaFFS.php:17
static writeRow( $key, $sep, $value)
Writes well-formed properties file row with key and value.
Definition JavaFFS.php:143
doAuthors(MessageCollection $collection)
Definition JavaFFS.php:238
static readRow( $line, $sep)
Parses non-empty properties file row to key and value.
Definition JavaFFS.php:161
getFileExtensions()
Return the commonly used file extensions for these formats.
Definition JavaFFS.php:21
static getExtraSchema()
Return a data structure that will be merged with the base schema.
Definition JavaFFS.php:250
readFromVariable( $data)
Definition JavaFFS.php:41
doHeader(MessageCollection $collection)
Definition JavaFFS.php:218
This file contains the class for core message collections implementation.
getAuthors()
Lists all translators that have contributed to the latest revisions of each translation.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:30
Message groups are usually configured in YAML, though the actual storage format does not matter,...