Development

HowToGenerateI18NFiles: sfMessageSource_gettext.class.php

You must first sign up to be able to contribute.

HowToGenerateI18NFiles: sfMessageSource_gettext.class.php

File sfMessageSource_gettext.class.php, 11.4 kB (added by t.kroha@seto-gmbh.de, 11 years ago)
Line 
1 <?php
2
3 /**
4  * sfMessageSource_gettext 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_gettext.class.php 1415 2006-06-11 08:33:51Z fabien $
17  * @package    symfony
18  * @subpackage i18n
19  */
20
21 /**
22  * sfMessageSource_gettext class.
23  *
24  * Using Gettext MO format as the message source for translation.
25  * The gettext classes are based on PEAR's gettext MO and PO classes.
26  *
27  * See the MessageSource::factory() method to instantiate this class.
28  *
29  * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
30  * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004
31  * @package System.I18N.core
32  */
33 class sfMessageSource_gettext extends sfMessageSource
34 {
35   /**
36    * Message data filename extension.
37    * @var string
38    */
39   protected $dataExt = '.mo';
40  
41   /**
42    * PO data filename extension
43    * @var string
44    */
45   protected $poExt = '.po';
46  
47   /**
48    * Separator between culture name and source.
49    * @var string
50    */
51   protected $dataSeparator = '.';
52     
53   function __construct($source)
54   {
55     $this->source = (string)$source;
56   }
57  
58
59   /**
60    * Load the messages from a MO file.
61    * @param string MO file.
62    * @return array of messages.
63    */
64   protected function &loadData($filename)
65   {
66     $mo = TGettext::factory('MO',$filename);
67     $mo->load();
68     $result = $mo->toArray();
69     
70     $results = array();
71     $count=0;
72     foreach($result['strings'] as $source => $target)
73     {
74       $results[$source][] = $target; //target
75       $results[$source][] = $count++; //id
76       $results[$source][] = ''; //comments
77     }   
78     return $results;
79   }
80  
81   /**
82    * Determin if the MO file source is valid.
83    * @param string MO file
84    * @return boolean true if valid, false otherwise.
85    */
86   protected function isValidSource($filename)
87   {
88     return is_file($filename);
89   }
90  
91   /**
92    * Get the MO file for a specific message catalogue and cultural
93    * vairant.
94    * @param string message catalogue
95    * @return string full path to the MO file.
96    */
97   protected function getSource($variant)
98   {
99     return $this->source.'/'.$variant;
100   }
101  
102   /**
103    * Get the last modified unix-time for this particular catalogue+variant.
104    * Just use the file modified time.
105    * @param string catalogue+variant
106    * @return int last modified in unix-time format.
107    */
108   protected function getLastModified($source)
109   {
110     if(is_file($source))
111       return filemtime($source);
112     else
113       return 0;
114   }
115  
116   /**
117    * Get all the variants of a particular catalogue.
118    * @param string catalogue name
119    * @return array list of all variants for this catalogue.
120    */
121   protected function getCatalogueList($catalogue)
122   {
123     $variants = explode('_',$this->culture);
124     $source = $catalogue.$this->dataExt;
125     
126     $catalogues = array($source);
127
128     $variant = null;
129         
130     for($i = 0; $i < count($variants); $i++)
131     {           
132       if(strlen($variants[$i])>0)
133       {
134         $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
135         $catalogues[] = $catalogue.$this->dataSeparator.
136                 $variant.$this->dataExt;
137       }
138     }
139     $byDir = $this->getCatalogueByDir($catalogue);
140     $catalogues = array_merge($byDir,array_reverse($catalogues));   
141     return $catalogues;
142   }
143
144  
145   /**
146    * Traverse through the directory structure to find the catalogues.
147    * This should only be called by getCatalogueList()
148    * @param string a particular catalogue.
149    * @return array a list of catalogues.
150    * @see getCatalogueList()
151    */
152   private function getCatalogueByDir($catalogue)
153   {
154     $variants = explode('_',$this->culture);
155     $catalogues = array();
156     
157     $variant = null;
158
159     for($i = 0; $i < count($variants); $i++)
160     {
161       if(strlen($variants[$i])>0)
162       {
163         $variant .= ($variant)?'_'.$variants[$i]:$variants[$i];
164         $catalogues[] = $variant.'/'.$catalogue.$this->dataExt;
165       }
166     }
167     return array_reverse($catalogues);
168   }
169     
170   /**
171    * Get the variant for a catalogue depending on the current culture.
172    * @param string catalogue
173    * @return string the variant.
174    * @see save()
175    * @see update()
176    * @see delete()
177    */
178   private function getVariants($catalogue='messages')
179   {
180     if(is_null($catalogue))
181       $catalogue = 'messages';
182  
183     foreach($this->getCatalogueList($catalogue) as $variant)
184     {
185       $file = $this->getSource($variant);
186       $po = $this->getPOFile($file);
187       if(is_file($file) || is_file($po))
188         return array($variant, $file, $po);
189     }
190     return false;
191   }
192  
193   private function getPOFile($MOFile)
194   {
195     $filebase = substr($MOFile, 0, strlen($MOFile)-strlen($this->dataExt));
196     return $filebase.$this->poExt;
197   }
198     
199   /**
200    * Save the list of untranslated blocks to the translation source.
201    * If the translation was not found, you should add those
202    * strings to the translation source via the <b>append()</b> method.
203    * @param string the catalogue to add to
204    * @return boolean true if saved successfuly, false otherwise.   
205    */
206   function save($catalogue='messages')
207   {
208     $messages = $this->untranslated;
209     
210     if(count($messages) <= 0) return false;
211     
212     $variants = $this->getVariants($catalogue);
213         
214     if($variants)
215       list($variant, $MOFile, $POFile) = $variants;
216     else
217       return false;
218       
219     if(is_writable($MOFile) == false)
220       throw new sfException("Unable to save to file {$MOFile}, file must be writable.");
221     if(is_writable($POFile) == false)
222       throw new sfException("Unable to save to file {$POFile}, file must be writable.");
223     
224     //set the strings as untranslated.
225     $strings = array();
226     foreach($messages as $message)
227       $strings[$message] = '';
228       
229     //load the PO
230     $po = TGettext::factory('PO',$POFile);
231     $po->load();
232     $result = $po->toArray();
233     
234     $existing = count($result['strings']);
235     
236     //add to strings to the existing message list
237     $result['strings'] = array_merge($result['strings'],$strings);
238     
239     $new = count($result['strings']);
240     
241     if($new > $existing)
242     {
243       //change the date 2004-12-25 12:26
244       $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
245           
246       $po->fromArray($result);
247       $mo = $po->toMO();
248       if($po->save() && $mo->save($MOFile))
249       {
250         if(!empty($this->cache)) 
251           $this->cache->clean($variant, $this->culture);     
252         return true;
253       }
254       else
255         return false;
256     }
257     return false;
258   }
259
260   /**
261    * Delete a particular message from the specified catalogue.
262    * @param string the source message to delete.
263    * @param string the catalogue to delete from.
264    * @return boolean true if deleted, false otherwise.
265    */
266   function delete($message, $catalogue='messages')
267   {
268     $variants = $this->getVariants($catalogue);
269     if($variants)
270       list($variant, $MOFile, $POFile) = $variants;
271     else
272       return false;
273           
274     if(is_writable($MOFile) == false)
275       throw new sfException("Unable to modify file {$MOFile}, file must be writable.");
276     if(is_writable($POFile) == false)
277       throw new sfException("Unable to modify file {$POFile}, file must be writable.");
278         
279     $po = TGettext::factory('PO',$POFile);
280     $po->load();
281     $result = $po->toArray();
282     
283     foreach($result['strings'] as $string => $value)
284     {
285       if($string == $message)
286       {
287         $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
288         unset($result['strings'][$string]);
289         
290         $po->fromArray($result);
291         $mo = $po->toMO();
292         if($po->save() && $mo->save($MOFile))
293         {
294           if(!empty($this->cache))
295             $this->cache->clean($variant, $this->culture);
296           return true;         
297         }
298         else
299           return false;       
300       }
301     }
302     
303     return false;
304   }
305     
306   /**
307    * Update the translation.
308    * @param string the source string.
309    * @param string the new translation string.
310    * @param string comments
311    * @param string the catalogue of the translation.
312    * @return boolean true if translation was updated, false otherwise.
313    */
314   function update($text, $target, $comments, $catalogue='messages')
315   {
316     $variants = $this->getVariants($catalogue);
317     if($variants)
318       list($variant, $MOFile, $POFile) = $variants;
319     else
320       return false;
321           
322     if(is_writable($MOFile) == false)
323       throw new sfException("Unable to update file {$MOFile}, file must be writable.");
324     if(is_writable($POFile) == false)
325       throw new sfException("Unable to update file {$POFile}, file must be writable.");
326         
327     
328     $po = TGettext::factory('PO',$POFile);
329     $po->load();
330     $result = $po->toArray();   
331     
332     foreach($result['strings'] as $string => $value)
333     {
334       if($string == $text)
335       {
336         $result['strings'][$string] = $target;
337         $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s');
338         
339         $po->fromArray($result);
340         $mo = $po->toMO();
341  
342         if($po->save() && $mo->save($MOFile))
343         {
344           if(!empty($this->cache))
345             $this->cache->clean($variant, $this->culture);
346           return true;         
347         }
348         else
349           return false;
350       }
351     }     
352     
353     return false;
354   }
355  
356  
357   /**
358    * Returns a list of catalogue as key and all it variants as value.
359    * @return array list of catalogues
360    */
361   function catalogues()
362   {
363     return $this->getCatalogues();
364   }
365  
366   /**
367    * Returns a list of catalogue and its culture ID. This takes care
368    * of directory structures.
369    * E.g. array('messages','en_AU')
370    * @return array list of catalogues
371    */
372   protected function getCatalogues($dir=null,$variant=null)
373   {
374     $dir = $dir?$dir:$this->source;
375     $files = scandir($dir);
376     
377     $catalogue = array();
378  
379     foreach($files as $file)
380     {     
381       if(is_dir($dir.'/'.$file)
382         && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file))
383       {
384         
385         $catalogue = array_merge($catalogue,
386                 $this->getCatalogues($dir.'/'.$file, $file));
387       }
388       
389       $pos = strpos($file,$this->dataExt);
390     
391       if($pos >0
392         && substr($file,-1*strlen($this->dataExt)) == $this->dataExt)
393       {
394         $name = substr($file,0,$pos);
395         $dot = strrpos($name,$this->dataSeparator);
396         $culture = $variant;
397         $cat = $name;
398         if(is_int($dot))
399         {
400           $culture = substr($name, $dot+1,strlen($name));
401           $cat = substr($name,0,$dot);
402         }
403         $details[0] = $cat;
404         $details[1] = $culture;
405         
406         
407         $catalogue[] = $details;
408       }
409     }
410     sort($catalogue);
411     
412     return $catalogue;
413   }   
414
415
416   /**
417   * Create an empty po file
418   * @param string variant to create
419   * @param string the default language (source)
420   * @param string encoding for output file
421   * @return the number of byte written
422   */
423   function init($variant, $defaultCuluture, $encoding, $catalogue='messages')
424   {
425     $result = 0;
426     
427     $fileNamePO = $this->getSource($variant.DIRECTORY_SEPARATOR.$catalogue.$this->poExt);
428     $fileNameMO = $this->getSource($variant.DIRECTORY_SEPARATOR.$catalogue.$this->dataExt);
429
430     if (!$this->isValidSource($fileNameMO) )
431     {
432       $result = touch($fileNamePO) && touch($fileNameMO);
433     }
434     
435     return $result;
436   }
437  
438 }
439
440
441