Skip to content

Commit 1e51629

Browse files
committed
add i18n functionality to view(Flux framework)
1 parent 617bf52 commit 1e51629

File tree

5 files changed

+88
-35
lines changed

5 files changed

+88
-35
lines changed

client/app/bundles/comments/components/CommentBox/CommentBox.jsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import BaseComponent from 'libs/components/BaseComponent';
22
import React, { PropTypes } from 'react';
3-
3+
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
44
import CommentForm from './CommentForm/CommentForm';
55
import CommentList, { CommentPropTypes } from './CommentList/CommentList';
66
import css from './CommentBox.scss';
7+
import { SelectLanguage } from 'libs/i18n/selectLanguage';
8+
import { defaultMessages, defaultLocale } from 'libs/i18n/default';
79

8-
export default class CommentBox extends BaseComponent {
10+
class CommentBox extends BaseComponent {
911
static propTypes = {
1012
pollInterval: PropTypes.number.isRequired,
1113
actions: PropTypes.shape({
@@ -17,6 +19,7 @@ export default class CommentBox extends BaseComponent {
1719
submitCommentError: React.PropTypes.string,
1820
$$comments: React.PropTypes.arrayOf(CommentPropTypes),
1921
}).isRequired,
22+
intl: intlShape.isRequired,
2023
};
2124

2225
componentDidMount() {
@@ -30,24 +33,28 @@ export default class CommentBox extends BaseComponent {
3033
}
3134

3235
render() {
33-
const { actions, data } = this.props;
36+
const { actions, data, intl } = this.props;
37+
const { formatMessage } = intl;
3438
const cssTransitionGroupClassNames = {
3539
enter: css.elementEnter,
3640
enterActive: css.elementEnterActive,
3741
leave: css.elementLeave,
3842
leaveActive: css.elementLeaveActive,
3943
};
44+
const locale = data.get('locale') || defaultLocale;
4045

4146
return (
4247
<div className="commentBox container">
4348
<h2>
44-
Comments {data.get('isFetching') && 'Loading...'}
49+
{formatMessage(defaultMessages.comments)}
50+
{data.get('isFetching') && formatMessage(defaultMessages.loading)}
4551
</h2>
46-
<p>
47-
<b>Text</b> supports Github Flavored Markdown.
48-
Comments older than 24 hours are deleted.<br />
49-
<b>Name</b> is preserved. <b>Text</b> is reset, between submits.
50-
</p>
52+
{ SelectLanguage(actions.setLocale, locale) }
53+
<ul>
54+
<li>{formatMessage(defaultMessages.descriptionSupportMarkdown)}</li>
55+
<li>{formatMessage(defaultMessages.descriptionDeleteRule)}</li>
56+
<li>{formatMessage(defaultMessages.descriptionSubmitRule)}</li>
57+
</ul>
5158
<CommentForm
5259
isSaving={data.get('isSaving')}
5360
error={data.get('submitCommentError')}
@@ -63,3 +70,5 @@ export default class CommentBox extends BaseComponent {
6370
);
6471
}
6572
}
73+
74+
export default injectIntl(CommentBox);

client/app/bundles/comments/components/CommentBox/CommentForm/CommentForm.jsx

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import NavItem from 'react-bootstrap/lib/NavItem';
1414
import Alert from 'react-bootstrap/lib/Alert';
1515
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
1616
import _ from 'lodash';
17-
17+
import { injectIntl, intlShape } from 'react-intl';
18+
import { defaultMessages } from 'libs/i18n/default';
1819
import BaseComponent from 'libs/components/BaseComponent';
1920

2021
import css from './CommentForm.scss';
2122

2223
const emptyComment = { author: '', text: '' };
23-
const textPlaceholder = 'Say something using markdown...';
2424

2525
function bsStyleFor(propName, error) {
2626
if (error) {
@@ -31,12 +31,13 @@ function bsStyleFor(propName, error) {
3131
return null;
3232
}
3333

34-
export default class CommentForm extends BaseComponent {
34+
class CommentForm extends BaseComponent {
3535
static propTypes = {
3636
isSaving: PropTypes.bool.isRequired,
3737
actions: PropTypes.object.isRequired,
3838
error: PropTypes.any,
3939
cssTransitionGroupClassNames: PropTypes.object.isRequired,
40+
intl: intlShape.isRequired,
4041
};
4142

4243
constructor(props, context) {
@@ -123,18 +124,19 @@ export default class CommentForm extends BaseComponent {
123124
}
124125

125126
formHorizontal() {
127+
const { formatMessage } = this.props.intl;
126128
return (
127129
<div>
128130
<hr />
129131
<Form horizontal className="commentForm form-horizontal" onSubmit={this.handleSubmit}>
130132
<FormGroup controlId="formHorizontalName">
131133
<Col componentClass={ControlLabel} sm={2}>
132-
Name
134+
{formatMessage(defaultMessages.inputNameLabel)}
133135
</Col>
134136
<Col sm={10}>
135137
<FormControl
136138
type="text"
137-
placeholder="Your Name"
139+
placeholder={formatMessage(defaultMessages.inputNamePlaceholder)}
138140
ref="horizontalAuthorNode"
139141
value={this.state.comment.author}
140142
onChange={this.handleChange}
@@ -145,13 +147,13 @@ export default class CommentForm extends BaseComponent {
145147
</FormGroup>
146148
<FormGroup controlId="formHorizontalName">
147149
<Col componentClass={ControlLabel} sm={2}>
148-
Text
150+
{formatMessage(defaultMessages.inputTextLabel)}
149151
</Col>
150152
<Col sm={10}>
151153
<FormControl
152154
type="textarea"
153155
label="Text"
154-
placeholder={textPlaceholder}
156+
placeholder={formatMessage(defaultMessages.inputTextPlaceholder)}
155157
ref="horizontalTextNode"
156158
value={this.state.comment.text}
157159
onChange={this.handleChange}
@@ -167,7 +169,9 @@ export default class CommentForm extends BaseComponent {
167169
className="btn btn-primary"
168170
disabled={this.props.isSaving}
169171
>
170-
{this.props.isSaving ? 'Saving...' : 'Post'}
172+
{this.props.isSaving
173+
? `${formatMessage(defaultMessages.inputSaving)}...`
174+
: formatMessage(defaultMessages.inputPost)}
171175
</Button>
172176
</Col>
173177
</FormGroup>
@@ -177,15 +181,16 @@ export default class CommentForm extends BaseComponent {
177181
}
178182

179183
formStacked() {
184+
const { formatMessage } = this.props.intl;
180185
return (
181186
<div>
182187
<hr />
183188
<form className="commentForm form form-stacked" onSubmit={this.handleSubmit}>
184189
<FormGroup controlId="formBasicName">
185-
<ControlLabel>Name</ControlLabel>
190+
<ControlLabel>{formatMessage(defaultMessages.inputNameLabel)}</ControlLabel>
186191
<FormControl
187192
type="text"
188-
placeholder="Your Name"
193+
placeholder={formatMessage(defaultMessages.inputNamePlaceholder)}
189194
ref="stackedAuthorNode"
190195
value={this.state.comment.author}
191196
onChange={this.handleChange}
@@ -196,11 +201,11 @@ export default class CommentForm extends BaseComponent {
196201
<FormGroup
197202
controlId="formBasicText"
198203
>
199-
<ControlLabel>Text</ControlLabel>
204+
<ControlLabel>{formatMessage(defaultMessages.inputTextLabel)}</ControlLabel>
200205
<FormControl
201206
type="textarea"
202207
label="Text"
203-
placeholder={textPlaceholder}
208+
placeholder={formatMessage(defaultMessages.inputTextPlaceholder)}
204209
ref="stackedTextNode"
205210
value={this.state.comment.text}
206211
onChange={this.handleChange}
@@ -214,7 +219,9 @@ export default class CommentForm extends BaseComponent {
214219
className="btn btn-primary"
215220
disabled={this.props.isSaving}
216221
>
217-
{this.props.isSaving ? 'Saving...' : 'Post'}
222+
{this.props.isSaving
223+
? `${formatMessage(defaultMessages.inputSaving)}...`
224+
: formatMessage(defaultMessages.inputPost)}
218225
</Button>
219226
</FormGroup>
220227
</form>
@@ -224,17 +231,18 @@ export default class CommentForm extends BaseComponent {
224231

225232
// Head up! We have some CSS modules going on here with the className props below.
226233
formInline() {
234+
const { formatMessage } = this.props.intl;
227235
return (
228236
<div>
229237
<hr />
230238
<Form inline className="commentForm" onSubmit={this.handleSubmit}>
231239
<FormGroup controlId="formInlineName" >
232240
<ControlLabel>
233-
Name
241+
{formatMessage(defaultMessages.inputNameLabel)}
234242
</ControlLabel>
235243
<FormControl
236244
type="text"
237-
placeholder="Your Name"
245+
placeholder={formatMessage(defaultMessages.inputNamePlaceholder)}
238246
ref="inlineAuthorNode"
239247
value={this.state.comment.author}
240248
onChange={this.handleChange}
@@ -245,12 +253,12 @@ export default class CommentForm extends BaseComponent {
245253
</FormGroup>
246254
<FormGroup controlId="formInlineName">
247255
<ControlLabel>
248-
Text
256+
{formatMessage(defaultMessages.inputTextLabel)}
249257
</ControlLabel>
250258
<FormControl
251259
type="textarea"
252260
label="Text"
253-
placeholder={textPlaceholder}
261+
placeholder={formatMessage(defaultMessages.inputTextPlaceholder)}
254262
ref="inlineTextNode"
255263
value={this.state.comment.text}
256264
onChange={this.handleChange}
@@ -264,7 +272,9 @@ export default class CommentForm extends BaseComponent {
264272
className="btn btn-primary"
265273
disabled={this.props.isSaving}
266274
>
267-
{this.props.isSaving ? 'Saving...' : 'Post'}
275+
{this.props.isSaving
276+
? `${formatMessage(defaultMessages.inputSaving)}...`
277+
: formatMessage(defaultMessages.inputPost)}
268278
</Button>
269279
</Form>
270280
</div>
@@ -284,7 +294,7 @@ export default class CommentForm extends BaseComponent {
284294
}, []);
285295

286296
return (
287-
<Alert bsStyle="danger" key="commentSubmissionError">
297+
<Alert bsStyle='danger' key='commentSubmissionError'>
288298
<strong>Your comment was not saved!</strong>
289299
<ul>
290300
{errorElements}
@@ -310,6 +320,7 @@ export default class CommentForm extends BaseComponent {
310320
}
311321

312322
const { cssTransitionGroupClassNames } = this.props;
323+
const { formatMessage } = this.props.intl;
313324

314325
// For animation with ReactCSSTransitionGroup
315326
// https://facebook.github.io/react/docs/animation.html
@@ -325,13 +336,15 @@ export default class CommentForm extends BaseComponent {
325336
{this.errorWarning()}
326337
</ReactCSSTransitionGroup>
327338

328-
<Nav bsStyle="pills" activeKey={this.state.formMode} onSelect={this.handleSelect}>
329-
<NavItem eventKey={0}>Horizontal Form</NavItem>
330-
<NavItem eventKey={1}>Stacked Form</NavItem>
331-
<NavItem eventKey={2}>Inline Form</NavItem>
339+
<Nav bsStyle='pills' activeKey={this.state.formMode} onSelect={this.handleSelect}>
340+
<NavItem eventKey={0}>{formatMessage(defaultMessages.formHorizontal)}</NavItem>
341+
<NavItem eventKey={1}>{formatMessage(defaultMessages.formStacked)}</NavItem>
342+
<NavItem eventKey={2}>{formatMessage(defaultMessages.formInline)}</NavItem>
332343
</Nav>
333344
{inputForm}
334345
</div>
335346
);
336347
}
337348
}
349+
350+
export default injectIntl(CommentForm);

client/app/bundles/comments/containers/NonRouterCommentsContainer.jsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import BaseComponent from 'libs/components/BaseComponent';
66

77
import CommentScreen from '../components/CommentScreen/CommentScreen';
88
import * as commentsActionCreators from '../actions/commentsActionCreators';
9+
import { IntlProvider } from 'react-intl';
10+
import { translations } from 'libs/i18n/translations';
11+
import { defaultLocale } from 'libs/i18n/default';
12+
13+
// polyfill for server-side rendering, required by react-intl
14+
import Intl from 'intl';
15+
global.Intl = Intl;
916

1017
function select(state) {
1118
// Which part of the Redux global state does our component want to receive as props?
@@ -21,8 +28,13 @@ class NonRouterCommentsContainer extends BaseComponent {
2128
render() {
2229
const { dispatch, data } = this.props;
2330
const actions = bindActionCreators(commentsActionCreators, dispatch);
31+
const locale = data.get('locale') || defaultLocale;
32+
const messages = translations[locale];
33+
2434
return (
25-
<CommentScreen {...{ actions, data }} />
35+
<IntlProvider locale={locale} key={locale} messages={messages}>
36+
<CommentScreen {...{ actions, data }} />
37+
</IntlProvider>
2638
);
2739
}
2840
}

client/app/bundles/comments/containers/RouterCommentsContainer.jsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ import { connect } from 'react-redux';
33
import { bindActionCreators } from 'redux';
44

55
import BaseComponent from 'libs/components/BaseComponent';
6-
6+
import { IntlProvider } from 'react-intl';
77
import CommentScreen from '../components/CommentScreen/CommentScreen';
88
import * as commentsActionCreators from '../actions/commentsActionCreators';
9+
import { translations } from 'libs/i18n/translations';
10+
import { defaultLocale } from 'libs/i18n/default';
11+
12+
// polyfill for server-side rendering, required by react-intl
13+
import Intl from 'intl';
14+
global.Intl = Intl;
915

1016
function select(state) {
1117
// Which part of the Redux global state does our component want to receive as props?
@@ -25,9 +31,13 @@ class RouterCommentsContainer extends BaseComponent {
2531
const { dispatch, data } = this.props;
2632
const actions = bindActionCreators(commentsActionCreators, dispatch);
2733
const locationState = this.props.location.state;
34+
const locale = data.get('locale') || defaultLocale;
35+
const messages = translations[locale];
2836

2937
return (
30-
<CommentScreen {...{ actions, data, locationState }} />
38+
<IntlProvider locale={locale} key={locale} messages={messages}>
39+
<CommentScreen {...{ actions, data, locationState }} />
40+
</IntlProvider>
3141
);
3242
}
3343
}

client/app/bundles/comments/startup/clientRegistration.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ import routerCommentsStore from '../store/routerCommentsStore';
77
import commentsStore from '../store/commentsStore';
88
import NavigationBarApp from './NavigationBarApp';
99

10+
import { addLocaleData } from 'react-intl';
11+
import en from 'react-intl/locale-data/en';
12+
import de from 'react-intl/locale-data/de';
13+
import ja from 'react-intl/locale-data/ja';
14+
import zh from 'react-intl/locale-data/zh';
15+
16+
// Initizalize all locales for react-intl.
17+
addLocaleData([...en, ...de, ...ja, ...zh]);
18+
1019
ReactOnRails.setOptions({
1120
traceTurbolinks: TRACE_TURBOLINKS, // eslint-disable-line no-undef
1221
});

0 commit comments

Comments
 (0)