Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#32 multilanguage support #54

Merged
merged 5 commits into from
Aug 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
23 changes: 23 additions & 0 deletions web/client/actions/I18NActions.jsx
Original file line number Diff line number Diff line change
@@ -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;
40 changes: 40 additions & 0 deletions web/client/actions/__tests__/I18NActions-test.jsx
Original file line number Diff line number Diff line change
@@ -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 ]);
});
});
8 changes: 8 additions & 0 deletions web/client/components/I18N/I18N.jsx
Original file line number Diff line number Diff line change
@@ -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');
49 changes: 49 additions & 0 deletions web/client/components/I18N/Message.jsx
Original file line number Diff line number Diff line change
@@ -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 <FormattedMessage locales={this.state.locales} message={this.state.msg} {...this.props.msgParams}/>;
}
});

module.exports = Message;
53 changes: 53 additions & 0 deletions web/client/components/I18N/__tests__/I18N.Message-test.jsx
Original file line number Diff line number Diff line change
@@ -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(<I18N.Message msgId={msgId}/>, 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);
});
});
49 changes: 49 additions & 0 deletions web/client/components/LangSelector/LangSelector.jsx
Original file line number Diff line number Diff line change
@@ -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(<option value={val} key={val}>{label}</option>);
}
}
return (
<Input type="select" bsSize="small" onChange={this.launchNewLangAction}>
{list}
</Input>
);
},
launchNewLangAction() {
var element = React.findDOMNode(this);
var selectNode = element.getElementsByTagName('select').item(0);
I18NActions.launch(I18NActions.CHANGE_LANG, {locale: selectNode.value});
}
});

module.exports = LangSelector;
59 changes: 59 additions & 0 deletions web/client/components/LangSelector/__tests__/LangSelector-test.jsx
Original file line number Diff line number Diff line change
@@ -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(<LangSelector/>, 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(<LangSelector/>, 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"}]);
});
});
10 changes: 10 additions & 0 deletions web/client/dispatchers/Dispatcher.jsx
Original file line number Diff line number Diff line change
@@ -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();
37 changes: 12 additions & 25 deletions web/client/examples/viewer/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -22,44 +24,29 @@ Api.getMergedConfig("../data/mapStoreConfig.json", url.parse(window.location.hre
});

React.render(<InfoButton
text="About"
title="About this app..."
text={<I18N.Message msgId="aboutLbl"/>}
title={<I18N.Message msgId="about_title"/>}
glyphicon="info-sign"
body={
<div>
<h1>MapStore 2</h1>
<p>
MapStore 2 is a framework to build web mapping applications using
standard mapping libraries, such as <a href="http://openlayers.org/">OpenLayers 3</a> and <a href="http://leafletjs.com/">Leaflet</a>.
<I18N.Message msgId="about_p0-0"/> <a href="http://openlayers.org/">OpenLayers 3</a> <I18N.Message msgId="about_p0-1"/> <a href="http://leafletjs.com/">Leaflet</a>.
</p>
<p>MapStore 2 has several example applications:</p>
<p><I18N.Message msgId="about_p1"/></p>
<ul>
<li>
MapViewer is a simple viewer of preconfigured maps (optionally
stored in a database using GeoStore)
<I18N.Message msgId="about_ul0_li0"/>
</li>
<li>
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 <a href="https://github.com/geosolutions-it/MapStore2/wiki">MapStore wiki</a>.
<I18N.Message msgId="about_ul0_li1"/> <a href="https://github.com/geosolutions-it/MapStore2/wiki">MapStore wiki</a>.
</li>
</ul>
<h2>License</h2>
<h2><I18N.Message msgId="about_h20"/></h2>
<p>
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.
<I18N.Message msgId="about_p3"/>
</p>
<h2>Contributing</h2>
<p>We welcome contributions in any form:</p>
<ul>
<li>pull requests for new features</li>
<li>pull requests for bug fixes</li>
<li>pull requests for documentation</li>
<li>funding for any combination of the above</li>
</ul>
<p>For more information check <a href="https://github.com/geosolutions-it/MapStore2/blob/master/CONTRIBUTING.md">this</a> page.</p>
<p><I18N.Message msgId="about_p5-0"/> <a href="https://github.com/geosolutions-it/MapStore2/blob/master/CONTRIBUTING.md"><I18N.Message msgId="about_a0"/></a> <I18N.Message msgId="about_p5-1"/></p>
</div>
}/>, document.getElementById("aboutContainer"));
React.render(<LangSelector/>, document.getElementById("langSelContainer"));
8 changes: 8 additions & 0 deletions web/client/examples/viewer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@
position: absolute;
margin: 8px;
}
#langSelContainer {
z-index: 10;
bottom: 0px;
left: 0px;
position: absolute;
margin: 8px;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="aboutContainer"></div>
<div id="langSelContainer"></div>
<script src="../../dist/mapstore-commons.js"></script>
<script src="../../dist/viewer.js"></script>
</body>
Expand Down
Loading