├── .eslintrc.json
├── .github
└── workflows
│ └── codeql-analysis.yml
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── dist
├── tmhDynamicLocale.js
├── tmhDynamicLocale.min.js
└── tmhDynamicLocale.min.js.map
├── karma.conf.js
├── karma.min.conf.js
├── package-lock.json
├── package.json
├── src
├── tmhDynamicLocale.d.ts
└── tmhDynamicLocale.js
├── test
└── tmhDynamicLocaleSpec.js
├── tmhDynamicLocale.min.js
└── tmhDynamicLocale.min.js.map
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true
5 | },
6 | "extends": "eslint:recommended",
7 | "parserOptions": {
8 | "ecmaVersion": 5
9 | },
10 | "rules": {
11 | "indent": [
12 | "error",
13 | 2
14 | ],
15 | "linebreak-style": [
16 | "error",
17 | "unix"
18 | ],
19 | "quotes": [
20 | "error",
21 | "single"
22 | ],
23 | "semi": [
24 | "error",
25 | "always"
26 | ]
27 | },
28 | "globals": {
29 | "angular": false,
30 | "define": false
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '42 21 * * 1'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | components
3 | .rcs
4 | *.swp
5 | .idea
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 8.9.1
4 |
5 | before_install:
6 | - npm install -g grunt-cli bower
7 |
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | CONTRIBUTING
2 | ============
3 |
4 | * Open a [Pull Request (PR)](https://github.com/lgalfaso/angular-dynamic-locale/pull/new/master)
5 | * Make sure your PR is on a **new branch** you created off of the latest version of master
6 | * Do **not** open a PR from your master branch
7 | * Open a PR to start a discussion even if the code isn't finished (easier to collect feedback this way)
8 | * Make sure all previous tests pass and add new tests for added behaviors
9 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | var BANNER =
5 | '/**\n' +
6 | ' * Angular Dynamic Locale - <%= pkg.version %>\n' +
7 | ' * https://github.com/lgalfaso/angular-dynamic-locale\n' +
8 | ' * License: MIT\n' +
9 | ' */\n';
10 |
11 | module.exports = function(grunt) {
12 | //grunt plugins
13 | grunt.loadNpmTasks('grunt-contrib-clean');
14 | grunt.loadNpmTasks('grunt-contrib-copy');
15 | grunt.loadNpmTasks('grunt-contrib-jshint');
16 | grunt.loadNpmTasks('grunt-eslint');
17 | grunt.loadNpmTasks('grunt-karma');
18 | grunt.loadNpmTasks('grunt-bump');
19 | grunt.loadNpmTasks('grunt-npm');
20 | grunt.loadNpmTasks('grunt-contrib-uglify');
21 | grunt.loadNpmTasks('grunt-contrib-concat');
22 |
23 | grunt.initConfig({
24 | pkg: grunt.file.readJSON('package.json'),
25 | jshint: {
26 | all: ['Gruntfile.js', 'src/*.js', 'test/*.js']
27 | },
28 | eslint: {
29 | target: ['src/**/*.js', 'test/**/*.js'],
30 | },
31 | karma: {
32 | unit: { configFile: 'karma.conf.js' },
33 | 'unit.min': {
34 | configFile: 'karma.min.conf.js'
35 | },
36 | autotest: {
37 | configFile: 'karma.conf.js',
38 | autoWatch: true,
39 | singleRun: false
40 | },
41 | travis: {
42 | configFile: 'karma.conf.js',
43 | reporters: 'dots',
44 | browsers: ['PhantomJS']
45 | }
46 | },
47 | concat: {
48 | options: {
49 | banner: BANNER,
50 | },
51 | dist: {
52 | src: ['src/tmhDynamicLocale.js'],
53 | dest: 'dist/tmhDynamicLocale.js',
54 | },
55 | },
56 | uglify: {
57 | all: {
58 | files: {
59 | 'tmhDynamicLocale.min.js': ['src/*.js'],
60 | 'dist/tmhDynamicLocale.min.js': ['src/*.js']
61 | },
62 | options: {
63 | banner: BANNER,
64 | sourceMap: true
65 | }
66 | }
67 | },
68 | bump: {
69 | options: {
70 | files: ['package.json', 'bower.json'],
71 | commitFiles: ['package.json', 'bower.json'],
72 | tagName: '%VERSION%'
73 | }
74 | },
75 | 'npm-publish': {
76 | options: {
77 | requires: ['jshint', 'karma:unit'],
78 | abortIfDirty: true
79 | }
80 | }
81 | });
82 | grunt.registerTask('release', ['jshint', 'eslint', 'karma:unit', 'concat', 'uglify:all', 'karma:unit.min', 'publish']);
83 | };
84 | }());
85 |
86 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2013 Lucas Galfasó
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular Dynamic Locale
2 |
3 |
4 | ### Requirements
5 |
6 | * **AngularJS v1.0.7+** is currently required.
7 |
8 | ### Changing the locale
9 |
10 | This module defines two services, these are `tmhDynamicLocale` and
11 | `tmhDynamicLocaleCache`.
12 |
13 | The service `tmhDynamicLocale` provides has one method `set(newLocale)` to
14 | change the locale.
15 |
16 | ```javascript
17 | tmhDynamicLocale.set('it');
18 | ```
19 |
20 | Keep in mind that the locale will be changed asynchronously
21 |
22 |
23 | After the locale is changed, the event `'$localeChangeSuccess'` will be
24 | triggered.
25 |
26 | Calling `tmhDynamicLocale.set` will return a promise that will be resolved
27 | when the locale is loaded and will resolve to the new locale.
28 |
29 | The service `tmhDynamicLocaleCache` is a `$cache` of all the loaded locales,
30 | where the key is the locale id and the value is the locale object.
31 |
32 |
33 | This module expects for the angular locales to be present at
34 | `angular/i18n/angular-locale_{{locale}}.js`.
35 | If the locales are at another URL, this can be changed at
36 | `tmhDynamicLocaleProvider` using `localeLocationPattern(string)`.
37 |
38 |
39 | It is possible to specify a storage location for the locale using
40 | `tmhDynamicLocaleProvider.useStorage(storageName)`, the name of the
41 | storage must follow the same signature as `$cookieStore`. The default
42 | storage location is to use a `$cache`, this default storage is not persistent.
43 |
44 | It is possible to ask the storage to be `$cookieStore` using the shortcut
45 | `tmhDynamicLocaleProvider.useCookieStorage()`, internally this is
46 | exactly as performing `tmhDynamicLocaleProvider.useStorage('$cookieStore')`
47 |
48 | Also it is possible to set a default language using `tmhDynamicLocaleProvider.defaultLocale(locale)`.
49 |
50 | ## Installation
51 |
52 | Using npm:
53 |
54 | `npm install angular-dynamic-locale`
55 |
56 | Using bower:
57 |
58 | `bower install angular-dynamic-locale`
59 |
60 | ## Usage
61 |
62 | Add the js file to your html:
63 |
64 | ```html
65 |
66 | ```
67 |
68 | Add the module to your dependencies:
69 |
70 | ```javascript
71 | angular.module('myApp', ['tmh.dynamicLocale', ...])
72 | ```
73 |
74 | Inject `tmhDynamicLocale` to your controller and set the desired locale:
75 |
76 | ```javascript
77 | angular.module('myApp').controller('myController', [..., 'tmhDynamicLocale',
78 | function(..., tmhDynamicLocale) {
79 | tmhDynamicLocale.set('en');
80 | }
81 | ])
82 | ```
83 |
84 | Also, here's an example of how you may optionally override the default path (`angular/i18n/angular-locale_{{locale}}.js`) to the stored Angular i18n locale files in your project:
85 |
86 | ```javascript
87 | angular.module('myApp', ['tmh.dynamicLocale'])
88 | .config(function(tmhDynamicLocaleProvider) {
89 | tmhDynamicLocaleProvider.localeLocationPattern('/base/node_modules/angular-i18n/angular-locale_{{locale}}.js');
90 | });
91 | ```
92 |
93 | Lastly, if you need to set a default locale, you can do it like this:
94 |
95 | ```javascript
96 | angular.module('myApp', ['tmh.dynamicLocale'])
97 | .config(function(tmhDynamicLocaleProvider) {
98 | tmhDynamicLocaleProvider.defaultLocale('en');
99 | });
100 | ```
101 |
102 | ## Development
103 |
104 | ### Requirements
105 |
106 | 0. Install [Node.js](http://nodejs.org/) and NPM (should come with)
107 |
108 | 1. Install global dependencies `grunt-cli` and `bower`:
109 |
110 | ```bash
111 | $ npm install -g grunt-cli bower
112 | ```
113 |
114 | 2. Install local dependencies:
115 |
116 | ```bash
117 | $ npm install
118 | ```
119 |
120 | ### Running the tests
121 |
122 | ```bash
123 | $ grunt karma:unit
124 | ```
125 | to run the test once
126 |
127 | or
128 |
129 | ```bash
130 | $ grunt karma:autotest
131 | ```
132 | to run the tests continuously
133 |
134 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Lucas Galfaso",
3 | "name": "angular-dynamic-locale",
4 | "main": "dist/tmhDynamicLocale.js",
5 | "description": "Angular Dynamic Locale",
6 | "ignore": [
7 | "node_modules",
8 | "bower_components",
9 | "test"
10 | ],
11 | "version": "0.1.38"
12 | }
13 |
--------------------------------------------------------------------------------
/dist/tmhDynamicLocale.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Angular Dynamic Locale - 0.1.38
3 | * https://github.com/lgalfaso/angular-dynamic-locale
4 | * License: MIT
5 | */
6 | (function (root, factory) {
7 | if (typeof define === 'function' && define.amd) {
8 | // AMD. Register as an anonymous module unless amdModuleId is set
9 | define([], function () {
10 | return (factory());
11 | });
12 | } else if (typeof exports === 'object') {
13 | // Node. Does not work with strict CommonJS, but
14 | // only CommonJS-like environments that support module.exports,
15 | // like Node.
16 | module.exports = factory();
17 | } else {
18 | factory();
19 | }
20 | }(this, function () {
21 | 'use strict';
22 | angular.module('tmh.dynamicLocale', []).config(['$provide', function($provide) {
23 | function makeStateful($delegate) {
24 | $delegate.$stateful = true;
25 | return $delegate;
26 | }
27 |
28 | $provide.decorator('dateFilter', ['$delegate', makeStateful]);
29 | $provide.decorator('numberFilter', ['$delegate', makeStateful]);
30 | $provide.decorator('currencyFilter', ['$delegate', makeStateful]);
31 |
32 | }])
33 | .constant('tmhDynamicLocale.STORAGE_KEY', 'tmhDynamicLocale.locale')
34 | .provider('tmhDynamicLocale', ['tmhDynamicLocale.STORAGE_KEY', function(STORAGE_KEY) {
35 |
36 | var defaultLocale,
37 | localeLocationPattern = 'angular/i18n/angular-locale_{{locale}}.js',
38 | nodeToAppend,
39 | storageFactory = 'tmhDynamicLocaleStorageCache',
40 | storage,
41 | storageKey = STORAGE_KEY,
42 | storageGet = 'get',
43 | storagePut = 'put',
44 | promiseCache = {},
45 | activeLocale,
46 | extraProperties = {};
47 |
48 | /**
49 | * Loads a script asynchronously
50 | *
51 | * @param {string} url The url for the script
52 | @ @param {function} callback A function to be called once the script is loaded
53 | */
54 | function loadScript(url, callback, errorCallback, $timeout) {
55 | var script = document.createElement('script'),
56 | element = nodeToAppend ? nodeToAppend : document.getElementsByTagName('body')[0],
57 | removed = false;
58 |
59 | script.type = 'text/javascript';
60 | if (script.readyState) { // IE
61 | script.onreadystatechange = function () {
62 | if (script.readyState === 'complete' ||
63 | script.readyState === 'loaded') {
64 | script.onreadystatechange = null;
65 | $timeout(
66 | function () {
67 | if (removed) return;
68 | removed = true;
69 | if (script.parentNode === element) {
70 | element.removeChild(script);
71 | }
72 | callback();
73 | }, 30, false);
74 | }
75 | };
76 | } else { // Others
77 | script.onload = function () {
78 | if (removed) return;
79 | removed = true;
80 | if (script.parentNode === element) {
81 | element.removeChild(script);
82 | }
83 | callback();
84 | };
85 | script.onerror = function () {
86 | if (removed) return;
87 | removed = true;
88 | if (script.parentNode === element) {
89 | element.removeChild(script);
90 | }
91 | errorCallback();
92 | };
93 | }
94 | script.src = url;
95 | script.async = true;
96 | element.appendChild(script);
97 | }
98 |
99 | /**
100 | * Loads a locale and replaces the properties from the current locale with the new locale information
101 | *
102 | * @param {string} localeUrl The path to the new locale
103 | * @param {Object} $locale The locale at the curent scope
104 | * @param {string} localeId The locale id to load
105 | * @param {Object} $rootScope The application $rootScope
106 | * @param {Object} $q The application $q
107 | * @param {Object} localeCache The current locale cache
108 | * @param {Object} $timeout The application $timeout
109 | */
110 | function loadLocale(localeUrl, $locale, localeId, $rootScope, $q, localeCache, $timeout) {
111 |
112 | function overrideValues(oldObject, newObject) {
113 | if (activeLocale !== localeId) {
114 | return;
115 | }
116 | angular.forEach(oldObject, function(value, key) {
117 | if (!newObject[key]) {
118 | delete oldObject[key];
119 | } else if (angular.isArray(newObject[key])) {
120 | oldObject[key].length = newObject[key].length;
121 | }
122 | });
123 | angular.forEach(newObject, function(value, key) {
124 | if (angular.isArray(newObject[key]) || angular.isObject(newObject[key])) {
125 | if (!oldObject[key]) {
126 | oldObject[key] = angular.isArray(newObject[key]) ? [] : {};
127 | }
128 | overrideValues(oldObject[key], newObject[key]);
129 | } else {
130 | oldObject[key] = newObject[key];
131 | }
132 | });
133 | }
134 |
135 |
136 | if (promiseCache[localeId]) {
137 | activeLocale = localeId;
138 | return promiseCache[localeId];
139 | }
140 |
141 | var cachedLocale,
142 | deferred = $q.defer();
143 | if (localeId === activeLocale) {
144 | deferred.resolve($locale);
145 | } else if ((cachedLocale = localeCache.get(localeId))) {
146 | activeLocale = localeId;
147 | $rootScope.$evalAsync(function() {
148 | overrideValues($locale, cachedLocale);
149 | storage[storagePut](storageKey, localeId);
150 | $rootScope.$broadcast('$localeChangeSuccess', localeId, $locale);
151 | deferred.resolve($locale);
152 | });
153 | } else {
154 | activeLocale = localeId;
155 | promiseCache[localeId] = deferred.promise;
156 | loadScript(localeUrl, function() {
157 | // Create a new injector with the new locale
158 | var localInjector = angular.injector(['ngLocale']),
159 | externalLocale = localInjector.get('$locale');
160 |
161 | overrideValues($locale, externalLocale);
162 | localeCache.put(localeId, externalLocale);
163 | delete promiseCache[localeId];
164 |
165 | $rootScope.$applyAsync(function() {
166 | storage[storagePut](storageKey, localeId);
167 | $rootScope.$broadcast('$localeChangeSuccess', localeId, $locale);
168 | deferred.resolve($locale);
169 | });
170 | }, function() {
171 | delete promiseCache[localeId];
172 |
173 | $rootScope.$applyAsync(function() {
174 | if (activeLocale === localeId) {
175 | activeLocale = $locale.id;
176 | }
177 | $rootScope.$broadcast('$localeChangeError', localeId);
178 | deferred.reject(localeId);
179 | });
180 | }, $timeout);
181 | }
182 | return deferred.promise;
183 | }
184 |
185 | this.localeLocationPattern = function(value) {
186 | if (value) {
187 | localeLocationPattern = value;
188 | return this;
189 | } else {
190 | return localeLocationPattern;
191 | }
192 | };
193 |
194 | this.appendScriptTo = function(nodeElement) {
195 | nodeToAppend = nodeElement;
196 | };
197 |
198 | this.useStorage = function(storageName) {
199 | storageFactory = storageName;
200 | storageGet = 'get';
201 | storagePut = 'put';
202 | };
203 |
204 | this.useCookieStorage = function() {
205 | if (angular.version.minor < 7) {
206 | this.useStorage('$cookieStore');
207 | } else {
208 | this.useStorage('$cookies');
209 | storageGet = 'getObject';
210 | storagePut = 'putObject';
211 | }
212 | };
213 |
214 | this.defaultLocale = function(value) {
215 | defaultLocale = value;
216 | };
217 |
218 | this.storageKey = function(value) {
219 | if (value) {
220 | storageKey = value;
221 | return this;
222 | } else {
223 | return storageKey;
224 | }
225 | };
226 |
227 | this.addLocalePatternValue = function(key, value) {
228 | extraProperties[key] = value;
229 | };
230 |
231 | this.$get = ['$rootScope', '$injector', '$interpolate', '$locale', '$q', 'tmhDynamicLocaleCache', '$timeout', function($rootScope, $injector, interpolate, locale, $q, tmhDynamicLocaleCache, $timeout) {
232 | var localeLocation = interpolate(localeLocationPattern);
233 |
234 | storage = $injector.get(storageFactory);
235 | $rootScope.$evalAsync(function() {
236 | var initialLocale;
237 | if ((initialLocale = (storage[storageGet](storageKey) || defaultLocale))) {
238 | loadLocaleFn(initialLocale);
239 | }
240 | });
241 | return {
242 | /**
243 | * @ngdoc method
244 | * @description
245 | * @param {string} value Sets the locale to the new locale. Changing the locale will trigger
246 | * a background task that will retrieve the new locale and configure the current $locale
247 | * instance with the information from the new locale
248 | */
249 | set: loadLocaleFn,
250 | /**
251 | * @ngdoc method
252 | * @description Returns the configured locale
253 | */
254 | get: function() {
255 | return activeLocale;
256 | }
257 | };
258 |
259 | function loadLocaleFn(localeId) {
260 | var baseProperties = {locale: localeId, angularVersion: angular.version.full};
261 | return loadLocale(localeLocation(angular.extend({}, extraProperties, baseProperties)), locale, localeId, $rootScope, $q, tmhDynamicLocaleCache, $timeout);
262 | }
263 | }];
264 | }]).provider('tmhDynamicLocaleCache', function() {
265 | this.$get = ['$cacheFactory', function($cacheFactory) {
266 | return $cacheFactory('tmh.dynamicLocales');
267 | }];
268 | }).provider('tmhDynamicLocaleStorageCache', function() {
269 | this.$get = ['$cacheFactory', function($cacheFactory) {
270 | return $cacheFactory('tmh.dynamicLocales.store');
271 | }];
272 | }).run(['tmhDynamicLocale', angular.noop]);
273 |
274 | return 'tmh.dynamicLocale';
275 |
276 | }));
277 |
--------------------------------------------------------------------------------
/dist/tmhDynamicLocale.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Angular Dynamic Locale - 0.1.38
3 | * https://github.com/lgalfaso/angular-dynamic-locale
4 | * License: MIT
5 | */
6 | !function(e){"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?module.exports=e():e()}(function(){"use strict";return angular.module("tmh.dynamicLocale",[]).config(["$provide",function(e){function t(e){return e.$stateful=!0,e}e.decorator("dateFilter",["$delegate",t]),e.decorator("numberFilter",["$delegate",t]),e.decorator("currencyFilter",["$delegate",t])}]).constant("tmhDynamicLocale.STORAGE_KEY","tmhDynamicLocale.locale").provider("tmhDynamicLocale",["tmhDynamicLocale.STORAGE_KEY",function(e){var u,y,p,$,s="angular/i18n/angular-locale_{{locale}}.js",d="tmhDynamicLocaleStorageCache",v=e,f="get",L="put",S={},g={};function h(e,t,o,n,a,r,c){function i(n,a){$===o&&(angular.forEach(n,function(e,t){a[t]?angular.isArray(a[t])&&(n[t].length=a[t].length):delete n[t]}),angular.forEach(a,function(e,t){angular.isArray(a[t])||angular.isObject(a[t])?(n[t]||(n[t]=angular.isArray(a[t])?[]:{}),i(n[t],a[t])):n[t]=a[t]}))}if(S[o])return S[$=o];var l,u,s,d,f,g,h,m=a.defer();return o===$?m.resolve(t):(l=r.get(o))?($=o,n.$evalAsync(function(){i(t,l),p[L](v,o),n.$broadcast("$localeChangeSuccess",o,t),m.resolve(t)})):(S[$=o]=m.promise,a=e,u=function(){var e=angular.injector(["ngLocale"]).get("$locale");i(t,e),r.put(o,e),delete S[o],n.$applyAsync(function(){p[L](v,o),n.$broadcast("$localeChangeSuccess",o,t),m.resolve(t)})},s=function(){delete S[o],n.$applyAsync(function(){$===o&&($=t.id),n.$broadcast("$localeChangeError",o),m.reject(o)})},d=c,f=document.createElement("script"),g=y||document.getElementsByTagName("body")[0],h=!1,f.type="text/javascript",f.readyState?f.onreadystatechange=function(){"complete"!==f.readyState&&"loaded"!==f.readyState||(f.onreadystatechange=null,d(function(){h||(h=!0,f.parentNode===g&&g.removeChild(f),u())},30,!1))}:(f.onload=function(){h||(h=!0,f.parentNode===g&&g.removeChild(f),u())},f.onerror=function(){h||(h=!0,f.parentNode===g&&g.removeChild(f),s())}),f.src=a,f.async=!0,g.appendChild(f)),m.promise}this.localeLocationPattern=function(e){return e?(s=e,this):s},this.appendScriptTo=function(e){y=e},this.useStorage=function(e){d=e,f="get",L="put"},this.useCookieStorage=function(){angular.version.minor<7?this.useStorage("$cookieStore"):(this.useStorage("$cookies"),f="getObject",L="putObject")},this.defaultLocale=function(e){u=e},this.storageKey=function(e){return e?(v=e,this):v},this.addLocalePatternValue=function(e,t){g[e]=t},this.$get=["$rootScope","$injector","$interpolate","$locale","$q","tmhDynamicLocaleCache","$timeout",function(n,e,t,a,o,r,c){var i=t(s);return p=e.get(d),n.$evalAsync(function(){var e;(e=p[f](v)||u)&&l(e)}),{set:l,get:function(){return $}};function l(e){var t={locale:e,angularVersion:angular.version.full};return h(i(angular.extend({},g,t)),a,e,n,o,r,c)}}]}]).provider("tmhDynamicLocaleCache",function(){this.$get=["$cacheFactory",function(e){return e("tmh.dynamicLocales")}]}).provider("tmhDynamicLocaleStorageCache",function(){this.$get=["$cacheFactory",function(e){return e("tmh.dynamicLocales.store")}]}).run(["tmhDynamicLocale",angular.noop]),"tmh.dynamicLocale"});
7 | //# sourceMappingURL=tmhDynamicLocale.min.js.map
--------------------------------------------------------------------------------
/dist/tmhDynamicLocale.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"tmhDynamicLocale.min.js","sources":["../src/tmhDynamicLocale.js"],"names":["factory","define","amd","exports","module","angular","config","$provide","makeStateful","$delegate","$stateful","decorator","constant","provider","STORAGE_KEY","defaultLocale","nodeToAppend","storage","activeLocale","localeLocationPattern","storageFactory","storageKey","storageGet","storagePut","promiseCache","extraProperties","loadLocale","localeUrl","$locale","localeId","$rootScope","$q","localeCache","$timeout","overrideValues","oldObject","newObject","forEach","value","key","isArray","length","isObject","cachedLocale","callback","errorCallback","script","element","removed","deferred","defer","resolve","get","$evalAsync","$broadcast","promise","url","externalLocale","injector","put","$applyAsync","id","reject","document","createElement","getElementsByTagName","type","readyState","onreadystatechange","parentNode","removeChild","onload","onerror","src","async","appendChild","this","appendScriptTo","nodeElement","useStorage","storageName","useCookieStorage","version","minor","addLocalePatternValue","$get","$injector","interpolate","locale","tmhDynamicLocaleCache","localeLocation","initialLocale","loadLocaleFn","set","baseProperties","angularVersion","full","extend","$cacheFactory","run","noop"],"mappings":"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"}
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | autoWatch: false,
4 | singleRun: true,
5 | logLevel: config.LOG_INFO,
6 | logColors: true,
7 | browsers: ['Chrome'],
8 | browserDisconnectTimeout: 10000,
9 | browserNoActivityTimeout: 10000,
10 | files: [
11 | 'node_modules/angular/angular.js',
12 | 'node_modules/angular-cookies/angular-cookies.js',
13 | 'node_modules/angular-mocks/angular-mocks.js',
14 | {pattern: 'node_modules/angular-i18n/*.js', included: false, served: true},
15 | 'src/*.js',
16 | 'test/*Spec.js'
17 | ],
18 | junitReporter: {
19 | outputFile: 'test_out/unit.xml',
20 | suite: 'unit'
21 | },
22 | frameworks: ['jasmine']
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/karma.min.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | autoWatch: false,
4 | singleRun: true,
5 | logLevel: config.LOG_INFO,
6 | logColors: true,
7 | browserDisconnectTimeout: 10000,
8 | browserNoActivityTimeout: 10000,
9 | browsers: ['Chrome'],
10 | files: [
11 | 'node_modules/angular/angular.js',
12 | 'node_modules/angular-cookies/angular-cookies.js',
13 | 'node_modules/angular-mocks/angular-mocks.js',
14 | {pattern: 'node_modules/angular-i18n/*.js', included: false, served: true},
15 | '*.min.js',
16 | 'test/*Spec.js'
17 | ],
18 | junitReporter: {
19 | outputFile: 'test_out/unit.xml',
20 | suite: 'unit'
21 | },
22 | frameworks: ['jasmine']
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-dynamic-locale",
3 | "version": "0.1.38",
4 | "description": "A minimal module that adds the ability to dynamically change the locale",
5 | "license": "MIT",
6 | "devDependencies": {
7 | "angular": "1.8.2",
8 | "angular-cookies": "1.8.2",
9 | "angular-i18n": "1.8.2",
10 | "angular-mocks": "1.8.2",
11 | "eslint": "^8.7.0",
12 | "eslint-config-google": "^0.14.0",
13 | "grunt": "^1.4.1",
14 | "grunt-bump": "^0.8.0",
15 | "grunt-contrib-clean": "^2.0.0",
16 | "grunt-contrib-concat": "^2.1.0",
17 | "grunt-contrib-copy": "^1.0.0",
18 | "grunt-contrib-jshint": "^3.1.1",
19 | "grunt-contrib-uglify": "^5.1.0",
20 | "grunt-contrib-watch": "^1.1.0",
21 | "grunt-eslint": "^24.0.0",
22 | "grunt-karma": "^4.0.2",
23 | "grunt-npm": "0.0.2",
24 | "jasmine-core": "^4.0.1",
25 | "karma": "^6.3.11",
26 | "karma-chrome-launcher": "^3.1.1",
27 | "karma-firefox-launcher": "^2.1.2",
28 | "karma-jasmine": "4.0.2",
29 | "karma-phantomjs-launcher": "1.0.4"
30 | },
31 | "main": "./src/tmhDynamicLocale.js",
32 | "scripts": {
33 | "test": "grunt karma:travis"
34 | },
35 | "repository": {
36 | "type": "git",
37 | "url": "git://github.com/lgalfaso/angular-dynamic-locale.git"
38 | },
39 | "types": "./src/tmhDynamicLocale.d.ts",
40 | "dependencies": {
41 | "@types/angular": "^1.8.4"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/tmhDynamicLocale.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for tmhDynamicLocale
2 | // Project: http://angularjs.org
3 | // Definitions by: Gerardo Lima
4 | // Definitions: https://github.com/lgalfaso/angular-dynamic-locale
5 | // TypeScript Version: 2.4.1
6 |
7 | import * as ng from 'angular';
8 |
9 | export as namespace tmh;
10 |
11 | declare namespace tmh
12 | {
13 | interface IDynamicLocale
14 | {
15 | set(locale: string): ng.IPromise;
16 | get(): string;
17 | }
18 | interface IDynamicLocaleProvider
19 | {
20 | localeLocationPattern(locationPattern: string): void;
21 | defaultLocale(locale: string): void;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/tmhDynamicLocale.js:
--------------------------------------------------------------------------------
1 | (function (root, factory) {
2 | if (typeof define === 'function' && define.amd) {
3 | // AMD. Register as an anonymous module unless amdModuleId is set
4 | define([], function () {
5 | return (factory());
6 | });
7 | } else if (typeof exports === 'object') {
8 | // Node. Does not work with strict CommonJS, but
9 | // only CommonJS-like environments that support module.exports,
10 | // like Node.
11 | module.exports = factory();
12 | } else {
13 | factory();
14 | }
15 | }(this, function () {
16 | 'use strict';
17 | angular.module('tmh.dynamicLocale', []).config(['$provide', function($provide) {
18 | function makeStateful($delegate) {
19 | $delegate.$stateful = true;
20 | return $delegate;
21 | }
22 |
23 | $provide.decorator('dateFilter', ['$delegate', makeStateful]);
24 | $provide.decorator('numberFilter', ['$delegate', makeStateful]);
25 | $provide.decorator('currencyFilter', ['$delegate', makeStateful]);
26 |
27 | }])
28 | .constant('tmhDynamicLocale.STORAGE_KEY', 'tmhDynamicLocale.locale')
29 | .provider('tmhDynamicLocale', ['tmhDynamicLocale.STORAGE_KEY', function(STORAGE_KEY) {
30 |
31 | var defaultLocale,
32 | localeLocationPattern = 'angular/i18n/angular-locale_{{locale}}.js',
33 | nodeToAppend,
34 | storageFactory = 'tmhDynamicLocaleStorageCache',
35 | storage,
36 | storageKey = STORAGE_KEY,
37 | storageGet = 'get',
38 | storagePut = 'put',
39 | promiseCache = {},
40 | activeLocale,
41 | extraProperties = {};
42 |
43 | /**
44 | * Loads a script asynchronously
45 | *
46 | * @param {string} url The url for the script
47 | @ @param {function} callback A function to be called once the script is loaded
48 | */
49 | function loadScript(url, callback, errorCallback, $timeout) {
50 | var script = document.createElement('script'),
51 | element = nodeToAppend ? nodeToAppend : document.getElementsByTagName('body')[0],
52 | removed = false;
53 |
54 | script.type = 'text/javascript';
55 | if (script.readyState) { // IE
56 | script.onreadystatechange = function () {
57 | if (script.readyState === 'complete' ||
58 | script.readyState === 'loaded') {
59 | script.onreadystatechange = null;
60 | $timeout(
61 | function () {
62 | if (removed) return;
63 | removed = true;
64 | if (script.parentNode === element) {
65 | element.removeChild(script);
66 | }
67 | callback();
68 | }, 30, false);
69 | }
70 | };
71 | } else { // Others
72 | script.onload = function () {
73 | if (removed) return;
74 | removed = true;
75 | if (script.parentNode === element) {
76 | element.removeChild(script);
77 | }
78 | callback();
79 | };
80 | script.onerror = function () {
81 | if (removed) return;
82 | removed = true;
83 | if (script.parentNode === element) {
84 | element.removeChild(script);
85 | }
86 | errorCallback();
87 | };
88 | }
89 | script.src = url;
90 | script.async = true;
91 | element.appendChild(script);
92 | }
93 |
94 | /**
95 | * Loads a locale and replaces the properties from the current locale with the new locale information
96 | *
97 | * @param {string} localeUrl The path to the new locale
98 | * @param {Object} $locale The locale at the curent scope
99 | * @param {string} localeId The locale id to load
100 | * @param {Object} $rootScope The application $rootScope
101 | * @param {Object} $q The application $q
102 | * @param {Object} localeCache The current locale cache
103 | * @param {Object} $timeout The application $timeout
104 | */
105 | function loadLocale(localeUrl, $locale, localeId, $rootScope, $q, localeCache, $timeout) {
106 |
107 | function overrideValues(oldObject, newObject) {
108 | if (activeLocale !== localeId) {
109 | return;
110 | }
111 | angular.forEach(oldObject, function(value, key) {
112 | if (!newObject[key]) {
113 | delete oldObject[key];
114 | } else if (angular.isArray(newObject[key])) {
115 | oldObject[key].length = newObject[key].length;
116 | }
117 | });
118 | angular.forEach(newObject, function(value, key) {
119 | if (angular.isArray(newObject[key]) || angular.isObject(newObject[key])) {
120 | if (!oldObject[key]) {
121 | oldObject[key] = angular.isArray(newObject[key]) ? [] : {};
122 | }
123 | overrideValues(oldObject[key], newObject[key]);
124 | } else {
125 | oldObject[key] = newObject[key];
126 | }
127 | });
128 | }
129 |
130 |
131 | if (promiseCache[localeId]) {
132 | activeLocale = localeId;
133 | return promiseCache[localeId];
134 | }
135 |
136 | var cachedLocale,
137 | deferred = $q.defer();
138 | if (localeId === activeLocale) {
139 | deferred.resolve($locale);
140 | } else if ((cachedLocale = localeCache.get(localeId))) {
141 | activeLocale = localeId;
142 | $rootScope.$evalAsync(function() {
143 | overrideValues($locale, cachedLocale);
144 | storage[storagePut](storageKey, localeId);
145 | $rootScope.$broadcast('$localeChangeSuccess', localeId, $locale);
146 | deferred.resolve($locale);
147 | });
148 | } else {
149 | activeLocale = localeId;
150 | promiseCache[localeId] = deferred.promise;
151 | loadScript(localeUrl, function() {
152 | // Create a new injector with the new locale
153 | var localInjector = angular.injector(['ngLocale']),
154 | externalLocale = localInjector.get('$locale');
155 |
156 | overrideValues($locale, externalLocale);
157 | localeCache.put(localeId, externalLocale);
158 | delete promiseCache[localeId];
159 |
160 | $rootScope.$applyAsync(function() {
161 | storage[storagePut](storageKey, localeId);
162 | $rootScope.$broadcast('$localeChangeSuccess', localeId, $locale);
163 | deferred.resolve($locale);
164 | });
165 | }, function() {
166 | delete promiseCache[localeId];
167 |
168 | $rootScope.$applyAsync(function() {
169 | if (activeLocale === localeId) {
170 | activeLocale = $locale.id;
171 | }
172 | $rootScope.$broadcast('$localeChangeError', localeId);
173 | deferred.reject(localeId);
174 | });
175 | }, $timeout);
176 | }
177 | return deferred.promise;
178 | }
179 |
180 | this.localeLocationPattern = function(value) {
181 | if (value) {
182 | localeLocationPattern = value;
183 | return this;
184 | } else {
185 | return localeLocationPattern;
186 | }
187 | };
188 |
189 | this.appendScriptTo = function(nodeElement) {
190 | nodeToAppend = nodeElement;
191 | };
192 |
193 | this.useStorage = function(storageName) {
194 | storageFactory = storageName;
195 | storageGet = 'get';
196 | storagePut = 'put';
197 | };
198 |
199 | this.useCookieStorage = function() {
200 | if (angular.version.minor < 7) {
201 | this.useStorage('$cookieStore');
202 | } else {
203 | this.useStorage('$cookies');
204 | storageGet = 'getObject';
205 | storagePut = 'putObject';
206 | }
207 | };
208 |
209 | this.defaultLocale = function(value) {
210 | defaultLocale = value;
211 | };
212 |
213 | this.storageKey = function(value) {
214 | if (value) {
215 | storageKey = value;
216 | return this;
217 | } else {
218 | return storageKey;
219 | }
220 | };
221 |
222 | this.addLocalePatternValue = function(key, value) {
223 | extraProperties[key] = value;
224 | };
225 |
226 | this.$get = ['$rootScope', '$injector', '$interpolate', '$locale', '$q', 'tmhDynamicLocaleCache', '$timeout', function($rootScope, $injector, interpolate, locale, $q, tmhDynamicLocaleCache, $timeout) {
227 | var localeLocation = interpolate(localeLocationPattern);
228 |
229 | storage = $injector.get(storageFactory);
230 | $rootScope.$evalAsync(function() {
231 | var initialLocale;
232 | if ((initialLocale = (storage[storageGet](storageKey) || defaultLocale))) {
233 | loadLocaleFn(initialLocale);
234 | }
235 | });
236 | return {
237 | /**
238 | * @ngdoc method
239 | * @description
240 | * @param {string} value Sets the locale to the new locale. Changing the locale will trigger
241 | * a background task that will retrieve the new locale and configure the current $locale
242 | * instance with the information from the new locale
243 | */
244 | set: loadLocaleFn,
245 | /**
246 | * @ngdoc method
247 | * @description Returns the configured locale
248 | */
249 | get: function() {
250 | return activeLocale;
251 | }
252 | };
253 |
254 | function loadLocaleFn(localeId) {
255 | var baseProperties = {locale: localeId, angularVersion: angular.version.full};
256 | return loadLocale(localeLocation(angular.extend({}, extraProperties, baseProperties)), locale, localeId, $rootScope, $q, tmhDynamicLocaleCache, $timeout);
257 | }
258 | }];
259 | }]).provider('tmhDynamicLocaleCache', function() {
260 | this.$get = ['$cacheFactory', function($cacheFactory) {
261 | return $cacheFactory('tmh.dynamicLocales');
262 | }];
263 | }).provider('tmhDynamicLocaleStorageCache', function() {
264 | this.$get = ['$cacheFactory', function($cacheFactory) {
265 | return $cacheFactory('tmh.dynamicLocales.store');
266 | }];
267 | }).run(['tmhDynamicLocale', angular.noop]);
268 |
269 | return 'tmh.dynamicLocale';
270 |
271 | }));
272 |
--------------------------------------------------------------------------------
/test/tmhDynamicLocaleSpec.js:
--------------------------------------------------------------------------------
1 | /* global describe:false, beforeEach:false, afterEach:false, inject:false,
2 | it:false, inject:false, expect:false, jasmine:false */
3 | (function () {
4 | 'use strict';
5 |
6 | // Minimal implementation to mock what was removed from Jasmine 1.x
7 | function createAsync(doneFn) {
8 | function Job() {
9 | this.next = [];
10 | }
11 | Job.prototype.done = function () {
12 | return this.runs(doneFn);
13 | };
14 | Job.prototype.runs = function (fn) {
15 | var newJob = new Job();
16 | this.next.push(function () {
17 | fn();
18 | newJob.start();
19 | });
20 | return newJob;
21 | };
22 | Job.prototype.waitsFor = function (fn, error, timeout) {
23 | var newJob = new Job();
24 | timeout = timeout || 5000;
25 | this.next.push(function () {
26 | var counter = 0,
27 | intervalId = window.setInterval(function () {
28 | if (fn()) {
29 | window.clearInterval(intervalId);
30 | newJob.start();
31 | }
32 | counter += 5;
33 | if (counter > timeout) {
34 | window.clearInterval(intervalId);
35 | throw new Error(error);
36 | }
37 | }, 5);
38 | });
39 | return newJob;
40 | };
41 | Job.prototype.start = function () {
42 | var i;
43 | for (i = 0; i < this.next.length; i += 1) {
44 | this.next[i]();
45 | }
46 | };
47 | return new Job();
48 | }
49 |
50 | describe('dynamicLocale', function() {
51 | beforeEach(module('tmh.dynamicLocale'));
52 | beforeEach(module(function(tmhDynamicLocaleProvider) {
53 | tmhDynamicLocaleProvider.localeLocationPattern('/base/node_modules/angular-i18n/angular-locale_{{locale}}.js');
54 | }));
55 |
56 | afterEach(function (done) {
57 | inject(function($locale, $timeout, tmhDynamicLocale) {
58 | var job = createAsync(done);
59 | job
60 | .runs(function() {
61 | tmhDynamicLocale.set('en-us');
62 | })
63 | .waitsFor(function() {
64 | $timeout.flush(50);
65 | return $locale.id === 'en-us';
66 | }, 'locale not reverted', 2000)
67 | .done();
68 | job.start();
69 | });
70 | });
71 |
72 | it('should (eventually) be able to change the locale', function(done) {
73 | inject(function($locale, $timeout, tmhDynamicLocale) {
74 | var job = createAsync(done);
75 | job
76 | .runs(function() {
77 | tmhDynamicLocale.set('es');
78 | })
79 | .waitsFor(function() {
80 | $timeout.flush(50);
81 | return $locale.id === 'es';
82 | }, 'locale not updated', 2000)
83 | .runs(function() {
84 | expect($locale.id).toBe('es');
85 | expect($locale.DATETIME_FORMATS.DAY['0']).toBe('domingo');
86 | })
87 | .done();
88 | job.start();
89 | });
90 | });
91 |
92 | it('should be able to change the locale back-and-forward within one digest', function(done) {
93 | inject(function($rootScope, $locale, $timeout, tmhDynamicLocale) {
94 | var job = createAsync(done);
95 | job
96 | .runs(function() {
97 | $rootScope.$apply(function() {
98 | tmhDynamicLocale.set('es');
99 | tmhDynamicLocale.set('en');
100 | tmhDynamicLocale.set('es');
101 | });
102 | })
103 | .waitsFor(function() {
104 | $timeout.flush(50);
105 | return $locale.id === 'es';
106 | }, 'locale not updated', 2000)
107 | .runs(function() {
108 | expect($locale.id).toBe('es');
109 | expect($locale.DATETIME_FORMATS.DAY['0']).toBe('domingo');
110 | })
111 | .done();
112 | job.start();
113 | });
114 | });
115 |
116 | it('should trigger an event when there it changes the locale', function(done) {
117 | inject(function($timeout, $locale, tmhDynamicLocale, $rootScope) {
118 | var callback = jasmine.createSpy();
119 | var job = createAsync(done);
120 | job
121 | .runs(function() {
122 | $rootScope.$apply();
123 | $rootScope.$on('$localeChangeSuccess', callback);
124 | tmhDynamicLocale.set('es');
125 | expect(callback.calls.count()).toBe(0);
126 | })
127 | .waitsFor(function() {
128 | $timeout.flush(50);
129 | return $locale.id === 'es';
130 | }, 'locale not updated', 2000)
131 | .runs(function() {
132 | expect(callback.calls.count()).toBe(1);
133 | expect(callback.calls.argsFor(0)[1]).toEqual('es');
134 | expect(callback.calls.argsFor(0)[2]).toEqual($locale);
135 | })
136 | .done();
137 | job.start();
138 | });
139 | });
140 |
141 | it('should trigger a failure even when the locale change fail', function(done) {
142 | inject(function($timeout, $locale, tmhDynamicLocale, $rootScope) {
143 | var job = createAsync(done);
144 | var callback = jasmine.createSpy();
145 |
146 | job
147 | .runs(function() {
148 | $rootScope.$apply();
149 | $rootScope.$on('$localeChangeError', callback);
150 | tmhDynamicLocale.set('invalidLocale').catch(function() {});
151 | expect(callback.calls.count()).toBe(0);
152 | })
153 | .waitsFor(function() {
154 | $timeout.flush(50);
155 | return callback.calls.count() !== 0;
156 | }, 'error not generated', 2000)
157 | .runs(function() {
158 | expect(callback.calls.count()).toBe(1);
159 | expect(callback.calls.argsFor(0)[1]).toEqual('invalidLocale');
160 | })
161 | .done();
162 | job.start();
163 | });
164 | });
165 |
166 | it('should return a promise that has the new locale', function(done) {
167 | inject(function($timeout, $locale, tmhDynamicLocale, $rootScope) {
168 | var job = createAsync(done);
169 | var callback = jasmine.createSpy();
170 |
171 | job
172 | .runs(function() {
173 | tmhDynamicLocale.set('es').then(callback);
174 | expect(callback.calls.count()).toBe(0);
175 | })
176 | .waitsFor(function() {
177 | $timeout.flush(50);
178 | return callback.calls.count() !== 0;
179 | }, 'locale not updated', 2000)
180 | .runs(function() {
181 | expect(callback.calls.argsFor(0)[0].id).toEqual('es');
182 | expect(callback.calls.argsFor(0)[0]).toEqual($locale);
183 | tmhDynamicLocale.set('it');
184 | })
185 | .waitsFor(function() {
186 | $timeout.flush(50);
187 | return $locale.id === 'it';
188 | }, 'locale not updated', 2000)
189 | .runs(function() {
190 | tmhDynamicLocale.set('es').then(callback);
191 | expect(callback.calls.count()).toBe(1);
192 | $rootScope.$apply();
193 | expect(callback.calls.count()).toBe(2);
194 | expect(callback.calls.argsFor(1)[0].id).toBe('es');
195 | expect(callback.calls.argsFor(1)[0]).toBe($locale);
196 | })
197 | .done();
198 | job.start();
199 | });
200 | });
201 |
202 | it('should reject the returned promise if it fails to load the locale', function(done) {
203 | inject(function($timeout, $locale, tmhDynamicLocale) {
204 | var callback = jasmine.createSpy();
205 | var errorCallback = jasmine.createSpy();
206 | var job = createAsync(done);
207 |
208 | job
209 | .runs(function() {
210 | tmhDynamicLocale.set('invalidLocale').then(callback, errorCallback);
211 | })
212 | .waitsFor(function() {
213 | $timeout.flush(50);
214 | return errorCallback.calls.count();
215 | }, 'promise not rejected', 2000)
216 | .runs(function() {
217 | expect(callback.calls.count()).toBe(0);
218 | expect(errorCallback.calls.count()).toBe(1);
219 | expect(errorCallback.calls.argsFor(0)[0]).toBe('invalidLocale');
220 | expect($locale.id).toBe('en-us');
221 | })
222 | .done();
223 | job.start();
224 | });
225 | });
226 |
227 | it('should be possible to retrieve the locale to be', function(done) {
228 | inject(function($timeout, $locale, tmhDynamicLocale) {
229 | var job = createAsync(done);
230 |
231 | job
232 | .runs(function() {
233 | tmhDynamicLocale.set('es');
234 | expect(tmhDynamicLocale.get()).toBe('es');
235 | })
236 | .waitsFor(function() {
237 | $timeout.flush(50);
238 | return $locale.id === 'es';
239 | }, 'locale not updated', 2000)
240 | .runs(function() {
241 | expect(tmhDynamicLocale.get()).toBe('es');
242 | })
243 | .done();
244 | job.start();
245 | });
246 | });
247 |
248 | it('should revert the configured locale when the new locale does not exist', function(done) {
249 | inject(function($timeout, $locale, tmhDynamicLocale) {
250 | var job = createAsync(done);
251 | var errorCallback = jasmine.createSpy();
252 |
253 | job
254 | .runs(function() {
255 | tmhDynamicLocale.set('es');
256 | })
257 | .waitsFor(function() {
258 | $timeout.flush(50);
259 | return $locale.id === 'es';
260 | }, 'locale not updated', 2000)
261 | .runs(function() {
262 | tmhDynamicLocale.set('invalidLocale').then(undefined, errorCallback);
263 | expect(tmhDynamicLocale.get()).toBe('invalidLocale');
264 | })
265 | .waitsFor(function() {
266 | $timeout.flush(50);
267 | return errorCallback.calls.count();
268 | }, 'promise not rejected', 2000)
269 | .runs(function() {
270 | expect(tmhDynamicLocale.get()).toBe('es');
271 | })
272 | .done();
273 | job.start();
274 | });
275 | });
276 |
277 | it('should change the already formatted numbers in the page', function(done) {
278 | inject(function($timeout, $locale, tmhDynamicLocale, $rootScope, $compile) {
279 | var job = createAsync(done);
280 | var element = null;
281 |
282 | job
283 | .runs(function() {
284 | element = $compile('{{val | number}}')($rootScope);
285 |
286 | $rootScope.val = 1234.5678;
287 | $rootScope.$apply();
288 | expect(element.text()).toBe('1,234.568');
289 |
290 | tmhDynamicLocale.set('es');
291 | })
292 | .waitsFor(function() {
293 | $timeout.flush(50);
294 | return $locale.id === 'es';
295 | }, 'locale not updated', 2000)
296 | .runs(function() {
297 | expect(element.text()).toBe('1.234,568');
298 | })
299 | .done();
300 | job.start();
301 | });
302 | });
303 |
304 | it('should keep already loaded locales at tmhDynamicLocaleCache', function(done) {
305 | inject(function($timeout, $locale, tmhDynamicLocale, tmhDynamicLocaleCache) {
306 | var job = createAsync(done);
307 | var esLocale = null;
308 |
309 | job
310 | .runs(function() {
311 | expect(tmhDynamicLocaleCache.info().size).toBe(0);
312 | tmhDynamicLocale.set('es');
313 | expect(tmhDynamicLocaleCache.info().size).toBe(0);
314 | })
315 | .waitsFor(function() {
316 | $timeout.flush(50);
317 | return $locale.id === 'es';
318 | }, 'locale not updated', 2000)
319 | .runs(function() {
320 | expect(tmhDynamicLocaleCache.info().size).toBe(1);
321 | expect(tmhDynamicLocaleCache.get('es')).toEqual($locale);
322 | esLocale = angular.copy($locale);
323 | tmhDynamicLocale.set('it');
324 | })
325 | .waitsFor(function() {
326 | $timeout.flush(50);
327 | return $locale.id === 'it';
328 | }, 'locale not updated', 2000)
329 | .runs(function() {
330 | expect(tmhDynamicLocaleCache.info().size).toBe(2);
331 | expect(tmhDynamicLocaleCache.get('es')).toEqual(esLocale);
332 | expect(tmhDynamicLocaleCache.get('it')).toEqual($locale);
333 | })
334 | .done();
335 | job.start();
336 | });
337 | });
338 |
339 | it('should use the cache when possible', function(done) {
340 | inject(function($timeout, $locale, tmhDynamicLocale, tmhDynamicLocaleCache, $rootScope) {
341 | var job = createAsync(done);
342 | var callback = jasmine.createSpy();
343 |
344 | job
345 | .runs(function() {
346 | tmhDynamicLocale.set('es');
347 | })
348 | .waitsFor(function() {
349 | $timeout.flush(50);
350 | return $locale.id === 'es';
351 | }, 'locale not updated', 2000)
352 | .runs(function() {
353 | tmhDynamicLocale.set('it');
354 | })
355 | .waitsFor(function() {
356 | $timeout.flush(50);
357 | return $locale.id === 'it';
358 | }, 'locale not updated', 2000)
359 | .runs(function() {
360 | tmhDynamicLocaleCache.get('es').DATETIME_FORMATS.DAY['0'] = 'Domingo';
361 | $rootScope.$on('$localeChangeSuccess', callback);
362 | tmhDynamicLocale.set('es');
363 | // Changing the locale should be done async even when this is done from the cache
364 | expect(callback.calls.count()).toBe(0);
365 | expect($locale.id).toBe('it');
366 | $rootScope.$apply();
367 | expect($locale.id).toBe('es');
368 | expect($locale.DATETIME_FORMATS.DAY['0']).toBe('Domingo');
369 | expect(callback.calls.count()).toBe(1);
370 | })
371 | .done();
372 | job.start();
373 | });
374 | });
375 |
376 | it('should do a deep copy of the locale elements', function(done) {
377 | inject(function($timeout, $locale, tmhDynamicLocale, tmhDynamicLocaleCache) {
378 | var job = createAsync(done);
379 |
380 | job
381 | .runs(function() {
382 | tmhDynamicLocale.set('es');
383 | })
384 | .waitsFor(function() {
385 | $timeout.flush(50);
386 | return $locale.id === 'es';
387 | }, 'locale not updated', 2000)
388 | .runs(function() {
389 | $locale.DATETIME_FORMATS.DAY['0'] = 'XXX';
390 | expect($locale.DATETIME_FORMATS.DAY['0']).not.toBe(tmhDynamicLocaleCache.get('es').DATETIME_FORMATS.DAY['0']);
391 | })
392 | .done();
393 | job.start();
394 | });
395 | });
396 |
397 | it('should be able to handle locales with extra elements', function(done) {
398 | inject(function($timeout, $locale, tmhDynamicLocale, tmhDynamicLocaleCache) {
399 | var job = createAsync(done);
400 | var weirdLocale;
401 |
402 | job
403 | .runs(function() {
404 | tmhDynamicLocale.set('es');
405 | })
406 | .waitsFor(function() {
407 | $timeout.flush(50);
408 | return $locale.id === 'es';
409 | }, 'locale not updated', 2000)
410 | .runs(function() {
411 | weirdLocale = angular.copy($locale);
412 | weirdLocale.id = 'xx';
413 | weirdLocale.EXTRA_PARAMETER = {foo: 'FOO'};
414 | weirdLocale.DATETIME_FORMATS.DAY['7'] = 'One More Day';
415 | tmhDynamicLocaleCache.put('xx', angular.copy(weirdLocale));
416 | tmhDynamicLocale.set('xx');
417 | })
418 | .waitsFor(function() {
419 | $timeout.flush(50);
420 | return $locale.id === 'xx';
421 | }, 'locale not updated', 2000)
422 | .runs(function() {
423 | expect($locale).toEqual(weirdLocale);
424 | expect($locale.EXTRA_PARAMETER).toEqual({foo: 'FOO'});
425 | tmhDynamicLocale.set('es');
426 | })
427 | .waitsFor(function() {
428 | $timeout.flush(50);
429 | return $locale.id === 'es';
430 | }, 'locale not updated', 2000)
431 | .runs(function() {
432 | expect($locale.EXTRA_PARAMETER).toBeUndefined();
433 | expect($locale.DATETIME_FORMATS.DAY['7']).toBeUndefined();
434 | expect($locale.DATETIME_FORMATS.DAY.length).toBe(7);
435 | })
436 | .done();
437 | job.start();
438 | });
439 | });
440 |
441 | describe('having a default locale', function() {
442 | beforeEach(module(function(tmhDynamicLocaleProvider) {
443 | tmhDynamicLocaleProvider.defaultLocale('it');
444 | }));
445 | it('should set the locale to the default locale', function(done) {
446 | inject(function($timeout, $locale, $rootScope) {
447 | var job = createAsync(done);
448 |
449 | job
450 | .runs(function() {
451 | expect($locale.id).toBe('en-us');
452 | $rootScope.$apply();
453 | })
454 | .waitsFor(function() {
455 | $timeout.flush(50);
456 | return $locale.id === 'it';
457 | }, 'locale not updated', 2000)
458 | .runs(function() {
459 | expect($locale.id).toBe('it');
460 | })
461 | .done();
462 | job.start();
463 | });
464 | });
465 | });
466 |
467 | describe('having a cookie storage', function () {
468 | beforeEach(module('ngCookies'));
469 | beforeEach(module(function(tmhDynamicLocaleProvider) {
470 | tmhDynamicLocaleProvider.useCookieStorage();
471 | }));
472 |
473 | if (angular.version.minor < 7) {
474 | it('should store the change in $cookieStore', function(done) {
475 | inject(function ($timeout, $locale, $cookieStore, tmhDynamicLocale) {
476 | $cookieStore.remove('tmhDynamicLocale.locale');
477 | var job = createAsync(done);
478 |
479 | job
480 | .runs(function() {
481 | tmhDynamicLocale.set('es');
482 | expect($cookieStore.get('tmhDynamicLocale.locale')).toBe(undefined);
483 | })
484 | .waitsFor(function() {
485 | $timeout.flush(50);
486 | return $locale.id === 'es';
487 | }, 'locale not updated', 2000)
488 | .runs(function() {
489 | expect($cookieStore.get('tmhDynamicLocale.locale')).toBe('es');
490 | })
491 | .done();
492 | job.start();
493 | });
494 | });
495 | }
496 | it('should store the change in $cookies', function(done) {
497 | inject(function ($timeout, $locale, $cookies, tmhDynamicLocale) {
498 | $cookies.remove('tmhDynamicLocale.locale');
499 | var job = createAsync(done);
500 |
501 | job
502 | .runs(function() {
503 | tmhDynamicLocale.set('es');
504 | expect($cookies.getObject('tmhDynamicLocale.locale')).toBe(undefined);
505 | })
506 | .waitsFor(function() {
507 | $timeout.flush(50);
508 | return $locale.id === 'es';
509 | }, 'locale not updated', 2000)
510 | .runs(function() {
511 | expect($cookies.getObject('tmhDynamicLocale.locale')).toBe('es');
512 | })
513 | .done();
514 | job.start();
515 | });
516 | });
517 | describe('reading the locale at initialization', function () {
518 | if (angular.version.minor < 7) {
519 | beforeEach(inject(function ($cookieStore, $rootScope) {
520 | $cookieStore.put('tmhDynamicLocale.locale', 'it');
521 | $rootScope.$apply();
522 | }));
523 | } else {
524 | beforeEach(inject(function ($cookies, $rootScope) {
525 | $cookies.putObject('tmhDynamicLocale.locale', 'it');
526 | $rootScope.$apply();
527 | }));
528 | }
529 |
530 | it('should load the locale on initialization', function(done) {
531 | inject(function ($timeout, $locale) {
532 | var job = createAsync(done);
533 |
534 | job
535 | .runs(function() {
536 | expect($locale.id).toBe('en-us');
537 | })
538 | .waitsFor(function() {
539 | $timeout.flush(50);
540 | return $locale.id === 'it';
541 | }, 'locale not updated', 2000)
542 | .runs(function() {
543 | expect($locale.id).toBe('it');
544 | })
545 | .done();
546 | job.start();
547 | });
548 | });
549 | });
550 | describe('and having a default language', function () {
551 | beforeEach(module(function(tmhDynamicLocaleProvider) {
552 | tmhDynamicLocaleProvider.defaultLocale('es');
553 | }));
554 | if (angular.version.minor < 7) {
555 | beforeEach(inject(function ($cookieStore, $rootScope) {
556 | $cookieStore.put('tmhDynamicLocale.locale', 'it');
557 | $rootScope.$apply();
558 | }));
559 | } else {
560 | beforeEach(inject(function ($cookies, $rootScope) {
561 | $cookies.putObject('tmhDynamicLocale.locale', 'it');
562 | $rootScope.$apply();
563 | }));
564 | }
565 |
566 | it('should load the locale on initialization', function(done) {
567 | inject(function ($timeout, $locale) {
568 | var job = createAsync(done);
569 |
570 | job
571 | .runs(function() {
572 | expect($locale.id).toBe('en-us');
573 | })
574 | .waitsFor(function() {
575 | $timeout.flush(50);
576 | return $locale.id === 'it';
577 | }, 'locale not updated', 2000)
578 | .runs(function() {
579 | expect($locale.id).toBe('it');
580 | })
581 | .done();
582 | job.start();
583 | });
584 | });
585 | });
586 | describe('and changing the name of the storageKey', function () {
587 | beforeEach(module(function(tmhDynamicLocaleProvider) {
588 | tmhDynamicLocaleProvider.storageKey('customStorageKeyName');
589 | }));
590 |
591 | if (angular.version.minor < 7) {
592 | it('should change the name of the storageKey', function(done) {
593 | inject(function ($timeout, $locale, $cookieStore, tmhDynamicLocale) {
594 | $cookieStore.remove('tmhDynamicLocale.locale');
595 | $cookieStore.remove('customStorageKeyName');
596 | var job = createAsync(done);
597 |
598 | job
599 | .runs(function() {
600 | tmhDynamicLocale.set('es');
601 | expect($cookieStore.get('customStorageKeyName')).toBe(undefined);
602 | expect($cookieStore.get('tmhDynamicLocale.locale')).toBe(undefined);
603 | })
604 | .waitsFor(function() {
605 | $timeout.flush(50);
606 | return $locale.id === 'es';
607 | }, 'locale not updated', 2000)
608 | .runs(function() {
609 | expect($cookieStore.get('tmhDynamicLocale.locale')).toBe(undefined);
610 | expect($cookieStore.get('customStorageKeyName')).toBe('es');
611 | })
612 | .done();
613 | job.start();
614 | });
615 | });
616 | }
617 |
618 |
619 | it('should change the name of the storageKey', function(done) {
620 | inject(function ($timeout, $locale, $cookies, tmhDynamicLocale) {
621 | $cookies.remove('tmhDynamicLocale.locale');
622 | $cookies.remove('customStorageKeyName');
623 | var job = createAsync(done);
624 |
625 | job
626 | .runs(function() {
627 | tmhDynamicLocale.set('es');
628 | expect($cookies.getObject('customStorageKeyName')).toBe(undefined);
629 | expect($cookies.getObject('tmhDynamicLocale.locale')).toBe(undefined);
630 | })
631 | .waitsFor(function() {
632 | $timeout.flush(50);
633 | return $locale.id === 'es';
634 | }, 'locale not updated', 2000)
635 | .runs(function() {
636 | expect($cookies.getObject('tmhDynamicLocale.locale')).toBe(undefined);
637 | expect($cookies.getObject('customStorageKeyName')).toBe('es');
638 | })
639 | .done();
640 | job.start();
641 | });
642 | });
643 | });
644 | });
645 |
646 | describe('loading locales using