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