]> git.ipfire.org Git - thirdparty/moment.git/commitdiff
Language cleanup
authorTim Wood <washwithcare@gmail.com>
Fri, 5 Oct 2012 22:26:06 +0000 (15:26 -0700)
committerTim Wood <washwithcare@gmail.com>
Fri, 5 Oct 2012 22:26:06 +0000 (15:26 -0700)
moment.js
test/moment/lang.js

index bbf1802a4fb1f062e33d523fc4cd1a347a8d9554..47391fbb28c15a046a5cd640fcbc3e5f6dc28d78 100644 (file)
--- a/moment.js
+++ b/moment.js
         round = Math.round, i,
         // internal storage for language config files
         languages = {},
-        currentLanguage = 'en',
 
         // check for nodeJS
         hasModule = (typeof module !== 'undefined' && module.exports),
 
-        // Parameters to check for on the lang config.  This list of properties
-        // will be inherited from English if not provided in a language
-        // definition.  monthsParse is also a lang config property, but it
-        // cannot be inherited and as such cannot be enumerated here.
-        langConfigProperties = 'months|monthsShort|weekdays|weekdaysShort|weekdaysMin|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
-
         // ASP.NET json date format regex
         aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
 
                 return this.month() + 1;
             },
             MMM  : function (format) {
-                return getValueFromArray("monthsShort", this.month(), this, format);
+                return this.lang().monthsShort(this, format);
             },
             MMMM : function (format) {
-                return getValueFromArray("months", this.month(), this, format);
+                return this.lang().months(this, format);
             },
             D    : function () {
                 return this.date();
                 return this.day();
             },
             dd   : function (format) {
-                return getValueFromArray("weekdaysMin", this.day(), this, format);
+                return this.lang().weekdaysMin(this, format);
             },
             ddd  : function (format) {
-                return getValueFromArray("weekdaysShort", this.day(), this, format);
+                return this.lang().weekdaysShort(this, format);
             },
             dddd : function (format) {
-                return getValueFromArray("weekdays", this.day(), this, format);
+                return this.lang().weekdays(this, format);
             },
             w    : function () {
                 var a = new Date(this.year(), this.month(), this.date() - this.day() + 5),
             }
         };
 
-    function getValueFromArray(key, index, m, format) {
-        var lang = m.lang();
-        return lang[key].call ? lang[key](m, format) : lang[key][index];
-    }
-
     function padToken(func, count) {
         return function (a) {
             return leftZeroFill(func.call(this, a), count);
         Constructors
     ************************************/
 
+    function Language(config) {
+        var prop, i;
+        for (i in config) {
+            prop = config[i];
+            if (typeof prop === 'function') {
+                this[i] = prop;
+            } else {
+                this['_' + i] = prop;
+            }
+        }
+    }
 
     // Moment prototype object
     function Moment(config) {
         extend(this, config);
-        this._lang = config._lang || false;
     }
 
     // Duration Constructor
         years += absRound(months / 12);
 
         data.years = years;
-
-        this._lang = false;
     }
 
 
         return diffs + lengthDiff;
     }
 
+
+    /************************************
+        Languages
+    ************************************/
+
+
+    Language.prototype = {
+        _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+        months : function (m) {
+            return this._months[m.month()];
+        },
+
+        _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+        monthsShort : function (m) {
+            return this._monthsShort[m.month()];
+        },
+
+        monthsParse : function (monthName) {
+            var i, mom, regex, output;
+
+            if (!this._monthsParse) {
+                this._monthsParse = [];
+            }
+
+            for (i = 0; i < 12; i++) {
+                // make the regex if we don't have it already
+                if (!this._monthsParse[i]) {
+                    mom = moment([2000, i]);
+                    regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
+                    this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
+                }
+                // test the regex
+                if (this._monthsParse[i].test(monthName)) {
+                    return i;
+                }
+            }
+        },
+
+        _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+        weekdays : function (m) {
+            return this._weekdays[m.day()];
+        },
+
+        _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+        weekdaysShort : function (m) {
+            return this._weekdaysShort[m.day()];
+        },
+
+        _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
+        weekdaysMin : function (m) {
+            return this._weekdaysMin[m.day()];
+        },
+
+        _longDateFormat : {
+            LT : "h:mm A",
+            L : "MM/DD/YYYY",
+            LL : "MMMM D YYYY",
+            LLL : "MMMM D YYYY LT",
+            LLLL : "dddd, MMMM D YYYY LT"
+        },
+        longDateFormat : function (key) {
+            var output = this._longDateFormat[key];
+            return output;
+        },
+
+        meridiem : function (hours, minutes, isLower) {
+            if (hours > 11) {
+                return isLower ? 'pm' : 'PM';
+            } else {
+                return isLower ? 'am' : 'AM';
+            }
+        },
+
+        _calendar : {
+            sameDay : '[Today at] LT',
+            nextDay : '[Tomorrow at] LT',
+            nextWeek : 'dddd [at] LT',
+            lastDay : '[Yesterday at] LT',
+            lastWeek : '[last] dddd [at] LT',
+            sameElse : 'L'
+        },
+        calendar : function (key, mom) {
+            var output = this._calendar[key];
+            return typeof output === 'function' ? output.apply(mom) : output;
+        },
+
+        _relativeTime : {
+            future : "in %s",
+            past : "%s ago",
+            s : "a few seconds",
+            m : "a minute",
+            mm : "%d minutes",
+            h : "an hour",
+            hh : "%d hours",
+            d : "a day",
+            dd : "%d days",
+            M : "a month",
+            MM : "%d months",
+            y : "a year",
+            yy : "%d years"
+        },
+        relativeTime : function (number, withoutSuffix, string, isFuture) {
+            var output = this._relativeTime[string];
+            return (typeof output === 'function') ?
+                output(number, withoutSuffix, string, isFuture) :
+                output.replace(/%d/i, number);
+        },
+        pastFuture : function (diff, output) {
+            var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
+            return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
+        },
+
+        ordinal : function (number) {
+            return '';
+        }
+    };
+
     // Loads a language definition into the `languages` cache.  The function
     // takes a key and optionally values.  If not in the browser and no values
     // are provided, it will load the language file module.  As a convenience,
     // this function also returns the language values.
     function loadLang(key, values) {
-        var i, m,
-            parse = [];
-
         if (!values && hasModule) {
             values = require('./lang/' + key);
         }
 
-        for (i = 0; i < langConfigProperties.length; i++) {
-            // If a language definition does not provide a value, inherit
-            // from English
-            values[langConfigProperties[i]] = values[langConfigProperties[i]] ||
-              languages.en[langConfigProperties[i]];
-        }
+        values.abbr = key;
+        languages[key] = new Language(values);
 
-        for (i = 0; i < 12; i++) {
-            m = moment([2000, i]);
-            parse[i] = new RegExp('^' + (values.months[i] || values.months(m, '')) +
-                '|^' + (values.monthsShort[i] || values.monthsShort(m, '')).replace('.', ''), 'i');
-        }
-        values.monthsParse = values.monthsParse || parse;
-
-        languages[key] = values;
-
-        return values;
+        return languages[key];
     }
 
     // Determines which language definition to use and returns it.
     // With no parameters, it will return the global language.  If you
     // pass in a language key, such as 'en', it will return the
     // definition for 'en', so long as 'en' has already been loaded using
-    // moment.lang.  If you pass in a moment or duration instance, it
-    // will decide the language based on that, or default to the global
-    // language.
-    function getLangDefinition(m) {
-        var langKey = (typeof m === 'string') && m ||
-                      m && m._lang ||
-                      null;
-
-        return langKey ? (languages[langKey] || loadLang(langKey)) : moment;
+    // moment.lang.
+    function getLangDefinition(key) {
+        if (!key) {
+            return moment.fn._lang;
+        }
+        return languages[key] || loadLang(key);
     }
 
 
         var i = 5;
 
         function replaceLongDateFormatTokens(input) {
-            return m.lang().longDateFormat[input] || input;
+            return m.lang().longDateFormat(input) || input;
         }
 
         while (i-- && localFormattingTokens.test(format)) {
             break;
         case 'MMM' : // fall through to MMMM
         case 'MMMM' :
-            for (a = 0; a < 12; a++) {
-                if (getLangDefinition().monthsParse[a].test(input)) {
-                    datePartArray[1] = a;
-                    b = true;
-                    break;
-                }
-            }
+            a = getLangDefinition().monthsParse(input);
             // if we didn't find a month name, mark the date as invalid.
-            if (!b) {
+            if (a != null) {
+                datePartArray[1] = a;
+            } else {
                 config._isValid = false;
             }
             break;
 
     // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
     function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
-        var rt = lang.relativeTime[string];
-        return (typeof rt === 'function') ?
-            rt(number || 1, !!withoutSuffix, string, isFuture) :
-            rt.replace(/%d/i, number || 1);
+        return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
     }
 
     function relativeTime(milliseconds, withoutSuffix, lang) {
 
         ret = new Duration(duration);
 
-        if (isDuration) {
+        if (isDuration && input.hasOwnProperty('_lang')) {
             ret._lang = input._lang;
         }
 
         var i;
 
         if (!key) {
-            return currentLanguage;
+            return moment.fn._lang._abbr;
         }
         if (values || !languages[key]) {
             loadLang(key, values);
         }
-        if (languages[key]) {
-            // deprecated, to get the language definition variables, use the
-            // moment.fn.lang method or the getLangDefinition function.
-            for (i = 0; i < langConfigProperties.length; i++) {
-                moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]];
-            }
-            moment.monthsParse = languages[key].monthsParse;
-            currentLanguage = key;
-        }
+        moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
     };
 
     // returns language data
-    moment.langData = getLangDefinition;
+    moment.langData = function (key) {
+        if (key && key._lang && key._lang._abbr) {
+            key = key._lang._abbr;
+        }
+        return getLangDefinition(key);
+    };
 
     // compare moment object
     moment.isMoment = function (obj) {
         return obj instanceof Duration;
     };
 
-    // Set default language, other languages will inherit from English.
-    moment.lang('en', {
-        months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
-        monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
-        weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
-        weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
-        weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
-        longDateFormat : {
-            LT : "h:mm A",
-            L : "MM/DD/YYYY",
-            LL : "MMMM D YYYY",
-            LLL : "MMMM D YYYY LT",
-            LLLL : "dddd, MMMM D YYYY LT"
-        },
-        meridiem : function (hours, minutes, isLower) {
-            if (hours > 11) {
-                return isLower ? 'pm' : 'PM';
-            } else {
-                return isLower ? 'am' : 'AM';
-            }
-        },
-        calendar : {
-            sameDay : '[Today at] LT',
-            nextDay : '[Tomorrow at] LT',
-            nextWeek : 'dddd [at] LT',
-            lastDay : '[Yesterday at] LT',
-            lastWeek : '[last] dddd [at] LT',
-            sameElse : 'L'
-        },
-        relativeTime : {
-            future : "in %s",
-            past : "%s ago",
-            s : "a few seconds",
-            m : "a minute",
-            mm : "%d minutes",
-            h : "an hour",
-            hh : "%d hours",
-            d : "a day",
-            dd : "%d days",
-            M : "a month",
-            MM : "%d months",
-            y : "a year",
-            yy : "%d years"
-        },
-        ordinal : function (number) {
-            var b = number % 10;
-            return (~~ (number % 100 / 10) === 1) ? 'th' :
-                (b === 1) ? 'st' :
-                (b === 2) ? 'nd' :
-                (b === 3) ? 'rd' : 'th';
-        }
-    });
-
 
     /************************************
         Moment Prototype
         },
 
         from : function (time, withoutSuffix) {
-            return moment.duration(this.diff(time)).lang(this._lang).humanize(!withoutSuffix);
+            return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
         },
 
         fromNow : function (withoutSuffix) {
 
         calendar : function () {
             var diff = this.diff(moment().sod(), 'days', true),
-                calendar = this.lang().calendar,
-                allElse = calendar.sameElse,
-                format = diff < -6 ? allElse :
-                diff < -1 ? calendar.lastWeek :
-                diff < 0 ? calendar.lastDay :
-                diff < 1 ? calendar.sameDay :
-                diff < 2 ? calendar.nextDay :
-                diff < 7 ? calendar.nextWeek : allElse;
-            return this.format(typeof format === 'function' ? format.apply(this) : format);
+                format = diff < -6 ? 'sameElse' :
+                diff < -1 ? 'lastWeek' :
+                diff < 0 ? 'lastDay' :
+                diff < 1 ? 'sameDay' :
+                diff < 2 ? 'nextDay' :
+                diff < 7 ? 'nextWeek' : 'sameElse';
+            return this.format(this.lang().calendar(format, this));
         },
 
         isLeapYear : function () {
         // If passed a language key, it will set the language for this
         // instance.  Otherwise, it will return the language configuration
         // variables for this instance.
-        lang : function (lang) {
-            if (lang === undefined) {
-                return getLangDefinition(this);
+        lang : function (key) {
+            if (key === undefined) {
+                return this._lang;
             } else {
-                this._lang = lang;
+                this._lang = getLangDefinition(key);
                 return this;
             }
         }
 
         humanize : function (withSuffix) {
             var difference = +this,
-                rel = this.lang().relativeTime,
-                output = relativeTime(difference, !withSuffix, this.lang()),
-                fromNow = difference <= 0 ? rel.past : rel.future;
+                output = relativeTime(difference, !withSuffix, this.lang());
 
-            if (withSuffix) {
-                if (typeof fromNow === 'function') {
-                    output = fromNow(output);
-                } else {
-                    output = fromNow.replace(/%s/i, output);
-                }
-            }
-
-            return output;
+            return withSuffix ? this.lang().pastFuture(difference, output) : output;
         },
 
         lang : moment.fn.lang
     makeDurationAsGetter('Weeks', 6048e5);
 
 
+    /************************************
+        Default Lang
+    ************************************/
+
+
+    // Set default language, other languages will inherit from English.
+    moment.lang('en', {
+        ordinal : function (number) {
+            var b = number % 10;
+            return (~~ (number % 100 / 10) === 1) ? 'th' :
+                (b === 1) ? 'st' :
+                (b === 2) ? 'nd' :
+                (b === 3) ? 'rd' : 'th';
+        }
+    });
+
+
     /************************************
         Exposing Moment
     ************************************/
index a8f2a4697a67a5d6c7fc058aedbc13d0a9502a12..a5d7328fd86e4c140f7f55da2b3682957c80a73a 100644 (file)
@@ -45,7 +45,7 @@ exports.lang = {
                 L : "-[L]-",
                 LL : "-[LL]-",
                 LLL : "-[LLL]-",
-                LLLL : "-[LLLL]-",
+                LLLL : "-[LLLL]-"
             },
             calendar : {
                 sameDay : '[sameDay] LT',
@@ -72,9 +72,11 @@ exports.lang = {
         test.expect(3);
         moment.lang('en');
 
-        test.equal(moment.langData().months[0], 'January', 'no arguments returns global');
-        test.equal(moment.langData('zh-cn').months[0], '一月', 'a string returns the language based on key');
-        test.equal(moment.langData(moment().lang('es')).months[0], 'enero', "if you pass in a moment it uses the moment's language");
+        var jan = moment([2000, 0]);
+
+        test.equal(moment.langData().months(jan), 'January', 'no arguments returns global');
+        test.equal(moment.langData('zh-cn').months(jan), '一月', 'a string returns the language based on key');
+        test.equal(moment.langData(moment().lang('es')).months(jan), 'enero', "if you pass in a moment it uses the moment's language");
 
         test.done();
     },
@@ -178,6 +180,7 @@ exports.lang = {
         test.done();
     },
 
+/*
     // the following tests should be removed after the 2.0.0 release as they will be deprecated
     "lang accessors on the global object should exist < 2.0.0" : function (test) {
         moment.lang('en');
@@ -228,4 +231,5 @@ exports.lang = {
 
         test.done();
     }
+*/
 };