Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
4
/**
5
 * Contains the Translation2_Admin_Container_gettext class
6
 *
7
 * PHP versions 4 and 5
8
 *
9
 * LICENSE: Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. The name of the author may not be used to endorse or promote products
17
 *    derived from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
 * IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
23
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 *
30
 * @category  Internationalization
31
 * @package   Translation2
32
 * @author    Lorenzo Alberton <l.alberton@quipo.it>
33
 * @author    Michael Wallner <mike@php.net>
34
 * @copyright 2004-2007 Lorenzo Alberton, Michael Wallner
35
 * @license   http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
36
 * @version   CVS: $Id: gettext.php 305985 2010-12-05 22:55:33Z clockwerx $
37
 * @link      http://pear.php.net/package/Translation2
38
 */
39
 
40
/**
41
 * require Translation2_Container_gettext class
42
 */
43
require_once 'Translation2/Container/gettext.php';
44
 
45
/**
46
 * Storage driver for storing/fetching data to/from a gettext file
47
 *
48
 * This storage driver requires the gettext extension
49
 *
50
 * @category  Internationalization
51
 * @package   Translation2
52
 * @author    Lorenzo Alberton <l.alberton@quipo.it>
53
 * @author    Michael Wallner <mike@php.net>
54
 * @copyright 2004-2007 Lorenzo Alberton, Michael Wallner
55
 * @license   http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
56
 * @version   CVS: $Id: gettext.php 305985 2010-12-05 22:55:33Z clockwerx $
57
 * @link      http://pear.php.net/package/Translation2
58
 */
59
class Translation2_Admin_Container_gettext extends Translation2_Container_gettext
60
{
61
    // {{{ class vars
62
 
63
    var $_bulk   = false;
64
    var $_queue  = array();
65
    var $_fields = array('name', 'meta', 'error_text', 'encoding');
66
 
67
    // }}}
68
    // {{{ addLang()
69
 
70
    /**
71
     * Creates a new entry in the langs_avail .ini file.
72
     *
73
     * @param array  $langData language data
74
     * @param string $path     path to gettext data dir
75
     *
76
     * @return  mixed   Returns true on success or PEAR_Error on failure.
77
     */
78
    function addLang($langData, $path = null)
79
    {
80
        if (!isset($path) || !is_string($path)) {
81
            $path = $this->_domains[$this->options['default_domain']];
82
        }
83
 
84
        $path .= '/'. $langData['lang_id'] . '/LC_MESSAGES';
85
 
86
        if (!is_dir($path)) {
87
            include_once 'System.php';
88
            if (!System::mkdir(array('-p', $path))) {
89
                return $this->raiseError(sprintf(
90
                        'Cannot create new language in path "%s"', $path
91
                    ),
92
                    TRANSLATION2_ERROR_CANNOT_CREATE_DIR
93
                );
94
            }
95
        }
96
 
97
        return true;
98
    }
99
 
100
    // }}}
101
    // {{{ addLangToList()
102
 
103
    /**
104
     * Creates a new entry in the langsAvail .ini file.
105
     * If the file doesn't exist yet, it is created.
106
     *
107
     * @param array $langData array('lang_id'    => 'en',
108
     *                              'name'       => 'english',
109
     *                              'meta'       => 'some meta info',
110
     *                              'error_text' => 'not available'
111
     *                              'encoding'   => 'iso-8859-1',
112
     * );
113
     *
114
     * @return true|PEAR_Error on failure
115
     */
116
    function addLangToList($langData)
117
    {
118
        if (PEAR::isError($changed = $this->_updateLangData($langData))) {
119
            return $changed;
120
        }
121
        return $changed ? $this->_writeLangsAvailFile() : true;
122
    }
123
 
124
    // }}}
125
    // {{{ add()
126
 
127
    /**
128
     * Add a new entry in the strings domain.
129
     *
130
     * @param string $stringID string ID
131
     * @param string $pageID   page/group ID
132
     * @param array  $strings  Associative array with string translations.
133
     *               Sample format:  array('en' => 'sample', 'it' => 'esempio')
134
     *
135
     * @return true|PEAR_Error on failure
136
     */
137
    function add($stringID, $pageID, $strings)
138
    {
139
        if (!isset($pageID)) {
140
            $pageID = $this->options['default_domain'];
141
        }
142
 
143
        $langs = array_intersect(array_keys($strings), $this->getLangs('ids'));
144
 
145
        if (!count($langs)) {
146
            return true; // really?
147
        }
148
 
149
        if ($this->_bulk) {
150
            foreach ($strings as $lang => $string) {
151
                if (in_array($lang, $langs)) {
152
                    $this->_queue['add'][$pageID][$lang][$stringID] = $string;
153
                }
154
            }
155
            return true;
156
        } else {
157
            $add = array();
158
            foreach ($strings as $lang => $string) {
159
                if (in_array($lang, $langs)) {
160
                    $add[$pageID][$lang][$stringID] = $string;
161
                }
162
            }
163
            return $this->_add($add);
164
        }
165
    }
166
 
167
    // }}}
168
    // {{{ remove()
169
 
170
    /**
171
     * Remove an entry from the domain.
172
     *
173
     * @param string $stringID string ID
174
     * @param string $pageID   page/group ID
175
     *
176
     * @return true|PEAR_Error on failure
177
     */
178
    function remove($stringID, $pageID)
179
    {
180
        if (!isset($pageID)) {
181
            $pageID = $this->options['default_domain'];
182
        }
183
 
184
        if ($this->_bulk) {
185
            $this->_queue['remove'][$pageID][$stringID] = true;
186
            return true;
187
        } else {
188
            $tmp = array($pageID => array($stringID => true));
189
            return $this->_remove($tmp);
190
        }
191
 
192
    }
193
 
194
    // }}}
195
    // {{{ removePage
196
 
197
    /**
198
     * Remove all the strings in the given page/group (domain)
199
     *
200
     * @param string $pageID page/group ID
201
     * @param string $path   path to gettext data dir
202
     *
203
     * @return mixed true on success, PEAR_Error on failure
204
     */
205
    function removePage($pageID = null, $path = null)
206
    {
207
        if (!isset($pageID)) {
208
            $pageID = $this->options['default_domain'];
209
        }
210
 
211
        if (!isset($path)) {
212
            if (!empty($this->_domains[$pageID])) {
213
                $path = $this->_domains[$pageID];
214
            } else {
215
                $path = $this->_domains[$this->options['default_domain']];
216
            }
217
        }
218
 
219
        if (PEAR::isError($e = $this->_removeDomain($pageID))) {
220
            return $e;
221
        }
222
 
223
        $this->fetchLangs();
224
        foreach ($this->langs as $langID => $lang) {
225
            $domain_file = $path .'/'. $langID .'/LC_MESSAGES/'. $pageID .'.';
226
            if (!@unlink($domain_file.'mo') || !@unlink($domain_file.'po')) {
227
                return $this->raiseError('Cannot delete page ' . $pageID. ' (file '.$domain_file.'.*)',
228
                    TRANSLATION2_ERROR
229
                );
230
            }
231
        }
232
 
233
        return true;
234
    }
235
 
236
    // }}}
237
    // {{{ update()
238
 
239
    /**
240
     * Update
241
     *
242
     * Alias for Translation2_Admin_Container_gettext::add()
243
     *
244
     * @param string $stringID string ID
245
     * @param string $pageID   page/group ID
246
     * @param array  $strings  strings
247
     *
248
     * @return  mixed
249
     * @access  public
250
     * @see add()
251
     */
252
    function update($stringID, $pageID, $strings)
253
    {
254
        return $this->add($stringID, $pageID, $strings);
255
    }
256
 
257
    // }}}
258
    // {{{ removeLang()
259
 
260
    /**
261
     * Remove Language
262
     *
263
     * @param string $langID language ID
264
     * @param bool   $force  (unused)
265
     *
266
     * @return true|PEAR_Error
267
     * @access public
268
     */
269
    function removeLang($langID, $force = false)
270
    {
271
        include_once 'System.php';
272
        foreach ((array) $this->_domains as $domain => $path) {
273
            if (is_dir($fp = $path .'/'. $langID)) {
274
                if (PEAR::isError($e = System::rm(array('-rf', $fp))) || !$e) {
275
                    return $e ? $e : PEAR::raiseError(sprintf(
276
                            'Could not remove language "%s" from domain "%s" '.
277
                            'in path "%s" (probably insufficient permissions)',
278
                            $langID, $domain, $path
279
                        ),
280
                        TRANSLATION2_ERROR
281
                    );
282
                }
283
            }
284
        }
285
        return true;
286
    }
287
 
288
    // }}}
289
    // {{{ updateLang()
290
 
291
    /**
292
     * Update the lang info in the langs_avail file
293
     *
294
     * @param array $langData language data
295
     *
296
     * @return mixed Returns true on success or PEAR_Error on failure.
297
     * @access public
298
     */
299
    function updateLang($langData)
300
    {
301
        if (PEAR::isError($changed = $this->_updateLangData($langData))) {
302
            return $changed;
303
        }
304
        return $changed ? $this->_writeLangsAvailFile() : true;
305
    }
306
 
307
    // }}}
308
    // {{{ getPageNames()
309
 
310
    /**
311
     * Get a list of all the domains
312
     *
313
     * @return array
314
     * @access public
315
     */
316
    function getPageNames()
317
    {
318
        return array_keys($this->_domains);
319
    }
320
 
321
    // }}}
322
    // {{{ begin()
323
 
324
    /**
325
     * Begin
326
     *
327
     * @return  void
328
     * @access  public
329
     */
330
    function begin()
331
    {
332
        $this->_bulk = true;
333
    }
334
 
335
    // }}}
336
    // {{{ commit()
337
 
338
    /**
339
     * Commit
340
     *
341
     * @return true|PEAR_Error on failure.
342
     * @access public
343
     */
344
    function commit()
345
    {
346
        $this->_bulk = false;
347
        if (isset($this->_queue['remove'])) {
348
            if (PEAR::isError($e = $this->_remove($this->_queue['remove']))) {
349
                return $e;
350
            }
351
        }
352
        if (isset($this->_queue['add'])) {
353
            if (PEAR::isError($e = $this->_add($this->_queue['add']))) {
354
                return $e;
355
            }
356
        }
357
        return true;
358
    }
359
 
360
    // }}}
361
    // {{{ _add()
362
 
363
    /**
364
     * Add
365
     *
366
     * @param array &$bulk array('pageID' => array([languages]))
367
     *
368
     * @return true|PEAR_Error on failure.
369
     * @access private
370
     */
371
    function _add(&$bulk)
372
    {
373
        include_once 'File/Gettext.php';
374
        $gtFile = &File_Gettext::factory($this->options['file_type']);
375
        $langs  = $this->getLangs('array');
376
 
377
        foreach ((array) $bulk as $pageID => $languages) {
378
            //create the new domain on demand
379
            if (!isset($this->_domains[$pageID])) {
380
                if (PEAR::isError($e = $this->_addDomain($pageID))) {
381
                    return $e;
382
                }
383
            }
384
            $path = $this->_domains[$pageID];
385
            if ($path[strlen($path)-1] != '/' && $path[strlen($path)-1] != '\\') {
386
                $path .= '/';
387
            }
388
            $file = '/LC_MESSAGES/'. $pageID .'.'. $this->options['file_type'];
389
 
390
            foreach ($languages as $lang => $strings) {
391
 
392
                if (is_file($path . $lang . $file)) {
393
                    if (PEAR::isError($e = $gtFile->load($path . $lang . $file))) {
394
                        return $e;
395
                    }
396
                }
397
 
398
                if (!isset($gtFile->meta['Content-Type'])) {
399
                    $gtFile->meta['Content-Type'] = 'text/plain; charset=';
400
                    if (isset($langs[$lang]['encoding'])) {
401
                        $gtFile->meta['Content-Type'] .= $langs[$lang]['encoding'];
402
                    } else {
403
                        $gtFile->meta['Content-Type'] .= $this->options['default_encoding'];
404
                    }
405
                }
406
 
407
                foreach ($strings as $stringID => $string) {
408
                    $gtFile->strings[$stringID] = $string;
409
                }
410
 
411
                if (PEAR::isError($e = $gtFile->save($path . $lang . $file))) {
412
                    return $e;
413
                }
414
 
415
                //refresh cache
416
                $this->cachedDomains[$lang][$pageID] = $gtFile->strings;
417
            }
418
        }
419
 
420
        $bulk = null;
421
        return true;
422
    }
423
 
424
    // }}}
425
    // {{{ _remove()
426
 
427
    /**
428
     * Remove
429
     *
430
     * @param array &$bulk array('pageID' => array([languages]))
431
     *
432
     * @return true|PEAR_Error on failure.
433
     * @access private
434
     */
435
    function _remove(&$bulk)
436
    {
437
        include_once 'File/Gettext.php';
438
        $gtFile = &File_Gettext::factory($this->options['file_type']);
439
 
440
        foreach ($this->getLangs('ids') as $lang) {
441
            foreach ((array) $bulk as $pageID => $stringIDs) {
442
                $file = sprintf(
443
                    '%s/%s/LC_MESSAGES/%s.%s',
444
                    $this->_domains[$pageID],
445
                    $lang,
446
                    $pageID,
447
                    $this->options['file_type']
448
                );
449
 
450
                if (is_file($file)) {
451
                    if (PEAR::isError($e = $gtFile->load($file))) {
452
                        return $e;
453
                    }
454
 
455
                    foreach (array_keys($stringIDs) as $stringID) {
456
                        unset($gtFile->strings[$stringID]);
457
                    }
458
 
459
                    if (PEAR::isError($e = $gtFile->save($file))) {
460
                        return $e;
461
                    }
462
 
463
                    //refresh cache
464
                    $this->cachedDomains[$lang][$pageID] = $gtFile->strings;
465
                }
466
            }
467
        }
468
 
469
        $bulk = null;
470
        return true;
471
    }
472
 
473
    // }}}
474
    // {{{ _addDomain()
475
 
476
    /**
477
     * Add the path-to-the-new-domain to the domains-path-INI-file
478
     *
479
     * @param string $pageID domain name
480
     *
481
     * @return true|PEAR_Error on failure
482
     * @access private
483
     */
484
    function _addDomain($pageID)
485
    {
486
        $domain_path = count($this->_domains) ? reset($this->_domains) : 'locale/';
487
 
488
        if (!is_resource($f = fopen($this->options['domains_path_file'], 'a'))) {
489
            return $this->raiseError(sprintf(
490
                    'Cannot write to domains path INI file "%s"',
491
                    $this->options['domains_path_file']
492
                ),
493
                TRANSLATION2_ERROR_CANNOT_WRITE_FILE
494
            );
495
        }
496
 
497
        $CRLF = $this->options['carriage_return'];
498
 
499
        while (true) {
500
            if (@flock($f, LOCK_EX)) {
501
                fwrite($f, $CRLF . $pageID . ' = ' . $domain_path . $CRLF);
502
                @flock($f, LOCK_UN);
503
                fclose($f);
504
                break;
505
            }
506
        }
507
 
508
        $this->_domains[$pageID] = $domain_path;
509
 
510
        return true;
511
    }
512
 
513
    // }}}
514
    // {{{ _removeDomain()
515
 
516
    /**
517
     * Remove the path-to-the-domain from the domains-path-INI-file
518
     *
519
     * @param string $pageID domain name
520
     *
521
     * @return true|PEAR_Error on failure
522
     * @access private
523
     */
524
    function _removeDomain($pageID)
525
    {
526
        $domain_path = count($this->_domains) ? reset($this->_domains) : 'locale/';
527
 
528
        if (!is_resource($f = fopen($this->options['domains_path_file'], 'r+'))) {
529
            return $this->raiseError(sprintf(
530
                    'Cannot write to domains path INI file "%s"',
531
                    $this->options['domains_path_file']
532
                ),
533
                TRANSLATION2_ERROR_CANNOT_WRITE_FILE
534
            );
535
        }
536
 
537
        $CRLF = $this->options['carriage_return'];
538
 
539
        while (true) {
540
            if (@flock($f, LOCK_EX)) {
541
                $pages = file($this->options['domains_path_file']);
542
                foreach ($pages as $page) {
543
                    if (preg_match('/^'.$pageID.'\s*=/', $page)) {
544
                        //skip
545
                        continue;
546
                    }
547
                    fwrite($f, $page . $CRLF);
548
                }
549
                fflush($f);
550
                ftruncate($f, ftell($f));
551
                @flock($f, LOCK_UN);
552
                fclose($f);
553
                break;
554
            }
555
        }
556
 
557
        unset($this->_domains[$pageID]);
558
 
559
        return true;
560
    }
561
 
562
    // }}}
563
    // {{{ _writeLangsAvailFile()
564
 
565
    /**
566
     * Write the langs_avail INI file
567
     *
568
     * @return true|PEAR_Error on failure.
569
     * @access private
570
     */
571
    function _writeLangsAvailFile()
572
    {
573
        if (PEAR::isError($langs = $this->getLangs())) {
574
            return $langs;
575
        }
576
 
577
        if (!is_resource($f = fopen($this->options['langs_avail_file'], 'w'))) {
578
            return $this->raiseError(sprintf(
579
                    'Cannot write to available langs INI file "%s"',
580
                    $this->options['langs_avail_file']
581
                ),
582
                TRANSLATION2_ERROR_CANNOT_WRITE_FILE
583
            );
584
        }
585
        $CRLF = $this->options['carriage_return'];
586
 
587
        @flock($f, LOCK_EX);
588
 
589
        foreach ($langs as $id => $data) {
590
            fwrite($f, '['. $id .']'. $CRLF);
591
            foreach ($this->_fields as $k) {
592
                if (isset($data[$k])) {
593
                    fwrite($f, $k . ' = ' . $data[$k] . $CRLF);
594
                }
595
            }
596
            fwrite($f, $CRLF);
597
        }
598
 
599
        @flock($f, LOCK_UN);
600
        fclose($f);
601
        return true;
602
    }
603
 
604
    // }}}
605
    // {{{ _updateLangData()
606
 
607
    /**
608
     * Update Lang Data
609
     *
610
     * @param array $langData language data
611
     *
612
     * @return true|PEAR_Error on failure.
613
     * @access private
614
     */
615
    function _updateLangData($langData)
616
    {
617
        if (PEAR::isError($langs = $this->getLangs())) {
618
            return $langs;
619
        }
620
 
621
        $lang    = &$langs[$langData['lang_id']];
622
        $changed = false;
623
        foreach ($this->_fields as $k) {
624
            if (    isset($langData[$k]) &&
625
                    (!isset($lang[$k]) || $langData[$k] != $lang[$k])) {
626
                $lang[$k] = $langData[$k];
627
                $changed  = true;
628
            }
629
        }
630
 
631
        if ($changed) {
632
            $lang['id']  = $langData['lang_id'];
633
            $this->langs = $langs;
634
        }
635
        return $changed;
636
    }
637
 
638
    // }}}
639
}
640
?>