From 0205c6f1edf3b90010413d4edfec07f1457e962e Mon Sep 17 00:00:00 2001 From: Mirco Bertelli Date: Thu, 13 Aug 2015 18:16:53 +0200 Subject: [PATCH 1/4] mulilanguage support - added general Flux elements: - Dispatcher - added Flux elements to manage multiple languages - I18NStore - I18NAction - added components: - I18N.Message to show formatted multilanguage messages - LangSelector to switch between languages --- package.json | 1 + web/client/actions/I18NActions.jsx | 23 +++ .../actions/__tests__/I18NActions-test.jsx | 40 +++++ web/client/components/I18N/I18N.jsx | 8 + web/client/components/I18N/Message.jsx | 49 ++++++ .../I18N/__tests__/I18N.Message-test.jsx | 53 +++++++ .../components/LangSelector/LangSelector.jsx | 49 ++++++ .../__tests__/LangSelector-test.jsx | 59 +++++++ web/client/dispatchers/Dispatcher.jsx | 10 ++ web/client/stores/I18NStore.jsx | 111 ++++++++++++++ .../stores/__tests__/I18NStore-test.jsx | 144 ++++++++++++++++++ web/client/stores/data.en-US | 15 ++ web/client/stores/data.it-IT | 15 ++ 13 files changed, 577 insertions(+) create mode 100644 web/client/actions/I18NActions.jsx create mode 100644 web/client/actions/__tests__/I18NActions-test.jsx create mode 100644 web/client/components/I18N/I18N.jsx create mode 100644 web/client/components/I18N/Message.jsx create mode 100644 web/client/components/I18N/__tests__/I18N.Message-test.jsx create mode 100644 web/client/components/LangSelector/LangSelector.jsx create mode 100644 web/client/components/LangSelector/__tests__/LangSelector-test.jsx create mode 100644 web/client/dispatchers/Dispatcher.jsx create mode 100644 web/client/stores/I18NStore.jsx create mode 100644 web/client/stores/__tests__/I18NStore-test.jsx create mode 100644 web/client/stores/data.en-US create mode 100644 web/client/stores/data.it-IT diff --git a/package.json b/package.json index da5439a7f5..67097de0f8 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "proj4": "~2.3.6", "react": "^0.13.3", "react-bootstrap": "^0.24.3", + "react-intl": "^1.2.0", "url": "~0.10.3" }, "scripts": { diff --git a/web/client/actions/I18NActions.jsx b/web/client/actions/I18NActions.jsx new file mode 100644 index 0000000000..0ba834d59a --- /dev/null +++ b/web/client/actions/I18NActions.jsx @@ -0,0 +1,23 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var assign = require('object-assign'); +var keymirror = require('keymirror'); + +var Dispatcher = require('../dispatchers/Dispatcher'); + +var I18NActions = { + launch(actionType, args) { + Dispatcher.dispatch(assign({type: actionType}, args)); + } +}; + +assign(I18NActions, keymirror({ + CHANGE_LANG: null +})); + +module.exports = I18NActions; diff --git a/web/client/actions/__tests__/I18NActions-test.jsx b/web/client/actions/__tests__/I18NActions-test.jsx new file mode 100644 index 0000000000..a957adaa7d --- /dev/null +++ b/web/client/actions/__tests__/I18NActions-test.jsx @@ -0,0 +1,40 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var expect = require('expect'); +var assign = require('object-assign'); +var Dispatcher = require('../../dispatchers/Dispatcher'); + +describe('This test for I18NActions', () => { + var I18NActions; + + beforeEach((done) => { + I18NActions = require('../I18NActions'); + setTimeout(done); + }); + + afterEach((done) => { + var name = require.resolve('../I18NActions'); + delete require.cache[name]; + setTimeout(done); + }); + + it('checks launch(actionType, args)', () => { + const aType = "test"; + const aArgs = { + k0: "v0", + k1: "v1" + }; + const testArgs = assign({type: aType}, aArgs); + const spy = expect.spyOn(Dispatcher, 'dispatch'); + I18NActions.launch(aType, aArgs); + + expect(spy.calls.length).toBe(1); + expect(spy.calls[0].context).toBe(Dispatcher); + expect(spy.calls[0].arguments).toEqual([ testArgs ]); + }); +}); diff --git a/web/client/components/I18N/I18N.jsx b/web/client/components/I18N/I18N.jsx new file mode 100644 index 0000000000..c432d48946 --- /dev/null +++ b/web/client/components/I18N/I18N.jsx @@ -0,0 +1,8 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +module.exports.Message = require('./Message'); diff --git a/web/client/components/I18N/Message.jsx b/web/client/components/I18N/Message.jsx new file mode 100644 index 0000000000..8420148a54 --- /dev/null +++ b/web/client/components/I18N/Message.jsx @@ -0,0 +1,49 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var React = require('react'); +var ReactIntl = require('react-intl'); +var FormattedMessage = ReactIntl.FormattedMessage; + +var I18NStore = require('../../stores/I18NStore'); + +var Message = React.createClass({ + propTypes: { + msgId: React.PropTypes.string.isRequired, + msgParams: React.PropTypes.object + }, + getInitialState() { + const currentLocale = I18NStore.getCurrentLocale(); + const msg = I18NStore.getMsgById(this.props.msgId); + return { + locales: currentLocale, + msg: msg + }; + }, + // it makes this component reactive when a new language is loaded in + // language store. + componentDidMount() { + I18NStore.register(I18NStore.Event.LANG_CHANGED, this.onLangChanged); + }, + componentWillUnmount() { + I18NStore.unregister(I18NStore.Event.LANG_CHANGED, this.onLangChanged); + }, + // it updates the state of this component when the language store loads a new one. + onLangChanged() { + const currentLocale = I18NStore.getCurrentLocale(); + const msg = I18NStore.getMsgById(this.props.msgId); + this.setState({ + locales: currentLocale, + msg: msg + }); + }, + render() { + return ; + } +}); + +module.exports = Message; diff --git a/web/client/components/I18N/__tests__/I18N.Message-test.jsx b/web/client/components/I18N/__tests__/I18N.Message-test.jsx new file mode 100644 index 0000000000..3bc3cf61f5 --- /dev/null +++ b/web/client/components/I18N/__tests__/I18N.Message-test.jsx @@ -0,0 +1,53 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var expect = require('expect'); + +var React = require('react/addons'); +var I18NStore = require('../../../stores/I18NStore'); +var I18N = require('../I18N'); +var I18NActions = require('../../../actions/I18NActions'); + +var ita = require('../../../stores/data.it-IT'); +var eng = require('../../../stores/data.en-US'); + +describe('This test for I18N.Message', () => { + afterEach((done) => { + React.unmountComponentAtNode(document.body); + document.body.innerHTML = ''; + setTimeout(done); + }); + + it('checks if the component reacts at I18NStore edits', () => { + var testMsg; + var currentData; + const msgId = "aboutLbl"; + const data = { + "en-US": eng, + "it-IT": ita + }; + currentData = data[I18NStore.getCurrentLocale()]; + testMsg = currentData.messages[msgId]; + + const cmp = React.render(, document.body); + expect(cmp).toExist(); + + const cmpDom = React.findDOMNode(cmp); + expect(cmpDom).toExist(); + expect(cmpDom.innerHTML).toBe(testMsg); + + const nextLocale = I18NStore.getCurrentLocale() === "it-It" ? "en-US" : "it-IT"; + I18NStore._emulate_dispatcher({ + locale: nextLocale, + type: I18NActions.CHANGE_LANG + }); + + currentData = data[I18NStore.getCurrentLocale()]; + testMsg = currentData.messages[msgId]; + expect(cmpDom.innerHTML).toBe(testMsg); + }); +}); diff --git a/web/client/components/LangSelector/LangSelector.jsx b/web/client/components/LangSelector/LangSelector.jsx new file mode 100644 index 0000000000..9ef8e962e2 --- /dev/null +++ b/web/client/components/LangSelector/LangSelector.jsx @@ -0,0 +1,49 @@ + +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var React = require('react'); +var BootstrapReact = require('react-bootstrap'); +var Input = BootstrapReact.Input; + +var I18NStore = require('../../stores/I18NStore'); +var I18NActions = require('../../actions/I18NActions'); + +var LangSelector = React.createClass({ + propTypes: { + locales: React.PropTypes.object + }, + getDefaultProps() { + return { + locales: I18NStore.getSupportedLocales() + }; + }, + render() { + var val; + var label; + var list = []; + for (let lang in this.props.locales) { + if (this.props.locales.hasOwnProperty(lang)) { + val = this.props.locales[lang]; + label = lang; + list.push(); + } + } + return ( + + {list} + + ); + }, + launchNewLangAction() { + var element = React.findDOMNode(this); + var selectNode = element.getElementsByTagName('select').item(0); + I18NActions.launch(I18NActions.CHANGE_LANG, {locale: selectNode.value}); + } +}); + +module.exports = LangSelector; diff --git a/web/client/components/LangSelector/__tests__/LangSelector-test.jsx b/web/client/components/LangSelector/__tests__/LangSelector-test.jsx new file mode 100644 index 0000000000..1867a5787b --- /dev/null +++ b/web/client/components/LangSelector/__tests__/LangSelector-test.jsx @@ -0,0 +1,59 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var expect = require('expect'); + +var React = require('react/addons'); +var I18NStore = require('../../../stores/I18NStore'); +var I18NActions = require('../../../actions/I18NActions'); +var LangSelector = require('../LangSelector'); + +describe('LangSelector', () => { + afterEach((done) => { + React.unmountComponentAtNode(document.body); + document.body.innerHTML = ''; + setTimeout(done); + }); + + it('checks default', () => { + var lbl; + var value; + + const cmp = React.render(, document.body); + expect(cmp).toExist(); + + const cmpDom = React.findDOMNode(cmp); + expect(cmpDom).toExist(); + expect(cmpDom.id).toNotExist(); + + const select = cmpDom.getElementsByTagName("select").item(0); + const opts = select.childNodes; + const langs = I18NStore.getSupportedLocales(); + + for (let i = 0; i < opts.length; i++) { + lbl = opts[i].innerHTML; + value = opts[i].value; + expect(langs.hasOwnProperty(lbl)).toBe(true); + expect(langs[lbl]).toBe(value); + } + }); + + it('checks if a change of the compo fires the proper action', () => { + const spy = expect.spyOn(I18NActions, 'launch'); + + const cmp = React.render(, document.body); + const cmpDom = React.findDOMNode(cmp); + const select = cmpDom.getElementsByTagName("select").item(0); + + select.value = "it-IT"; + React.addons.TestUtils.Simulate.change(select, {target: {value: 'it-IT'}}); + // select.children[1].click(); + + expect(spy.calls.length).toBe(1); + expect(spy.calls[0].arguments).toEqual([I18NActions.CHANGE_LANG, {locale: "it-IT"}]); + }); +}); diff --git a/web/client/dispatchers/Dispatcher.jsx b/web/client/dispatchers/Dispatcher.jsx new file mode 100644 index 0000000000..ccdf52787c --- /dev/null +++ b/web/client/dispatchers/Dispatcher.jsx @@ -0,0 +1,10 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var Dispatcher = require('flux').Dispatcher; + +module.exports = new Dispatcher(); diff --git a/web/client/stores/I18NStore.jsx b/web/client/stores/I18NStore.jsx new file mode 100644 index 0000000000..d18619e567 --- /dev/null +++ b/web/client/stores/I18NStore.jsx @@ -0,0 +1,111 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var EventEmitter = require('events').EventEmitter; +var assign = require('object-assign'); +var keymirror = require('keymirror'); + +var Dispatcher = require('../dispatchers/Dispatcher'); +var I18NActions = require('../actions/I18NActions'); + +var it = require('./data.it-IT'); +var us = require('./data.en-US'); + +const StaticLangStore = { + "it-IT": it, + "en-US": us +}; +const AvailableLang = { + "English": "en-US", + "Italiano": "it-IT" +}; + +const _getDefaultLang = () => { + var lang; + var retval; + if (navigator) { + lang = navigator.language || navigator.browserLanguage; + } + if (!lang) { + retval = "en-US"; + } else { + for (let l in AvailableLang) { + if (AvailableLang.hasOwnProperty(l)) { + if (lang === AvailableLang[l]) { + retval = lang; + } + } + } + retval = "en-US"; + } + return retval; +}; + +var _i18nStore = { + locales: AvailableLang, + data: StaticLangStore[_getDefaultLang()] +}; + +var I18NStore = assign({}, EventEmitter.prototype, { + Event: keymirror({ + LANG_CHANGED: null + }), + register(event, handler) { + this.on(event, handler); + }, + unregister(event, handler) { + this.removeListener(event, handler); + }, + getCurrentLocale() { + return _i18nStore.data.locale; + }, + getSupportedLocales() { + return _i18nStore.locales; + }, + trigger(event) { + this.emit(event); + }, + getMsgById(id) { + return _i18nStore.data.messages[id]; + } +}); + +// this private function does: +// - gets internationalization data given a locale value +// - changes the internal state of this store +// - moves on calling the given callback function +const _fetchIntlData = (locale, callback) => { + _i18nStore.data = StaticLangStore[locale]; + callback(); +}; + +// this function manages actions which come from the dispatcher +const _actionsManager = action => { + switch (action.type) { + case I18NActions.CHANGE_LANG: { + _fetchIntlData(action.locale, () => { + I18NStore.trigger(I18NStore.Event.LANG_CHANGED); + }); + } break; + default: // ignore other kind of actions. + } +}; + +Dispatcher.register(_actionsManager); + +module.exports = I18NStore; + +// these stuff was exported to easly perform unit tests. +const _emulateDispatcher = action => { + _actionsManager(action); +}; +module.exports._emulate_dispatcher = _emulateDispatcher; + +const _setMockedData = obj => { + _i18nStore = obj; +}; +module.exports._set_mocked_data = _setMockedData; diff --git a/web/client/stores/__tests__/I18NStore-test.jsx b/web/client/stores/__tests__/I18NStore-test.jsx new file mode 100644 index 0000000000..157b878246 --- /dev/null +++ b/web/client/stores/__tests__/I18NStore-test.jsx @@ -0,0 +1,144 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +var expect = require('expect'); +var I18NActions = require('../../actions/I18NActions'); + +describe('This test for I18NStore', () => { + var I18NStore; + beforeEach((done) => { + I18NStore = require('../I18NStore'); + setTimeout(done); + }); + + afterEach((done) => { + var name = require.resolve('../I18NStore'); + delete require.cache[name]; + setTimeout(done); + }); + + it('checks getCurrentLocale()', () => { + const mockStoreData = { + data: { + locale: "foo" + } + }; + I18NStore._set_mocked_data(mockStoreData); + + expect(I18NStore.getCurrentLocale()).toBe(mockStoreData.data.locale); + }); + + it('checks default locale', () => { + var locale; + if (navigator) { + locale = navigator.language || navigator.browserLanguage; + if (locale && (locale === "it-IT" || locale === "en-US")) { + expect(I18NStore.getCurrentLocale()).toBe(locale); + } + } + expect(I18NStore.getCurrentLocale()).toBe("en-US"); + }); + + it('checks getSupportedLocales()', () => { + const mockStoreData = { + locales: { + "l0": "00", + "l1": "01" + } + }; + I18NStore._set_mocked_data(mockStoreData); + + const output = I18NStore.getSupportedLocales(); + for (let p in mockStoreData.locales) { + if (mockStoreData.locales.hasOwnProperty(p)) { + expect(output[p]).toBe(mockStoreData.locales[p]); + } + } + for (let p in output) { + if (output.hasOwnProperty(p)) { + expect(mockStoreData.locales[p]).toBe(output[p]); + } + } + }); + + it('checks getMsgById(id)', () => { + const mockStoreData = { + data: { + messages: { + "id0": "id0", + "id1": "id1", + "id2": "id2" + } + } + }; + I18NStore._set_mocked_data(mockStoreData); + + for (let id in mockStoreData.data.messages) { + if (mockStoreData.data.messages.hasOwnProperty(id)) { + expect(I18NStore.getMsgById(id)).toBe(id); + } + } + }); + + it('checks register(event, handler)', () => { + const mockEvent = "anEvent"; + const testHandlers = { h() {} }; + const spy = expect.spyOn(testHandlers, "h"); + + I18NStore.register(mockEvent, testHandlers.h); + I18NStore.emit(mockEvent); + + expect(spy.calls.length).toBe(1); + }); + + it('checks trigger(event)', () => { + const mockEvent = "anEvent"; + const testHandlers = { h() {} }; + const spy = expect.spyOn(testHandlers, "h"); + + I18NStore.register(mockEvent, testHandlers.h); + I18NStore.trigger(mockEvent); + + expect(spy.calls.length).toBe(1); + }); + + it('unregister(event, handler)', () => { + const mockEvent = "anEvent"; + const testHandlers = { h() {} }; + const spy = expect.spyOn(testHandlers, "h"); + + I18NStore.register(mockEvent, testHandlers.h); + I18NStore.trigger(mockEvent); + + expect(spy.calls.length).toBe(1); + + I18NStore.unregister(mockEvent, testHandlers.h); + I18NStore.trigger(mockEvent); + + expect(spy.calls.length).toBe(1); + }); + + it('checks the correctness of I18NActions.CHANGE_LANG menagement', () => { + const currentLocale = I18NStore.getCurrentLocale(); + const locales = I18NStore.getSupportedLocales(); + var newLocale; + + for (let l in locales) { + if (locales.hasOwnProperty(l)) { + if (locales[l] !== currentLocale) { + newLocale = locales[l]; + } + } + } + I18NStore._emulate_dispatcher({ + locale: newLocale, + type: I18NActions.CHANGE_LANG + }); + + expect(I18NStore.getCurrentLocale()).toBe(newLocale); + }); +}); diff --git a/web/client/stores/data.en-US b/web/client/stores/data.en-US new file mode 100644 index 0000000000..f3de741fb7 --- /dev/null +++ b/web/client/stores/data.en-US @@ -0,0 +1,15 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +module.exports = { + locale: "en-US", + messages: { + msgId0: "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} on {takenDate, date, long}.", + htmlTest: "{name} {surname}", + aboutLbl: "About" + } +}; diff --git a/web/client/stores/data.it-IT b/web/client/stores/data.it-IT new file mode 100644 index 0000000000..a436358f83 --- /dev/null +++ b/web/client/stores/data.it-IT @@ -0,0 +1,15 @@ +/** + * Copyright 2015, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ +module.exports = { + locale: "it-IT", + messages: { + msgId0: "{name} ha scattato {numPhotos, plural, =0 {nessuna foto} other {# foto}} il {takenDate, date, long}.", + htmlTest: "{name} {surname}", + aboutLbl: "Informazioni" + } +}; From d99a2913ede2c48e02f4241b229b35a170e19030 Mon Sep 17 00:00:00 2001 From: Mirco Bertelli Date: Fri, 14 Aug 2015 18:52:06 +0200 Subject: [PATCH 2/4] code refactoring to increase test coverage --- web/client/stores/I18NStore.jsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/web/client/stores/I18NStore.jsx b/web/client/stores/I18NStore.jsx index d18619e567..843618769c 100644 --- a/web/client/stores/I18NStore.jsx +++ b/web/client/stores/I18NStore.jsx @@ -26,23 +26,19 @@ const AvailableLang = { const _getDefaultLang = () => { var lang; - var retval; if (navigator) { lang = navigator.language || navigator.browserLanguage; } - if (!lang) { - retval = "en-US"; - } else { + if (lang) { for (let l in AvailableLang) { if (AvailableLang.hasOwnProperty(l)) { if (lang === AvailableLang[l]) { - retval = lang; + return lang; } } } - retval = "en-US"; } - return retval; + return "en-US"; }; var _i18nStore = { From 6387ecabdb9e092f94bcf3851b563605f013b6bd Mon Sep 17 00:00:00 2001 From: Mirco Bertelli Date: Fri, 14 Aug 2015 19:29:52 +0200 Subject: [PATCH 3/4] updated viewer with italian and english --- web/client/examples/viewer/app.jsx | 37 +++++++++------------------ web/client/examples/viewer/index.html | 8 ++++++ web/client/stores/data.en-US | 17 +++++++++--- web/client/stores/data.it-IT | 13 +++++++++- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/web/client/examples/viewer/app.jsx b/web/client/examples/viewer/app.jsx index 42457f78b8..28dd61f1bc 100644 --- a/web/client/examples/viewer/app.jsx +++ b/web/client/examples/viewer/app.jsx @@ -8,6 +8,8 @@ var React = require('react'); var MapViewController = require('../../components/Map/MapViewController'); var InfoButton = require('../../components/InfoButton/InfoButton'); +var I18N = require('../../components/I18N/I18N'); +var LangSelector = require('../../components/LangSelector/LangSelector'); var ConfigUtils = require('../../utils/ConfigUtils'); var url = require('url'); @@ -22,44 +24,29 @@ Api.getMergedConfig("../data/mapStoreConfig.json", url.parse(window.location.hre }); React.render(} + title={} glyphicon="info-sign" body={

MapStore 2

- MapStore 2 is a framework to build web mapping applications using - standard mapping libraries, such as OpenLayers 3 and Leaflet. + OpenLayers 3 Leaflet.

-

MapStore 2 has several example applications:

+

  • - MapViewer is a simple viewer of preconfigured maps (optionally - stored in a database using GeoStore) +
  • - MapPublisher has been developed to create, save and share in a - simple and intuitive way maps and mashups created selecting - contents by server like OpenStreetMap, Google Maps, MapQuest or - specific servers provided by your organization or third party. - For more information check the MapStore wiki. + MapStore wiki.
-

License

+

- MapStore 2 is Free and Open Source software, it is based on - OpenLayers 3, Leaflet and ReactJS, and is licensed under the - Simplified BSD License. +

-

Contributing

-

We welcome contributions in any form:

-
    -
  • pull requests for new features
  • -
  • pull requests for bug fixes
  • -
  • pull requests for documentation
  • -
  • funding for any combination of the above
  • -
-

For more information check this page.

+

}/>, document.getElementById("aboutContainer")); +React.render(, document.getElementById("langSelContainer")); diff --git a/web/client/examples/viewer/index.html b/web/client/examples/viewer/index.html index b4fcdccb40..b186d0a656 100644 --- a/web/client/examples/viewer/index.html +++ b/web/client/examples/viewer/index.html @@ -24,11 +24,19 @@ position: absolute; margin: 8px; } + #langSelContainer { + z-index: 10; + bottom: 0px; + left: 0px; + position: absolute; + margin: 8px; + }
+
diff --git a/web/client/stores/data.en-US b/web/client/stores/data.en-US index f3de741fb7..1352dee0ef 100644 --- a/web/client/stores/data.en-US +++ b/web/client/stores/data.en-US @@ -8,8 +8,19 @@ module.exports = { locale: "en-US", messages: { - msgId0: "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} on {takenDate, date, long}.", - htmlTest: "{name} {surname}", - aboutLbl: "About" + "msgId0": "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} on {takenDate, date, long}.", + "htmlTest": "{name} {surname}", + "about_title": "About this app...", + "aboutLbl": "About", + "about_p0-0": "MapStore 2 is a framework to build web mapping applications using standard mapping libraries, such as", + "about_p0-1": "and", + "about_p1": "MapStore 2 has several example applications:", + "about_ul0_li0": "MapViewer is a simple viewer of preconfigured maps (optionally stored in a database using GeoStore)", + "about_ul0_li1": "MapPublisher has been developed to create, save and share in a simple and intuitive way maps and mashups created selecting contents by server like OpenStreetMap, Google Maps, MapQuest or specific servers provided by your organization or third party. For more information check the", + "about_h20": "License", + "about_p3": "MapStore 2 is Free and Open Source software, it is based on OpenLayers 3, Leaflet and ReactJS, and is licensed under the Simplified BSD License.", + "about_p5-0": "For more information check", + "about_a0": "this", + "about_p5-1": "page." } }; diff --git a/web/client/stores/data.it-IT b/web/client/stores/data.it-IT index a436358f83..8c33f06a25 100644 --- a/web/client/stores/data.it-IT +++ b/web/client/stores/data.it-IT @@ -10,6 +10,17 @@ module.exports = { messages: { msgId0: "{name} ha scattato {numPhotos, plural, =0 {nessuna foto} other {# foto}} il {takenDate, date, long}.", htmlTest: "{name} {surname}", - aboutLbl: "Informazioni" + "about_title": "Riguardo a questa app...", + "aboutLbl": "Info", + "about_p0-0": "MapStore 2 un framework per realizzare applicazioni di web mapping usando librerie di mapping standard, come", + "about_p0-1": "e", + "about_p1": "MapStore 2 contiene alcune applicazioni di esempio:", + "about_ul0_li0": "MapViewer è un semplice visualizzatore di mappe preconfigurate (opzionalmente memorizzare in un database usando GeoStore)", + "about_ul0_li1": "MapPublisher è stato sviluppato per creare, salvare a condividere in modo semplice ed intuitivo mappe e mashups creati selezionando contenuti da server come OpenStreetMap, Google Maps, MapQuest o da specifici servizi forniti dalla tua organizzazione o da terze parti. Per maggiori informazioni controlla la", + "about_h20": "Licenza", + "about_p3": "MapStore 2 è un software Free ed Open Source, basato su OpenLayers 3, Leaflet e ReactJS, ed è rilasciato sotto licenza Simplified BSD License.", + "about_p5-0": "Per informazioni controlla", + "about_a0": "questa", + "about_p5-1": "pagina." } }; From c6da86cb61df8e4620d3ec05a712c493dfb3776d Mon Sep 17 00:00:00 2001 From: Mirco Bertelli Date: Mon, 24 Aug 2015 15:13:47 +0200 Subject: [PATCH 4/4] Test coverage improving Little edit for I18NStore initialization to make possible test the delault locale value. --- web/client/stores/I18NStore.jsx | 27 +++++++++++-------- .../stores/__tests__/I18NStore-test.jsx | 6 +++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/web/client/stores/I18NStore.jsx b/web/client/stores/I18NStore.jsx index 843618769c..0c0b364923 100644 --- a/web/client/stores/I18NStore.jsx +++ b/web/client/stores/I18NStore.jsx @@ -24,16 +24,12 @@ const AvailableLang = { "Italiano": "it-IT" }; -const _getDefaultLang = () => { - var lang; - if (navigator) { - lang = navigator.language || navigator.browserLanguage; - } - if (lang) { +const _getDefaultLang = locale => { + if (locale) { for (let l in AvailableLang) { if (AvailableLang.hasOwnProperty(l)) { - if (lang === AvailableLang[l]) { - return lang; + if (locale === AvailableLang[l]) { + return locale; } } } @@ -41,9 +37,15 @@ const _getDefaultLang = () => { return "en-US"; }; -var _i18nStore = { - locales: AvailableLang, - data: StaticLangStore[_getDefaultLang()] +var _i18nStore; + +const _initStore = () => { + var l = navigator ? navigator.language || navigator.browserLanguage : "en-US"; + + _i18nStore = { + locales: AvailableLang, + data: StaticLangStore[_getDefaultLang(l)] + }; }; var I18NStore = assign({}, EventEmitter.prototype, { @@ -91,6 +93,8 @@ const _actionsManager = action => { } }; +_initStore(); + Dispatcher.register(_actionsManager); module.exports = I18NStore; @@ -105,3 +109,4 @@ const _setMockedData = obj => { _i18nStore = obj; }; module.exports._set_mocked_data = _setMockedData; +module.exports._get_default_language = _getDefaultLang; diff --git a/web/client/stores/__tests__/I18NStore-test.jsx b/web/client/stores/__tests__/I18NStore-test.jsx index 157b878246..2479dcd2e5 100644 --- a/web/client/stores/__tests__/I18NStore-test.jsx +++ b/web/client/stores/__tests__/I18NStore-test.jsx @@ -21,6 +21,12 @@ describe('This test for I18NStore', () => { setTimeout(done); }); + it('checks _getDefaultLang()', () => { + expect(I18NStore._get_default_language()).toEqual("en-US"); + expect(I18NStore._get_default_language("it-IT")).toEqual("it-IT"); + expect(I18NStore._get_default_language("something")).toEqual("en-US"); + }); + it('checks getCurrentLocale()', () => { const mockStoreData = { data: {