From 2e76e337a672f21daa0c4a88dfb0a020cdfc20dd Mon Sep 17 00:00:00 2001
From: Kuba Brecka <kuba.brecka@gmail.com>
Date: Thu, 7 Mar 2013 20:05:18 +0100
Subject: [PATCH] 1st attempt to support i18n in dates and times

---
 app/assets/javascripts/application.js.erb     |   2 +
 .../discourse/helpers/application_helpers.js  |  25 ++--
 app/assets/javascripts/external/humane.js     | 134 ------------------
 .../javascripts/locales/date_locales.js       |  36 +++++
 config/locales/client.cs.yml                  |   1 +
 config/locales/client.en.yml                  |   1 +
 lib/age_words.rb                              |  13 +-
 7 files changed, 54 insertions(+), 158 deletions(-)
 delete mode 100644 app/assets/javascripts/external/humane.js
 create mode 100644 app/assets/javascripts/locales/date_locales.js

diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb
index 8525d0bed..686ca618d 100644
--- a/app/assets/javascripts/application.js.erb
+++ b/app/assets/javascripts/application.js.erb
@@ -24,6 +24,8 @@
 //= require ./discourse/helpers/i18n_helpers
 //= require ./discourse
 
+//= require ./locales/date_locales.js
+
 // Stuff we need to load first
 //= require_tree ./discourse/mixins
 //= require ./discourse/views/view
diff --git a/app/assets/javascripts/discourse/helpers/application_helpers.js b/app/assets/javascripts/discourse/helpers/application_helpers.js
index 0c51062d2..55c95911e 100644
--- a/app/assets/javascripts/discourse/helpers/application_helpers.js
+++ b/app/assets/javascripts/discourse/helpers/application_helpers.js
@@ -1,5 +1,3 @@
-/*global humaneDate:true */
-
 /**
   Breaks up a long string
 
@@ -162,7 +160,7 @@ Handlebars.registerHelper('avatar', function(user, options) {
 Handlebars.registerHelper('unboundDate', function(property, options) {
   var dt;
   dt = new Date(Ember.Handlebars.get(this, property, options));
-  return dt.format("{d} {Mon}, {yyyy} {hh}:{mm}");
+  return dt.format("long");
 });
 
 /**
@@ -176,9 +174,9 @@ Handlebars.registerHelper('editDate', function(property, options) {
   dt = Date.create(Ember.Handlebars.get(this, property, options));
   yesterday = new Date() - (60 * 60 * 24 * 1000);
   if (yesterday > dt.getTime()) {
-    return dt.format("{d} {Mon}, {yyyy} {hh}:{mm}");
+    return dt.format("long");
   } else {
-    return humaneDate(dt);
+    return dt.relative();
   }
 });
 
@@ -215,7 +213,7 @@ Handlebars.registerHelper('number', function(property, options) {
   @for Handlebars
 **/
 Handlebars.registerHelper('date', function(property, options) {
-  var displayDate, dt, fiveDaysAgo, fullReadable, humanized, leaveAgo, val;
+  var displayDate, dt, fiveDaysAgo, oneMinuteAgo, fullReadable, humanized, leaveAgo, val;
   if (property.hash) {
     if (property.hash.leaveAgo) {
       leaveAgo = property.hash.leaveAgo === "true";
@@ -229,23 +227,26 @@ Handlebars.registerHelper('date', function(property, options) {
     return new Handlebars.SafeString("&mdash;");
   }
   dt = new Date(val);
-  fullReadable = dt.format("{d} {Mon}, {yyyy} {hh}:{mm}");
+  fullReadable = dt.format("long");
   displayDate = "";
   fiveDaysAgo = (new Date()) - 432000000;
-  if (fiveDaysAgo > (dt.getTime())) {
+  oneMinuteAgo = (new Date()) - 60000;
+  if (oneMinuteAgo <= dt.getTime() && dt.getTime() <= (new Date())) {
+    displayDate = Em.String.i18n("now");
+  } else if (fiveDaysAgo > (dt.getTime())) {
     if ((new Date()).getFullYear() !== dt.getFullYear()) {
-      displayDate = dt.format("{d} {Mon} '{yy}");
+      displayDate = dt.format("short");
     } else {
-      displayDate = dt.format("{d} {Mon}");
+      displayDate = dt.format("short_no_year");
     }
   } else {
-    humanized = humaneDate(dt);
+    humanized = dt.relative();
     if (!humanized) {
       return "";
     }
     displayDate = humanized;
     if (!leaveAgo) {
-      displayDate = displayDate.replace(' ago', '');
+        displayDate = (dt.millisecondsAgo()).duration();
     }
   }
   return new Handlebars.SafeString("<span class='date' title='" + fullReadable + "'>" + displayDate + "</span>");
diff --git a/app/assets/javascripts/external/humane.js b/app/assets/javascripts/external/humane.js
deleted file mode 100644
index adbd630ed..000000000
--- a/app/assets/javascripts/external/humane.js
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Javascript Humane Dates
- * Copyright (c) 2008 Dean Landolt (deanlandolt.com)
- * Re-write by Zach Leatherman (zachleat.com)
- *
- * Adopted from the John Resig's pretty.js
- * at http://ejohn.org/blog/javascript-pretty-date
- * and henrah's proposed modification
- * at http://ejohn.org/blog/javascript-pretty-date/#comment-297458
- *
- * Licensed under the MIT license.
- */
-
-function humaneDate(date, compareTo){
-
-    if(!date) {
-        return;
-    }
-
-    var lang = {
-            ago: 'ago',
-            from: '',
-            now: 'just now',
-            minute: 'minute',
-            minutes: 'minutes',
-            hour: 'hour',
-            hours: 'hours',
-            day: 'day',
-            days: 'days',
-            week: 'week',
-            weeks: 'weeks',
-            month: 'month',
-            months: 'months',
-            year: 'year',
-            years: 'years'
-        },
-        formats = [
-            [60, lang.now],
-            [3600, lang.minute, lang.minutes, 60], // 60 minutes, 1 minute
-            [86400, lang.hour, lang.hours, 3600], // 24 hours, 1 hour
-            [604800, lang.day, lang.days, 86400], // 7 days, 1 day
-            [2628000, lang.week, lang.weeks, 604800], // ~1 month, 1 week
-            [31536000, lang.month, lang.months, 2628000], // 1 year, ~1 month
-            [Infinity, lang.year, lang.years, 31536000] // Infinity, 1 year
-        ],
-        isString = typeof date == 'string',
-        date = isString ?
-                    new Date(('' + date).replace(/-/g,"/").replace(/[TZ]/g," ")) :
-                    date,
-        compareTo = compareTo || new Date,
-        seconds = (compareTo - date +
-                        (compareTo.getTimezoneOffset() -
-                            // if we received a GMT time from a string, doesn't include time zone bias
-                            // if we got a date object, the time zone is built in, we need to remove it.
-                            (isString ? 0 : date.getTimezoneOffset())
-                        ) * 60000
-                    ) / 1000,
-        token;
-
-    if(seconds < 0) {
-        seconds = Math.abs(seconds);
-        token = lang.from ? ' ' + lang.from : '';
-    } else {
-        token = lang.ago ? ' ' + lang.ago : '';
-    }
-
-    /*
-     * 0 seconds && < 60 seconds        Now
-     * 60 seconds                       1 Minute
-     * > 60 seconds && < 60 minutes     X Minutes
-     * 60 minutes                       1 Hour
-     * > 60 minutes && < 24 hours       X Hours
-     * 24 hours                         1 Day
-     * > 24 hours && < 7 days           X Days
-     * 7 days                           1 Week
-     * > 7 days && < ~ 1 Month          X Weeks
-     * ~ 1 Month                        1 Month
-     * > ~ 1 Month && < 1 Year          X Months
-     * 1 Year                           1 Year
-     * > 1 Year                         X Years
-     *
-     * Single units are +10%. 1 Year shows first at 1 Year + 10%
-     */
-
-    function normalize(val, single)
-    {
-        var margin = 0.1;
-        if(val >= single && val <= single * (1+margin)) {
-            return single;
-        }
-        return val;
-    }
-
-    for(var i = 0, format = formats[0]; formats[i]; format = formats[++i]) {
-        if(seconds < format[0]) {
-            if(i === 0) {
-                // Now
-                return format[1];
-            }
-
-            var val = Math.ceil(normalize(seconds, format[3]) / (format[3]));
-            return val +
-                    ' ' +
-                    (val != 1 ? format[2] : format[1]) +
-                    (i > 0 ? token : '');
-        }
-    }
-};
-
-if(typeof jQuery != 'undefined') {
-    jQuery.fn.humaneDates = function(options)
-    {
-        var settings = jQuery.extend({
-            'lowercase': false
-        }, options);
-
-        return this.each(function()
-        {
-            var $t = jQuery(this),
-                date = $t.attr('datetime') || $t.attr('title');
-
-            date = humaneDate(date);
-
-            if(date && settings['lowercase']) {
-                date = date.toLowerCase();
-            }
-
-            if(date && $t.html() != date) {
-                // don't modify the dom if we don't have to
-                $t.html(date);
-            }
-        });
-    };
-}
\ No newline at end of file
diff --git a/app/assets/javascripts/locales/date_locales.js b/app/assets/javascripts/locales/date_locales.js
new file mode 100644
index 000000000..10aadc96b
--- /dev/null
+++ b/app/assets/javascripts/locales/date_locales.js
@@ -0,0 +1,36 @@
+// fix EN locale
+Date.getLocale('en').short_no_year = '{d} {Mon}';
+
+// create CS locale
+Date.addLocale('cs', {
+    'plural': true,
+    'capitalizeUnit': false,
+    'months': 'ledna,února,března,dubna,května,června,července,srpna,září,října,listopadu,prosince',
+    'weekdays': 'neděle,pondělí,úterý,středa,čtvrtek,pátek,sobota',
+    'units': 'milisekund:a|y||ou|ami,sekund:a|y||ou|ami,minut:a|y||ou|ami,hodin:a|y||ou|ami,den|dny|dnů|dnem|dny,týden|týdny|týdnů|týdnem|týdny,měsíc:|e|ů|em|emi,rok|roky|let|rokem|lety',
+    'short': '{d}. {month} {yyyy}',
+    'short_no_year': '{d}. {month}',
+    'long': '{d}. {month} {yyyy} {H}:{mm}',
+    'full': '{weekday} {d}. {month} {yyyy} {H}:{mm}:{ss}',
+    'relative': function(num, unit, ms, format) {
+        var numberWithUnit, last = num.toString().slice(-1);
+        var mult;
+        if (format === 'past' || format === 'future') {
+            if (num === 1) mult = 3;
+            else mult = 4;
+        } else {
+            if (num === 1) mult = 0;
+            else if (num >= 2 && num <= 4) mult = 1;
+            else mult = 2;
+        }
+        numberWithUnit = num + ' ' + this.units[(mult * 8) + unit];
+        switch(format) {
+            case 'duration':  return numberWithUnit;
+            case 'past':      return 'před ' + numberWithUnit;
+            case 'future':    return 'za ' + numberWithUnit;
+        }
+    }
+});
+
+// set the current date locale
+Date.setLocale(I18n.locale);
diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml
index 4e5f25ac5..c5b4d8df8 100644
--- a/config/locales/client.cs.yml
+++ b/config/locales/client.cs.yml
@@ -24,6 +24,7 @@ cs:
     you: "Vy"
     ok: "ok"
     or: "nebo"
+    now: "právě teď"
 
     suggested_topics:
       title: "Doporučená témata"
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 102d0970a..4463e7db4 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -24,6 +24,7 @@ en:
     you: "You"
     ok: "ok"
     or: "or"
+    now: "just now"
 
     suggested_topics:
       title: "Suggested Topics"
diff --git a/lib/age_words.rb b/lib/age_words.rb
index 1d9bf6fda..1c590d0b3 100644
--- a/lib/age_words.rb
+++ b/lib/age_words.rb
@@ -3,18 +3,7 @@ module AgeWords
   def self.age_words(secs)
     return "&mdash;" if secs.blank?
 
-    mins = (secs / 60.0)
-    hours = (mins / 60.0)
-    days = (hours / 24.0)
-    months = (days / 30.0)
-    years = (months / 12.0)
-
-    return "#{years.floor}y" if years > 1
-    return "#{months.floor}mo" if months > 1
-    return "#{days.floor}d" if days > 1
-    return "#{hours.floor}h" if hours > 1
-    return "&lt; 1m" if mins < 1
-    return "#{mins.floor}m"
+    return FreedomPatches::Rails4.distance_of_time_in_words(Time.now, Time.now + secs)
   end
 
 end
\ No newline at end of file