From 2f19de11e4c77b0f9195c3868960d8105541359f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Thomas=20M=C3=BCller?= Date: Fri, 24 Oct 2014 13:53:01 +0200 Subject: [PATCH] adding console command to generate javascript translation files based on existing php translation files read server side translations from json files --- core/command/l10n/createjs.php | 120 +++++++++++++++++++++++++++++++++ core/register_command.php | 1 + l10n/l10n.pl | 19 ++++-- lib/private/l10n.php | 64 +++++++++--------- tests/data/l10n/cs.json | 6 ++ tests/data/l10n/cs.php | 5 -- tests/data/l10n/de.json | 6 ++ tests/data/l10n/de.php | 5 -- tests/data/l10n/ru.json | 6 ++ tests/data/l10n/ru.php | 5 -- tests/lib/l10n.php | 6 +- 11 files changed, 187 insertions(+), 56 deletions(-) create mode 100644 core/command/l10n/createjs.php create mode 100644 tests/data/l10n/cs.json delete mode 100644 tests/data/l10n/cs.php create mode 100644 tests/data/l10n/de.json delete mode 100644 tests/data/l10n/de.php create mode 100644 tests/data/l10n/ru.json delete mode 100644 tests/data/l10n/ru.php diff --git a/core/command/l10n/createjs.php b/core/command/l10n/createjs.php new file mode 100644 index 00000000000..f7d232bcc37 --- /dev/null +++ b/core/command/l10n/createjs.php @@ -0,0 +1,120 @@ + and + * Copyright (c) 2014 Stephen Colebrook + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Core\Command\L10n; + +use DirectoryIterator; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class CreateJs extends Command { + + protected function configure() { + $this + ->setName('l10n:createjs') + ->setDescription('Create javascript translation files for a given app') + ->addArgument( + 'app', + InputOption::VALUE_REQUIRED, + 'name of the app' + ) + ->addArgument( + 'lang', + InputOption::VALUE_OPTIONAL, + 'name of the language' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $app = $input->getArgument('app'); + $lang = $input->getArgument('lang'); + + $path = \OC_App::getAppPath($app); + if ($path === false) { + $output->writeln("The app <$app> is unknown."); + return; + } + $languages = $lang; + if (empty($lang)) { + $languages= $this->getAllLanguages($path); + } + + foreach($languages as $lang) { + $this->writeFiles($app, $path, $lang, $output); + } + } + + private function getAllLanguages($path) { + $result = array(); + foreach (new DirectoryIterator("$path/l10n") as $fileInfo) { + if($fileInfo->isDot()) { + continue; + } + if($fileInfo->isDir()) { + continue; + } + if($fileInfo->getExtension() !== 'php') { + continue; + } + $result[]= substr($fileInfo->getBasename(), 0, -4); + } + + return $result; + } + + private function writeFiles($app, $path, $lang, OutputInterface $output) { + list($translations, $plurals) = $this->loadTranslations($path, $lang); + $this->writeJsFile($app, $path, $lang, $output, $translations, $plurals); + $this->writeJsonFile($path, $lang, $output, $translations, $plurals); + } + + private function writeJsFile($app, $path, $lang, OutputInterface $output, $translations, $plurals) { + $jsFile = "$path/l10n/$lang.js"; + if (file_exists($jsFile)) { + $output->writeln("File already exists: $jsFile"); + return; + } + $content = "OC.L10N.register(\n \"$app\",\n {\n "; + $jsTrans = array(); + foreach ($translations as $id => $val) { + if (is_array($val)) { + $val = '[ ' . join(',', $val) . ']'; + } + $jsTrans[] = "\"$id\" : \"$val\""; + } + $content .= join(",\n ", $jsTrans); + $content .= "\n},\n\"$plurals\");\n"; + + file_put_contents($jsFile, $content); + $output->writeln("Javascript translation file generated: $jsFile"); + } + + private function writeJsonFile($path, $lang, OutputInterface $output, $translations, $plurals) { + $jsFile = "$path/l10n/$lang.json"; + if (file_exists($jsFile)) { + $output->writeln("File already exists: $jsFile"); + return; + } + $content = array('translations' => $translations, 'pluralForm' => $plurals); + file_put_contents($jsFile, json_encode($content)); + $output->writeln("Json translation file generated: $jsFile"); + } + + private function loadTranslations($path, $lang) { + $phpFile = "$path/l10n/$lang.php"; + $TRANSLATIONS = array(); + $PLURAL_FORMS = ''; + require $phpFile; + + return array($TRANSLATIONS, $PLURAL_FORMS); + } +} diff --git a/core/register_command.php b/core/register_command.php index aaf10d946b2..c5d9b6e342d 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -22,4 +22,5 @@ $application->add(new OC\Core\Command\Maintenance\Repair($repair, OC_Config::get $application->add(new OC\Core\Command\User\Report()); $application->add(new OC\Core\Command\User\ResetPassword(\OC::$server->getUserManager())); $application->add(new OC\Core\Command\User\LastSeen()); +$application->add(new OC\Core\Command\L10n\CreateJs()); diff --git a/l10n/l10n.pl b/l10n/l10n.pl index 8b12f1abaed..7443a5f941d 100644 --- a/l10n/l10n.pl +++ b/l10n/l10n.pl @@ -120,7 +120,7 @@ if( $task eq 'read' ){ my $language = ( $file =~ /\.js$/ ? 'Python' : 'PHP'); my $joinexisting = ( -e $output ? '--join-existing' : ''); print " Reading $file\n"; - `xgettext --output="$output" $joinexisting $keywords --language=$language "$file" --add-comments=TRANSLATORS --from-code=UTF-8 --package-version="6.0.0" --package-name="ownCloud Core" --msgid-bugs-address="translations\@owncloud.org"`; + `xgettext --output="$output" $joinexisting $keywords --language=$language "$file" --add-comments=TRANSLATORS --from-code=UTF-8 --package-version="8.0.0" --package-name="ownCloud Core" --msgid-bugs-address="translations\@owncloud.org"`; } chdir( $whereami ); } @@ -176,19 +176,24 @@ elsif( $task eq 'write' ){ s/\$/\\\$/g; } - # Write PHP file - open( OUT, ">$language.php" ); - print OUT "$language.js" ); print OUT "OC.L10N.register(\n \"$app\",\n {\n "; print OUT join( ",\n ", @js_strings ); print OUT "\n},\n\"$plurals\");\n"; close( OUT ); + # Write json file + open( OUT, ">$language.json" ); + print OUT "{ \"translations\": "; + print OUT "{\n "; + print OUT join( ",\n ", @js_strings ); + print OUT "\n},\"pluralForm\" :\"$plurals\"\n}"; + close( OUT ); + } chdir( $whereami ); } diff --git a/lib/private/l10n.php b/lib/private/l10n.php index ee144cd221c..ab94d017a25 100644 --- a/lib/private/l10n.php +++ b/lib/private/l10n.php @@ -54,12 +54,12 @@ class OC_L10N implements \OCP\IL10N { /** * Plural forms (string) */ - private $plural_form_string = 'nplurals=2; plural=(n != 1);'; + private $pluralFormString = 'nplurals=2; plural=(n != 1);'; /** * Plural forms (function) */ - private $plural_form_function = null; + private $pluralFormFunction = null; /** * get an L10N instance @@ -90,16 +90,26 @@ class OC_L10N implements \OCP\IL10N { /** * @param string $transFile + * @return bool */ - public function load($transFile) { + public function load($transFile, $mergeTranslations = false) { $this->app = true; - include $transFile; - if(isset($TRANSLATIONS) && is_array($TRANSLATIONS)) { - $this->translations = $TRANSLATIONS; + + $json = json_decode(file_get_contents($transFile), true); + if (!is_array($json)) { + return false; } - if(isset($PLURAL_FORMS)) { - $this->plural_form_string = $PLURAL_FORMS; + + $this->pluralFormString = $json['pluralForm']; + $translations = $json['translations']; + + if ($mergeTranslations) { + $this->translations = array_merge($this->translations, $translations); + } else { + $this->translations = $translations; } + + return true; } protected function init() { @@ -118,35 +128,27 @@ class OC_L10N implements \OCP\IL10N { if(array_key_exists($app.'::'.$lang, self::$cache)) { $this->translations = self::$cache[$app.'::'.$lang]['t']; } else{ - $i18ndir = self::findI18nDir($app); + $i18nDir = self::findI18nDir($app); + $transFile = strip_tags($i18nDir).strip_tags($lang).'.json'; // Texts are in $i18ndir // (Just no need to define date/time format etc. twice) - if((OC_Helper::isSubDirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/core/l10n/') - || OC_Helper::isSubDirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/lib/l10n/') - || OC_Helper::isSubDirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/settings') - || OC_Helper::isSubDirectory($i18ndir.$lang.'.php', OC_App::getAppPath($app).'/l10n/') + if((OC_Helper::isSubDirectory($transFile, OC::$SERVERROOT.'/core/l10n/') + || OC_Helper::isSubDirectory($transFile, OC::$SERVERROOT.'/lib/l10n/') + || OC_Helper::isSubDirectory($transFile, OC::$SERVERROOT.'/settings') + || OC_Helper::isSubDirectory($transFile, OC_App::getAppPath($app).'/l10n/') ) - && file_exists($i18ndir.$lang.'.php')) { - // Include the file, save the data from $CONFIG - $transFile = strip_tags($i18ndir).strip_tags($lang).'.php'; - include $transFile; - if(isset($TRANSLATIONS) && is_array($TRANSLATIONS)) { - $this->translations = $TRANSLATIONS; + && file_exists($transFile)) { + // load the translations file + if($this->load($transFile)) { //merge with translations from theme $theme = OC_Config::getValue( "theme" ); if (!is_null($theme)) { $transFile = OC::$SERVERROOT.'/themes/'.$theme.substr($transFile, strlen(OC::$SERVERROOT)); if (file_exists($transFile)) { - include $transFile; - if (isset($TRANSLATIONS) && is_array($TRANSLATIONS)) { - $this->translations = array_merge($this->translations, $TRANSLATIONS); - } + $this->load($transFile, true); } } } - if(isset($PLURAL_FORMS)) { - $this->plural_form_string = $PLURAL_FORMS; - } } self::$cache[$app.'::'.$lang]['t'] = $this->translations; @@ -273,10 +275,10 @@ class OC_L10N implements \OCP\IL10N { */ public function getPluralFormFunction() { $this->init(); - if(is_null($this->plural_form_function)) { - $this->plural_form_function = $this->createPluralFormFunction($this->plural_form_string); + if(is_null($this->pluralFormFunction)) { + $this->pluralFormFunction = $this->createPluralFormFunction($this->pluralFormString); } - return $this->plural_form_function; + return $this->pluralFormFunction; } /** @@ -479,8 +481,8 @@ class OC_L10N implements \OCP\IL10N { if(is_dir($dir)) { $files=scandir($dir); foreach($files as $file) { - if(substr($file, -4, 4) === '.php' && substr($file, 0, 4) !== 'l10n') { - $i = substr($file, 0, -4); + if(substr($file, -5, 5) === '.json' && substr($file, 0, 4) !== 'l10n') { + $i = substr($file, 0, -5); $available[] = $i; } } diff --git a/tests/data/l10n/cs.json b/tests/data/l10n/cs.json new file mode 100644 index 00000000000..c86f41aa077 --- /dev/null +++ b/tests/data/l10n/cs.json @@ -0,0 +1,6 @@ +{ + "translations" : { + "_%n window_::_%n windows_" : ["%n okno", "%n okna", "%n oken"] + }, + "pluralForm" : "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" +} diff --git a/tests/data/l10n/cs.php b/tests/data/l10n/cs.php deleted file mode 100644 index de106ede026..00000000000 --- a/tests/data/l10n/cs.php +++ /dev/null @@ -1,5 +0,0 @@ - array("%n okno", "%n okna", "%n oken") -); -$PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/tests/data/l10n/de.json b/tests/data/l10n/de.json new file mode 100644 index 00000000000..c2b6f34c081 --- /dev/null +++ b/tests/data/l10n/de.json @@ -0,0 +1,6 @@ +{ + "translations" : { + "_%n file_::_%n files_": ["%n Datei", "%n Dateien"] + }, + "pluralForm" : "nplurals=2; plural=(n != 1);" +} diff --git a/tests/data/l10n/de.php b/tests/data/l10n/de.php deleted file mode 100644 index 93c9ab4209e..00000000000 --- a/tests/data/l10n/de.php +++ /dev/null @@ -1,5 +0,0 @@ - array("%n Datei", "%n Dateien") -); -$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/tests/data/l10n/ru.json b/tests/data/l10n/ru.json new file mode 100644 index 00000000000..177b14a6b20 --- /dev/null +++ b/tests/data/l10n/ru.json @@ -0,0 +1,6 @@ +{ + "translations" : { + "_%n file_::_%n files_" : ["%n файл", "%n файла", "%n файлов"] + }, + "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);" +} diff --git a/tests/data/l10n/ru.php b/tests/data/l10n/ru.php deleted file mode 100644 index b778e8d79af..00000000000 --- a/tests/data/l10n/ru.php +++ /dev/null @@ -1,5 +0,0 @@ - array("%n файл", "%n файла", "%n файлов") -); -$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/tests/lib/l10n.php b/tests/lib/l10n.php index a97fa22f05c..df86fcfda81 100644 --- a/tests/lib/l10n.php +++ b/tests/lib/l10n.php @@ -10,7 +10,7 @@ class Test_L10n extends PHPUnit_Framework_TestCase { public function testGermanPluralTranslations() { $l = new OC_L10N('test'); - $transFile = OC::$SERVERROOT.'/tests/data/l10n/de.php'; + $transFile = OC::$SERVERROOT.'/tests/data/l10n/de.json'; $l->load($transFile); $this->assertEquals('1 Datei', (string)$l->n('%n file', '%n files', 1)); @@ -19,7 +19,7 @@ class Test_L10n extends PHPUnit_Framework_TestCase { public function testRussianPluralTranslations() { $l = new OC_L10N('test'); - $transFile = OC::$SERVERROOT.'/tests/data/l10n/ru.php'; + $transFile = OC::$SERVERROOT.'/tests/data/l10n/ru.json'; $l->load($transFile); $this->assertEquals('1 файл', (string)$l->n('%n file', '%n files', 1)); @@ -44,7 +44,7 @@ class Test_L10n extends PHPUnit_Framework_TestCase { public function testCzechPluralTranslations() { $l = new OC_L10N('test'); - $transFile = OC::$SERVERROOT.'/tests/data/l10n/cs.php'; + $transFile = OC::$SERVERROOT.'/tests/data/l10n/cs.json'; $l->load($transFile); $this->assertEquals('1 okno', (string)$l->n('%n window', '%n windows', 1)); -- 2.39.5