acl node reset not working
[tine20] / tine20 / langHelper.php
1 #!/usr/bin/env php
2 <?php
3 /**
4  * lang helper
5  *
6  * @package     HelperScripts
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Cornelius Weiss <c.weiss@metaways.de>
9  * @copyright   Copyright (c) 2007-2012 Metaways Infosystems GmbH (http://www.metaways.de)
10  * 
11  * @todo        add filter for applications
12  */
13
14 if (isset($_SERVER['HTTP_HOST'])) {
15     die('not allowed!');
16 }
17
18 $paths = array(
19     realpath(dirname(__FILE__)),
20     realpath(dirname(__FILE__) . '/library'),
21     realpath(dirname(__FILE__) . '/vendor/zendframework/zendframework1/library'),
22     get_include_path()
23 );
24 set_include_path(implode(PATH_SEPARATOR, $paths));
25
26 require_once 'Zend/Loader/Autoloader.php';
27 $autoloader = Zend_Loader_Autoloader::getInstance();
28 $autoloader->setFallbackAutoloader(true);
29
30 /**
31  * path to tine 2.0 checkout
32  */
33 global $tine20path;
34 $tine20path = dirname(__FILE__);
35
36 /**
37  * options
38  */
39 try {
40     $opts = new Zend_Console_Getopt(
41     array(
42         'verbose|v'       => 'Output messages',
43         'clean|c'         => 'Cleanup all tmp files',
44         'wipe|w'          => 'wipe all local translations',
45         'update|u'        => 'Update lang files (shortcut for --pot --potmerge --mo --clean)',
46         'package=s'       => 'Create a translation package',
47         'app=s'           => 'Work only on this Application',
48         'pot'             => '(re) generate xgettext po template files',
49         'potmerge'        => 'merge pot contents into po files',
50         'statistics'      => 'generate lang statistics',
51         'contribute=s'    => 'merge contributed translations of <path to archive> (implies --update)',
52         'language=s'      => 'contributed language or language to handle',
53         'mo'              => 'Build mo files',
54         'newlang=s'       => 'Add new language',
55         'overwrite'       => '  overwrite existing lang files',
56         'git'             => 'Add new/updated lang files to git',
57         'help|h'          => 'Display this help Message',
58         
59         //'filter=s'        => 'Filter for applications'
60     ));
61     $opts->parse();
62 } catch (Zend_Console_Getopt_Exception $e) {
63    echo $e->getUsageMessage();
64    exit;
65 }
66
67 // Check app Parameter
68 if(!empty($opts->app)) {
69     if(!array_key_exists($opts->app, Tinebase_Translation::getTranslationDirs())) {
70         echo chr(10);
71         echo 'Application "' . $opts->app . '" not found!'. chr(10);
72         echo chr(10);
73         exit;
74     } else {
75         echo 'Working on Application "' . $opts->app . '"...'. chr(10) ;
76     }
77
78 }
79
80 if ($opts->wipe) {
81     foreach (Tinebase_Translation::getTranslationDirs() as $appName => $translationPath) {
82         
83         if( ! checkAppName($appName, $_verbose)) {
84             continue;
85         }
86         
87         if ($_verbose) {
88             echo "Processing $appName po files \n";
89         }
90         
91         `cd "$translationPath" 
92         rm *`;
93     }
94 }
95
96 if (count($opts->toArray()) === 0  || $opts->h) {
97     echo $opts->getUsageMessage();
98     exit;
99 }
100
101 if ($opts->u || $opts->contribute) {
102     $opts->pot = $opts->potmerge = $opts->mo = $opts->c = true;
103 }
104
105 if ($opts->pot) {
106     generatePOTFiles($opts->v);
107 }
108
109 if ($opts->potmerge) {
110     potmerge($opts->v);
111 }
112
113 if ($opts->newlang) {
114     generateNewTranslationFiles($opts->newlang, $opts->v, $opts->overwrite);
115     potmerge($opts->v);
116     msgfmt($opts->v);
117     if($opts->git) {
118         gitAdd($opts->newlang);
119     }
120     $opts->c = true;
121 }
122
123 if($opts->contribute) {
124     $_verbose = $opts->v;
125     if (!isset ($opts->language)) {
126         echo "Error: you need to specify the contributed language (--language) \n";
127         exit;
128     }
129     if (! isset ($opts->contribute)) {
130         echo "You need to specify an archive of the lang updates!  \n";
131         exit;
132     }
133     if (! is_file($opts->contribute)) {
134         echo "Archive file '" . $opts->contribute . "' could not be found! \n";
135         exit;
136     }
137     contributorsMerge($opts->v, $opts->language, $opts->contribute);
138     echo "merging completed :-) \n";
139 }
140
141 if ($opts->mo) {
142     msgfmt($opts->v);
143 }
144
145 if ($opts->c || $opts->package) {
146     // remove translation backups of msgmerge
147     `cd "$tine20path"
148     find . -type f -iname "*.po~" -exec rm {} \;`;
149 }
150 if ($opts->statistics) {
151     statistics($opts->v);
152 }
153
154 if ($opts->package) {
155     buildpackage($opts->v, $opts->{'package'} ?: NULL);
156 }
157
158 /**
159  * returns list of existing langugages
160  * (those, having a correspoinding Tinebase po file)
161  *
162  * @return array 
163  */
164 function getExistingLanguages($_verbose)
165 {
166     global $tine20path;
167     
168     $langs = array();
169     foreach (scandir("$tine20path/Tinebase/translations") as $poFile) {
170         if (substr($poFile, -3) == '.po') {
171             $langCode = substr($poFile, 0, -3);
172             if ($_verbose) {
173                 echo "found language '$langCode'\n";
174             }
175             
176             $langs[] = $langCode;
177         }
178     }
179     
180     return $langs;
181 }
182
183 /**
184  * Checks if Application is needed
185  * @param bool $verbose should a message appear on returning false 
186  * @param string $appName
187  */
188 function checkAppName($appName, $verbose) {
189     
190     global $opts;
191     
192     if(!empty($opts->app)) {
193         $ret =  strtolower($appName) == strtolower($opts->app);
194     } else {
195         $ret = true;
196     }
197     
198     if ($verbose && ! $ret) {
199         echo 'Skipping Application ' . $appName .chr(10);
200     }
201     
202     return $ret;
203 }
204
205 /**
206  * checks if language parameter is set and verifies if translation
207  * of the language defined by the langCode should be created
208  * @param string $langCode
209  * @param bool $verbose should a message appear on returning false
210  * @return bool
211  */
212 function checkLang($langCode, $verbose) {
213     global $opts;
214     
215     if(! empty($opts->language)) {
216         $ret = ($langCode == $opts->language);
217     } else {
218         $ret = true;
219     }
220     
221     if ($verbose && ! $ret) {
222         echo 'Skipping Language ' . $langCode .chr(10);
223     }
224     
225     return $ret;
226 }
227
228 /**
229  * checks wether a translation exists or not
230  * 
231  * @param  string $_locale
232  * @return bool
233  */
234 function translationExists($_locale)
235 {
236     foreach (Tinebase_Translation::getTranslationDirs() as $dir) {
237         if (file_exists("$dir/$_locale.po")) {
238             return true;
239         }
240     }
241     return false;
242 }
243
244 /**
245  * (re) generates po template files
246  */
247 function generatePOTFiles($_verbose)
248 {
249     global $tine20path;
250     if (file_exists("$tine20path/Tinebase/js/tine-all.js")) {
251         die("You need to remove tine-all.js before updating lang files! \n");
252     }
253     
254     foreach (Tinebase_Translation::getTranslationDirs() as $appName => $translationPath) {
255         
256         if( ! checkAppName($appName, $_verbose)) {
257             continue;
258         }
259         
260         if ($_verbose) {
261             echo "Creating $appName template \n";
262         }
263         $appPath = "$translationPath/../";
264         
265         generateNewTranslationFile('en', 'GB', $appName, getPluralForm('English'), "$translationPath/template.pot",  $_verbose);
266         
267         `cd "$appPath" 
268         find . -type f -iname "*.php" -or -type f -iname "*.js" -or -type f -iname "*.xml" | grep -v node_modules | xgettext --force-po --omit-header -j -o translations/template.pot -L Python --from-code=utf-8 -k=_ -f - 2> /dev/null`;
269         
270     }
271 }
272
273 /**
274  * potmerge
275  */
276 function potmerge($_verbose)
277 {
278     
279     $langs = getExistingLanguages($_verbose);
280     $msgDebug = $_verbose ? '' : '2> /dev/null';
281     
282     foreach (Tinebase_Translation::getTranslationDirs() as $appName => $translationPath) {
283         
284         if( ! checkAppName($appName, $_verbose)) {
285             continue;
286         }
287         
288         if ($_verbose) {
289             echo "Processing $appName po files \n";
290         }
291         
292         if ($_verbose) {
293            echo "creating en.po from template.po\n";
294         }
295         generateNewTranslationFile('en', 'GB', $appName, getPluralForm('English'), "$translationPath/en.po",  $_verbose);
296         $enHeader = file_get_contents("$translationPath/en.po");
297         `cd "$translationPath"
298          msgen template.pot > en.po $msgDebug`;
299          
300         foreach ($langs as $langCode) {
301             
302             if (! checkLang($langCode, $_verbose)) continue;
303             
304             $poFile = "$translationPath/$langCode.po";
305             
306             if (! is_file($poFile)) {
307                 if ($_verbose) {
308                     echo "Adding non exising translation $langCode for $appName\n";
309                 }
310                 
311                 if (strpos($langCode, '_') !== FALSE) {
312                     list ($language, $region) = explode('_', $langCode);
313                 } else {
314                     $language = $langCode;
315                     $region = '';
316                 }
317     
318                 $locale = new Zend_Locale('en');
319                 $languageName = $locale->getTranslation($language, 'language');
320                 $regionName = ($region) ? $locale->getTranslation($region, 'country') : '';
321                 $pluralForm = getPluralForm($languageName);
322                 
323                 generateNewTranslationFile($languageName, $regionName, $appName, $pluralForm, $poFile, $_verbose);
324             }
325
326             if ($_verbose) {
327                echo $poFile . ": ";
328             }
329             `cd "$translationPath"
330              msgmerge --no-fuzzy-matching --no-wrap $poFile template.pot $msgDebug -o $poFile`;
331         }
332     }
333 }
334
335 /**
336  * contributorsMerge
337  *
338  * @param bool   $_verbose
339  * @param string $_language
340  * @param string $_archive
341  */
342 function contributorsMerge($_verbose, $_language, $_archive)
343 {
344     global $tine20path;
345     $tmpdir = '/tmp/tinetranslations/';
346     `rm -Rf $tmpdir`;
347     `mkdir $tmpdir`;
348     //`cp $archive $tmpdir`;
349     switch (substr($_archive, -4)) {
350         case '.zip':
351             `unzip -d $tmpdir '$_archive'`;
352             break;
353         default:
354             echo "Error: Only zip archives are supported \n";
355             exit;
356             break;
357     }
358     
359     $basePath = $tmpdir;
360     while (true) {
361         $contents = scandir($basePath);
362         if (count($contents ) == 3) {
363             $basePath .= $contents[2] . '/';
364             if (! is_dir($basePath)) {
365                 echo "Error: Could not find translations! \n";
366                 exit;
367             }
368         } elseif ($contents[2] == '__MACOSX') {
369             // max os places a hiddes __MACOSX in the archives
370             $basePath .= $contents[3] . '/';
371             if (! is_dir($basePath)) {
372                 echo "Error: Could not find translations! \n";
373                 exit;
374             }
375         } else {
376             break;
377         }
378     }
379     
380     foreach ($contents as $appName) {
381         if ($appName{0} == '.' || $appName{0} == '_') continue;
382         
383         if( ! checkAppName($appName, $_verbose)) {
384             continue;
385         }
386         
387         if ($_verbose) {
388             echo "Processing translation updates for $appName \n";
389         }
390         
391         $tinePoFile        = "$tine20path/$appName/translations/$_language.po";
392         $contributedPoFile = "$basePath/$appName/translations/$_language.po";
393         
394         if (! is_file($tinePoFile)) {
395             echo "Error: could not find langfile $_language.po in Tine 2.0's $appName \n";
396             continue;
397             exit;
398         }
399         if (! is_file($contributedPoFile)) {
400             //check leggacy
401             $contributedPoFile = "$basePath/$appName/$_language.po";
402             if (! is_file($contributedPoFile)) {
403                 echo "Warning: could not find langfile $_language.po in contributor's $appName \n";
404                 continue;
405             }
406         }
407         // do the actual merging
408         $output = '2> /dev/null';
409         if ($_verbose) {
410            echo $_language . ".po : ";
411            $output = '';
412         }
413         `msgmerge --no-fuzzy-matching --update '$contributedPoFile'  $tinePoFile $output`;
414         `cp '$contributedPoFile' $tinePoFile`;
415     }
416 }
417
418 /**
419  * msgfmt
420  */
421 function msgfmt ($_verbose)
422 {
423     foreach (Tinebase_Translation::getTranslationDirs() as $appName => $translationPath) {
424         
425         if( ! checkAppName($appName, $_verbose)) {
426             continue;
427         }
428         
429         if ($_verbose) {
430             echo "Entering $appName \n";
431         }
432         foreach (scandir($translationPath) as $poFile) {
433             if (substr($poFile, -3) == '.po') {
434                 $langName = substr($poFile, 0, -3);
435                 if ($_verbose) {
436                     echo "Processing $appName/$poFile \n";
437                 }
438                 // create mo file
439                 `cd "$translationPath"
440                 msgfmt -o $langName.mo $poFile`;
441             }
442         }
443     }
444 }
445
446 /**
447  * create package file for translators
448  * 
449  * @param boolean $_verbose
450  * @param string $_archive file or directory
451  */
452 function buildpackage($_verbose, $_archive)
453 {
454     $destDir = __DIR__;
455     $tmpdir = '/tmp/tinetranslations/';
456     `rm -Rf $tmpdir`;
457     `mkdir $tmpdir`;
458     
459     foreach (Tinebase_Translation::getTranslationDirs() as $appName => $translationPath) {
460         
461         if( ! checkAppName($appName, $_verbose)) {
462             continue;
463         }
464         
465         `mkdir $tmpdir/$appName`;
466         generateNewTranslationFile('en', 'GB', $appName, getPluralForm('English'), "$tmpdir/$appName/$appName.pot",  $_verbose);
467         `cat $translationPath/template.pot >> $tmpdir/$appName/$appName.pot`;
468         `cp $translationPath/*.po $tmpdir/$appName/`;
469     }
470     
471     if ($_archive && is_dir($_archive)) {
472         `cp -r $tmpdir/* $_archive`;
473         
474         if (is_dir("$_archive/.bzr")) {
475             `cd $_archive
476             bzr add *
477             bzr commit -m 'Tine 2.0 Translations'
478             bzr push`;
479         }
480     } else {
481         $filename = ($_archive && strpos($_archive, 'tar.gz') !== FALSE) ? $_archive : 'lp-lang-package.tar.gz';
482         `cd "$tmpdir"
483          tar -czf $filename *`;
484         `mv $tmpdir/$filename {$destDir}`;
485     }
486 }
487
488 /**
489  * generate statistics
490  *
491  * @param  bool $_verbose
492  * @return void
493  */
494 function statistics($_verbose)
495 {
496     global $tine20path;
497     $statsFile = "$tine20path/langstatistics.json";
498     $locale = new Zend_Locale('en');
499     
500     $langStats       = array();
501     $poFilesStats    = array();
502     
503     foreach (Tinebase_Translation::getTranslationDirs() as $appName => $translationPath) {
504         
505         if( ! checkAppName($appName, $_verbose)) {
506             continue;
507         }
508         
509         if ($_verbose) {
510             echo "Entering $appName \n";
511         }
512         $appStats[$appName] = array();
513         foreach (scandir($translationPath) as $poFile) {
514             if (substr($poFile, -3) == '.po') {
515                 if ($_verbose) {
516                     echo "Processing $appName/$poFile \n";
517                 }
518                 
519                 $langCode = substr($poFile, 0, -3);
520                 $langLocale = new Zend_Locale($langCode);
521                 
522                 $statsOutput = `msgfmt --statistics $translationPath/$poFile 2>&1`;
523                 $statsParts = explode(',', $statsOutput);
524                 $statsParts = preg_replace('/^\s*(\d+).*/i', '$1', $statsParts);
525
526                 $translated = $fuzzy = $untranslated = $total = 0;
527                 switch (count($statsParts)) {
528                     case 1:
529                         $translated     = $statsParts[0];
530                         break;
531                     case 2:
532                         $translated     = $statsParts[0];
533                         $untranslated   = $statsParts[1];
534                         break;
535                     case 3:
536                         $translated     = $statsParts[0];
537                         $fuzzy          = $statsParts[1];
538                         $untranslated   = $statsParts[2];
539                         break;
540                     default:
541                         echo "Unexpected statistic return \n";
542                         exit;
543                 }
544                 $total = array_sum($statsParts);
545                 
546                 $poFileStats = array(
547                     'locale'       => $langCode,
548                     'language'     => $locale->getTranslation($langLocale->getLanguage(), 'language'),
549                     'region'       => $locale->getTranslation($langLocale->getRegion(), 'country'),
550                     'appname'      => $appName,
551                     'translated'   => (int)$translated,
552                     'fuzzy'        => (int)$fuzzy,
553                     'untranslated' => (int)$untranslated,
554                     'total'        => array_sum($statsParts),
555                 );
556                 $poFilesStats[] = $poFileStats;
557                 
558                 // sum up lang statistics
559                 $langStats[$langCode] = (isset($langStats[$langCode]) || array_key_exists($langCode,$langStats)) ? $langStats[$langCode] : array(
560                     'locale'       => '',
561                     'language'     => '',
562                     'region'       => $locale->getTranslation($langLocale->getRegion(), 'country'),
563                     'translated'   => 0,
564                     'fuzzy'        => 0,
565                     'untranslated' => 0,
566                     'total'        => 0
567                 );
568                 
569                 $langStats[$langCode]['locale']        = $langCode;
570                 $langStats[$langCode]['language']      = $locale->getTranslation($langLocale->getLanguage(), 'language');
571                 $langStats[$langCode]['region']        = $locale->getTranslation($langLocale->getRegion(), 'country');
572                 $langStats[$langCode]['appname']       = 'all';
573                 $langStats[$langCode]['translated']   += $poFileStats['translated'];
574                 $langStats[$langCode]['fuzzy']        += $poFileStats['fuzzy'];
575                 $langStats[$langCode]['untranslated'] += $poFileStats['untranslated'];
576                 $langStats[$langCode]['total']        += $poFileStats['total'];
577             }
578         }
579     }
580     
581     // clean up unwanted messages.mo
582     `rm messages.mo`;
583     
584     $results = array(
585         'version'      => Tinebase_Helper::getDevelopmentRevision(),
586         'langStats'    => array_values($langStats),
587         'poFilesStats' => $poFilesStats
588     );
589     
590     file_put_contents($statsFile, Zend_Json::encode($results));
591 }
592
593 /**
594  * generates po file with appropriate header
595  *
596  * @param  string $_languageName
597  * @param  string $_regionName
598  * @param  string $_appName
599  * @param  bool   $_verbose
600  * @return void
601  */
602 function generateNewTranslationFile($_languageName, $_regionName, $_appName, $_pluralForm, $_file, $_verbose=false)
603 {
604     global $tine20path;
605
606     $poHeader = 
607 'msgid ""
608 msgstr ""
609 "Project-Id-Version: Tine 2.0 - ' . $_appName . '\n"
610 "POT-Creation-Date: 2008-05-17 22:12+0100\n"
611 "PO-Revision-Date: 2008-07-29 21:14+0100\n"
612 "Last-Translator: Cornelius Weiss <c.weiss@metaways.de>\n"
613 "Language-Team: Tine 2.0 Translators\n"
614 "MIME-Version: 1.0\n"
615 "Content-Type: text/plain; charset=UTF-8\n"
616 "Content-Transfer-Encoding: 8bit\n"
617 "X-Poedit-Language: ' . $_languageName . '\n"
618 "X-Poedit-Country: ' . strtoupper($_regionName) . '\n"
619 "X-Poedit-SourceCharset: utf-8\n"
620 "Plural-Forms: ' . $_pluralForm . '\n"
621
622 ';
623             
624     if ($_verbose) {
625         echo "  Writing $_languageName po header for $_appName \n";
626     }
627     file_put_contents($_file, $poHeader);
628 }
629
630
631 /**
632  * generates po files with appropriate header for a given locale and all apps
633  * 
634  * @param  string $_locale
635  * @return void
636  */
637 function generateNewTranslationFiles($_locale, $_verbose=false, $_overwrite=false)
638 {
639     list ($language, $region) = explode('_', $_locale);
640     
641     $locale = new Zend_Locale('en');
642     $languageName = $locale->getTranslation($language, 'language');
643     $regionName = $locale->getTranslation($region, 'country');
644     
645     if (!$languageName) {
646         die("Language '$language' is not valid / known \n");
647     }
648     if ($region && ! $regionName) {
649         die("Region '$region' is not valid / known \n");
650     }
651     $regionName = $region ? $regionName : 'Not Specified / Any';
652     
653     if (translationExists($_locale)) {
654         if ($_overwrite) {
655             if ($_verbose) echo "Overwriting existing lang files for $_locale \n";
656         } else {
657             die("Translations for $_locale already exist \n");
658         }
659     }
660     
661     if ($_verbose) {
662         echo "Generation new lang files for \n";
663         echo "  Language: $languageName \n";
664         echo "  Region: $regionName \n";
665     }
666     
667     $pluralForm = getPluralForm($languageName);
668     
669     foreach (Tinebase_Translation::getTranslationDirs() as $appName => $translationPath) {
670         
671         if( ! checkAppName($appName, $_verbose)) {
672             continue;
673         }
674         
675         $file = "$translationPath/$_locale.po";
676         generateNewTranslationFile($languageName, $regionName, $appName, $pluralForm, $file, $_verbose);
677     }
678     
679     
680 }
681
682 /**
683  * returns plural form of given language
684  * 
685  * @link http://www.gnu.org/software/automake/manual/gettext/Plural-forms.html
686  * @param  string $_languageName
687  * @return string 
688  */
689 function getPluralForm($_languageName)
690 {
691     switch ($_languageName) {
692         // Asian family
693         case 'Japanese' :
694         case 'Korean' :
695         case 'Vietnamese' :
696         case 'Chinese' :
697         case 'Thai' :
698         // Turkic/Altaic family
699         case 'Turkish' :
700             return 'nplurals=1; plural=0;';
701             
702         // Germanic family
703         case 'Danish' :
704         case 'Dutch' :
705         case 'English' :
706         case 'Faroese' :
707         case 'German' :
708         case 'Norwegian' :
709         case 'Norwegian BokmÃ¥l' :
710         case 'Swedish' :
711         // Finno-Ugric family
712         case 'Estonian' :
713         case 'Finnish' :
714         // Latin/Greek family
715         case 'Greek' :
716         // Semitic family
717         case 'Hebrew' :
718         // Romanic family
719         case 'Italian' :
720         case 'Portuguese' :
721         case 'Spanish' :
722         case 'Catalan' :
723         // Artificial
724         case 'Esperanto' :
725         // Finno-Ugric family
726         case 'Hungarian' :
727         // ?
728         case 'Bulgarian' :
729             $pluralForm = 'nplurals=2; plural=n != 1;';
730             break;
731             
732         // Romanic family
733         case 'French' :
734             $pluralForm = 'nplurals=2; plural=n>1;';
735             break;
736         case 'Brazilian Portuguese' :
737             $pluralForm = 'nplurals=2; plural=n != 1;';
738             break;
739             
740         // Baltic family
741         case 'Latvian' :
742             $pluralForm = 'nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;';
743             break;
744             
745         // Celtic
746         case 'Gaeilge' :
747             $pluralForm = 'nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;';
748             break;
749             
750         // Romanic family
751         case 'Romanian' :
752             $pluralForm = 'nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;';
753             break;
754             
755         // Baltic family
756         case 'Lithuanian' :
757             $pluralForm = 'nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2;';
758             break;
759             
760         // Slavic family
761         case 'Croatian' :
762         case 'Serbian' :
763         case 'Russian' :
764         case 'Ukrainian' :
765             $pluralForm = 'nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;';
766             break;
767             
768         // Slavic family
769         case 'Slovak' :
770         case 'Czech' :
771             $pluralForm = 'nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;';
772             break;
773             
774         // Slavic family
775         case 'Polish' :
776             $pluralForm = 'nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;';
777             break;
778         
779         // Slavic family
780         case 'Slovenian' :
781         case 'Albanian' :
782             $pluralForm = 'nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;';
783             break;
784         
785         // Mixed
786         case 'Persian' :
787                 $pluralForm = 'nplurals=1; plural=0;';
788                 break;
789         
790         default :
791             die ("Error: Plural form of $_languageName is not defined! \n");
792             
793     }
794     return $pluralForm;
795 }
796
797 function gitAdd($_locale)
798 {
799     foreach (Tinebase_Translation::getTranslationDirs() as $dir) {
800         if (file_exists("$dir/$_locale.po")) {
801             `cd "$dir"
802             git add "$dir/$_locale.po"`;
803         }
804         if (file_exists("$dir/$_locale.mo")) {
805             `cd "$dir"
806             git add "$dir/$_locale.mo"`;
807         }
808     }
809 }