[ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 3 /** 4 * sfMessageSource_XLIFF class file. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the BSD License. 8 * 9 * Copyright(c) 2004 by Qiang Xue. All rights reserved. 10 * 11 * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue} 12 * The latest version of PRADO can be obtained from: 13 * {@link http://prado.sourceforge.net/} 14 * 15 * @author Wei Zhuo <weizhuo[at]gmail[dot]com> 16 * @version $Id: sfMessageSource_XLIFF.class.php 2834 2006-11-27 14:09:05Z fabien $ 17 * @package symfony 18 * @subpackage i18n 19 */ 20 21 /** 22 * sfMessageSource_XLIFF class. 23 * 24 * Using XML XLIFF format as the message source for translation. 25 * Details and example of XLIFF can be found in the following URLs. 26 * 27 * # http://www.opentag.com/xliff.htm 28 * # http://www-106.ibm.com/developerworks/xml/library/x-localis2/ 29 * 30 * See the MessageSource::factory() method to instantiate this class. 31 * 32 * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com> 33 * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004 34 * @package System.I18N.core 35 */ 36 class sfMessageSource_XLIFF extends sfMessageSource 37 { 38 /** 39 * Message data filename extension. 40 * @var string 41 */ 42 protected $dataExt = '.xml'; 43 44 /** 45 * Separator between culture name and source. 46 * @var string 47 */ 48 protected $dataSeparator = '.'; 49 50 /** 51 * Constructor. 52 * 53 * @param string the directory where the messages are stored. 54 * @see MessageSource::factory(); 55 */ 56 function __construct($source) 57 { 58 $this->source = (string)$source; 59 } 60 61 /** 62 * Load the messages from a XLIFF file. 63 * 64 * @param string XLIFF file. 65 * @return array of messages. 66 */ 67 protected function &loadData($filename) 68 { 69 //load it. 70 71 $XML = simplexml_load_file($filename); 72 73 if (!$XML) 74 { 75 return false; 76 } 77 78 $translationUnit = $XML->xpath('//trans-unit'); 79 80 $translations = array(); 81 82 foreach ($translationUnit as $unit) 83 { 84 $source = (string)$unit->source; 85 $translations[$source][] = (string)$unit->target; 86 $translations[$source][]= (string)$unit['id']; 87 $translations[$source][]= (string)$unit->note; 88 } 89 90 return $translations; 91 } 92 93 /** 94 * Get the last modified unix-time for this particular catalogue+variant. 95 * Just use the file modified time. 96 * 97 * @param string catalogue+variant 98 * @return int last modified in unix-time format. 99 */ 100 protected function getLastModified($source) 101 { 102 if (is_file($source)) 103 { 104 return filemtime($source); 105 } 106 else 107 { 108 return 0; 109 } 110 } 111 112 /** 113 * Get the XLIFF file for a specific message catalogue and cultural variant. 114 * 115 * @param string message catalogue 116 * @return string full path to the XLIFF file. 117 */ 118 protected function getSource($variant) 119 { 120 return $this->source.'/'.$variant; 121 } 122 123 /** 124 * Determin if the XLIFF file source is valid. 125 * 126 * @param string XLIFF file 127 * @return boolean true if valid, false otherwise. 128 */ 129 protected function isValidSource($source) 130 { 131 return is_file($source); 132 } 133 134 /** 135 * Get all the variants of a particular catalogue. 136 * 137 * @param string catalogue name 138 * @return array list of all variants for this catalogue. 139 */ 140 protected function getCatalogueList($catalogue) 141 { 142 $variants = explode('_', $this->culture); 143 $source = $catalogue.$this->dataExt; 144 145 $catalogues = array($source); 146 147 $variant = null; 148 149 for ($i = 0, $max = count($variants); $i < $max; $i++) 150 { 151 if (strlen($variants[$i]) > 0) 152 { 153 $variant .= ($variant) ? '_'.$variants[$i] : $variants[$i]; 154 $catalogues[] = $catalogue.$this->dataSeparator.$variant.$this->dataExt; 155 } 156 } 157 158 $byDir = $this->getCatalogueByDir($catalogue); 159 $catalogues = array_merge($byDir, array_reverse($catalogues)); 160 161 return $catalogues; 162 } 163 164 /** 165 * Traverse through the directory structure to find the catalogues. 166 * This should only be called by getCatalogueList() 167 * 168 * @param string a particular catalogue. 169 * @return array a list of catalogues. 170 * @see getCatalogueList() 171 */ 172 protected function getCatalogueByDir($catalogue) 173 { 174 $variants = explode('_', $this->culture); 175 $catalogues = array(); 176 177 $variant = null; 178 179 for ($i = 0, $max = count($variants); $i < $max; $i++) 180 { 181 if (strlen($variants[$i]) > 0) 182 { 183 $variant .= ($variant) ? '_'.$variants[$i] : $variants[$i]; 184 $catalogues[] = $variant.'/'.$catalogue.$this->dataExt; 185 } 186 } 187 188 return array_reverse($catalogues); 189 } 190 191 /** 192 * Returns a list of catalogue and its culture ID. 193 * E.g. array('messages','en_AU') 194 * 195 * @return array list of catalogues 196 * @see getCatalogues() 197 */ 198 public function catalogues() 199 { 200 return $this->getCatalogues(); 201 } 202 203 /** 204 * Returns a list of catalogue and its culture ID. This takes care 205 * of directory structures. 206 * E.g. array('messages','en_AU') 207 * 208 * @return array list of catalogues 209 */ 210 protected function getCatalogues($dir = null, $variant = null) 211 { 212 $dir = $dir ? $dir : $this->source; 213 $files = scandir($dir); 214 215 $catalogue = array(); 216 217 foreach ($files as $file) 218 { 219 if (is_dir($dir.'/'.$file) && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/', $file)) 220 { 221 $catalogue = array_merge($catalogue, $this->getCatalogues($dir.'/'.$file, $file)); 222 } 223 224 $pos = strpos($file,$this->dataExt); 225 if ($pos > 0 && substr($file, -1 * strlen($this->dataExt)) == $this->dataExt) 226 { 227 $name = substr($file, 0, $pos); 228 $dot = strrpos($name, $this->dataSeparator); 229 $culture = $variant; 230 $cat = $name; 231 if (is_int($dot)) 232 { 233 $culture = substr($name, $dot + 1,strlen($name)); 234 $cat = substr($name, 0, $dot); 235 } 236 $details[0] = $cat; 237 $details[1] = $culture; 238 239 $catalogue[] = $details; 240 } 241 } 242 sort($catalogue); 243 244 return $catalogue; 245 } 246 247 /** 248 * Get the variant for a catalogue depending on the current culture. 249 * 250 * @param string catalogue 251 * @return string the variant. 252 * @see save() 253 * @see update() 254 * @see delete() 255 */ 256 protected function getVariants($catalogue = 'messages') 257 { 258 if (is_null($catalogue)) 259 { 260 $catalogue = 'messages'; 261 } 262 263 foreach ($this->getCatalogueList($catalogue) as $variant) 264 { 265 $file = $this->getSource($variant); 266 if (is_file($file)) 267 { 268 return array($variant, $file); 269 } 270 } 271 272 return false; 273 } 274 275 /** 276 * Save the list of untranslated blocks to the translation source. 277 * If the translation was not found, you should add those 278 * strings to the translation source via the <b>append()</b> method. 279 * 280 * @param string the catalogue to add to 281 * @return boolean true if saved successfuly, false otherwise. 282 */ 283 public function save($catalogue = 'messages') 284 { 285 $messages = $this->untranslated; 286 if (count($messages) <= 0) 287 { 288 return false; 289 } 290 291 $variants = $this->getVariants($catalogue); 292 if ($variants) 293 { 294 list($variant, $filename) = $variants; 295 } 296 else 297 { 298 return false; 299 } 300 301 if (is_writable($filename) == false) 302 { 303 throw new sfException("Unable to save to file {$filename}, file must be writable."); 304 } 305 306 // create a new dom, import the existing xml 307 $dom = DOMDocument::load($filename); 308 309 // find the body element 310 $xpath = new DomXPath($dom); 311 $body = $xpath->query('//body')->item(0); 312 313 $count = $xpath->query('//trans-unit')->length; 314 315 // for each message add it to the XML file using DOM 316 foreach ($messages as $message) 317 { 318 $unit = $dom->createElement('trans-unit'); 319 $unit->setAttribute('id', ++$count); 320 321 $source = $dom->createElement('source', $message); 322 $target = $dom->createElement('target', ''); 323 324 $unit->appendChild($dom->createTextNode("\n")); 325 $unit->appendChild($source); 326 $unit->appendChild($dom->createTextNode("\n")); 327 $unit->appendChild($target); 328 $unit->appendChild($dom->createTextNode("\n")); 329 330 $body->appendChild($dom->createTextNode("\n")); 331 $body->appendChild($unit); 332 $body->appendChild($dom->createTextNode("\n")); 333 } 334 335 $fileNode = $xpath->query('//file')->item(0); 336 $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z')); 337 338 // save it and clear the cache for this variant 339 $dom->save($filename); 340 if (!empty($this->cache)) 341 { 342 $this->cache->clean($variant, $this->culture); 343 } 344 345 return true; 346 } 347 348 /** 349 * Update the translation. 350 * 351 * @param string the source string. 352 * @param string the new translation string. 353 * @param string comments 354 * @param string the catalogue to save to. 355 * @return boolean true if translation was updated, false otherwise. 356 */ 357 public function update($text, $target, $comments, $catalogue = 'messages') 358 { 359 $variants = $this->getVariants($catalogue); 360 if ($variants) 361 { 362 list($variant, $filename) = $variants; 363 } 364 else 365 { 366 return false; 367 } 368 369 if (is_writable($filename) == false) 370 { 371 throw new sfException("Unable to update file {$filename}, file must be writable."); 372 } 373 374 // create a new dom, import the existing xml 375 $dom = DOMDocument::load($filename); 376 377 // find the body element 378 $xpath = new DomXPath($dom); 379 $units = $xpath->query('//trans-unit'); 380 381 // for each of the existin units 382 foreach ($units as $unit) 383 { 384 $found = false; 385 $targetted = false; 386 $commented = false; 387 388 //in each unit, need to find the source, target and comment nodes 389 //it will assume that the source is before the target. 390 foreach ($unit->childNodes as $node) 391 { 392 // source node 393 if ($node->nodeName == 'source' && $node->firstChild->wholeText == $text) 394 { 395 $found = true; 396 } 397 398 // found source, get the target and notes 399 if ($found) 400 { 401 // set the new translated string 402 if ($node->nodeName == 'target') 403 { 404 $node->nodeValue = $target; 405 $targetted = true; 406 } 407 408 // set the notes 409 if (!empty($comments) && $node->nodeName == 'note') 410 { 411 $node->nodeValue = $comments; 412 $commented = true; 413 } 414 } 415 } 416 417 // append a target 418 if ($found && !$targetted) 419 { 420 $unit->appendChild($dom->createElement('target',$target)); 421 } 422 423 // append a note 424 if ($found && !$commented && !empty($comments)) 425 { 426 $unit->appendChild($dom->createElement('note',$comments)); 427 } 428 429 // finished searching 430 if ($found) 431 { 432 break; 433 } 434 } 435 436 $fileNode = $xpath->query('//file')->item(0); 437 $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z')); 438 439 if ($dom->save($filename) > 0) 440 { 441 if (!empty($this->cache)) 442 { 443 $this->cache->clean($variant, $this->culture); 444 } 445 446 return true; 447 } 448 449 return false; 450 } 451 452 /** 453 * Delete a particular message from the specified catalogue. 454 * 455 * @param string the source message to delete. 456 * @param string the catalogue to delete from. 457 * @return boolean true if deleted, false otherwise. 458 */ 459 public function delete($message, $catalogue='messages') 460 { 461 $variants = $this->getVariants($catalogue); 462 if ($variants) 463 { 464 list($variant, $filename) = $variants; 465 } 466 else 467 { 468 return false; 469 } 470 471 if (is_writable($filename) == false) 472 { 473 throw new sfException("Unable to modify file {$filename}, file must be writable."); 474 } 475 476 // create a new dom, import the existing xml 477 $dom = DOMDocument::load($filename); 478 479 // find the body element 480 $xpath = new DomXPath($dom); 481 $units = $xpath->query('//trans-unit'); 482 483 // for each of the existin units 484 foreach ($units as $unit) 485 { 486 //in each unit, need to find the source, target and comment nodes 487 //it will assume that the source is before the target. 488 foreach ($unit->childNodes as $node) 489 { 490 // source node 491 if ($node->nodeName == 'source' && $node->firstChild->wholeText == $message) 492 { 493 // we found it, remove and save the xml file. 494 $unit->parentNode->removeChild($unit); 495 496 $fileNode = $xpath->query('//file')->item(0); 497 $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z')); 498 499 if ($dom->save($filename) > 0) 500 { 501 if (!empty($this->cache)) 502 { 503 $this->cache->clean($variant, $this->culture); 504 } 505 506 return true; 507 } 508 else 509 { 510 return false; 511 } 512 } 513 } 514 } 515 516 return false; 517 } 518 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |