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