Development

HowToGenerateI18NFiles: sfMessageSource_XLIFF.class.php_module

You must first sign up to be able to contribute.

HowToGenerateI18NFiles: sfMessageSource_XLIFF.class.php_module

File sfMessageSource_XLIFF.class.php_module, 18.0 kB (added by ETS_DIDACTIC_MEDIA(MUNICH), 10 years ago)

Modified sfMessageSource_XLIFF.class.php for use with Modules

Line 
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: //depot/vekuma/MAIN/src/lib/symfony/i18n/sfMessageSource_XLIFF.class.php#1 $
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    * @param string the directory where the messages are stored.
53    * @see MessageSource::factory();
54    */
55   function __construct($source)
56   {
57     $this->source = (string)$source; 
58   }
59
60   /**
61    * Load the messages from a XLIFF file.
62    * @param string XLIFF file.
63    * @return array of messages.
64    */
65   protected function &loadData($filename)
66   {
67     //load it.
68    
69     $XML = simplexml_load_file($filename);
70
71     if(!$XML) return false;
72
73     $translationUnit = $XML->xpath('//trans-unit');
74      
75     $translations = array();
76      
77     foreach($translationUnit as $unit)
78     {
79       $source = (string)$unit->source;
80       $translations[$source][] = (string)$unit->target;
81       $translations[$source][]= (string)$unit['id'];   
82       $translations[$source][]= (string)$unit->note;   
83     }   
84     return $translations;
85   }
86  
87   /**
88    * Get the last modified unix-time for this particular catalogue+variant.
89    * Just use the file modified time.
90    * @param string catalogue+variant
91    * @return int last modified in unix-time format.
92    */
93   protected function getLastModified($source)
94   {
95     if(is_file($source))
96       return filemtime($source);
97     else
98       return 0;
99   }
100  
101   /**
102    * Get the XLIFF file for a specific message catalogue and cultural
103    * vairant.
104    * @param string message catalogue
105    * @return string full path to the XLIFF file.
106    */
107   public function getSource($variant)
108   {
109     return $this->source.'/'.$variant;
110   }
111  
112   /**
113    * Determin if the XLIFF file source is valid.
114    * @param string XLIFF file
115    * @return boolean true if valid, false otherwise.
116    */
117   protected function isValidSource($source)
118   {
119     return is_file($source);
120   }
121  
122   /**
123    * Get all the variants of a particular catalogue.
124    * @param string catalogue name
125    * @return array list of all variants for this catalogue.
126    */
127   protected function getCatalogueList($catalogue)
128   {
129     $variants = explode('_',$this->culture);
130     $source = $catalogue.$this->dataExt;
131    
132     $catalogues = array($source);
133
134     $variant = null;
135        
136     for($i = 0; $i < count($variants); $i++)
137     {           
138       if(strlen($variants[$i])>0)
139       {
140         $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
141         $catalogues[] = $catalogue.$this->dataSeparator.
142                 $variant.$this->dataExt;
143       }
144     }
145    
146     $byDir = $this->getCatalogueByDir($catalogue);
147     $catalogues = array_merge($byDir,array_reverse($catalogues));   
148     return $catalogues;
149   }
150  
151   /**
152    * Traverse through the directory structure to find the catalogues.
153    * This should only be called by getCatalogueList()
154    * @param string a particular catalogue.
155    * @return array a list of catalogues.
156    * @see getCatalogueList()
157    */
158   private function getCatalogueByDir($catalogue)
159   {
160     $variants = explode('_',$this->culture);
161     $catalogues = array();
162    
163     $variant = null;
164
165     for($i = 0; $i < count($variants); $i++)
166     {
167       if(strlen($variants[$i])>0)
168       {
169         $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
170         $catalogues[] = $variant.'/'.$catalogue.$this->dataExt;
171       }
172     }
173     return array_reverse($catalogues);
174   }
175  
176   /**
177    * Returns a list of catalogue and its culture ID.
178    * E.g. array('messages','en_AU')
179    * @return array list of catalogues
180    * @see getCatalogues()
181    */
182   public function catalogues()
183   {
184     return $this->getCatalogues();
185   }
186  
187   /**
188    * Returns a list of catalogue and its culture ID. This takes care
189    * of directory structures.
190    * E.g. array('messages','en_AU')
191    * @return array list of catalogues
192    */
193   protected function getCatalogues($dir=null,$variant=null)
194   {
195     $dir = $dir?$dir:$this->source;
196     $files = scandir($dir);
197    
198     $catalogue = array();
199    
200     foreach($files as $file)
201     {
202       if(is_dir($dir.'/'.$file)
203         && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file))
204       {
205         $catalogue = array_merge($catalogue,
206                 $this->getCatalogues($dir.'/'.$file, $file));
207       }
208      
209       $pos = strpos($file,$this->dataExt);     
210       if($pos >0
211         && substr($file,-1*strlen($this->dataExt)) == $this->dataExt)
212       {
213         $name = substr($file,0,$pos);
214         $dot = strrpos($name,$this->dataSeparator);
215         $culture = $variant;
216         $cat = $name;
217         if(is_int($dot))
218         {
219           $culture = substr($name, $dot+1,strlen($name));
220           $cat = substr($name,0,$dot);
221         }
222         $details[0] = $cat;
223         $details[1] = $culture;
224        
225        
226         $catalogue[] = $details;
227       }
228     }
229     sort($catalogue);
230     return $catalogue;
231   }
232  
233   /**
234    * Get the variant for a catalogue depending on the current culture.
235    * @param string catalogue
236    * @return string the variant.
237    * @see save()
238    * @see update()
239    * @see delete()
240    */
241   private function getVariants($catalogue='messages')
242   {
243    
244     if(is_null($catalogue))
245       $catalogue = 'messages';
246    
247     foreach($this->getCatalogueList($catalogue) as $variant)
248     {
249       $file = $this->getSource($variant);
250       if(is_file($file))
251         return array($variant, $file);
252     }
253     return false;
254   }
255
256   /**
257    * Save the list of untranslated blocks to the translation source.
258    * If the translation was not found, you should add those
259    * strings to the translation source via the <b>append()</b> method.
260    * @param string the catalogue to add to
261    * @file optional file to save to
262    * @return boolean true if saved successfuly, false otherwise.
263    */
264   public function save($catalogue='messages'){
265
266     $messages = $this->untranslated; 
267     if(count($messages) <= 0) return false;   
268
269     $variants = $this->getVariants($catalogue);
270        
271     if($variants)
272       list($variant, $filename) = $variants;
273     else
274       return false;
275      
276     if(is_writable($filename) == false)
277       throw new sfException("Unable to save to file {$filename}, file must be writable.");
278    
279     //create a new dom, import the existing xml
280     $dom = new DOMDocument();
281         $dom->load($filename);
282    
283     //find the body element
284     $xpath = new DomXPath($dom);
285       $body = $xpath->query('//body')->item(0);
286      
287     $count = $xpath->query('//trans-unit')->length;
288      
289     //for each message add it to the XML file using DOM
290       foreach($messages as $message)
291       {
292       $unit = $dom->createElement('trans-unit');
293       $unit->setAttribute('id',++$count);
294    
295       $source = $dom->createElement('source', $message);       
296       $target = $dom->createElement('target','');
297    
298       $unit->appendChild($dom->createTextNode("\n"));
299       $unit->appendChild($source);
300       $unit->appendChild($dom->createTextNode("\n"));
301       $unit->appendChild($target);
302       $unit->appendChild($dom->createTextNode("\n"));
303
304       $body->appendChild($dom->createTextNode("\n"));
305       $body->appendChild($unit);
306       $body->appendChild($dom->createTextNode("\n"));
307       }
308    
309      
310       $fileNode = $xpath->query('//file')->item(0);
311       $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
312      
313       //save it and clear the cache for this variant
314       $dom->save($filename, LIBXML_NOEMPTYTAG);
315      
316       //throw all untranslated messages away!
317       $this->untranslated = array();
318     if(!empty($this->cache) && !isset($file))
319         $this->cache->clean($variant, $this->culture);
320      
321       return true;
322   }
323  
324   /**
325    * Save the list of untranslated blocks to the translation source.
326    * If the translation was not found, you should add those
327    * strings to the translation source via the <b>append()</b> method.
328    * @param string the catalogue to add to
329    * @param file optional file to save to
330    * @return boolean true if saved successfuly, false otherwise.
331    */
332   public function saveModified($catalogue='messages', $file){
333     /*$catalogueArray = array();
334     $catalogueArray = split('#', $catalogue);
335     $catalogue = $catalogueArray[0];
336     if (isset($catalogueArray[2])) {
337         $file = $catalogueArray[1];
338     }
339     else {
340         $file = null;
341     }*/
342    
343     $messages = $this->untranslated; 
344     if(count($messages) <= 0) return false;   
345
346     $variants = $this->getVariants($catalogue);
347      
348    
349        
350     if (isset($file)){
351         $filename = $file;
352     }
353     else {
354         if ($variants) {
355             list($variant, $filename) = $variants;
356         }
357         else {
358             return false;       
359         }
360     }
361  
362     echo "WRITE TO:".$filename."\n";
363        
364     if(is_writable($filename) == false)
365       throw new sfException("Unable to save to file {$filename}, file must be writable.");
366    
367     //create a new dom, import the existing xml
368     $dom = new DOMDocument();
369         $dom->load($filename);
370    
371     //find the body element
372     $xpath = new DomXPath($dom);
373       $body = $xpath->query('//body')->item(0);
374      
375     $count = $xpath->query('//trans-unit')->length;
376      
377     //for each message add it to the XML file using DOM
378       foreach($messages as $message)
379       {
380       $unit = $dom->createElement('trans-unit');
381       $unit->setAttribute('id',++$count);
382    
383       $source = $dom->createElement('source', $message);       
384       $target = $dom->createElement('target','');
385    
386       $unit->appendChild($dom->createTextNode("\n"));
387       $unit->appendChild($source);
388       $unit->appendChild($dom->createTextNode("\n"));
389       $unit->appendChild($target);
390       $unit->appendChild($dom->createTextNode("\n"));
391
392       $body->appendChild($dom->createTextNode("\n"));
393       $body->appendChild($unit);
394       $body->appendChild($dom->createTextNode("\n"));
395       }
396    
397      
398       $fileNode = $xpath->query('//file')->item(0);
399       $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
400      
401       //save it and clear the cache for this variant
402       $dom->save($filename, LIBXML_NOEMPTYTAG);
403      
404       //throw all untranslated messages away!
405       $this->untranslated = array();
406     if(!empty($this->cache) && !isset($file))
407         $this->cache->clean($variant, $this->culture);
408      
409       return true;
410   }
411
412   /**
413    * Update the translation.
414    * @param string the source string.
415    * @param string the new translation string.
416    * @param string comments
417    * @param string the catalogue to save to.
418    * @return boolean true if translation was updated, false otherwise.
419    */
420   public function update($text, $target, $comments, $catalogue='messages')
421   {
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 sfException("Unable to update 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       $found = false;
443       $targetted = false;
444       $commented = false;
445      
446       //in each unit, need to find the source, target and comment nodes
447       //it will assume that the source is before the target.
448       foreach($unit->childNodes as $node)
449       {
450         //source node
451         if($node->nodeName == 'source'
452           && $node->firstChild->wholeText == $text)
453         {
454             $found = true;
455         }
456        
457         //found source, get the target and notes
458         if($found)
459         {
460           //set the new translated string
461           if($node->nodeName == 'target')
462           {
463             $node->nodeValue = $target;
464             $targetted = true;
465           }   
466           //set the notes     
467           if(!empty($comments) && $node->nodeName == 'note')
468           {
469             $node->nodeValue = $comments;
470             $commented = true;
471           }
472         }
473       }
474      
475       //append a target
476       if($found && !$targetted)
477         $unit->appendChild($dom->createElement('target',$target));
478        
479       //append a note
480       if($found && !$commented && !empty($comments))
481         $unit->appendChild($dom->createElement('note',$comments));
482      
483       //finished searching
484       if($found) break;
485     }
486    
487       $fileNode = $xpath->query('//file')->item(0);
488       $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
489          
490     if($dom->save($filename) >0)
491     {
492       if(!empty($this->cache))
493         $this->cache->clean($variant, $this->culture);   
494       return true;
495     }
496    
497     return false;
498   }
499  
500   /**
501    * Delete a particular message from the specified catalogue.
502    * @param string the source message to delete.
503    * @param string the catalogue to delete from.
504    * @return boolean true if deleted, false otherwise.
505    */
506   public function delete($message, $catalogue='messages')
507   {
508     $variants = $this->getVariants($catalogue);
509     if($variants)
510       list($variant, $filename) = $variants;
511     else
512       return false;
513  
514     echo "WRITE TO:".$filename."\n";
515      
516     if(is_writable($filename) == false)
517       throw new sfException("Unable to modify file {$filename}, file must be writable.");
518    
519     //create a new dom, import the existing xml
520     $dom = DOMDocument::load($filename);
521    
522     //find the body element
523     $xpath = new DomXPath($dom);   
524     $units = $xpath->query('//trans-unit');
525      
526     //for each of the existin units
527     foreach($units as $unit)
528     {
529       //in each unit, need to find the source, target and comment nodes
530       //it will assume that the source is before the target.
531       foreach($unit->childNodes as $node)
532       {
533         //source node
534         if($node->nodeName == 'source'
535           && $node->firstChild->wholeText == $message)
536         {
537          
538           //we found it, remove and save the xml file.
539           $unit->parentNode->removeChild($unit);
540          
541             $fileNode = $xpath->query('//file')->item(0);
542             $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
543          
544           if($dom->save($filename) >0)
545           {
546             if(!empty($this->cache))
547               $this->cache->clean($variant, $this->culture);   
548             return true;
549           }
550           else return false;
551                  
552         }
553       }
554      
555     }
556
557     return false;
558   }
559  
560     /**
561     * Create an empty XLIFF file
562     *
563     * @param string variant to create
564     * @param string the default language (source)
565     * @param string encoding for output file
566     * @param string module module name for which the file will be created
567     * @return the number of byte written
568     */
569     function init($variant, $defaultCuluture, $encoding, $catalogue='messages'){       
570         $result = 0;
571      
572         $fileName = $this->getSource
573             ($catalogue.$this->dataSeparator.$variant.$this->dataExt);
574      
575       if (!$this->isValidSource($fileName)){
576           $out = '<?xml version="1.0" encoding="';
577           $out .= ($encoding)?'utf-8"?>'."\n":$encoding.'" ?>'."\n";
578           $out .= '<xliff version="1.0">'."\n";
579           $out .= '  <file orginal="global" source-language="'.$defaultCuluture
580               .'" datatype="plaintext" date="2006-03-22T00:15:43Z">'."\n";
581           $out .= '    <body>'."\n";
582           $out .= '    </body>'."\n";
583           $out .= '  </file>'."\n";
584           $out .= '</xliff>'."\n";
585      
586           $result = file_put_contents($fileName, $out);
587         }
588      
589         return $result;
590     }
591    
592     /**
593     * Create an empty XLIFF file
594     *
595     * @param string variant to create
596     * @param string the default language (source)
597     * @param string encoding for output file
598     * @param string module module name for which the file will be created
599     * @return the number of byte written
600     */
601     function initModified($variant, $defaultCuluture, $encoding, $catalogue='messages', $module){
602
603         $result = 0;
604      
605         if (!$module){
606             $fileName = $this->getSource
607                 ($catalogue.$this->dataSeparator.$variant.$this->dataExt);
608             echo $fileName." ";
609         }
610         else {
611            $modifier = '../modules/'.$module.'/i18n/';
612            $fileName = $this->getSource($modifier.$catalogue.
613                $this->dataSeparator.$variant.$this->dataExt);
614            echo $fileName." ";
615         }
616      
617         //
618         if ($this->isValidSource($fileName) && isset($module)){
619            unlink($fileName);
620         }
621      
622       if (!$this->isValidSource($fileName)){
623           $out = '<?xml version="1.0" encoding="';
624           $out .= ($encoding)?'utf-8"?>'."\n":$encoding.'" ?>'."\n";
625           $out .= '<xliff version="1.0">'."\n";
626           $out .= '  <file orginal="global" source-language="'.$defaultCuluture
627               .'" datatype="plaintext" date="2006-03-22T00:15:43Z">'."\n";
628           $out .= '    <body>'."\n";
629           $out .= '    </body>'."\n";
630           $out .= '  </file>'."\n";
631           $out .= '</xliff>'."\n";
632      
633           $result = file_put_contents($fileName, $out);
634         }
635      
636         return $result;
637     }
638  
639 }
640
641 ?>