~fabiocbalbuquerque/sahana-agasti/web-services

« back to all changes in this revision

Viewing changes to lib/vendor/symfony/lib/i18n/sfMessageSource_XLIFF.class.php

  • Committer: Usman Akeju
  • Date: 2010-12-13 22:16:24 UTC
  • Revision ID: usman.akeju@mail.cuny.edu-20101213221624-k0ms0swt2cf8zmu8
- initial commit; migrating to new branch internally

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 23810 2009-11-12 11:07:44Z Kris.Wallsmith $
 
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    symfony
 
35
 * @subpackage i18n
 
36
 */
 
37
class sfMessageSource_XLIFF extends sfMessageSource_File
 
38
{
 
39
  /**
 
40
   * Message data filename extension.
 
41
   * @var string
 
42
   */
 
43
  protected $dataExt = '.xml';
 
44
 
 
45
  /**
 
46
   * Loads the messages from a XLIFF file.
 
47
   *
 
48
   * @param string $filename  XLIFF file.
 
49
   * @return array|false An array of messages or false if there was a problem loading the file.
 
50
   */
 
51
  public function &loadData($filename)
 
52
  {
 
53
    libxml_use_internal_errors(true);
 
54
    if (!$xml = simplexml_load_file($filename))
 
55
    {
 
56
      $error = false;
 
57
 
 
58
      return $error;
 
59
    }
 
60
    libxml_use_internal_errors(false);
 
61
 
 
62
    $translationUnit = $xml->xpath('//trans-unit');
 
63
 
 
64
    $translations = array();
 
65
 
 
66
    foreach ($translationUnit as $unit)
 
67
    {
 
68
      $source = (string) $unit->source;
 
69
      $translations[$source][] = (string) $unit->target;
 
70
      $translations[$source][] = (string) $unit['id'];
 
71
      $translations[$source][] = (string) $unit->note;
 
72
    }
 
73
 
 
74
    return $translations;
 
75
  }
 
76
 
 
77
  /**
 
78
   * Creates and returns a new DOMDocument instance
 
79
   *
 
80
   * @param  string  $xml  XML string
 
81
   *
 
82
   * @return DOMDocument
 
83
   */
 
84
  protected function createDOMDocument($xml = null)
 
85
  {
 
86
    $domimp = new DOMImplementation();
 
87
    $doctype = $domimp->createDocumentType('xliff', '-//XLIFF//DTD XLIFF//EN', 'http://www.oasis-open.org/committees/xliff/documents/xliff.dtd');
 
88
    $dom = $domimp->createDocument('', '', $doctype);
 
89
    $dom->formatOutput = true;
 
90
    $dom->preserveWhiteSpace = false;
 
91
 
 
92
    if (null !== $xml && is_string($xml))
 
93
    {
 
94
      // Add header for XML with UTF-8
 
95
      if (!preg_match('/<\?xml/', $xml))
 
96
      {
 
97
        $xml = '<?xml version="1.0" encoding="UTF-8"?>'."\n".$xml;
 
98
      }
 
99
 
 
100
      $dom->loadXML($xml);
 
101
    }
 
102
 
 
103
    return $dom;
 
104
  }
 
105
 
 
106
  /**
 
107
   * Gets the variant for a catalogue depending on the current culture.
 
108
   *
 
109
   * @param string $catalogue catalogue
 
110
   * @return string the variant.
 
111
   * @see save()
 
112
   * @see update()
 
113
   * @see delete()
 
114
   */
 
115
  protected function getVariants($catalogue = 'messages')
 
116
  {
 
117
    if (null === $catalogue)
 
118
    {
 
119
      $catalogue = 'messages';
 
120
    }
 
121
 
 
122
    foreach ($this->getCatalogueList($catalogue) as $variant)
 
123
    {
 
124
      $file = $this->getSource($variant);
 
125
      if (is_file($file))
 
126
      {
 
127
        return array($variant, $file);
 
128
      }
 
129
    }
 
130
 
 
131
    return false;
 
132
  }
 
133
 
 
134
  /**
 
135
   * Saves the list of untranslated blocks to the translation source.
 
136
   * If the translation was not found, you should add those
 
137
   * strings to the translation source via the <b>append()</b> method.
 
138
   *
 
139
   * @param string $catalogue the catalogue to add to
 
140
   * @return boolean true if saved successfuly, false otherwise.
 
141
   */
 
142
  public function save($catalogue = 'messages')
 
143
  {
 
144
    $messages = $this->untranslated;
 
145
    if (count($messages) <= 0)
 
146
    {
 
147
      return false;
 
148
    }
 
149
 
 
150
    $variants = $this->getVariants($catalogue);
 
151
    if ($variants)
 
152
    {
 
153
      list($variant, $filename) = $variants;
 
154
    }
 
155
    else
 
156
    {
 
157
      list($variant, $filename) = $this->createMessageTemplate($catalogue);
 
158
    }
 
159
 
 
160
    if (is_writable($filename) == false)
 
161
    {
 
162
      throw new sfException(sprintf("Unable to save to file %s, file must be writable.", $filename));
 
163
    }
 
164
 
 
165
    // create a new dom, import the existing xml
 
166
    $dom = $this->createDOMDocument();
 
167
    @$dom->load($filename);
 
168
 
 
169
    // find the body element
 
170
    $xpath = new DomXPath($dom);
 
171
    $body = $xpath->query('//body')->item(0);
 
172
 
 
173
    if (null === $body)
 
174
    {
 
175
      //create and try again
 
176
      $this->createMessageTemplate($catalogue);
 
177
      $dom->load($filename);
 
178
      $xpath = new DomXPath($dom);
 
179
      $body = $xpath->query('//body')->item(0);
 
180
    }
 
181
 
 
182
    // find the biggest "id" used
 
183
    $lastNodes = $xpath->query('//trans-unit[not(@id <= preceding-sibling::trans-unit/@id) and not(@id <= following-sibling::trans-unit/@id)]');
 
184
    if (null !== $last = $lastNodes->item(0))
 
185
    {
 
186
      $count = intval($last->getAttribute('id'));
 
187
    }
 
188
    else
 
189
    {
 
190
      $count = 0;
 
191
    }
 
192
 
 
193
    // for each message add it to the XML file using DOM
 
194
    foreach ($messages as $message)
 
195
    {
 
196
      $unit = $dom->createElement('trans-unit');
 
197
      $unit->setAttribute('id', ++$count);
 
198
 
 
199
      $source = $dom->createElement('source');
 
200
      $source->appendChild($dom->createTextNode($message));
 
201
      $target = $dom->createElement('target');
 
202
      $target->appendChild($dom->createTextNode(''));
 
203
 
 
204
      $unit->appendChild($source);
 
205
      $unit->appendChild($target);
 
206
 
 
207
      $body->appendChild($unit);
 
208
    }
 
209
 
 
210
    $fileNode = $xpath->query('//file')->item(0);
 
211
    $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
 
212
 
 
213
    $dom = $this->createDOMDocument($dom->saveXML());
 
214
 
 
215
    // save it and clear the cache for this variant
 
216
    $dom->save($filename);
 
217
    if ($this->cache)
 
218
    {
 
219
      $this->cache->remove($variant.':'.$this->culture);
 
220
    }
 
221
 
 
222
    return true;
 
223
  }
 
224
 
 
225
  /**
 
226
   * Updates the translation.
 
227
   *
 
228
   * @param string $text      the source string.
 
229
   * @param string $target    the new translation string.
 
230
   * @param string $comments  comments
 
231
   * @param string $catalogue the catalogue to save to.
 
232
   * @return boolean true if translation was updated, false otherwise.
 
233
   */
 
234
  public function update($text, $target, $comments, $catalogue = 'messages')
 
235
  {
 
236
    $variants = $this->getVariants($catalogue);
 
237
    if ($variants)
 
238
    {
 
239
      list($variant, $filename) = $variants;
 
240
    }
 
241
    else
 
242
    {
 
243
      return false;
 
244
    }
 
245
 
 
246
    if (is_writable($filename) == false)
 
247
    {
 
248
      throw new sfException(sprintf("Unable to update file %s, file must be writable.", $filename));
 
249
    }
 
250
 
 
251
    // create a new dom, import the existing xml
 
252
    $dom = $this->createDOMDocument();
 
253
    $dom->load($filename);
 
254
 
 
255
    // find the body element
 
256
    $xpath = new DomXPath($dom);
 
257
    $units = $xpath->query('//trans-unit');
 
258
 
 
259
    // for each of the existin units
 
260
    foreach ($units as $unit)
 
261
    {
 
262
      $found = false;
 
263
      $targetted = false;
 
264
      $commented = false;
 
265
 
 
266
      //in each unit, need to find the source, target and comment nodes
 
267
      //it will assume that the source is before the target.
 
268
      foreach ($unit->childNodes as $node)
 
269
      {
 
270
        // source node
 
271
        if ($node->nodeName == 'source' && $node->firstChild->wholeText == $text)
 
272
        {
 
273
          $found = true;
 
274
        }
 
275
 
 
276
        // found source, get the target and notes
 
277
        if ($found)
 
278
        {
 
279
          // set the new translated string
 
280
          if ($node->nodeName == 'target')
 
281
          {
 
282
            $node->nodeValue = $target;
 
283
            $targetted = true;
 
284
          }
 
285
 
 
286
          // set the notes
 
287
          if (!empty($comments) && $node->nodeName == 'note')
 
288
          {
 
289
            $node->nodeValue = $comments;
 
290
            $commented = true;
 
291
          }
 
292
        }
 
293
      }
 
294
 
 
295
      // append a target
 
296
      if ($found && !$targetted)
 
297
      {
 
298
        $targetNode = $dom->createElement('target');
 
299
        $targetNode->appendChild($dom->createTextNode($target));
 
300
        $unit->appendChild($targetNode);
 
301
      }
 
302
 
 
303
      // append a note
 
304
      if ($found && !$commented && !empty($comments))
 
305
      {
 
306
        $commentsNode = $dom->createElement('note');
 
307
        $commentsNode->appendChild($dom->createTextNode($comments));
 
308
        $unit->appendChild($commentsNode);
 
309
      }
 
310
 
 
311
      // finished searching
 
312
      if ($found)
 
313
      {
 
314
        break;
 
315
      }
 
316
    }
 
317
 
 
318
    $fileNode = $xpath->query('//file')->item(0);
 
319
    $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
 
320
 
 
321
    if ($dom->save($filename) > 0)
 
322
    {
 
323
      if ($this->cache)
 
324
      {
 
325
        $this->cache->remove($variant.':'.$this->culture);
 
326
      }
 
327
 
 
328
      return true;
 
329
    }
 
330
 
 
331
    return false;
 
332
  }
 
333
 
 
334
  /**
 
335
   * Deletes a particular message from the specified catalogue.
 
336
   *
 
337
   * @param string $message   the source message to delete.
 
338
   * @param string $catalogue the catalogue to delete from.
 
339
   * @return boolean true if deleted, false otherwise.
 
340
   */
 
341
  public function delete($message, $catalogue='messages')
 
342
  {
 
343
    $variants = $this->getVariants($catalogue);
 
344
    if ($variants)
 
345
    {
 
346
      list($variant, $filename) = $variants;
 
347
    }
 
348
    else
 
349
    {
 
350
      return false;
 
351
    }
 
352
 
 
353
    if (is_writable($filename) == false)
 
354
    {
 
355
      throw new sfException(sprintf("Unable to modify file %s, file must be writable.", $filename));
 
356
    }
 
357
 
 
358
    // create a new dom, import the existing xml
 
359
    $dom = $this->createDOMDocument();
 
360
    $dom->load($filename);
 
361
 
 
362
    // find the body element
 
363
    $xpath = new DomXPath($dom);
 
364
    $units = $xpath->query('//trans-unit');
 
365
 
 
366
    // for each of the existin units
 
367
    foreach ($units as $unit)
 
368
    {
 
369
      //in each unit, need to find the source, target and comment nodes
 
370
      //it will assume that the source is before the target.
 
371
      foreach ($unit->childNodes as $node)
 
372
      {
 
373
        // source node
 
374
        if ($node->nodeName == 'source' && $node->firstChild->wholeText == $message)
 
375
        {
 
376
          // we found it, remove and save the xml file.
 
377
          $unit->parentNode->removeChild($unit);
 
378
 
 
379
          $fileNode = $xpath->query('//file')->item(0);
 
380
          $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z'));
 
381
 
 
382
          if ($dom->save($filename) > 0)
 
383
          {
 
384
            if (!empty($this->cache))
 
385
            {
 
386
              $this->cache->remove($variant.':'.$this->culture);
 
387
            }
 
388
 
 
389
            return true;
 
390
          }
 
391
          else
 
392
          {
 
393
            return false;
 
394
          }
 
395
        }
 
396
      }
 
397
    }
 
398
 
 
399
    return false;
 
400
  }
 
401
 
 
402
  protected function createMessageTemplate($catalogue)
 
403
  {
 
404
    if (null === $catalogue)
 
405
    {
 
406
      $catalogue = 'messages';
 
407
    }
 
408
 
 
409
    $variants = $this->getCatalogueList($catalogue);
 
410
    $variant = array_shift($variants);
 
411
    $file = $this->getSource($variant);
 
412
    $dir = dirname($file);
 
413
    if (!is_dir($dir))
 
414
    {
 
415
      @mkdir($dir);
 
416
      @chmod($dir, 0777);
 
417
    }
 
418
 
 
419
    if (!is_dir($dir))
 
420
    {
 
421
      throw new sfException(sprintf("Unable to create directory %s.", $dir));
 
422
    }
 
423
 
 
424
    $dom = $this->createDOMDocument($this->getTemplate($catalogue));
 
425
    file_put_contents($file, $dom->saveXML());
 
426
    chmod($file, 0777);
 
427
 
 
428
    return array($variant, $file);
 
429
  }
 
430
 
 
431
  protected function getTemplate($catalogue)
 
432
  {
 
433
    $date = date('c');
 
434
 
 
435
    return <<<EOD
 
436
<?xml version="1.0" encoding="UTF-8"?>
 
437
<!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd" >
 
438
<xliff version="1.0">
 
439
  <file source-language="EN" target-language="{$this->culture}" datatype="plaintext" original="$catalogue" date="$date" product-name="$catalogue">
 
440
    <header />
 
441
    <body>
 
442
    </body>
 
443
  </file>
 
444
</xliff>
 
445
EOD;
 
446
  }
 
447
}