]> git.ipfire.org Git - thirdparty/moment.git/commitdiff
Implement locale inheritance and locale updating
authorIskren Chernev <iskren.chernev@gmail.com>
Fri, 27 Nov 2015 05:54:49 +0000 (07:54 +0200)
committerIskren Chernev <iskren.chernev@gmail.com>
Sun, 6 Mar 2016 08:34:56 +0000 (00:34 -0800)
src/lib/locale/constructor.js
src/lib/locale/locale.js
src/lib/locale/locales.js
src/lib/locale/set.js
src/lib/units/week-year.js
src/lib/utils/is-object.js [new file with mode: 0644]
src/moment.js
src/test/moment/locale_inheritance.js [new file with mode: 0644]
src/test/moment/locale_update.js [new file with mode: 0644]

index 2c17b5d007cdc5a7dd3975ada1f55009a793a723..c32b73ee11c6d3e6759d91237c169ff343ba852d 100644 (file)
@@ -1,2 +1,5 @@
-export function Locale() {
+export function Locale(config) {
+    if (config != null) {
+        this.set(config);
+    }
 }
index 2fc3c46ff0031c04e366bd78ee0f795859c77483..9657a5b58eca2dd78795ffc7bd5d939394b0719f 100644 (file)
@@ -4,6 +4,7 @@ import './prototype';
 import {
     getSetGlobalLocale,
     defineLocale,
+    updateLocale,
     getLocale
 } from './locales';
 
@@ -18,6 +19,7 @@ import {
 export {
     getSetGlobalLocale,
     defineLocale,
+    updateLocale,
     getLocale,
     listMonths,
     listMonthsShort,
index 00114cf552beff5ebd40149a1c772fba48b399a6..64eebd3ea1d62f108f448bd23d5f6240528bd9e1 100644 (file)
@@ -1,6 +1,8 @@
 import isArray from '../utils/is-array';
 import isUndefined from '../utils/is-undefined';
 import compareArrays from '../utils/compare-arrays';
+import { deprecateSimple } from '../utils/deprecate';
+import { mergeConfigs } from './set';
 import { Locale } from './constructor';
 
 // internal storage for locale config files
@@ -76,11 +78,25 @@ export function getSetGlobalLocale (key, values) {
     return globalLocale._abbr;
 }
 
-export function defineLocale (name, values) {
-    if (values !== null) {
-        values.abbr = name;
-        locales[name] = locales[name] || new Locale();
-        locales[name].set(values);
+export function defineLocale (name, config) {
+    if (config !== null) {
+        config.abbr = name;
+        if (locales[name] != null) {
+            deprecateSimple('defineLocaleOverride',
+                    'use moment.updateLocale(localeName, config) to change ' +
+                    'an existing locale. moment.defineLocale(localeName, ' +
+                    'config) should only be used for creating a new locale');
+            config = mergeConfigs(locales[name]._config, config);
+        } else if (config.parentLocale != null) {
+            if (locales[config.parentLocale] != null) {
+                config = mergeConfigs(locales[config.parentLocale]._config, config);
+            } else {
+                // treat as if there is no base config
+                deprecateSimple('parentLocaleUndefined',
+                        'specified parentLocale is not defined yet');
+            }
+        }
+        locales[name] = new Locale(config);
 
         // backwards compat for now: also set the locale
         getSetGlobalLocale(name);
@@ -93,6 +109,31 @@ export function defineLocale (name, values) {
     }
 }
 
+export function updateLocale(name, config) {
+    if (config != null) {
+        var locale;
+        if (locales[name] != null) {
+            config = mergeConfigs(locales[name]._config, config);
+        }
+        locale = new Locale(config);
+        locale.parentLocale = locales[name];
+        locales[name] = locale;
+
+        // backwards compat for now: also set the locale
+        getSetGlobalLocale(name);
+    } else {
+        // pass null for config to unupdate, useful for tests
+        if (locales[name] != null) {
+            if (locales[name].parentLocale != null) {
+                locales[name] = locales[name].parentLocale;
+            } else if (locales[name] != null) {
+                delete locales[name];
+            }
+        }
+    }
+    return locales[name];
+}
+
 // returns locale data
 export function getLocale (key) {
     var locale;
index 062e842f1694d0ee406d930eeefece8872d4ff19..1fd1389024fe2fbcfed0ccd507b8e06f01c9a642 100644 (file)
@@ -1,4 +1,7 @@
 import isFunction from '../utils/is-function';
+import extend from '../utils/extend';
+import isObject from '../utils/is-object';
+import hasOwnProp from '../utils/has-own-prop';
 
 export function set (config) {
     var prop, i;
@@ -10,7 +13,26 @@ export function set (config) {
             this['_' + i] = prop;
         }
     }
+    this._config = config;
     // Lenient ordinal parsing accepts just a number in addition to
     // number + (possibly) stuff coming from _ordinalParseLenient.
     this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
 }
+
+export function mergeConfigs(parentConfig, childConfig) {
+    var res = extend({}, parentConfig), prop;
+    for (prop in childConfig) {
+        if (hasOwnProp(childConfig, prop)) {
+            if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
+                res[prop] = {};
+                extend(res[prop], parentConfig[prop]);
+                extend(res[prop], childConfig[prop]);
+            } else if (childConfig[prop] != null) {
+                res[prop] = childConfig[prop];
+            } else {
+                delete res[prop];
+            }
+        }
+    }
+    return res;
+}
index 44c4f33090140cc69aeeac50703e6126f846c3ef..29d1fec55a04e509c67134b4d7d239a0e648cbe5 100644 (file)
@@ -93,7 +93,6 @@ function setWeekAll(weekYear, week, weekday, dow, doy) {
     var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
         date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
 
-    // console.log("got", weekYear, week, weekday, "set", date.toISOString());
     this.year(date.getUTCFullYear());
     this.month(date.getUTCMonth());
     this.date(date.getUTCDate());
diff --git a/src/lib/utils/is-object.js b/src/lib/utils/is-object.js
new file mode 100644 (file)
index 0000000..6018e12
--- /dev/null
@@ -0,0 +1,3 @@
+export default function isObject(input) {
+    return Object.prototype.toString.call(input) === '[object Object]';
+}
index fccc13d69a06054a34831f83c54daccb30cc0825..df9afa81f6a2949ee9e7b15ca7e3ca3a38dffdee 100644 (file)
@@ -23,6 +23,7 @@ import {
 
 import {
     defineLocale,
+    updateLocale,
     getSetGlobalLocale as locale,
     getLocale          as localeData,
     listMonths         as months,
@@ -63,6 +64,7 @@ moment.isDuration            = isDuration;
 moment.monthsShort           = monthsShort;
 moment.weekdaysMin           = weekdaysMin;
 moment.defineLocale          = defineLocale;
+moment.updateLocale          = updateLocale;
 moment.weekdaysShort         = weekdaysShort;
 moment.normalizeUnits        = normalizeUnits;
 moment.relativeTimeThreshold = relativeTimeThreshold;
diff --git a/src/test/moment/locale_inheritance.js b/src/test/moment/locale_inheritance.js
new file mode 100644 (file)
index 0000000..510d4b7
--- /dev/null
@@ -0,0 +1,165 @@
+import { module, test } from '../qunit';
+import moment from '../../moment';
+
+module('locale inheritance');
+
+test('calendar', function (assert) {
+    moment.defineLocale('base-cal', {
+        calendar : {
+            sameDay: '[Today at] HH:mm',
+            nextDay: '[Tomorrow at] HH:mm',
+            nextWeek: '[Next week at] HH:mm',
+            lastDay: '[Yesterday at] HH:mm',
+            lastWeek: '[Last week at] HH:mm',
+            sameElse: '[whatever]'
+        }
+    });
+    moment.defineLocale('child-cal', {
+        parentLocale: 'base-cal',
+        calendar: {
+            sameDay: '[Today] HH:mm',
+            nextDay: '[Tomorrow] HH:mm',
+            nextWeek: '[Next week] HH:mm'
+        }
+    });
+
+    moment.locale('child-cal');
+    var anchor = moment.utc('2015-05-05T12:00:00', moment.ISO_8601);
+    assert.equal(anchor.clone().add(3, 'hours').calendar(anchor), 'Today 15:00', 'today uses child version');
+    assert.equal(anchor.clone().add(1, 'day').calendar(anchor), 'Tomorrow 12:00', 'tomorrow uses child version');
+    assert.equal(anchor.clone().add(3, 'days').calendar(anchor), 'Next week 12:00', 'next week uses child version');
+
+    assert.equal(anchor.clone().subtract(1, 'day').calendar(anchor), 'Yesterday at 12:00', 'yesterday uses parent version');
+    assert.equal(anchor.clone().subtract(3, 'days').calendar(anchor), 'Last week at 12:00', 'last week uses parent version');
+    assert.equal(anchor.clone().subtract(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version -');
+    assert.equal(anchor.clone().add(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version +');
+});
+
+test('missing', function (assert) {
+    moment.defineLocale('base-cal-2', {
+        calendar: {
+            sameDay: '[Today at] HH:mm',
+            nextDay: '[Tomorrow at] HH:mm',
+            nextWeek: '[Next week at] HH:mm',
+            lastDay: '[Yesterday at] HH:mm',
+            lastWeek: '[Last week at] HH:mm',
+            sameElse: '[whatever]'
+        }
+    });
+    moment.defineLocale('child-cal-2', {
+        parentLocale: 'base-cal-2'
+    });
+    moment.locale('child-cal-2');
+    var anchor = moment.utc('2015-05-05T12:00:00', moment.ISO_8601);
+    assert.equal(anchor.clone().add(3, 'hours').calendar(anchor), 'Today at 15:00', 'today uses parent version');
+    assert.equal(anchor.clone().add(1, 'day').calendar(anchor), 'Tomorrow at 12:00', 'tomorrow uses parent version');
+    assert.equal(anchor.clone().add(3, 'days').calendar(anchor), 'Next week at 12:00', 'next week uses parent version');
+    assert.equal(anchor.clone().subtract(1, 'day').calendar(anchor), 'Yesterday at 12:00', 'yesterday uses parent version');
+    assert.equal(anchor.clone().subtract(3, 'days').calendar(anchor), 'Last week at 12:00', 'last week uses parent version');
+    assert.equal(anchor.clone().subtract(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version -');
+    assert.equal(anchor.clone().add(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version +');
+});
+
+// Test function vs obj both directions
+
+test('long date format', function (assert) {
+    moment.defineLocale('base-ldf', {
+        longDateFormat : {
+            LTS  : 'h:mm:ss A',
+            LT   : 'h:mm A',
+            L    : 'MM/DD/YYYY',
+            LL   : 'MMMM D, YYYY',
+            LLL  : 'MMMM D, YYYY h:mm A',
+            LLLL : 'dddd, MMMM D, YYYY h:mm A'
+        }
+    });
+    moment.defineLocale('child-ldf', {
+        parentLocale: 'base-ldf',
+        longDateFormat: {
+            LLL  : '[child] MMMM D, YYYY h:mm A',
+            LLLL : '[child] dddd, MMMM D, YYYY h:mm A'
+        }
+    });
+
+    moment.locale('child-ldf');
+    var anchor = moment.utc('2015-09-06T12:34:56', moment.ISO_8601);
+    assert.equal(anchor.format('LTS'), '12:34:56 PM', 'LTS uses base');
+    assert.equal(anchor.format('LT'), '12:34 PM', 'LT uses base');
+    assert.equal(anchor.format('L'), '09/06/2015', 'L uses base');
+    assert.equal(anchor.format('l'), '9/6/2015', 'l uses base');
+    assert.equal(anchor.format('LL'), 'September 6, 2015', 'LL uses base');
+    assert.equal(anchor.format('ll'), 'Sep 6, 2015', 'll uses base');
+    assert.equal(anchor.format('LLL'), 'child September 6, 2015 12:34 PM', 'LLL uses child');
+    assert.equal(anchor.format('lll'), 'child Sep 6, 2015 12:34 PM', 'lll uses child');
+    assert.equal(anchor.format('LLLL'), 'child Sunday, September 6, 2015 12:34 PM', 'LLLL uses child');
+    assert.equal(anchor.format('llll'), 'child Sun, Sep 6, 2015 12:34 PM', 'llll uses child');
+});
+
+test('ordinal', function (assert) {
+    moment.defineLocale('base-ordinal-1', {
+        ordinal : '%dx'
+    });
+    moment.defineLocale('child-ordinal-1', {
+        parentLocale: 'base-ordinal-1',
+        ordinal : '%dy'
+    });
+
+    assert.equal(moment.utc('2015-02-03', moment.ISO_8601).format('Do'), '3y', 'ordinal uses child string');
+
+    moment.defineLocale('base-ordinal-2', {
+        ordinal : '%dx'
+    });
+    moment.defineLocale('child-ordinal-2', {
+        parentLocale: 'base-ordinal-2',
+        ordinal : function (num) {
+            return num + 'y';
+        }
+    });
+
+    assert.equal(moment.utc('2015-02-03', moment.ISO_8601).format('Do'), '3y', 'ordinal uses child function');
+
+    moment.defineLocale('base-ordinal-3', {
+        ordinal : function (num) {
+            return num + 'x';
+        }
+    });
+    moment.defineLocale('child-ordinal-3', {
+        parentLocale: 'base-ordinal-3',
+        ordinal : '%dy'
+    });
+
+    assert.equal(moment.utc('2015-02-03', moment.ISO_8601).format('Do'), '3y', 'ordinal uses child string (overwrite parent function)');
+});
+
+test('ordinal parse', function (assert) {
+    moment.defineLocale('base-ordinal-parse-1', {
+        ordinalParse : /\d{1,2}x/
+    });
+    moment.defineLocale('child-ordinal-parse-1', {
+        parentLocale: 'base-ordinal-parse-1',
+        ordinalParse : /\d{1,2}y/
+    });
+
+    assert.ok(moment.utc('2015-01-1y', 'YYYY-MM-Do', true).isValid(), 'ordinal parse uses child');
+
+    moment.defineLocale('base-ordinal-parse-2', {
+        ordinalParse : /\d{1,2}x/
+    });
+    moment.defineLocale('child-ordinal-parse-2', {
+        parentLocale: 'base-ordinal-parse-2',
+        ordinalParse : null
+    });
+
+    assert.ok(moment.utc('2015-01-1', 'YYYY-MM-Do', true).isValid(), 'ordinal parse uses child (default)');
+});
+
+test('months', function (assert) {
+    moment.defineLocale('base-months', {
+        months : 'One_Two_Three_Four_Five_Six_Seven_Eight_Nine_Ten_Eleven_Twelve'.split('_')
+    });
+    moment.defineLocale('child-months', {
+        parentLocale: 'base-months',
+        months : 'First_Second_Third_Fourth_Fifth_Sixth_Seventh_Eighth_Ninth_Tenth_Eleventh_Twelveth '.split('_')
+    });
+    assert.ok(moment.utc('2015-01-01', 'YYYY-MM-DD').format('MMMM'), 'First', 'months uses child');
+});
diff --git a/src/test/moment/locale_update.js b/src/test/moment/locale_update.js
new file mode 100644 (file)
index 0000000..13ca2e9
--- /dev/null
@@ -0,0 +1,167 @@
+import { module, test } from '../qunit';
+import moment from '../../moment';
+
+module('locale update');
+
+test('calendar', function (assert) {
+    moment.defineLocale('cal', null);
+    moment.defineLocale('cal', {
+        calendar : {
+            sameDay: '[Today at] HH:mm',
+            nextDay: '[Tomorrow at] HH:mm',
+            nextWeek: '[Next week at] HH:mm',
+            lastDay: '[Yesterday at] HH:mm',
+            lastWeek: '[Last week at] HH:mm',
+            sameElse: '[whatever]'
+        }
+    });
+    moment.updateLocale('cal', {
+        calendar: {
+            sameDay: '[Today] HH:mm',
+            nextDay: '[Tomorrow] HH:mm',
+            nextWeek: '[Next week] HH:mm'
+        }
+    });
+
+    moment.locale('cal');
+    var anchor = moment.utc('2015-05-05T12:00:00', moment.ISO_8601);
+    assert.equal(anchor.clone().add(3, 'hours').calendar(anchor), 'Today 15:00', 'today uses child version');
+    assert.equal(anchor.clone().add(1, 'day').calendar(anchor), 'Tomorrow 12:00', 'tomorrow uses child version');
+    assert.equal(anchor.clone().add(3, 'days').calendar(anchor), 'Next week 12:00', 'next week uses child version');
+
+    assert.equal(anchor.clone().subtract(1, 'day').calendar(anchor), 'Yesterday at 12:00', 'yesterday uses parent version');
+    assert.equal(anchor.clone().subtract(3, 'days').calendar(anchor), 'Last week at 12:00', 'last week uses parent version');
+    assert.equal(anchor.clone().subtract(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version -');
+    assert.equal(anchor.clone().add(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version +');
+});
+
+test('missing', function (assert) {
+    moment.defineLocale('cal-2', null);
+    moment.defineLocale('cal-2', {
+        calendar: {
+            sameDay: '[Today at] HH:mm',
+            nextDay: '[Tomorrow at] HH:mm',
+            nextWeek: '[Next week at] HH:mm',
+            lastDay: '[Yesterday at] HH:mm',
+            lastWeek: '[Last week at] HH:mm',
+            sameElse: '[whatever]'
+        }
+    });
+    moment.updateLocale('cal-2', {
+    });
+    moment.locale('cal-2');
+    var anchor = moment.utc('2015-05-05T12:00:00', moment.ISO_8601);
+    assert.equal(anchor.clone().add(3, 'hours').calendar(anchor), 'Today at 15:00', 'today uses parent version');
+    assert.equal(anchor.clone().add(1, 'day').calendar(anchor), 'Tomorrow at 12:00', 'tomorrow uses parent version');
+    assert.equal(anchor.clone().add(3, 'days').calendar(anchor), 'Next week at 12:00', 'next week uses parent version');
+    assert.equal(anchor.clone().subtract(1, 'day').calendar(anchor), 'Yesterday at 12:00', 'yesterday uses parent version');
+    assert.equal(anchor.clone().subtract(3, 'days').calendar(anchor), 'Last week at 12:00', 'last week uses parent version');
+    assert.equal(anchor.clone().subtract(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version -');
+    assert.equal(anchor.clone().add(7, 'days').calendar(anchor), 'whatever', 'sameElse uses parent version +');
+});
+
+// Test function vs obj both directions
+
+test('long date format', function (assert) {
+    moment.defineLocale('ldf', null);
+    moment.defineLocale('ldf', {
+        longDateFormat : {
+            LTS  : 'h:mm:ss A',
+            LT   : 'h:mm A',
+            L    : 'MM/DD/YYYY',
+            LL   : 'MMMM D, YYYY',
+            LLL  : 'MMMM D, YYYY h:mm A',
+            LLLL : 'dddd, MMMM D, YYYY h:mm A'
+        }
+    });
+    moment.updateLocale('ldf', {
+        longDateFormat: {
+            LLL  : '[child] MMMM D, YYYY h:mm A',
+            LLLL : '[child] dddd, MMMM D, YYYY h:mm A'
+        }
+    });
+
+    moment.locale('ldf');
+    var anchor = moment.utc('2015-09-06T12:34:56', moment.ISO_8601);
+    assert.equal(anchor.format('LTS'), '12:34:56 PM', 'LTS uses base');
+    assert.equal(anchor.format('LT'), '12:34 PM', 'LT uses base');
+    assert.equal(anchor.format('L'), '09/06/2015', 'L uses base');
+    assert.equal(anchor.format('l'), '9/6/2015', 'l uses base');
+    assert.equal(anchor.format('LL'), 'September 6, 2015', 'LL uses base');
+    assert.equal(anchor.format('ll'), 'Sep 6, 2015', 'll uses base');
+    assert.equal(anchor.format('LLL'), 'child September 6, 2015 12:34 PM', 'LLL uses child');
+    assert.equal(anchor.format('lll'), 'child Sep 6, 2015 12:34 PM', 'lll uses child');
+    assert.equal(anchor.format('LLLL'), 'child Sunday, September 6, 2015 12:34 PM', 'LLLL uses child');
+    assert.equal(anchor.format('llll'), 'child Sun, Sep 6, 2015 12:34 PM', 'llll uses child');
+});
+
+test('ordinal', function (assert) {
+    moment.defineLocale('ordinal-1', null);
+    moment.defineLocale('ordinal-1', {
+        ordinal : '%dx'
+    });
+    moment.updateLocale('ordinal-1', {
+        ordinal : '%dy'
+    });
+
+    assert.equal(moment.utc('2015-02-03', moment.ISO_8601).format('Do'), '3y', 'ordinal uses child string');
+
+    moment.defineLocale('ordinal-2', null);
+    moment.defineLocale('ordinal-2', {
+        ordinal : '%dx'
+    });
+    moment.defineLocale('ordinal-2', {
+        parentLocale: 'ordinal-2',
+        ordinal : function (num) {
+            return num + 'y';
+        }
+    });
+
+    assert.equal(moment.utc('2015-02-03', moment.ISO_8601).format('Do'), '3y', 'ordinal uses child function');
+
+    moment.defineLocale('ordinal-3', null);
+    moment.defineLocale('ordinal-3', {
+        ordinal : function (num) {
+            return num + 'x';
+        }
+    });
+    moment.updateLocale('ordinal-3', {
+        ordinal : '%dy'
+    });
+
+    assert.equal(moment.utc('2015-02-03', moment.ISO_8601).format('Do'), '3y', 'ordinal uses child string (overwrite parent function)');
+});
+
+test('ordinal parse', function (assert) {
+    moment.defineLocale('ordinal-parse-1', null);
+    moment.defineLocale('ordinal-parse-1', {
+        ordinalParse : /\d{1,2}x/
+    });
+    moment.updateLocale('ordinal-parse-1', {
+        ordinalParse : /\d{1,2}y/
+    });
+
+    assert.ok(moment.utc('2015-01-1y', 'YYYY-MM-Do', true).isValid(), 'ordinal parse uses child');
+
+    moment.defineLocale('ordinal-parse-2', null);
+    moment.defineLocale('ordinal-parse-2', {
+        ordinalParse : /\d{1,2}x/
+    });
+    moment.updateLocale('ordinal-parse-2', {
+        ordinalParse : null
+    });
+
+    assert.ok(moment.utc('2015-01-1', 'YYYY-MM-Do', true).isValid(), 'ordinal parse uses child (default)');
+});
+
+test('months', function (assert) {
+    moment.defineLocale('months', null);
+    moment.defineLocale('months', {
+        months : 'One_Two_Three_Four_Five_Six_Seven_Eight_Nine_Ten_Eleven_Twelve'.split('_')
+    });
+    moment.updateLocale('months', {
+        parentLocale: 'base-months',
+        months : 'First_Second_Third_Fourth_Fifth_Sixth_Seventh_Eighth_Ninth_Tenth_Eleventh_Twelveth '.split('_')
+    });
+    assert.ok(moment.utc('2015-01-01', 'YYYY-MM-DD').format('MMMM'), 'First', 'months uses child');
+});