diff --git a/analytics_dashboard/static/js/load/init-page.js b/analytics_dashboard/static/js/load/init-page.js index d483868d4..63bfedc54 100644 --- a/analytics_dashboard/static/js/load/init-page.js +++ b/analytics_dashboard/static/js/load/init-page.js @@ -1,9 +1,14 @@ /** * Initializes page with the model and various UI elements that need JS hooks. */ -define(['jquery', 'load/init-models', 'load/init-tooltips'], function($, models) { +define([ + 'jquery', 'utils/fix-language', 'load/init-models', 'load/init-tooltips' +], function($, fixLanguage, models) { 'use strict'; + // set the standardized language code + window.language = fixLanguage(window.language); + // initialize tracking require(['load/init-tracking'], function(initTracking) { initTracking(models); diff --git a/analytics_dashboard/static/js/test/specs/fix-language-spec.js b/analytics_dashboard/static/js/test/specs/fix-language-spec.js new file mode 100644 index 000000000..855ade54e --- /dev/null +++ b/analytics_dashboard/static/js/test/specs/fix-language-spec.js @@ -0,0 +1,30 @@ +// eslint-disable-next-line import/no-unresolved +define(['utils/fix-language'], function(fixLanguages) { + 'use strict'; + + function expectLanguages(actualLanguages, expectedLanguageCode) { + actualLanguages.forEach(function(languageCode) { + expect(fixLanguages(languageCode), expectedLanguageCode); + }); + } + + describe('window.language', function() { + it('should default to English if an invalid argument is provided or not known to CLDR', function() { + expectLanguages([null, '', 1, false, 'not-real'], 'en'); + }); + + it('should return zh if the given language code is either zh-cn, zh-sg and zh-hans(-*) ', function() { + expectLanguages(['zh-cn', 'zh-sg', 'zh-hans', 'zh-hans-cn'], 'zh'); + }); + + it('should return zh-Hant if the given language code is either zh-tw, zh-hk, zh-mo and zh-hant-* ', + function() { + expectLanguages(['zh-tw', 'zh-hk', 'zh-mo', 'zh-hant-tw'], 'zh-Hant'); + } + ); + + it('should return correct-casing any given cased language code', function() { + expectLanguages(['en-gb', 'en-GB', 'EN-GB'], 'en-GB'); + }); + }); +}); diff --git a/analytics_dashboard/static/js/test/specs/globalization-spec.js b/analytics_dashboard/static/js/test/specs/globalization-spec.js index 296597e86..90b6449b8 100644 --- a/analytics_dashboard/static/js/test/specs/globalization-spec.js +++ b/analytics_dashboard/static/js/test/specs/globalization-spec.js @@ -1,42 +1,11 @@ // eslint-disable-next-line import/no-unresolved -define(['json!cldr-data/availableLocales.json', 'utils/globalization'], function(availableLocales) { +define(['utils/globalization'], function(Globalization) { 'use strict'; - var lowerLocalesMapping = {}; - availableLocales.availableLocales.forEach(function(locale) { - lowerLocalesMapping[locale.toLowerCase()] = locale; - }); - - describe('fixLanguageCode', function() { - it('should default to English if an invalid argument is provided', function() { - expect(fixLanguageCode(null, lowerLocalesMapping)).toEqual('en'); - expect(fixLanguageCode('', lowerLocalesMapping)).toEqual('en'); - expect(fixLanguageCode(1, lowerLocalesMapping)).toEqual('en'); - expect(fixLanguageCode(true, lowerLocalesMapping)).toEqual('en'); - }); - - it('should return zh if the given language code is either zh-cn, zh-sg and zh-hans(-*) ', function() { - expect(fixLanguageCode('zh-cn', lowerLocalesMapping)).toEqual('zh'); - expect(fixLanguageCode('zh-sg', lowerLocalesMapping)).toEqual('zh'); - expect(fixLanguageCode('zh-hans', lowerLocalesMapping)).toEqual('zh'); - expect(fixLanguageCode('zh-hans-cn', lowerLocalesMapping)).toEqual('zh'); - }); - - it('should return zh-Hant if the given language code is either zh-tw, zh-hk, zh-mo and zh-hant-* ', function() { - expect(fixLanguageCode('zh-tw', lowerLocalesMapping)).toEqual('zh-Hant'); - expect(fixLanguageCode('zh-hk', lowerLocalesMapping)).toEqual('zh-Hant'); - expect(fixLanguageCode('zh-mo', lowerLocalesMapping)).toEqual('zh-Hant'); - expect(fixLanguageCode('zh-hant-tw', lowerLocalesMapping)).toEqual('zh-Hant'); - }); - - it('should return en if the given language code is not known to CLDR', function() { - expect(fixLanguageCode('not-real', lowerLocalesMapping)).toEqual('en'); - }); - - it('should return correct-casing any given cased language code', function() { - expect(fixLanguageCode('en-gb', lowerLocalesMapping)).toEqual('en-GB'); - expect(fixLanguageCode('en-GB', lowerLocalesMapping)).toEqual('en-GB'); - expect(fixLanguageCode('EN-GB', lowerLocalesMapping)).toEqual('en-GB'); + describe('Globalization', function() { + // globalization functionality (e.g. number formatting) is tested in utils-spec + it('should be returned', function() { + expect(Globalization).toBeDefined(); }); }); }); diff --git a/analytics_dashboard/static/js/utils/fix-language.js b/analytics_dashboard/static/js/utils/fix-language.js new file mode 100644 index 000000000..3a0935149 --- /dev/null +++ b/analytics_dashboard/static/js/utils/fix-language.js @@ -0,0 +1,44 @@ +/** + * In order to localize numbers and dates, the language codes must made those in + * CLDR. This module standardizes language code argument so that the + * correct language settings are loaded. + */ +define([ + 'json!cldr-data/availableLocales.json' // eslint-disable-line import/no-unresolved +], function(availableLocales) { + 'use strict'; + + var lowerLocalesMapping = {}; + availableLocales.availableLocales.forEach(function(locale) { + lowerLocalesMapping[locale.toLowerCase()] = locale; + }); + + return function(languageCode) { + var fixedLanguageCode; + + if (!languageCode || typeof languageCode !== 'string') { + return 'en'; + } + + fixedLanguageCode = languageCode.toLowerCase(); + + // CLDR uses zh for Simplified Chinese, while Django may use different strings. + if (fixedLanguageCode === 'zh-cn' || fixedLanguageCode === 'zh-sg' || + fixedLanguageCode.indexOf('zh-hans') === 0) { + fixedLanguageCode = 'zh'; + } + // CLDR uses zh-hant for Traditional Chinese, while Django may use different strings. + if (fixedLanguageCode === 'zh-tw' || fixedLanguageCode === 'zh-hk' || + fixedLanguageCode === 'zh-mo' || fixedLanguageCode.indexOf('zh-hant') === 0) { + fixedLanguageCode = 'zh-hant'; + } + + // There doesn't seem to be an onFailure event for the text! plugin. Make sure we only pass valid language codes + // so the plugin does not attempt to load non-existent files. + if (fixedLanguageCode in lowerLocalesMapping) { + return lowerLocalesMapping[fixedLanguageCode]; + } + + return 'en'; + }; +}); diff --git a/analytics_dashboard/static/js/utils/globalization.js b/analytics_dashboard/static/js/utils/globalization.js index 16360e525..fbcae2596 100644 --- a/analytics_dashboard/static/js/utils/globalization.js +++ b/analytics_dashboard/static/js/utils/globalization.js @@ -2,56 +2,23 @@ if (window.language === undefined) { // should only occur in test environments window.language = 'en'; } -function fixLanguageCode(languageCode, lowerLocalesMapping) { - 'use strict'; - var fixedLanguageCode; - - if (!languageCode || typeof languageCode !== 'string') { - return 'en'; - } - - fixedLanguageCode = languageCode.toLowerCase(); - - // CLDR uses zh for Simplified Chinese, while Django may use different strings. - if (fixedLanguageCode === 'zh-cn' || fixedLanguageCode === 'zh-sg' || - fixedLanguageCode.indexOf('zh-hans') === 0) { - fixedLanguageCode = 'zh'; - } - // CLDR uses zh-hant for Traditional Chinese, while Django may use different strings. - if (fixedLanguageCode === 'zh-tw' || fixedLanguageCode === 'zh-hk' || - fixedLanguageCode === 'zh-mo' || fixedLanguageCode.indexOf('zh-hant') === 0) { - fixedLanguageCode = 'zh-hant'; - } - - // There doesn't seem to be an onFailure event for the text! plugin. Make sure we only pass valid language codes - // so the plugin does not attempt to load non-existent files. - if (fixedLanguageCode in lowerLocalesMapping) { - return lowerLocalesMapping[fixedLanguageCode]; - } - - return 'en'; -} - +/** + * Returns the Globalize object for localizing dates and numbers. window.language + * is expected to be standardized to those used in CLDR. See js/load/init-page.js + * for setting window.language. + */ define([ 'globalize', 'json!cldr-data/supplemental/likelySubtags.json', 'json!cldr-data/supplemental/numberingSystems.json', - 'json!cldr-data/availableLocales.json', // eslint-disable-line import/no-unresolved - 'json!cldr-data/main/' + window.language + '/numbers.json', + 'json!cldr-data/main/' + window.language + '/numbers.json', // language fix already applied (e.g. en-gb is en-GB) 'globalize/number' -], function(Globalize, likelySubtags, numberingSystems, availableLocales, numbers) { +], function(Globalize, likelySubtags, numberingSystems, numbers) { 'use strict'; - var lowerLocalesMapping = {}; - availableLocales.availableLocales.forEach(function(locale) { - lowerLocalesMapping[locale.toLowerCase()] = locale; - }); - - window.language = fixLanguageCode(window.language, lowerLocalesMapping); - + Globalize.load(numbers); Globalize.load(likelySubtags); Globalize.load(numberingSystems); - Globalize.load(numbers); return Globalize(window.language); }); diff --git a/karma.conf.js b/karma.conf.js index 18cf60550..8fda441db 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -19,7 +19,12 @@ module.exports = function(config) { {pattern: 'analytics_dashboard/static/vendor/**/*.js', included: false}, {pattern: 'analytics_dashboard/static/bower_components/**/*.js', included: false}, {pattern: 'analytics_dashboard/static/bower_components/**/*.underscore', included: false}, - {pattern: 'analytics_dashboard/static/bower_components/**/*.json', included: false}, + // limiting the cldr json files to load (we don't use the other ones and loading too many + // throws errors on a mac) + {pattern: 'analytics_dashboard/static/bower_components/cldr-data/supplemental/*.json', included: false}, + {pattern: 'analytics_dashboard/static/bower_components/cldr-data/availableLocales.json', included: false}, + {pattern: 'analytics_dashboard/static/bower_components/cldr-data/**/numbers.json', included: false}, + {pattern: 'analytics_dashboard/static/js/load/*.js', included: false}, {pattern: 'analytics_dashboard/static/js/models/**/*.js', included: false}, {pattern: 'analytics_dashboard/static/js/views/**/*.js', included: false}, {pattern: 'analytics_dashboard/static/js/utils/**/*.js', included: false},