implementing a plural translation solution, currently only the JS part

This commit is contained in:
El RIDO 2015-09-06 15:54:43 +02:00
parent eee7b0144a
commit c83ba8256f
4 changed files with 130 additions and 101 deletions

View File

@ -57,36 +57,27 @@
"Diskussion", "Diskussion",
"Toggle navigation": "Toggle navigation":
"Navigation umschalten", "Navigation umschalten",
"5 minutes": "%d seconds": ["%d Sekunde", "%d Sekunden"],
"5 Minuten", "%d minutes": ["%d Minute", "%d Minuten"],
"10 minutes": "%d hours": ["%d Stunde", "%d Stunden"],
"10 Minuten", "%d days": ["%d Tag", "%d Tage"],
"1 hour": "%d weeks": ["%d Woche", "%d Wochen"],
"1 Stunde", "%d months": ["%d Monat", "%d Monate"],
"1 day": "%d years": ["%d Jahr", "%d Jahre"],
"1 Tag",
"1 week":
"1 Woche",
"1 month":
"1 Monat",
"1 year":
"1 Jahr",
"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.": "This document will expire in %d seconds.":
"Dieses Dokument läuft in %s ab.", ["Dieses Dokument läuft in einer Sekunde ab.", "Dieses Dokument läuft in %d Sekunden ab."],
"%d second": "einer Sekunde", "This document will expire in %d minutes.":
"%d seconds": "%d Sekunden", ["Dieses Dokument läuft in einer Minute ab.", "Dieses Dokument läuft in %d Minuten ab."],
"%d minute": "einer Minute", "This document will expire in %d hours.":
"%d minutes": "%d Minuten", ["Dieses Dokument läuft in einer Stunde ab.", "Dieses Dokument läuft in %d Stunden ab."],
"%d hour": "einer Stunde", "This document will expire in %d days.":
"%d hours": "%d Stunden", ["Dieses Dokument läuft in einem Tag ab.", "Dieses Dokument läuft in %d Tagen ab."],
"%d day": "einem Tag", "This document will expire in %d months.":
"%d days": "%d Tagen", ["Dieses Dokument läuft in einem Monat ab.", "Dieses Dokument läuft in %d Monaten ab."],
"%d month": "einem Monat",
"%d months": "%d Monaten",
"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?)":

View File

@ -57,36 +57,27 @@
"Discussion", "Discussion",
"Toggle navigation": "Toggle navigation":
"Basculer la navigation", "Basculer la navigation",
"5 minutes": "%d seconds": ["%d second", "%d seconds"],
"5 minutes", "%d minutes": ["%d minute", "%d minutes"],
"10 minutes": "%d hours": ["%d heure", "%d heures"],
"10 minutes", "%d days": ["%d jour", "%d jours"],
"1 hour": "%d weeks": ["%d semaine", "%d semaines"],
"1 heure", "%d months": ["%d mois", "%d mois"],
"1 day": "%d years": ["%d an", "%d ans"],
"1 jour",
"1 week":
"1 semaine",
"1 month":
"1 mois",
"1 year":
"1 an",
"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 %d seconds.":
"This document will expire in %s.", ["This document will expire in %d second.", "This document will expire in %d seconds."],
"%d second": "%d second", "This document will expire in %d minutes.":
"%d seconds": "%d seconds", ["This document will expire in %d minute.", "This document will expire in %d minutes."],
"%d minute": "%d minute", "This document will expire in %d hours.":
"%d minutes": "%d minutes", ["This document will expire in %d hour.", "This document will expire in %d hours."],
"%d hour": "%d hour", "This document will expire in %d days.":
"%d hours": "%d hours", ["This document will expire in %d day.", "This document will expire in %d days."],
"%d day": "%d day", "This document will expire in %d months.":
"%d days": "%d days", ["This document will expire in %d month.", "This document will expire in %d months."],
"%d month": "%d month",
"%d months": "%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?)":

View File

@ -57,36 +57,27 @@
"Dyskusja", "Dyskusja",
"Toggle navigation": "Toggle navigation":
"Przełącz nawigację", "Przełącz nawigację",
"5 minutes": "%d seconds": ["%d second", "%d second", "%d second"],
"5 minut", "%d minutes": ["%d minut", "%d minut", "%d minut"],
"10 minutes": "%d hours": ["%d godzina", "%d godzina", "%d godzina"],
"10 minut", "%d days": ["%d dzień", "%d dzień", "%d dzień"],
"1 hour": "%d weeks": ["%d tydzień", "%d tydzień", "%d tydzień"],
"1 godzina", "%d months": ["%d miesiąc", "%d miesiąc", "%d miesiąc"],
"1 day": "%d years": ["%d rok", "%d rok", "%d rok"],
"1 dzień",
"1 week":
"1 tydzień",
"1 month":
"1 miesiąc",
"1 year":
"1 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 %d seconds.":
"This document will expire in %s.", ["This document will expire in %d second.", "This document will expire in %d seconds."],
"%d second": "%d second", "This document will expire in %d minutes.":
"%d seconds": "%d seconds", ["This document will expire in %d minute.", "This document will expire in %d minutes."],
"%d minute": "%d minute", "This document will expire in %d hours.":
"%d minutes": "%d minutes", ["This document will expire in %d hour.", "This document will expire in %d hours."],
"%d hour": "%d hour", "This document will expire in %d days.":
"%d hours": "%d hours", ["This document will expire in %d day.", "This document will expire in %d days."],
"%d day": "%d day", "This document will expire in %d months.":
"%d days": "%d days", ["This document will expire in %d month.", "This document will expire in %d months."],
"%d month": "%d month",
"%d months": "%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?)":

View File

@ -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), var v = Math.floor(seconds);
format = '%d second' + ((v > 1) ? 's' : ''); return [v, 'second'];
return i18n._(format, v);
} }
if (seconds < 60 * 60) if (seconds < 60 * 60)
{ {
var v = Math.floor(seconds / 60), var v = Math.floor(seconds / 60);
format = '%d minute' + ((v > 1) ? 's' : ''); return [v, 'minute'];
return i18n._(format, v);
} }
if (seconds < 60 * 60 * 24) if (seconds < 60 * 60 * 24)
{ {
var v = Math.floor(seconds / (60 * 60)), var v = Math.floor(seconds / (60 * 60));
format = '%d hour' + ((v > 1) ? 's' : ''); return [v, 'hour'];
return i18n._(format, v);
} }
// 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)), var v = Math.floor(seconds / (60 * 60 * 24));
format = '%d day' + ((v > 1) ? 's' : ''); return [v, 'day'];
return i18n._(format, v);
} }
var v = Math.floor(seconds / (60 * 60 * 24 * 30)), var v = Math.floor(seconds / (60 * 60 * 24 * 30));
format = '%d month' + ((v > 1) ? 's' : ''); return [v, 'month'];
return i18n._(format, v);
}, },
/** /**
@ -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)