Просмотр исходного кода

implementing a plural translation solution, currently only the JS part

El RIDO 11 лет назад
Родитель
Сommit
c83ba8256f
4 измененных файлов с 130 добавлено и 101 удалено
  1. 17 26
      i18n/de.json
  2. 17 26
      i18n/fr.json
  3. 17 26
      i18n/pl.json
  4. 79 23
      js/zerobin.js

+ 17 - 26
i18n/de.json

@@ -57,36 +57,27 @@
         "Diskussion",
         "Diskussion",
     "Toggle navigation":
     "Toggle navigation":
         "Navigation umschalten",
         "Navigation umschalten",
-    "5 minutes":
-        "5 Minuten",
-    "10 minutes":
-        "10 Minuten",
-    "1 hour":
-        "1 Stunde",
-    "1 day":
-        "1 Tag",
-    "1 week":
-        "1 Woche",
-    "1 month":
-        "1 Monat",
-    "1 year":
-        "1 Jahr",
+    "%d seconds": ["%d Sekunde", "%d Sekunden"],
+    "%d minutes": ["%d Minute", "%d Minuten"],
+    "%d hours": ["%d Stunde", "%d Stunden"],
+    "%d days": ["%d Tag", "%d Tage"],
+    "%d weeks": ["%d Woche", "%d Wochen"],
+    "%d months": ["%d Monat", "%d Monate"],
+    "%d years": ["%d Jahr", "%d Jahre"],
     "Never":
     "Never":
         "Nie",
         "Nie",
     "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
     "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
         "Hinweis: Dies ist ein Versuchsdienst. Daten können jederzeit gelöscht werden. Kätzchen werden sterben wenn Du diesen Dienst missbrauchst.",
         "Hinweis: Dies ist ein Versuchsdienst. Daten können jederzeit gelöscht werden. Kätzchen werden sterben wenn Du diesen Dienst missbrauchst.",
-    "This document will expire in %s.":
-        "Dieses Dokument läuft in %s ab.",
-    "%d second": "einer Sekunde",
-    "%d seconds": "%d Sekunden",
-    "%d minute": "einer Minute",
-    "%d minutes": "%d Minuten",
-    "%d hour": "einer Stunde",
-    "%d hours": "%d Stunden",
-    "%d day": "einem Tag",
-    "%d days": "%d Tagen",
-    "%d month": "einem Monat",
-    "%d months": "%d Monaten",
+    "This document will expire in %d seconds.":
+        ["Dieses Dokument läuft in einer Sekunde ab.", "Dieses Dokument läuft in %d Sekunden ab."],
+    "This document will expire in %d minutes.":
+        ["Dieses Dokument läuft in einer Minute ab.", "Dieses Dokument läuft in %d Minuten ab."],
+    "This document will expire in %d hours.":
+        ["Dieses Dokument läuft in einer Stunde ab.", "Dieses Dokument läuft in %d Stunden ab."],
+    "This document will expire in %d days.":
+        ["Dieses Dokument läuft in einem Tag ab.", "Dieses Dokument läuft in %d Tagen ab."],
+    "This document will expire in %d months.":
+        ["Dieses Dokument läuft in einem Monat ab.", "Dieses Dokument läuft in %d Monaten ab."],
     "Please enter the password for this paste:":
     "Please enter the password for this paste:":
         "Bitte gib das Passwort für diesen Text ein:",
         "Bitte gib das Passwort für diesen Text ein:",
     "Could not decrypt data (Wrong key?)":
     "Could not decrypt data (Wrong key?)":

+ 17 - 26
i18n/fr.json

@@ -57,36 +57,27 @@
         "Discussion",
         "Discussion",
     "Toggle navigation":
     "Toggle navigation":
         "Basculer la navigation",
         "Basculer la navigation",
-    "5 minutes":
-        "5 minutes",
-    "10 minutes":
-        "10 minutes",
-    "1 hour":
-        "1 heure",
-    "1 day":
-        "1 jour",
-    "1 week":
-        "1 semaine",
-    "1 month":
-        "1 mois",
-    "1 year":
-        "1 an",
+    "%d seconds": ["%d second", "%d seconds"],
+    "%d minutes": ["%d minute", "%d minutes"],
+    "%d hours": ["%d heure", "%d heures"],
+    "%d days": ["%d jour", "%d jours"],
+    "%d weeks": ["%d semaine", "%d semaines"],
+    "%d months": ["%d mois", "%d mois"],
+    "%d years": ["%d an", "%d ans"],
     "Never":
     "Never":
         "Jamais",
         "Jamais",
     "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
     "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
         "Note : Ceci est un service de test : les données peuvent être supprimées à tout moment. Des chatons mourront si vous utilisez ce service de manière abusive.",
         "Note : Ceci est un service de test : les données peuvent être supprimées à tout moment. Des chatons mourront si vous utilisez ce service de manière abusive.",
-    "This document will expire in %s.":
-        "This document will expire in %s.",
-    "%d second": "%d second",
-    "%d seconds": "%d seconds",
-    "%d minute": "%d minute",
-    "%d minutes": "%d minutes",
-    "%d hour": "%d hour",
-    "%d hours": "%d hours",
-    "%d day": "%d day",
-    "%d days": "%d days",
-    "%d month": "%d month",
-    "%d months": "%d months",
+    "This document will expire in %d seconds.":
+        ["This document will expire in %d second.", "This document will expire in %d seconds."],
+    "This document will expire in %d minutes.":
+        ["This document will expire in %d minute.", "This document will expire in %d minutes."],
+    "This document will expire in %d hours.":
+        ["This document will expire in %d hour.", "This document will expire in %d hours."],
+    "This document will expire in %d days.":
+        ["This document will expire in %d day.", "This document will expire in %d days."],
+    "This document will expire in %d months.":
+        ["This document will expire in %d month.", "This document will expire in %d months."],
     "Please enter the password for this paste:":
     "Please enter the password for this paste:":
         "Please enter the password for this paste:",
         "Please enter the password for this paste:",
     "Could not decrypt data (Wrong key?)":
     "Could not decrypt data (Wrong key?)":

+ 17 - 26
i18n/pl.json

@@ -57,36 +57,27 @@
         "Dyskusja",
         "Dyskusja",
     "Toggle navigation":
     "Toggle navigation":
         "Przełącz nawigację",
         "Przełącz nawigację",
-    "5 minutes":
-        "5 minut",
-    "10 minutes":
-        "10 minut",
-    "1 hour":
-        "1 godzina",
-    "1 day":
-        "1 dzień",
-    "1 week":
-        "1 tydzień",
-    "1 month":
-        "1 miesiąc",
-    "1 year":
-        "1 rok",
+    "%d seconds": ["%d second", "%d second", "%d second"],
+    "%d minutes": ["%d minut", "%d minut", "%d minut"],
+    "%d hours": ["%d godzina", "%d godzina", "%d godzina"],
+    "%d days": ["%d dzień", "%d dzień", "%d dzień"],
+    "%d weeks": ["%d tydzień", "%d tydzień", "%d tydzień"],
+    "%d months": ["%d miesiąc", "%d miesiąc", "%d miesiąc"],
+    "%d years": ["%d rok", "%d rok", "%d rok"],
     "Never":
     "Never":
         "Nigdy",
         "Nigdy",
     "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
     "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
         "Notka: To jest usługa testowa. Dane mogą zostać usunięte w dowolnym momencie. Kociątka umrą, jeśli nadużyjesz tej usługi.",
         "Notka: To jest usługa testowa. Dane mogą zostać usunięte w dowolnym momencie. Kociątka umrą, jeśli nadużyjesz tej usługi.",
-    "This document will expire in %s.":
-        "This document will expire in %s.",
-    "%d second": "%d second",
-    "%d seconds": "%d seconds",
-    "%d minute": "%d minute",
-    "%d minutes": "%d minutes",
-    "%d hour": "%d hour",
-    "%d hours": "%d hours",
-    "%d day": "%d day",
-    "%d days": "%d days",
-    "%d month": "%d month",
-    "%d months": "%d months",
+    "This document will expire in %d seconds.":
+        ["This document will expire in %d second.", "This document will expire in %d seconds."],
+    "This document will expire in %d minutes.":
+        ["This document will expire in %d minute.", "This document will expire in %d minutes."],
+    "This document will expire in %d hours.":
+        ["This document will expire in %d hour.", "This document will expire in %d hours."],
+    "This document will expire in %d days.":
+        ["This document will expire in %d day.", "This document will expire in %d days."],
+    "This document will expire in %d months.":
+        ["This document will expire in %d month.", "This document will expire in %d months."],
     "Please enter the password for this paste:":
     "Please enter the password for this paste:":
         "Please enter the password for this paste:",
         "Please enter the password for this paste:",
     "Could not decrypt data (Wrong key?)":
     "Could not decrypt data (Wrong key?)":

+ 79 - 23
js/zerobin.js

@@ -20,41 +20,36 @@ $(function() {
      */
      */
     var helper = {
     var helper = {
         /**
         /**
-         * Converts a duration (in seconds) into human readable format.
+         * Converts a duration (in seconds) into human friendly approximation.
          *
          *
          * @param int seconds
          * @param int seconds
-         * @return string
+         * @return array
          */
          */
         secondsToHuman: function(seconds)
         secondsToHuman: function(seconds)
         {
         {
             if (seconds < 60)
             if (seconds < 60)
             {
             {
-                var v = Math.floor(seconds),
-                    format = '%d second' + ((v > 1) ? 's' : '');
-                return i18n._(format, v);
+                var v = Math.floor(seconds);
+                return [v, 'second'];
             }
             }
             if (seconds < 60 * 60)
             if (seconds < 60 * 60)
             {
             {
-                var v = Math.floor(seconds / 60),
-                    format = '%d minute' + ((v > 1) ? 's' : '');
-                return i18n._(format, v);
+                var v = Math.floor(seconds / 60);
+                return [v, 'minute'];
             }
             }
             if (seconds < 60 * 60 * 24)
             if (seconds < 60 * 60 * 24)
             {
             {
-                var v = Math.floor(seconds / (60 * 60)),
-                    format = '%d hour' + ((v > 1) ? 's' : '');
-                return i18n._(format, v);
+                var v = Math.floor(seconds / (60 * 60));
+                return [v, 'hour'];
             }
             }
             // If less than 2 months, display in days:
             // If less than 2 months, display in days:
             if (seconds < 60 * 60 * 24 * 60)
             if (seconds < 60 * 60 * 24 * 60)
             {
             {
-                var v = Math.floor(seconds / (60 * 60 * 24)),
-                    format = '%d day' + ((v > 1) ? 's' : '');
-                return i18n._(format, v);
+                var v = Math.floor(seconds / (60 * 60 * 24));
+                return [v, 'day'];
             }
             }
-            var v = Math.floor(seconds / (60 * 60 * 24 * 30)),
-                format = '%d month' + ((v > 1) ? 's' : '');
-            return i18n._(format, v);
+            var v = Math.floor(seconds / (60 * 60 * 24 * 30));
+            return [v, 'month'];
         },
         },
 
 
         /**
         /**
@@ -258,7 +253,29 @@ $(function() {
      * internationalization methods
      * internationalization methods
      */
      */
     var i18n = {
     var i18n = {
-        supportedLanguages: ['de', 'fr', 'pl'], // and the built in 'en'
+        /**
+         * supported languages, minus the built in 'en'
+         */
+        supportedLanguages: ['de', 'fr', 'pl'],
+
+        /**
+         * per language functions to use to determine the plural form
+         * From: http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
+         *
+         * @param int number
+         * @return int array key
+         */
+        pluralRules: {
+            de: function(n) {
+                return (n != 1 ? 1 : 0);
+            },
+            fr: function(n) {
+                return (n > 1 ? 1 : 0);
+            },
+            pl: function(n) {
+                return (n == 1 ? 0 : n%10 >= 2 && n %10 <=4 && (n%100 < 10 || n%100 >= 20) ? 1 : 2);
+            }
+        },
 
 
         /**
         /**
          * translate a string, alias for translate()
          * translate a string, alias for translate()
@@ -281,30 +298,64 @@ $(function() {
          */
          */
         translate: function()
         translate: function()
         {
         {
-            var args = arguments;
+            var args = arguments, messageId, usesPlurals;
             if (typeof arguments[0] == 'object') args = arguments[0];
             if (typeof arguments[0] == 'object') args = arguments[0];
-            var messageId = args[0];
+            if (usesPlurals = $.isArray(args[0]))
+            {
+                // use the first plural form as messageId, otherwise the singular
+                messageId = (args[0].length > 1 ? args[0][1] : args[0][0]);
+            }
+            else
+            {
+                messageId = args[0];
+            }
             if (messageId.length == 0) return messageId;
             if (messageId.length == 0) return messageId;
             if (!this.translations.hasOwnProperty(messageId))
             if (!this.translations.hasOwnProperty(messageId))
             {
             {
                 console.log('Missing translation for: ' + messageId);
                 console.log('Missing translation for: ' + messageId);
-                this.translations[messageId] = messageId;
+                this.translations[messageId] = args[0];
+            }
+            if (usesPlurals && $.isArray(this.translations[messageId]))
+            {
+                var n = parseInt(args[1] || 1),
+                    key = this.pluralRules[this.language](n),
+                    maxKey = this.translations[messageId].length - 1;
+                if (key > maxKey) key = maxKey;
+                args[0] = this.translations[messageId][key];
+                args[1] = n;
+            }
+            else
+            {
+                args[0] = this.translations[messageId];
             }
             }
-            args[0] = this.translations[messageId];
             return helper.sprintf(args);
             return helper.sprintf(args);
         },
         },
 
 
+        /**
+         * load translations into cache, then execute callback function
+         *
+         * @param function callback
+         */
         loadTranslations: function(callback)
         loadTranslations: function(callback)
         {
         {
             var language = (navigator.language || navigator.userLanguage).substring(0, 2);
             var language = (navigator.language || navigator.userLanguage).substring(0, 2);
             // note that 'en' is built in, so no translation is necessary
             // note that 'en' is built in, so no translation is necessary
             if (this.supportedLanguages.indexOf(language) == -1) return;
             if (this.supportedLanguages.indexOf(language) == -1) return;
             $.getJSON('i18n/' + language + '.json', function(data) {
             $.getJSON('i18n/' + language + '.json', function(data) {
+                i18n.language = language;
                 i18n.translations = data;
                 i18n.translations = data;
                 callback();
                 callback();
             });
             });
         },
         },
 
 
+        /**
+         * built in language
+         */
+        language: 'en',
+
+        /**
+         * translation cache
+         */
         translations: {}
         translations: {}
     }
     }
 
 
@@ -495,8 +546,13 @@ $(function() {
             // Display paste expiration.
             // Display paste expiration.
             if (comments[0].meta.expire_date)
             if (comments[0].meta.expire_date)
             {
             {
+                var expiration = helper.secondsToHuman(comments[0].meta.remaining_time),
+                    expirationLabel = [
+                        'This document will expire in %d ' + expiration[1] + '.',
+                        'This document will expire in %d ' + expiration[1] + 's.'
+                    ];
                 this.remainingTime.removeClass('foryoureyesonly')
                 this.remainingTime.removeClass('foryoureyesonly')
-                                   .text(i18n._('This document will expire in %s.', helper.secondsToHuman(comments[0].meta.remaining_time)))
+                                   .text(i18n._(expirationLabel, expiration[0]))
                                    .removeClass('hidden');
                                    .removeClass('hidden');
             }
             }
             if (comments[0].meta.burnafterreading)
             if (comments[0].meta.burnafterreading)