35 * Fills up with the entries from MO file $filename |
35 * Fills up with the entries from MO file $filename |
36 * |
36 * |
37 * @param string $filename MO file to load |
37 * @param string $filename MO file to load |
38 * @return bool True if the import from file was successful, otherwise false. |
38 * @return bool True if the import from file was successful, otherwise false. |
39 */ |
39 */ |
40 function import_from_file( $filename ) { |
40 public function import_from_file( $filename ) { |
41 $reader = new POMO_FileReader( $filename ); |
41 $reader = new POMO_FileReader( $filename ); |
42 |
42 |
43 if ( ! $reader->is_resource() ) { |
43 if ( ! $reader->is_resource() ) { |
44 return false; |
44 return false; |
45 } |
45 } |
94 |
94 |
95 /** |
95 /** |
96 * @param resource $fh |
96 * @param resource $fh |
97 * @return true |
97 * @return true |
98 */ |
98 */ |
99 function export_to_file_handle( $fh ) { |
99 public function export_to_file_handle( $fh ) { |
100 $entries = array_filter( $this->entries, array( $this, 'is_entry_good_for_export' ) ); |
100 $entries = array_filter( $this->entries, array( $this, 'is_entry_good_for_export' ) ); |
101 ksort( $entries ); |
101 ksort( $entries ); |
102 $magic = 0x950412de; |
102 $magic = 0x950412de; |
103 $revision = 0; |
103 $revision = 0; |
104 $total = count( $entries ) + 1; // All the headers are one entry. |
104 $total = count( $entries ) + 1; // All the headers are one entry. |
105 $originals_lenghts_addr = 28; |
105 $originals_lengths_addr = 28; |
106 $translations_lenghts_addr = $originals_lenghts_addr + 8 * $total; |
106 $translations_lengths_addr = $originals_lengths_addr + 8 * $total; |
107 $size_of_hash = 0; |
107 $size_of_hash = 0; |
108 $hash_addr = $translations_lenghts_addr + 8 * $total; |
108 $hash_addr = $translations_lengths_addr + 8 * $total; |
109 $current_addr = $hash_addr; |
109 $current_addr = $hash_addr; |
110 fwrite( |
110 fwrite( |
111 $fh, |
111 $fh, |
112 pack( |
112 pack( |
113 'V*', |
113 'V*', |
114 $magic, |
114 $magic, |
115 $revision, |
115 $revision, |
116 $total, |
116 $total, |
117 $originals_lenghts_addr, |
117 $originals_lengths_addr, |
118 $translations_lenghts_addr, |
118 $translations_lengths_addr, |
119 $size_of_hash, |
119 $size_of_hash, |
120 $hash_addr |
120 $hash_addr |
121 ) |
121 ) |
122 ); |
122 ); |
123 fseek( $fh, $originals_lenghts_addr ); |
123 fseek( $fh, $originals_lengths_addr ); |
124 |
124 |
125 // Headers' msgid is an empty string. |
125 // Headers' msgid is an empty string. |
126 fwrite( $fh, pack( 'VV', 0, $current_addr ) ); |
126 fwrite( $fh, pack( 'VV', 0, $current_addr ) ); |
127 $current_addr++; |
127 $current_addr++; |
128 $originals_table = "\0"; |
128 $originals_table = "\0"; |
155 |
155 |
156 /** |
156 /** |
157 * @param Translation_Entry $entry |
157 * @param Translation_Entry $entry |
158 * @return string |
158 * @return string |
159 */ |
159 */ |
160 function export_original( $entry ) { |
160 public function export_original( $entry ) { |
161 // TODO: Warnings for control characters. |
161 // TODO: Warnings for control characters. |
162 $exported = $entry->singular; |
162 $exported = $entry->singular; |
163 if ( $entry->is_plural ) { |
163 if ( $entry->is_plural ) { |
164 $exported .= "\0" . $entry->plural; |
164 $exported .= "\0" . $entry->plural; |
165 } |
165 } |
171 |
171 |
172 /** |
172 /** |
173 * @param Translation_Entry $entry |
173 * @param Translation_Entry $entry |
174 * @return string |
174 * @return string |
175 */ |
175 */ |
176 function export_translations( $entry ) { |
176 public function export_translations( $entry ) { |
177 // TODO: Warnings for control characters. |
177 // TODO: Warnings for control characters. |
178 return $entry->is_plural ? implode( "\0", $entry->translations ) : $entry->translations[0]; |
178 return $entry->is_plural ? implode( "\0", $entry->translations ) : $entry->translations[0]; |
179 } |
179 } |
180 |
180 |
181 /** |
181 /** |
182 * @return string |
182 * @return string |
183 */ |
183 */ |
184 function export_headers() { |
184 public function export_headers() { |
185 $exported = ''; |
185 $exported = ''; |
186 foreach ( $this->headers as $header => $value ) { |
186 foreach ( $this->headers as $header => $value ) { |
187 $exported .= "$header: $value\n"; |
187 $exported .= "$header: $value\n"; |
188 } |
188 } |
189 return $exported; |
189 return $exported; |
191 |
191 |
192 /** |
192 /** |
193 * @param int $magic |
193 * @param int $magic |
194 * @return string|false |
194 * @return string|false |
195 */ |
195 */ |
196 function get_byteorder( $magic ) { |
196 public function get_byteorder( $magic ) { |
197 // The magic is 0x950412de. |
197 // The magic is 0x950412de. |
198 |
198 |
199 // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 |
199 // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 |
200 $magic_little = (int) - 1794895138; |
200 $magic_little = (int) - 1794895138; |
201 $magic_little_64 = (int) 2500072158; |
201 $magic_little_64 = (int) 2500072158; |
212 |
212 |
213 /** |
213 /** |
214 * @param POMO_FileReader $reader |
214 * @param POMO_FileReader $reader |
215 * @return bool True if the import was successful, otherwise false. |
215 * @return bool True if the import was successful, otherwise false. |
216 */ |
216 */ |
217 function import_from_reader( $reader ) { |
217 public function import_from_reader( $reader ) { |
218 $endian_string = MO::get_byteorder( $reader->readint32() ); |
218 $endian_string = MO::get_byteorder( $reader->readint32() ); |
219 if ( false === $endian_string ) { |
219 if ( false === $endian_string ) { |
220 return false; |
220 return false; |
221 } |
221 } |
222 $reader->setEndian( $endian_string ); |
222 $reader->setEndian( $endian_string ); |
227 if ( $reader->strlen( $header ) != 24 ) { |
227 if ( $reader->strlen( $header ) != 24 ) { |
228 return false; |
228 return false; |
229 } |
229 } |
230 |
230 |
231 // Parse header. |
231 // Parse header. |
232 $header = unpack( "{$endian}revision/{$endian}total/{$endian}originals_lenghts_addr/{$endian}translations_lenghts_addr/{$endian}hash_length/{$endian}hash_addr", $header ); |
232 $header = unpack( "{$endian}revision/{$endian}total/{$endian}originals_lengths_addr/{$endian}translations_lengths_addr/{$endian}hash_length/{$endian}hash_addr", $header ); |
233 if ( ! is_array( $header ) ) { |
233 if ( ! is_array( $header ) ) { |
234 return false; |
234 return false; |
235 } |
235 } |
236 |
236 |
237 // Support revision 0 of MO format specs, only. |
237 // Support revision 0 of MO format specs, only. |
238 if ( 0 != $header['revision'] ) { |
238 if ( 0 != $header['revision'] ) { |
239 return false; |
239 return false; |
240 } |
240 } |
241 |
241 |
242 // Seek to data blocks. |
242 // Seek to data blocks. |
243 $reader->seekto( $header['originals_lenghts_addr'] ); |
243 $reader->seekto( $header['originals_lengths_addr'] ); |
244 |
244 |
245 // Read originals' indices. |
245 // Read originals' indices. |
246 $originals_lengths_length = $header['translations_lenghts_addr'] - $header['originals_lenghts_addr']; |
246 $originals_lengths_length = $header['translations_lengths_addr'] - $header['originals_lengths_addr']; |
247 if ( $originals_lengths_length != $header['total'] * 8 ) { |
247 if ( $originals_lengths_length != $header['total'] * 8 ) { |
248 return false; |
248 return false; |
249 } |
249 } |
250 |
250 |
251 $originals = $reader->read( $originals_lengths_length ); |
251 $originals = $reader->read( $originals_lengths_length ); |
252 if ( $reader->strlen( $originals ) != $originals_lengths_length ) { |
252 if ( $reader->strlen( $originals ) != $originals_lengths_length ) { |
253 return false; |
253 return false; |
254 } |
254 } |
255 |
255 |
256 // Read translations' indices. |
256 // Read translations' indices. |
257 $translations_lenghts_length = $header['hash_addr'] - $header['translations_lenghts_addr']; |
257 $translations_lengths_length = $header['hash_addr'] - $header['translations_lengths_addr']; |
258 if ( $translations_lenghts_length != $header['total'] * 8 ) { |
258 if ( $translations_lengths_length != $header['total'] * 8 ) { |
259 return false; |
259 return false; |
260 } |
260 } |
261 |
261 |
262 $translations = $reader->read( $translations_lenghts_length ); |
262 $translations = $reader->read( $translations_lengths_length ); |
263 if ( $reader->strlen( $translations ) != $translations_lenghts_length ) { |
263 if ( $reader->strlen( $translations ) != $translations_lengths_length ) { |
264 return false; |
264 return false; |
265 } |
265 } |
266 |
266 |
267 // Transform raw data into set of indices. |
267 // Transform raw data into set of indices. |
268 $originals = $reader->str_split( $originals, 8 ); |
268 $originals = $reader->str_split( $originals, 8 ); |
309 * 0x04 as context separator or 0x00 as singular/plural separator |
309 * 0x04 as context separator or 0x00 as singular/plural separator |
310 * @param string $translation translation string from MO file. Might contain |
310 * @param string $translation translation string from MO file. Might contain |
311 * 0x00 as a plural translations separator |
311 * 0x00 as a plural translations separator |
312 * @return Translation_Entry Entry instance. |
312 * @return Translation_Entry Entry instance. |
313 */ |
313 */ |
314 function &make_entry( $original, $translation ) { |
314 public function &make_entry( $original, $translation ) { |
315 $entry = new Translation_Entry(); |
315 $entry = new Translation_Entry(); |
316 // Look for context, separated by \4. |
316 // Look for context, separated by \4. |
317 $parts = explode( "\4", $original ); |
317 $parts = explode( "\4", $original ); |
318 if ( isset( $parts[1] ) ) { |
318 if ( isset( $parts[1] ) ) { |
319 $original = $parts[1]; |
319 $original = $parts[1]; |