From 22d1d56772050253330f86f93da4c61a742e898f Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Mon, 4 Jan 2016 21:08:29 +0100 Subject: [PATCH] Bug 21156: Add plural translation capabilities to JS files It adds Javascript equivalent of Koha::I18N's exported subroutines, and they are used the same way. String extraction is done only on *.js files and require gettext 0.19 (available in Debian jessie, and also in wheezy-backports) It adds Javascript library Gettext.js for handling translation and a Perl script po2json to transform PO file into JSON. Gettext.js and po2json both come from Locale::Simple. There are several tools named po2json. It's simpler to integrate this one into Koha than to check if the good one is installed on the system. Locale::Simple is not needed. To avoid polluting the global namespace too much, this patch also introduce a global JS object named Koha and add some stuff in Koha.i18n Test plan: 1. Add a translatable string in a JS file. For example, add this: alert(__nx("There is one item", "There are {count} items", 3, {count: 3})); to staff-global.js 2. cd misc/translator && ./translate update fr-FR 3. Open misc/translator/po/fr-FR-messages-js.po, verify that your string is present, and translate it 4. cd misc/translator && ./translate install fr-FR 5. (Optional) Verify that koha-tmpl/intranet-tmpl/prog/fr-FR/js/locale_data.js exists and contains your translation 6. Open your browser on the staff main page, change language and verify that the message is translated 7. Repeat 1-6 on OPAC side --- C4/Installer/PerlDependencies.pm | 2 +- koha-tmpl/intranet-tmpl/js/Gettext.js | 1264 ++++++++++++++++++++ koha-tmpl/intranet-tmpl/js/i18n.js | 51 + .../prog/en/includes/doc-head-close.inc | 8 + koha-tmpl/opac-tmpl/bootstrap/js/Gettext.js | 1264 ++++++++++++++++++++ koha-tmpl/opac-tmpl/bootstrap/js/i18n.js | 51 + misc/translator/LangInstaller.pm | 70 +- misc/translator/po2json | 249 ++++ 8 files changed, 2948 insertions(+), 11 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/js/Gettext.js create mode 100644 koha-tmpl/intranet-tmpl/js/i18n.js create mode 100644 koha-tmpl/opac-tmpl/bootstrap/js/Gettext.js create mode 100644 koha-tmpl/opac-tmpl/bootstrap/js/i18n.js create mode 100755 misc/translator/po2json diff --git a/C4/Installer/PerlDependencies.pm b/C4/Installer/PerlDependencies.pm index 84f5fe4930..c2772b1c1d 100644 --- a/C4/Installer/PerlDependencies.pm +++ b/C4/Installer/PerlDependencies.pm @@ -142,7 +142,7 @@ our $PERL_DEPS = { 'Locale::PO' => { 'usage' => 'Core', 'required' => '1', - 'min_ver' => '0.17' + 'min_ver' => '0.24' }, 'LWP::Simple' => { 'usage' => 'Core', diff --git a/koha-tmpl/intranet-tmpl/js/Gettext.js b/koha-tmpl/intranet-tmpl/js/Gettext.js new file mode 100644 index 0000000000..ce6bf96877 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/js/Gettext.js @@ -0,0 +1,1264 @@ +/* +Pure Javascript implementation of Uniforum message translation. +Copyright (C) 2008 Joshua I. Miller , all rights reserved + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Library General Public License as published +by the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. + +=head1 NAME + +Javascript Gettext - Javascript implemenation of GNU Gettext API. + +=head1 SYNOPSIS + + // ////////////////////////////////////////////////////////// + // Optimum caching way + + + + var gt = new Gettext({ "domain" : "myDomain" }); + // rest is the same + + + // ////////////////////////////////////////////////////////// + // The reson the shortcuts aren't exported by default is because they'd be + // glued to the single domain you created. So, if you're adding i18n support + // to some js library, you should use it as so: + + if (typeof(MyNamespace) == 'undefined') MyNamespace = {}; + MyNamespace.MyClass = function () { + var gtParms = { "domain" : 'MyNamespace_MyClass' }; + this.gt = new Gettext(gtParams); + return this; + }; + MyNamespace.MyClass.prototype._ = function (msgid) { + return this.gt.gettext(msgid); + }; + MyNamespace.MyClass.prototype.something = function () { + var myString = this._("this will get translated"); + }; + + // ////////////////////////////////////////////////////////// + // Adding the shortcuts to a global scope is easier. If that's + // ok in your app, this is certainly easier. + var myGettext = new Gettext({ 'domain' : 'myDomain' }); + function _ (msgid) { + return myGettext.gettext(msgid); + } + alert( _("text") ); + + // ////////////////////////////////////////////////////////// + // Data structure of the json data + // NOTE: if you're loading via the + + // in domain.json + json_locale_data = { + "mydomain" : { + // po header fields + "" : { + "plural-forms" : "...", + "lang" : "en", + }, + // all the msgid strings and translations + "msgid" : [ "msgid_plural", "translation", "plural_translation" ], + }, + }; + // please see the included bin/po2json script for the details on this format + +This method also allows you to use unsupported file formats, so long as you can parse them into the above format. + +=item 2. Use AJAX to load language file. + +Use XMLHttpRequest (actually, SJAX - syncronous) to load an external resource. + +Supported external formats are: + +=over + +=item * Javascript Object Notation (.json) + +(see bin/po2json) + + type=application/json + +=item * Uniforum Portable Object (.po) + +(see GNU Gettext's xgettext) + + type=application/x-po + +=item * Machine Object (compiled .po) (.mo) + +NOTE: .mo format isn't actually supported just yet, but support is planned. + +(see GNU Gettext's msgfmt) + + type=application/x-mo + +=back + +=back + +=head1 METHODS + +The following methods are implemented: + + new Gettext(args) + textdomain (domain) + gettext (msgid) + dgettext (domainname, msgid) + dcgettext (domainname, msgid, LC_MESSAGES) + ngettext (msgid, msgid_plural, count) + dngettext (domainname, msgid, msgid_plural, count) + dcngettext (domainname, msgid, msgid_plural, count, LC_MESSAGES) + pgettext (msgctxt, msgid) + dpgettext (domainname, msgctxt, msgid) + dcpgettext (domainname, msgctxt, msgid, LC_MESSAGES) + npgettext (msgctxt, msgid, msgid_plural, count) + dnpgettext (domainname, msgctxt, msgid, msgid_plural, count) + dcnpgettext (domainname, msgctxt, msgid, msgid_plural, count, LC_MESSAGES) + strargs (string, args_array) + + +=head2 new Gettext (args) + +Several methods of loading locale data are included. You may specify a plugin or alternative method of loading data by passing the data in as the "locale_data" option. For example: + + var get_locale_data = function () { + // plugin does whatever to populate locale_data + return locale_data; + }; + var gt = new Gettext( 'domain' : 'messages', + 'locale_data' : get_locale_data() ); + +The above can also be used if locale data is specified in a statically included + + + + + [% IF ( login ) %] [% Asset.css("css/login.css") | $raw %] [% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/Gettext.js b/koha-tmpl/opac-tmpl/bootstrap/js/Gettext.js new file mode 100644 index 0000000000..ce6bf96877 --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/js/Gettext.js @@ -0,0 +1,1264 @@ +/* +Pure Javascript implementation of Uniforum message translation. +Copyright (C) 2008 Joshua I. Miller , all rights reserved + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU Library General Public License as published +by the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. + +=head1 NAME + +Javascript Gettext - Javascript implemenation of GNU Gettext API. + +=head1 SYNOPSIS + + // ////////////////////////////////////////////////////////// + // Optimum caching way + + + + var gt = new Gettext({ "domain" : "myDomain" }); + // rest is the same + + + // ////////////////////////////////////////////////////////// + // The reson the shortcuts aren't exported by default is because they'd be + // glued to the single domain you created. So, if you're adding i18n support + // to some js library, you should use it as so: + + if (typeof(MyNamespace) == 'undefined') MyNamespace = {}; + MyNamespace.MyClass = function () { + var gtParms = { "domain" : 'MyNamespace_MyClass' }; + this.gt = new Gettext(gtParams); + return this; + }; + MyNamespace.MyClass.prototype._ = function (msgid) { + return this.gt.gettext(msgid); + }; + MyNamespace.MyClass.prototype.something = function () { + var myString = this._("this will get translated"); + }; + + // ////////////////////////////////////////////////////////// + // Adding the shortcuts to a global scope is easier. If that's + // ok in your app, this is certainly easier. + var myGettext = new Gettext({ 'domain' : 'myDomain' }); + function _ (msgid) { + return myGettext.gettext(msgid); + } + alert( _("text") ); + + // ////////////////////////////////////////////////////////// + // Data structure of the json data + // NOTE: if you're loading via the + + // in domain.json + json_locale_data = { + "mydomain" : { + // po header fields + "" : { + "plural-forms" : "...", + "lang" : "en", + }, + // all the msgid strings and translations + "msgid" : [ "msgid_plural", "translation", "plural_translation" ], + }, + }; + // please see the included bin/po2json script for the details on this format + +This method also allows you to use unsupported file formats, so long as you can parse them into the above format. + +=item 2. Use AJAX to load language file. + +Use XMLHttpRequest (actually, SJAX - syncronous) to load an external resource. + +Supported external formats are: + +=over + +=item * Javascript Object Notation (.json) + +(see bin/po2json) + + type=application/json + +=item * Uniforum Portable Object (.po) + +(see GNU Gettext's xgettext) + + type=application/x-po + +=item * Machine Object (compiled .po) (.mo) + +NOTE: .mo format isn't actually supported just yet, but support is planned. + +(see GNU Gettext's msgfmt) + + type=application/x-mo + +=back + +=back + +=head1 METHODS + +The following methods are implemented: + + new Gettext(args) + textdomain (domain) + gettext (msgid) + dgettext (domainname, msgid) + dcgettext (domainname, msgid, LC_MESSAGES) + ngettext (msgid, msgid_plural, count) + dngettext (domainname, msgid, msgid_plural, count) + dcngettext (domainname, msgid, msgid_plural, count, LC_MESSAGES) + pgettext (msgctxt, msgid) + dpgettext (domainname, msgctxt, msgid) + dcpgettext (domainname, msgctxt, msgid, LC_MESSAGES) + npgettext (msgctxt, msgid, msgid_plural, count) + dnpgettext (domainname, msgctxt, msgid, msgid_plural, count) + dcnpgettext (domainname, msgctxt, msgid, msgid_plural, count, LC_MESSAGES) + strargs (string, args_array) + + +=head2 new Gettext (args) + +Several methods of loading locale data are included. You may specify a plugin or alternative method of loading data by passing the data in as the "locale_data" option. For example: + + var get_locale_data = function () { + // plugin does whatever to populate locale_data + return locale_data; + }; + var gt = new Gettext( 'domain' : 'messages', + 'locale_data' : get_locale_data() ); + +The above can also be used if locale data is specified in a statically included