Extracted strings and fixes on localization support

Language chooser in the UI
This commit is contained in:
Tomas Bures 2018-11-18 21:31:22 +01:00
parent 9f449c0a2f
commit dc7789c17b
126 changed files with 2919 additions and 2028 deletions

View file

@ -1,24 +1,40 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {HTTPMethod} from '../../lib/axios';
import { translate, Trans } from 'react-i18next';
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../../lib/page';
import {withTranslation} from '../../lib/i18n';
import {
withForm, Form, FormSendMethod, InputField, TextArea, TableSelect, ButtonRow, Button,
Fieldset, Dropdown, AlignedRow, ACEEditor, StaticField, CheckBox
NavButton,
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../../lib/page';
import {
AlignedRow,
Button,
ButtonRow,
CheckBox,
Dropdown,
Form,
FormSendMethod,
InputField,
withForm
} from '../../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
import {DeleteModalDialog, RestActionModalDialog} from "../../lib/modals";
import interoperableErrors from '../../../../shared/interoperable-errors';
import validators from '../../../../shared/validators';
import { parseDate, parseBirthday, DateFormat } from '../../../../shared/date';
import { SubscriptionStatus } from '../../../../shared/lists';
import {getFieldTypes, getSubscriptionStatusLabels} from './helpers';
import moment from 'moment-timezone';
import {withErrorHandling} from '../../lib/error-handling';
import {RestActionModalDialog} from "../../lib/modals";
import interoperableErrors
from '../../../../shared/interoperable-errors';
import {SubscriptionStatus} from '../../../../shared/lists';
import {
getFieldTypes,
getSubscriptionStatusLabels
} from './helpers';
import moment
from 'moment-timezone';
@translate()
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@ -82,11 +98,11 @@ export default class CUD extends Component {
const emailServerValidation = state.getIn(['email', 'serverValidation']);
if (!state.getIn(['email', 'value'])) {
state.setIn(['email', 'error'], t('Email must not be empty'));
state.setIn(['email', 'error'], t('emailMustNotBeEmpty-1'));
} else if (!emailServerValidation) {
state.setIn(['email', 'error'], t('Validation is in progress...'));
state.setIn(['email', 'error'], t('validationIsInProgress'));
} else if (emailServerValidation.exists) {
state.setIn(['email', 'error'], t('Another subscription with the same email already exists.'));
state.setIn(['email', 'error'], t('anotherSubscriptionWithTheSameEmail'));
} else {
state.setIn(['email', 'error'], null);
}
@ -110,7 +126,7 @@ export default class CUD extends Component {
try {
this.disableForm();
this.setFormStatusMessage('info', t('Saving ...'));
this.setFormStatusMessage('info', t('saving'));
const submitSuccessful = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
data.status = parseInt(data.status);
@ -122,17 +138,17 @@ export default class CUD extends Component {
});
if (submitSuccessful) {
this.navigateToWithFlashMessage(`/lists/${this.props.list.id}/subscriptions`, 'success', t('Susbscription saved'));
this.navigateToWithFlashMessage(`/lists/${this.props.list.id}/subscriptions`, 'success', t('susbscriptionSaved'));
} else {
this.enableForm();
this.setFormStatusMessage('warning', t('There are errors in the form. Please fix them and submit again.'));
this.setFormStatusMessage('warning', t('thereAreErrorsInTheFormPleaseFixThemAnd'));
}
} catch (error) {
if (error instanceof interoperableErrors.DuplicitEmailError) {
this.setFormStatusMessage('danger',
<span>
<strong>{t('Your updates cannot be saved.')}</strong>{' '}
{t('It seems that another subscription with the same email has been created in the meantime. Refresh your page to start anew. Please note that your changes will be lost.')}
<strong>{t('yourUpdatesCannotBeSaved')}</strong>{' '}
{t('itSeemsThatAnotherSubscriptionWithThe')}
</span>
);
return;
@ -152,7 +168,7 @@ export default class CUD extends Component {
.map(key => ({key, label: this.subscriptionStatusLabels[key]}));
const tzOptions = [
{ key: '', label: t('Not selected') },
{ key: '', label: t('notSelected') },
...moment.tz.names().map(tz => ({ key: tz.toLowerCase(), label: tz }))
];
@ -166,33 +182,33 @@ export default class CUD extends Component {
{isEdit &&
<div>
<RestActionModalDialog
title={t('Confirm deletion')}
message={t('Are you sure you want to delete subscription for "{{email}}"?', {name: this.getFormValue('email')})}
title={t('confirmDeletion')}
message={t('areYouSureYouWantToDeleteSubscriptionFor', {email: this.getFormValue('email') || ''})}
stateOwner={this}
visible={this.props.action === 'delete'}
actionMethod={HTTPMethod.DELETE}
actionUrl={`rest/subscriptions/${this.props.list.id}/${this.props.entity.id}`}
backUrl={`/lists/${this.props.list.id}/subscriptions/${this.props.entity.id}/edit`}
successUrl={`/lists/${this.props.list.id}/subscriptions`}
actionInProgressMsg={t('Deleting subscription ...')}
actionDoneMsg={t('Subscription deleted')}/>
actionInProgressMsg={t('deletingSubscription')}
actionDoneMsg={t('subscriptionDeleted')}/>
</div>
}
<Title>{isEdit ? t('Edit Subscription') : t('Create Subscription')}</Title>
<Title>{isEdit ? t('editSubscription') : t('createSubscription')}</Title>
<Form stateOwner={this} onSubmitAsync={::this.submitHandler}>
<InputField id="email" label={t('Email')}/>
<InputField id="email" label={t('email')}/>
{customFields}
<hr />
<Dropdown id="tz" label={t('Timezone')} options={tzOptions}/>
<Dropdown id="tz" label={t('timezone')} options={tzOptions}/>
<Dropdown id="status" label={t('Subscription status')} options={statusOptions}/>
<Dropdown id="status" label={t('subscriptionStatus')} options={statusOptions}/>
<CheckBox id="is_test" text={t('Test user?')} help={t('If checked then this subscription can be used for previewing campaign messages')}/>
<CheckBox id="is_test" text={t('testUser?')} help={t('ifCheckedThenThisSubscriptionCanBeUsed')}/>
{!isEdit &&
<AlignedRow>
@ -203,8 +219,8 @@ export default class CUD extends Component {
</AlignedRow>
}
<ButtonRow>
<Button type="submit" className="btn-primary" icon="ok" label={t('Save')}/>
{isEdit && <NavButton className="btn-danger" icon="remove" label={t('Delete')} linkTo={`/lists/${this.props.list.id}/subscriptions/${this.props.entity.id}/delete`}/>}
<Button type="submit" className="btn-primary" icon="ok" label={t('save')}/>
{isEdit && <NavButton className="btn-danger" icon="remove" label={t('delete')} linkTo={`/lists/${this.props.list.id}/subscriptions/${this.props.entity.id}/delete`}/>}
</ButtonRow>
</Form>
</div>

View file

@ -2,7 +2,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { withTranslation } from '../../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton} from '../../lib/page';
import {withAsyncErrorHandler, withErrorHandling} from '../../lib/error-handling';
import { Table } from '../../lib/table';
@ -22,7 +22,7 @@ import {
tableDeleteDialogRender
} from "../../lib/modals";
@translate()
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@ -86,10 +86,10 @@ export default class List extends Component {
const segments = this.props.segments;
const columns = [
{ data: 1, title: t('ID'), render: data => <code>{data}</code> },
{ data: 2, title: t('Email') },
{ data: 3, title: t('Status'), render: (data, display, rowData) => this.subscriptionStatusLabels[data] + (rowData[5] ? ', ' + t('Blacklisted') : '') },
{ data: 4, title: t('Created'), render: data => data ? moment(data).fromNow() : '' }
{ data: 1, title: t('id'), render: data => <code>{data}</code> },
{ data: 2, title: t('email') },
{ data: 3, title: t('status'), render: (data, display, rowData) => this.subscriptionStatusLabels[data] + (rowData[5] ? ', ' + t('blacklisted') : '') },
{ data: 4, title: t('created'), render: data => data ? moment(data).fromNow() : '' }
];
let colIdx = 6;
@ -114,20 +114,20 @@ export default class List extends Component {
const actions = [];
actions.push({
label: <Icon icon="edit" title={t('Edit')}/>,
label: <Icon icon="edit" title={t('edit')}/>,
link: `/lists/${this.props.list.id}/subscriptions/${data[0]}/edit`
});
if (data[3] === SubscriptionStatus.SUBSCRIBED) {
actions.push({
label: <Icon icon="off" title={t('Unsubscribe')}/>,
label: <Icon icon="off" title={t('unsubscribe')}/>,
action: () => this.unsubscribeSubscription(data[0])
});
}
if (!data[5]) {
actions.push({
label: <Icon icon="ban-circle" title={t('Blacklist')}/>,
label: <Icon icon="ban-circle" title={t('blacklist')}/>,
action: () => this.blacklistSubscription(data[2])
});
}
@ -140,7 +140,7 @@ export default class List extends Component {
}
const segmentOptions = [
{key: '', label: t('All subscriptions')},
{key: '', label: t('allSubscriptions')},
...segments.map(x => ({ key: x.id.toString(), label: x.name}))
];
@ -154,14 +154,14 @@ export default class List extends Component {
// FIXME - presents segments in a data table as in campaign edit
return (
<div>
{tableDeleteDialogRender(this, `rest/subscriptions/${this.props.list.id}`, t('Deleting subscription ...'), t('Subscription deleted'))}
{tableDeleteDialogRender(this, `rest/subscriptions/${this.props.list.id}`, t('deletingSubscription'), t('subscriptionDeleted'))}
<Toolbar>
<a href={getPublicUrl(`subscription/${this.props.list.cid}`)}><Button label={t('Subscription Form')} className="btn-default"/></a>
<a href={getUrl(`subscriptions/export/${this.props.list.id}/`+ (this.props.segmentId || 0))}><Button label={t('Export as CSV')} className="btn-primary"/></a>
<NavButton linkTo={`/lists/${this.props.list.id}/subscriptions/create`} className="btn-primary" icon="plus" label={t('Add Subscriber')}/>
<a href={getPublicUrl(`subscription/${this.props.list.cid}`)}><Button label={t('subscriptionForm')} className="btn-default"/></a>
<a href={getUrl(`subscriptions/export/${this.props.list.id}/`+ (this.props.segmentId || 0))}><Button label={t('exportAsCsv')} className="btn-primary"/></a>
<NavButton linkTo={`/lists/${this.props.list.id}/subscriptions/create`} className="btn-primary" icon="plus" label={t('addSubscriber')}/>
</Toolbar>
<Title>{t('Subscribers')}</Title>
<Title>{t('subscribers')}</Title>
{list.description &&
<div className="well well-sm">{list.description}</div>
@ -169,7 +169,7 @@ export default class List extends Component {
<div className="well well-sm">
<Form format="inline" stateOwner={this}>
<Dropdown format="inline" className="input-sm" id="segment" label={t('Segment')} options={segmentOptions}/>
<Dropdown format="inline" className="input-sm" id="segment" label={t('segment')} options={segmentOptions}/>
</Form>
</div>

View file

@ -10,10 +10,10 @@ import 'brace/mode/json';
export function getSubscriptionStatusLabels(t) {
const subscriptionStatusLabels = {
[SubscriptionStatus.SUBSCRIBED]: t('Subscribed'),
[SubscriptionStatus.UNSUBSCRIBED]: t('Unubscribed'),
[SubscriptionStatus.BOUNCED]: t('Bounced'),
[SubscriptionStatus.COMPLAINED]: t('Complained'),
[SubscriptionStatus.SUBSCRIBED]: t('subscribed'),
[SubscriptionStatus.UNSUBSCRIBED]: t('unubscribed'),
[SubscriptionStatus.BOUNCED]: t('bounced'),
[SubscriptionStatus.COMPLAINED]: t('complained'),
};
return subscriptionStatusLabels;
@ -52,7 +52,7 @@ export function getFieldTypes(t) {
validate: (groupedField, state) => {
const value = state.getIn([getFieldColumn(groupedField), 'value']).trim();
if (value !== '' && isNaN(value)) {
state.setIn([getFieldColumn(groupedField), 'error'], t('Value must be a number'));
state.setIn([getFieldColumn(groupedField), 'error'], t('valueMustBeANumber'));
} else {
state.setIn([getFieldColumn(groupedField), 'error'], null);
}
@ -77,7 +77,7 @@ export function getFieldTypes(t) {
const value = state.getIn([getFieldColumn(groupedField), 'value']);
const date = parseDate(groupedField.settings.dateFormat, value);
if (value !== '' && !date) {
state.setIn([getFieldColumn(groupedField), 'error'], t('Date is invalid'));
state.setIn([getFieldColumn(groupedField), 'error'], t('dateIsInvalid'));
} else {
state.setIn([getFieldColumn(groupedField), 'error'], null);
}
@ -102,7 +102,7 @@ export function getFieldTypes(t) {
const value = state.getIn([getFieldColumn(groupedField), 'value']);
const date = parseBirthday(groupedField.settings.dateFormat, value);
if (value !== '' && !date) {
state.setIn([getFieldColumn(groupedField), 'error'], t('Date is invalid'));
state.setIn([getFieldColumn(groupedField), 'error'], t('dateIsInvalid'));
} else {
state.setIn([getFieldColumn(groupedField), 'error'], null);
}