Permalink
Browse files

Merge pull request #5 from karalis/master

we can have meta translations in the translation bar
  • Loading branch information...
2 parents 10971f6 + 1dd88f2 commit 83d18caca9f27e55c1f966b72dd0e25f68b51a38 @gnagno committed Jul 18, 2011
View
1 Gemfile
@@ -19,6 +19,7 @@ gem 'uglifier'
gem 'execjs'
gem 'jquery-rails'
gem 'sass-rails'
+gem "i18n-js", :git => "https://github.com/fnando/i18n-js.git"
if RUBY_VERSION < '1.9'
gem "ruby-debug", ">= 0.10.3"
View
8 Gemfile.lock
@@ -1,3 +1,10 @@
+GIT
+ remote: https://github.com/fnando/i18n-js.git
+ revision: c560abc4b52d28fe4839cc95421430ebfc4c0a69
+ specs:
+ i18n-js (1.0.2)
+ i18n
+
GEM
remote: http://rubygems.org/
specs:
@@ -167,6 +174,7 @@ DEPENDENCIES
database_cleaner
execjs
factory_girl_rails (~> 1.1.beta1)
+ i18n-js!
jquery-rails
launchy
rails (= 3.1.0.rc4)
View
14 app/assets/javascripts/lost-i18n-translation.js
@@ -1,17 +1,23 @@
$(document).ready(function() {
+ //FIXME: find a way to set these dynamically
+ I18n.defaultLocale = "en";
+ I18n.locale = "en";
+
var $translation_missing_count = $('.translation_missing').length;
var $translation_present_count = $('.translation_present').length;
var $newdiv = $('<div id="lost-i18n-translation-div"/>');
+ var sep = " | "
+ $newdiv.append("Current locale : " + I18n.currentLocale() + sep);
var $tr_missing = $('<span id="lost-i18n-translation-tr-missing-count" />');
- $tr_missing.append( $translation_missing_count );
- $newdiv.append("Missing translations: " , $tr_missing );
-
+ $tr_missing.append( $translation_missing_count + sep);
+ $newdiv.append(I18n.t("lost_i18n_translation.missing_label") + ": " , $tr_missing);
+
var $tr_present = $('<span id="lost-i18n-translation-tr-present-count" />');
$tr_present.append( $translation_present_count );
- $newdiv.append("Present translations: ", $tr_present );
+ $newdiv.append(I18n.t("lost_i18n_translation.present_label") + ": ", $tr_present);
$('body').append($newdiv);
});
View
4 config/i18n-js.yml
@@ -0,0 +1,4 @@
+# Find more details about this configuration file at http://github.com/fnando/i18n-js
+translations:
+ - file: "public/javascripts/translations.js"
+ only: "*"
View
4 config/locales/lost_i18n_translation.en.yml
@@ -0,0 +1,4 @@
+en:
+ lost_i18n_translation:
+ missing_label: "Missing translations"
+ present_label: "Present translations"
View
2 spec/dummy/app/assets/javascripts/application.js
@@ -8,3 +8,5 @@
//= require jquery_ujs
//= require_tree .
//= require lost-i18n-translation
+//= require i18n
+//= require translations
View
6 spec/dummy/app/controllers/application_controller.rb
@@ -1,3 +1,9 @@
class ApplicationController < ActionController::Base
protect_from_forgery
+ before_filter :export_i18n_messages
+
+ private
+ def export_i18n_messages
+ SimplesIdeias::I18n.export! if Rails.env.development?
+ end
end
View
2 spec/dummy/public/javascripts/translations.js
@@ -0,0 +1,2 @@
+var I18n = I18n || {};
+I18n.translations = {"en":{"date":{"formats":{"default":"%Y-%m-%d","short":"%b %d","long":"%B %d, %Y"},"day_names":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"abbr_day_names":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"month_names":[null,"January","February","March","April","May","June","July","August","September","October","November","December"],"abbr_month_names":[null,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"order":["year","month","day"]},"time":{"formats":{"default":"%a, %d %b %Y %H:%M:%S %z","short":"%d %b %H:%M","long":"%B %d, %Y %H:%M"},"am":"am","pm":"pm"},"support":{"array":{"words_connector":", ","two_words_connector":" and ","last_word_connector":", and "}},"errors":{"format":"%{attribute} %{message}","messages":{"inclusion":"is not included in the list","exclusion":"is reserved","invalid":"is invalid","confirmation":"doesn't match confirmation","accepted":"must be accepted","empty":"can't be empty","blank":"can't be blank","too_long":"is too long (maximum is %{count} characters)","too_short":"is too short (minimum is %{count} characters)","wrong_length":"is the wrong length (should be %{count} characters)","not_a_number":"is not a number","not_an_integer":"must be an integer","greater_than":"must be greater than %{count}","greater_than_or_equal_to":"must be greater than or equal to %{count}","equal_to":"must be equal to %{count}","less_than":"must be less than %{count}","less_than_or_equal_to":"must be less than or equal to %{count}","odd":"must be odd","even":"must be even"}},"activerecord":{"errors":{"messages":{"taken":"has already been taken","record_invalid":"Validation failed: %{errors}"}}},"number":{"format":{"separator":".","delimiter":",","precision":3,"significant":false,"strip_insignificant_zeros":false},"currency":{"format":{"format":"%u%n","unit":"$","separator":".","delimiter":",","precision":2,"significant":false,"strip_insignificant_zeros":false}},"percentage":{"format":{"delimiter":""}},"precision":{"format":{"delimiter":""}},"human":{"format":{"delimiter":"","precision":3,"significant":true,"strip_insignificant_zeros":true},"storage_units":{"format":"%n %u","units":{"byte":{"one":"Byte","other":"Bytes"},"kb":"KB","mb":"MB","gb":"GB","tb":"TB"}},"decimal_units":{"format":"%n %u","units":{"unit":"","thousand":"Thousand","million":"Million","billion":"Billion","trillion":"Trillion","quadrillion":"Quadrillion"}}}},"datetime":{"distance_in_words":{"half_a_minute":"half a minute","less_than_x_seconds":{"one":"less than 1 second","other":"less than %{count} seconds"},"x_seconds":{"one":"1 second","other":"%{count} seconds"},"less_than_x_minutes":{"one":"less than a minute","other":"less than %{count} minutes"},"x_minutes":{"one":"1 minute","other":"%{count} minutes"},"about_x_hours":{"one":"about 1 hour","other":"about %{count} hours"},"x_days":{"one":"1 day","other":"%{count} days"},"about_x_months":{"one":"about 1 month","other":"about %{count} months"},"x_months":{"one":"1 month","other":"%{count} months"},"about_x_years":{"one":"about 1 year","other":"about %{count} years"},"over_x_years":{"one":"over 1 year","other":"over %{count} years"},"almost_x_years":{"one":"almost 1 year","other":"almost %{count} years"}},"prompts":{"year":"Year","month":"Month","day":"Day","hour":"Hour","minute":"Minute","second":"Seconds"}},"helpers":{"select":{"prompt":"Please select"},"submit":{"create":"Create %{model}","update":"Update %{model}","submit":"Save %{model}"}},"lost_i18n_translation":{"missing_label":"Missing translations","present_label":"Present translations"},"hello":"Hello world"}};
View
2 spec/dummy/vendor/assets/javascripts/translations.js
@@ -0,0 +1,2 @@
+var I18n = I18n || {};
+I18n.translations = {"en":{"date":{"formats":{"default":"%Y-%m-%d","short":"%b %d","long":"%B %d, %Y"},"day_names":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"abbr_day_names":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"month_names":[null,"January","February","March","April","May","June","July","August","September","October","November","December"],"abbr_month_names":[null,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"order":["year","month","day"]},"time":{"formats":{"default":"%a, %d %b %Y %H:%M:%S %z","short":"%d %b %H:%M","long":"%B %d, %Y %H:%M"},"am":"am","pm":"pm"},"support":{"array":{"words_connector":", ","two_words_connector":" and ","last_word_connector":", and "}},"errors":{"format":"%{attribute} %{message}","messages":{"inclusion":"is not included in the list","exclusion":"is reserved","invalid":"is invalid","confirmation":"doesn't match confirmation","accepted":"must be accepted","empty":"can't be empty","blank":"can't be blank","too_long":"is too long (maximum is %{count} characters)","too_short":"is too short (minimum is %{count} characters)","wrong_length":"is the wrong length (should be %{count} characters)","not_a_number":"is not a number","not_an_integer":"must be an integer","greater_than":"must be greater than %{count}","greater_than_or_equal_to":"must be greater than or equal to %{count}","equal_to":"must be equal to %{count}","less_than":"must be less than %{count}","less_than_or_equal_to":"must be less than or equal to %{count}","odd":"must be odd","even":"must be even"}},"activerecord":{"errors":{"messages":{"taken":"has already been taken","record_invalid":"Validation failed: %{errors}"}}},"number":{"format":{"separator":".","delimiter":",","precision":3,"significant":false,"strip_insignificant_zeros":false},"currency":{"format":{"format":"%u%n","unit":"$","separator":".","delimiter":",","precision":2,"significant":false,"strip_insignificant_zeros":false}},"percentage":{"format":{"delimiter":""}},"precision":{"format":{"delimiter":""}},"human":{"format":{"delimiter":"","precision":3,"significant":true,"strip_insignificant_zeros":true},"storage_units":{"format":"%n %u","units":{"byte":{"one":"Byte","other":"Bytes"},"kb":"KB","mb":"MB","gb":"GB","tb":"TB"}},"decimal_units":{"format":"%n %u","units":{"unit":"","thousand":"Thousand","million":"Million","billion":"Billion","trillion":"Trillion","quadrillion":"Quadrillion"}}}},"datetime":{"distance_in_words":{"half_a_minute":"half a minute","less_than_x_seconds":{"one":"less than 1 second","other":"less than %{count} seconds"},"x_seconds":{"one":"1 second","other":"%{count} seconds"},"less_than_x_minutes":{"one":"less than a minute","other":"less than %{count} minutes"},"x_minutes":{"one":"1 minute","other":"%{count} minutes"},"about_x_hours":{"one":"about 1 hour","other":"about %{count} hours"},"x_days":{"one":"1 day","other":"%{count} days"},"about_x_months":{"one":"about 1 month","other":"about %{count} months"},"x_months":{"one":"1 month","other":"%{count} months"},"about_x_years":{"one":"about 1 year","other":"about %{count} years"},"over_x_years":{"one":"over 1 year","other":"over %{count} years"},"almost_x_years":{"one":"almost 1 year","other":"almost %{count} years"}},"prompts":{"year":"Year","month":"Month","day":"Day","hour":"Hour","minute":"Minute","second":"Seconds"}},"helpers":{"select":{"prompt":"Please select"},"submit":{"create":"Create %{model}","update":"Update %{model}","submit":"Save %{model}"}},"lost_i18n_translation":{"missing_label":"Missing translations","present_label":"Present translations"},"hello":"Hello world"}};
View
413 vendor/assets/javascripts/i18n.js
@@ -0,0 +1,413 @@
+// Instantiate the object
+var I18n = I18n || {};
+
+// Set default locale to english
+I18n.defaultLocale = "en";
+
+// Set default separator
+I18n.defaultSeparator = ".";
+
+// Set current locale to null
+I18n.locale = null;
+
+// Set the placeholder format. Accepts `{{placeholder}}` and `%{placeholder}`.
+I18n.PLACEHOLDER = /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm;
+
+I18n.isValidNode = function(obj, node) {
+ // local undefined variable in case another library corrupts window.undefined
+ var undef;
+ return obj[node] !== null && obj[node] !== undef;
+}
+
+I18n.lookup = function(scope, options) {
+ var translations = this.prepareOptions(I18n.translations);
+ var messages = translations[I18n.currentLocale()];
+ options = this.prepareOptions(options);
+
+ if (!messages) {
+ return;
+ }
+
+ if (typeof(scope) == "object") {
+ scope = scope.join(this.defaultSeparator);
+ }
+
+ if (options.scope) {
+ scope = options.scope.toString() + this.defaultSeparator + scope;
+ }
+
+ scope = scope.split(this.defaultSeparator);
+
+ while (scope.length > 0) {
+ var currentScope = scope.shift();
+ messages = messages[currentScope];
+
+ if (!messages) {
+ break;
+ }
+ }
+
+ if (!messages && this.isValidNode(options, "defaultValue")) {
+ messages = options.defaultValue;
+ }
+
+ return messages;
+};
+
+// Merge serveral hash options, checking if value is set before
+// overwriting any value. The precedence is from left to right.
+//
+// I18n.prepareOptions({name: "John Doe"}, {name: "Mary Doe", role: "user"});
+// #=> {name: "John Doe", role: "user"}
+//
+I18n.prepareOptions = function() {
+ var options = {};
+ var opts;
+ var count = arguments.length;
+
+ for (var i = 0; i < count; i++) {
+ opts = arguments[i];
+
+ if (!opts) {
+ continue;
+ }
+
+ for (var key in opts) {
+ if (!this.isValidNode(options, key)) {
+ options[key] = opts[key];
+ }
+ }
+ }
+
+ return options;
+};
+
+I18n.interpolate = function(message, options) {
+ options = this.prepareOptions(options);
+ var matches = message.match(this.PLACEHOLDER);
+
+ if (!matches) {
+ return message;
+ }
+
+ var placeholder, value, name;
+
+ for (var i = 0; placeholder = matches[i]; i++) {
+ name = placeholder.replace(this.PLACEHOLDER, "$1");
+
+ value = options[name];
+
+ if (!this.isValidNode(options, name)) {
+ value = "[missing " + placeholder + " value]";
+ }
+
+ regex = new RegExp(placeholder.replace(/\{/gm, "\\{").replace(/\}/gm, "\\}"));
+ message = message.replace(regex, value);
+ }
+
+ return message;
+};
+
+I18n.translate = function(scope, options) {
+ options = this.prepareOptions(options);
+ var translation = this.lookup(scope, options);
+
+ try {
+ if (typeof(translation) == "object") {
+ if (typeof(options.count) == "number") {
+ return this.pluralize(options.count, scope, options);
+ } else {
+ return translation;
+ }
+ } else {
+ return this.interpolate(translation, options);
+ }
+ } catch(err) {
+ return this.missingTranslation(scope);
+ }
+};
+
+I18n.localize = function(scope, value) {
+ switch (scope) {
+ case "currency":
+ return this.toCurrency(value);
+ case "number":
+ scope = this.lookup("number.format");
+ return this.toNumber(value, scope);
+ case "percentage":
+ return this.toPercentage(value);
+ default:
+ if (scope.match(/^(date|time)/)) {
+ return this.toTime(scope, value);
+ } else {
+ return value.toString();
+ }
+ }
+};
+
+I18n.parseDate = function(d) {
+ var matches, date;
+ matches = d.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ |T](\d{2}):(\d{2}):(\d{2}))?(Z)?/);
+
+ if (matches) {
+ // date/time strings: yyyy-mm-dd hh:mm:ss or yyyy-mm-dd or yyyy-mm-ddThh:mm:ssZ
+ for (var i = 1; i <= 6; i++) {
+ matches[i] = parseInt(matches[i], 10) || 0;
+ }
+
+ // month starts on 0
+ matches[2] -= 1;
+
+ if (matches[7]) {
+ date = new Date(Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]));
+ } else {
+ date = new Date(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]);
+ }
+ } else if (typeof(d) == "number") {
+ // UNIX timestamp
+ date = new Date();
+ date.setTime(d);
+ } else {
+ // an arbitrary javascript string
+ date = new Date();
+ date.setTime(Date.parse(d));
+ }
+
+ return date;
+};
+
+I18n.toTime = function(scope, d) {
+ var date = this.parseDate(d);
+ var format = this.lookup(scope);
+
+ if (date.toString().match(/invalid/i)) {
+ return date.toString();
+ }
+
+ if (!format) {
+ return date.toString();
+ }
+
+ return this.strftime(date, format);
+};
+
+I18n.strftime = function(date, format) {
+ var options = this.lookup("date");
+
+ if (!options) {
+ return date.toString();
+ }
+ options.meridian = options.meridian || ["AM", "PM"];
+
+ var weekDay = date.getDay();
+ var day = date.getDate();
+ var year = date.getFullYear();
+ var month = date.getMonth() + 1;
+ var hour = date.getHours();
+ var hour12 = hour;
+ var meridian = hour > 11 ? 1 : 0;
+ var secs = date.getSeconds();
+ var mins = date.getMinutes();
+ var offset = date.getTimezoneOffset();
+ var absOffsetHours = Math.floor(Math.abs(offset / 60));
+ var absOffsetMinutes = Math.abs(offset) - (absOffsetHours * 60);
+ var timezoneoffset = (offset > 0 ? "-" : "+") + (absOffsetHours.toString().length < 2 ? "0" + absOffsetHours : absOffsetHours) + (absOffsetMinutes.toString().length < 2 ? "0" + absOffsetMinutes : absOffsetMinutes);
+
+ if (hour12 > 12) {
+ hour12 = hour12 - 12;
+ } else if (hour12 === 0) {
+ hour12 = 12;
+ }
+
+ var padding = function(n) {
+ var s = "0" + n.toString();
+ return s.substr(s.length - 2);
+ };
+
+ var f = format;
+ f = f.replace("%a", options.abbr_day_names[weekDay]);
+ f = f.replace("%A", options.day_names[weekDay]);
+ f = f.replace("%b", options.abbr_month_names[month]);
+ f = f.replace("%B", options.month_names[month]);
+ f = f.replace("%d", padding(day));
+ f = f.replace("%-d", day);
+ f = f.replace("%H", padding(hour));
+ f = f.replace("%-H", hour);
+ f = f.replace("%I", padding(hour12));
+ f = f.replace("%-I", hour12);
+ f = f.replace("%m", padding(month));
+ f = f.replace("%-m", month);
+ f = f.replace("%M", padding(mins));
+ f = f.replace("%-M", mins);
+ f = f.replace("%p", options.meridian[meridian]);
+ f = f.replace("%S", padding(secs));
+ f = f.replace("%-S", secs);
+ f = f.replace("%w", weekDay);
+ f = f.replace("%y", padding(year));
+ f = f.replace("%-y", padding(year).replace(/^0+/, ""));
+ f = f.replace("%Y", year);
+ f = f.replace("%z", timezoneoffset);
+
+ return f;
+};
+
+I18n.toNumber = function(number, options) {
+ options = this.prepareOptions(
+ options,
+ this.lookup("number.format"),
+ {precision: 3, separator: ".", delimiter: ",", strip_insignificant_zeros: false}
+ );
+
+ var negative = number < 0;
+ var string = Math.abs(number).toFixed(options.precision).toString();
+ var parts = string.split(".");
+
+ number = parts[0];
+ var precision = parts[1];
+
+ var n = [];
+
+ while (number.length > 0) {
+ n.unshift(number.substr(Math.max(0, number.length - 3), 3));
+ number = number.substr(0, number.length -3);
+ }
+
+ var formattedNumber = n.join(options.delimiter);
+
+ if (options.precision > 0) {
+ formattedNumber += options.separator + parts[1];
+ }
+
+ if (negative) {
+ formattedNumber = "-" + formattedNumber;
+ }
+
+ if (options.strip_insignificant_zeros) {
+ var regex = {
+ separator: new RegExp(options.separator.replace(/\./, "\\.") + "$")
+ , zeros: /0+$/
+ };
+
+ formattedNumber = formattedNumber
+ .replace(regex.zeros, "")
+ .replace(regex.separator, "");
+ }
+
+ return formattedNumber;
+};
+
+I18n.toCurrency = function(number, options) {
+ options = this.prepareOptions(
+ options,
+ this.lookup("number.currency.format"),
+ this.lookup("number.format"),
+ {unit: "$", precision: 2, format: "%u%n", delimiter: ",", separator: "."}
+ );
+
+ number = this.toNumber(number, options);
+ number = options.format
+ .replace("%u", options.unit)
+ .replace("%n", number);
+
+ return number;
+};
+
+I18n.toHumanSize = function(number, options) {
+ var kb = 1024
+ , size = number
+ , iterations = 0
+ , unit
+ , precision
+ ;
+
+ while (size >= kb && iterations < 4) {
+ size = size / kb;
+ iterations += 1;
+ }
+
+ if (iterations === 0) {
+ unit = this.t("number.human.storage_units.units.byte", {count: size});
+ precision = 0;
+ } else {
+ unit = this.t("number.human.storage_units.units." + [null, "kb", "mb", "gb", "tb"][iterations]);
+ precision = (size - Math.floor(size) === 0) ? 0 : 1;
+ }
+
+ options = this.prepareOptions(
+ options,
+ {precision: precision, format: "%n%u", delimiter: ""}
+ );
+
+ number = this.toNumber(size, options);
+ number = options.format
+ .replace("%u", unit)
+ .replace("%n", number);
+
+ return number;
+};
+
+I18n.toPercentage = function(number, options) {
+ options = this.prepareOptions(
+ options,
+ this.lookup("number.percentage.format"),
+ this.lookup("number.format"),
+ {precision: 3, separator: ".", delimiter: ""}
+ );
+
+ number = this.toNumber(number, options);
+ return number + "%";
+};
+
+I18n.pluralize = function(count, scope, options) {
+ var translation;
+
+ try {
+ translation = this.lookup(scope, options);
+ } catch (error) {}
+
+ if (!translation) {
+ return this.missingTranslation(scope);
+ }
+
+ var message;
+ options = this.prepareOptions(options);
+ options.count = count.toString();
+
+ switch(Math.abs(count)) {
+ case 0:
+ message = this.isValidNode(translation, "zero") ? translation.zero :
+ this.isValidNode(translation, "none") ? translation.none :
+ this.isValidNode(translation, "other") ? translation.other :
+ this.missingTranslation(scope, "zero");
+ break;
+ case 1:
+ message = this.isValidNode(translation, "one") ? translation.one : this.missingTranslation(scope, "one");
+ break;
+ default:
+ message = this.isValidNode(translation, "other") ? translation.other : this.missingTranslation(scope, "other");
+ }
+
+ return this.interpolate(message, options);
+};
+
+I18n.missingTranslation = function() {
+ var message = '[missing "' + this.currentLocale();
+ var count = arguments.length;
+
+ for (var i = 0; i < count; i++) {
+ message += "." + arguments[i];
+ }
+
+ message += '" translation]';
+
+ return message;
+};
+
+I18n.currentLocale = function() {
+ return (I18n.locale || I18n.defaultLocale);
+};
+
+// shortcuts
+I18n.t = I18n.translate;
+I18n.l = I18n.localize;
+I18n.p = I18n.pluralize;

0 comments on commit 83d18ca

Please sign in to comment.