Skip to content

Commit

Permalink
[i18n] Translate ML missed labels (#29256)
Browse files Browse the repository at this point in the history
* Translate missed labels in ML

* Add translation

* Resolve review comments

* Update snapshot

* Fix test

* fix unit tests

* Remove snapshot
Nox911 authored Jan 29, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 43c938b commit fc1d146
Showing 18 changed files with 202 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -217,6 +217,15 @@ function getDetailsItems(anomaly, examples, filter) {
}

export class AnomalyDetails extends Component {
static propTypes = {
anomaly: PropTypes.object.isRequired,
examples: PropTypes.array,
definition: PropTypes.object,
isAggregatedData: PropTypes.bool,
filter: PropTypes.func,
influencersLimit: PropTypes.number,
tabIndex: PropTypes.number.isRequired
};

constructor(props) {
super(props);
@@ -274,14 +283,26 @@ export class AnomalyDetails extends Component {
<Fragment>
<EuiFlexItem key={`example-terms`}>
<EuiText size="xs">
<h4 className="mlAnomalyCategoryExamples__header">Terms</h4>&nbsp;
<h4 className="mlAnomalyCategoryExamples__header">
{
i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.termsTitle', {
defaultMessage: 'Terms'
})
}
</h4>&nbsp;
<EuiIconTip
aria-label="Description"
aria-label={i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.termsDescriptionAriaLabel', {
defaultMessage: 'Description'
})}
type="questionInCircle"
color="subdued"
size="s"
content={`A space separated list of the common tokens that are matched in values of the category
(may have been truncated to a max character limit of ${MAX_CHARS})`}
content={<FormattedMessage
id="xpack.ml.anomaliesTable.anomalyDetails.termsDescriptionTooltip"
defaultMessage="A space separated list of the common tokens that are matched in values of the category
(may have been truncated to a max character limit of {maxChars})"
values={{ maxChars: MAX_CHARS }}
/>}
/>
</EuiText>
<EuiText size="xs">
@@ -294,14 +315,26 @@ export class AnomalyDetails extends Component {
<Fragment>
<EuiFlexItem key={`example-regex`}>
<EuiText size="xs">
<h4 className="mlAnomalyCategoryExamples__header">Regex</h4>&nbsp;
<h4 className="mlAnomalyCategoryExamples__header">
{
i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.regexTitle', {
defaultMessage: 'Regex'
})
}
</h4>&nbsp;
<EuiIconTip
aria-label="Description"
aria-label={i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.regexDescriptionAriaLabel', {
defaultMessage: 'Description'
})}
type="questionInCircle"
color="subdued"
size="s"
content={`The regular expression that is used to search for values that match the category
(may have been truncated to a max character limit of ${MAX_CHARS})`}
content={<FormattedMessage
id="xpack.ml.anomaliesTable.anomalyDetails.regexDescriptionTooltip"
defaultMessage="The regular expression that is used to search for values that match the category
(may have been truncated to a max character limit of {maxChars})"
values={{ maxChars: MAX_CHARS }}
/>}
/>
</EuiText>
<EuiText size="xs">
@@ -316,7 +349,13 @@ export class AnomalyDetails extends Component {
<EuiFlexItem key={`example${i}`}>
{(i === 0 && definition !== undefined) &&
<EuiText size="s">
<h4>Examples</h4>
<h4>
{
i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.examplesTitle', {
defaultMessage: 'Examples'
})
}
</h4>
</EuiText>}
<span className="mlAnomalyCategoryExamples__item">{example}</span>
</EuiFlexItem>
@@ -528,13 +567,3 @@ export class AnomalyDetails extends Component {
}
}
}

AnomalyDetails.propTypes = {
anomaly: PropTypes.object.isRequired,
examples: PropTypes.array,
definition: PropTypes.object,
isAggregatedData: PropTypes.bool,
filter: PropTypes.func,
influencersLimit: PropTypes.number,
tabIndex: PropTypes.number.isRequired
};
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@


import React from 'react';
import { shallow, mount } from 'enzyme';
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
import { AnomalyDetails } from './anomaly_details';

const props = {
@@ -68,7 +68,7 @@ const props = {
describe('AnomalyDetails', () => {

test('Renders with anomaly details tab selected by default', () => {
const wrapper = shallow(
const wrapper = shallowWithIntl(
<AnomalyDetails {...props} />
);

@@ -81,7 +81,7 @@ describe('AnomalyDetails', () => {
...props,
tabIndex: 1
};
const wrapper = shallow(
const wrapper = shallowWithIntl(
<AnomalyDetails {...categoryTabProps} />
);
expect(wrapper.prop('initialSelectedTab').id).toBe('Category examples');
@@ -97,7 +97,7 @@ describe('AnomalyDetails', () => {
}
};

const wrapper = mount(
const wrapper = mountWithIntl(
<AnomalyDetails {...categoryTabProps} />
);

@@ -113,7 +113,7 @@ describe('AnomalyDetails', () => {
definition: undefined
};

const wrapper = mount(
const wrapper = mountWithIntl(
<AnomalyDetails {...categoryTabProps} />
);

@@ -131,7 +131,7 @@ describe('AnomalyDetails', () => {
}
};

const wrapper = mount(
const wrapper = mountWithIntl(
<AnomalyDetails {...categoryTabProps} />
);

@@ -149,7 +149,7 @@ describe('AnomalyDetails', () => {
}
};

const wrapper = mount(
const wrapper = mountWithIntl(
<AnomalyDetails {...categoryTabProps} />
);

Original file line number Diff line number Diff line change
@@ -140,7 +140,6 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component {
};

viewSeries = () => {
const { intl } = this.props;
const record = this.props.anomaly.source;
const bounds = this.props.timefilter.getActiveBounds();
const from = bounds.min.toISOString(); // e.g. 2016-02-08T16:00:00.000Z
@@ -175,10 +174,7 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component {
jobIds: [record.job_id]
},
refreshInterval: {
display: intl.formatMessage({
id: 'xpack.ml.anomaliesTable.linksMenu.offLabel',
defaultMessage: 'Off'
}),
display: 'Off',
pause: false,
value: 0
},
@@ -297,10 +293,7 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component {
// Use rison to build the URL .
const _g = rison.encode({
refreshInterval: {
display: intl.formatMessage({
id: 'xpack.ml.anomaliesTable.linksMenu.offLabel',
defaultMessage: 'Off'
}),
display: 'Off',
pause: false,
value: 0
},
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import { ml } from '../../../../services/ml_api_service';
import { chunk } from 'lodash';
import moment from 'moment';
import { i18n } from '@kbn/i18n';

const CHUNK_SIZE = 10000;
const IMPORT_RETRIES = 5;
@@ -51,7 +52,9 @@ export class Importer {
if (!id || !index) {
return {
success: false,
error: 'no ID or index supplied'
error: i18n.translate('xpack.ml.fileDatavisualizer.importView.noIdOrIndexSuppliedErrorMessage', {
defaultMessage: 'no ID or index supplied'
})
};
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<div class="job-timepicker-modal">
<ml-message-bar ></ml-message-bar>
<h1
tooltip="Start datafeed for {{jobId}}"
tooltip="{{ ::'xpack.ml.jobTimePickerModal.startDatafeedForJobTooltip' | i18n: {
defaultMessage: 'Start datafeed for {jobId}',
values: { jobId }
} }}"
class="euiTitle"
i18n-id="xpack.ml.jobTimePickerModal.startDatafeedForJobTitle"
i18n-default-message="Start datafeed for {jobId}"
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ import { mlJobService } from 'plugins/ml/services/job_service';
import { checkPermission } from 'plugins/ml/privilege/check_privilege';
import { ML_DATA_PREVIEW_COUNT } from 'plugins/ml/../common/util/job_utils';
import { MLJobEditor } from '../ml_job_editor';
import { FormattedMessage } from '@kbn/i18n/react';

export class DatafeedPreviewPane extends Component {

@@ -40,12 +41,18 @@ export class DatafeedPreviewPane extends Component {
if (canPreviewDatafeed === false) {
return (
<EuiCallOut
title="You do not have permission to view the datafeed preview"
title={<FormattedMessage
id="xpack.ml.jobsList.jobDetails.noPermissionToViewDatafeedPreviewTitle"
defaultMessage="You do not have permission to view the datafeed preview"
/>}
color="warning"
iconType="alert"
>
<p>
Please contact your administrator
<FormattedMessage
id="xpack.ml.jobsList.jobDetails.pleaseContactYourAdministratorLabel"
defaultMessage="Please contact your administrator"
/>
</p>
</EuiCallOut>);
} else if (loading === true) {
Original file line number Diff line number Diff line change
@@ -255,7 +255,16 @@ class JobsListUI extends Component {
return (
<EuiBasicTable
loading={loading === true}
noItemsMessage={loading ? 'Loading jobs...' : 'No jobs found'}
noItemsMessage={loading ?
intl.formatMessage({
id: 'xpack.ml.jobsList.loadingJobsLabel',
defaultMessage: 'Loading jobs…'
}) :
intl.formatMessage({
id: 'xpack.ml.jobsList.noJobsFoundLabel',
defaultMessage: 'No jobs found'
})
}
itemId="id"
className={`jobs-list-table ${selectedJobsClass}`}
items={pageOfItems}
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ import { ml } from '../../../../../services/ml_api_service';
import { GroupList } from './group_list';
import { NewGroupInput } from './new_group_input';
import { mlMessageBarService } from '../../../../../components/messagebar/messagebar_service';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';

function createSelectedGroups(jobs, groups) {
const jobIds = jobs.map(j => j.id);
@@ -54,7 +55,13 @@ function createSelectedGroups(jobs, groups) {
return selectedGroups;
}

export class GroupSelector extends Component {
export const GroupSelector = injectI18n(class GroupSelector extends Component {
static propTypes = {
jobs: PropTypes.array.isRequired,
allJobIds: PropTypes.array.isRequired,
refreshJobs: PropTypes.func.isRequired,
};

constructor(props) {
super(props);

@@ -191,6 +198,7 @@ export class GroupSelector extends Component {
}

render() {
const { intl } = this.props;
const {
groups,
selectedGroups,
@@ -199,17 +207,22 @@ export class GroupSelector extends Component {
const button = (
<EuiToolTip
position="bottom"
content={`Edit job groups`}
content={<FormattedMessage
id="xpack.ml.jobsList.multiJobActions.groupSelector.editJobGroupsButtonTooltip"
defaultMessage="Edit job groups"
/>}
>
<EuiButtonIcon
iconType="indexEdit"
aria-label="Edit job groups"
aria-label={intl.formatMessage({
id: 'xpack.ml.jobsList.multiJobActions.groupSelector.editJobGroupsButtonAriaLabel',
defaultMessage: 'Edit job groups'
})}
onClick={() => this.togglePopover()}
disabled={this.canUpdateJob === false}
/>
</EuiToolTip>
);
const s = (this.props.jobs.length > 1 ? 's' : '');

return (
<EuiPopover
@@ -220,7 +233,13 @@ export class GroupSelector extends Component {
closePopover={() => this.closePopover()}
>
<div className="group-selector">
<EuiPopoverTitle>Apply groups to job{s}</EuiPopoverTitle>
<EuiPopoverTitle>
<FormattedMessage
id="xpack.ml.jobsList.multiJobActions.groupSelector.applyGroupsToJobTitle"
defaultMessage="Apply groups to {jobsCount, plural, one {job} other {jobs}}"
values={{ jobsCount: this.props.jobs.length }}
/>
</EuiPopoverTitle>

<GroupList
groups={groups}
@@ -245,7 +264,10 @@ export class GroupSelector extends Component {
onClick={this.applyChanges}
isDisabled={(edited === false)}
>
Apply
<FormattedMessage
id="xpack.ml.jobsList.multiJobActions.groupSelector.applyButtonLabel"
defaultMessage="Apply"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
@@ -254,9 +276,4 @@ export class GroupSelector extends Component {
</EuiPopover>
);
}
}
GroupSelector.propTypes = {
jobs: PropTypes.array.isRequired,
allJobIds: PropTypes.array.isRequired,
refreshJobs: PropTypes.func.isRequired,
};
});
Original file line number Diff line number Diff line change
@@ -19,9 +19,16 @@ import {
keyCodes,
} from '@elastic/eui';

import { injectI18n } from '@kbn/i18n/react';

import { validateGroupNames } from '../../../validate_job';

export class NewGroupInput extends Component {
export const NewGroupInput = injectI18n(class NewGroupInput extends Component {
static propTypes = {
addNewGroup: PropTypes.func.isRequired,
allJobIds: PropTypes.array.isRequired,
};

constructor(props) {
super(props);

@@ -38,7 +45,10 @@ export class NewGroupInput extends Component {
if (tempNewGroupName === '') {
groupsValidationError = '';
} else if (this.props.allJobIds.includes(tempNewGroupName)) {
groupsValidationError = 'A job with this ID already exists. Groups and jobs cannot use the same ID.';
groupsValidationError = this.props.intl.formatMessage({
id: 'xpack.ml.jobsList.multiJobActions.groupSelector.groupsAndJobsCanNotUseSameIdErrorMessage',
defaultMessage: 'A job with this ID already exists. Groups and jobs cannot use the same ID.'
});
} else {
groupsValidationError = validateGroupNames([tempNewGroupName]).message;
}
@@ -65,6 +75,7 @@ export class NewGroupInput extends Component {
}

render() {
const { intl } = this.props;
const {
tempNewGroupName,
groupsValidationError,
@@ -82,7 +93,10 @@ export class NewGroupInput extends Component {
>
<EuiFieldText
compressed
placeholder="Add new group"
placeholder={intl.formatMessage({
id: 'xpack.ml.jobsList.multiJobActions.groupSelector.addNewGroupPlaceholder',
defaultMessage: 'Add new group'
})}
value={tempNewGroupName}
onChange={this.changeTempNewGroup}
onKeyDown={this.newGroupKeyPress}
@@ -96,7 +110,10 @@ export class NewGroupInput extends Component {
<EuiButtonIcon
onClick={this.addNewGroup}
iconType="plusInCircle"
aria-label="Add"
aria-label={intl.formatMessage({
id: 'xpack.ml.jobsList.multiJobActions.groupSelector.addButtonAriaLabel',
defaultMessage: 'Add'
})}
disabled={(tempNewGroupName === '' || groupsValidationError !== '')}
/>
</EuiFormRow>
@@ -105,9 +122,4 @@ export class NewGroupInput extends Component {
</div>
);
}
}

NewGroupInput.propTypes = {
addNewGroup: PropTypes.func.isRequired,
allJobIds: PropTypes.array.isRequired,
};
});
Original file line number Diff line number Diff line change
@@ -39,7 +39,9 @@ module.directive('mlBucketSpanEstimator', function (i18n) {
const errorHandler = (error) => {
console.log('Bucket span could not be estimated', error);
$scope.ui.bucketSpanEstimator.status = STATUS.FAILED;
$scope.ui.bucketSpanEstimator.message = 'Bucket span could not be estimated';
$scope.ui.bucketSpanEstimator.message = i18n('xpack.ml.newJob.simple.bucketSpanEstimator.bucketSpanCouldNotBeEstimatedMessage', {
defaultMessage: 'Bucket span could not be estimated'
});
$scope.$applyAsync();
};

Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
on-select="selectField()"
ng-disabled="formConfig.overField === undefined || jobState === JOB_STATE.RUNNING || jobState === JOB_STATE.STOPPING || jobState === JOB_STATE.FINISHED"
append-to-body=true>
<ui-select-match placeholder="Add field">
<ui-select-match placeholder="{{ ::'xpack.ml.newJob.simple.fieldsSelectionPopulation.addFieldPlaceholder' | i18n: {defaultMessage: 'Add field'} }}">
<ml-field-type-icon type="$select.selected.mlType"></ml-field-type-icon>{{$select.selected.name}}
</ui-select-match>
<ui-select-choices repeat="field in ui.fields | filter: { name: $select.search }" group-by="'mlType'">
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
ng-change="changeJobIDCase(formConfig)"
ng-disabled="jobState === JOB_STATE.RUNNING || jobState === JOB_STATE.STOPPING || jobState === JOB_STATE.FINISHED"
class="form-control lowercase" />
<div ng-hide="ui.validation.checks.jobId.valid" class="validation-error">{{ ( ui.validation.checks.jobId.message || "Enter a name for the job" ) }}</div>
<div ng-hide="ui.validation.checks.jobId.valid" class="validation-error">{{ ( ui.validation.checks.jobId.message || enterNameForJobLabel ) }}</div>
</div>

<div class="form-group">
Original file line number Diff line number Diff line change
@@ -26,6 +26,9 @@ module.directive('mlGeneralJobDetails', function () {
$scope.showAdvancedButtonAriaLabel = i18n('xpack.ml.newJob.simple.generalJobDetails.showAdvancedButtonAriaLabel', {
defaultMessage: 'Show Advanced'
});
$scope.enterNameForJobLabel = i18n('xpack.ml.newJob.simple.generalJobDetails.enterNameForJobLabel', {
defaultMessage: 'Enter a name for the job'
});
}
};
});
Original file line number Diff line number Diff line change
@@ -22,7 +22,11 @@ exports[`Settings Renders settings page 1`] = `
textTransform="none"
>
<h2>
Job Management
<FormattedMessage
defaultMessage="Job Management"
id="xpack.ml.settings.jobManagementTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
@@ -48,7 +52,11 @@ exports[`Settings Renders settings page 1`] = `
size="l"
type="button"
>
Calendar management
<FormattedMessage
defaultMessage="Calendar management"
id="xpack.ml.settings.calendarManagementButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
@@ -64,7 +72,11 @@ exports[`Settings Renders settings page 1`] = `
size="l"
type="button"
>
Filter Lists
<FormattedMessage
defaultMessage="Filter Lists"
id="xpack.ml.settings.filterListsButtonLabel"
values={Object {}}
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
25 changes: 19 additions & 6 deletions x-pack/plugins/ml/public/settings/breadcrumbs.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@


import { ML_BREADCRUMB } from '../breadcrumbs';
import { i18n } from '@kbn/i18n';


export function getSettingsBreadcrumbs() {
@@ -20,7 +21,9 @@ export function getCalendarManagementBreadcrumbs() {
return [
...getSettingsBreadcrumbs(),
{
text: 'Calendar management',
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagementLabel', {
defaultMessage: 'Calendar management'
}),
href: '#/settings/calendars_list'
}
];
@@ -30,7 +33,9 @@ export function getCreateCalendarBreadcrumbs() {
return [
...getCalendarManagementBreadcrumbs(),
{
text: 'Create',
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagement.createLabel', {
defaultMessage: 'Create'
}),
href: '#/settings/calendars_list/new_calendar'
}
];
@@ -40,7 +45,9 @@ export function getEditCalendarBreadcrumbs() {
return [
...getCalendarManagementBreadcrumbs(),
{
text: 'Edit',
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagement.editLabel', {
defaultMessage: 'Edit'
}),
href: '#/settings/calendars_list/edit_calendar'
}
];
@@ -50,7 +57,9 @@ export function getFilterListsBreadcrumbs() {
return [
...getSettingsBreadcrumbs(),
{
text: 'Filter lists',
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterListsLabel', {
defaultMessage: 'Filter lists'
}),
href: '#/settings/filter_lists'
}
];
@@ -60,7 +69,9 @@ export function getCreateFilterListBreadcrumbs() {
return [
...getFilterListsBreadcrumbs(),
{
text: 'Create',
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterLists.createLabel', {
defaultMessage: 'Create'
}),
href: '#/settings/filter_lists/new'
}
];
@@ -70,7 +81,9 @@ export function getEditFilterListBreadcrumbs() {
return [
...getFilterListsBreadcrumbs(),
{
text: 'Edit',
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterLists.editLabel', {
defaultMessage: 'Edit'
}),
href: '#/settings/filter_lists/edit'
}
];
18 changes: 15 additions & 3 deletions x-pack/plugins/ml/public/settings/settings.js
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ import {

import chrome from 'ui/chrome';

import { FormattedMessage } from '@kbn/i18n/react';

export function Settings({
canGetFilters,
@@ -36,7 +37,12 @@ export function Settings({
>
<EuiPageContentHeader>
<EuiTitle>
<h2>Job Management</h2>
<h2>
<FormattedMessage
id="xpack.ml.settings.jobManagementTitle"
defaultMessage="Job Management"
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>

@@ -49,7 +55,10 @@ export function Settings({
href={`${chrome.getBasePath()}/app/ml#/settings/calendars_list`}
isDisabled={canGetCalendars === false}
>
Calendar management
<FormattedMessage
id="xpack.ml.settings.calendarManagementButtonLabel"
defaultMessage="Calendar management"
/>
</EuiButtonEmpty>
</EuiFlexItem>

@@ -61,7 +70,10 @@ export function Settings({
href={`${chrome.getBasePath()}/app/ml#/settings/filter_lists`}
isDisabled={canGetFilters === false}
>
Filter Lists
<FormattedMessage
id="xpack.ml.settings.filterListsButtonLabel"
defaultMessage="Filter Lists"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
13 changes: 9 additions & 4 deletions x-pack/plugins/ml/public/settings/settings_directive.js
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import { getSettingsBreadcrumbs } from './breadcrumbs';

import uiRoutes from 'ui/routes';
import { timefilter } from 'ui/timefilter';
import { I18nProvider } from '@kbn/i18n/react';

const template = `
<ml-nav-menu name="settings" />
@@ -55,10 +56,14 @@ module.directive('mlSettings', function () {
scope: {},
link: function (scope, element) {
ReactDOM.render(
React.createElement(Settings, {
canGetFilters,
canGetCalendars
}),
<I18nProvider>
{React.createElement(
Settings, {
canGetFilters,
canGetCalendars
})
}
</I18nProvider>,
element[0]
);
}
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@
</div>

<ml-loading-indicator
label="Loading"
label="{{ ::'xpack.ml.timeSeriesExplorer.loadingLabel' | i18n: {defaultMessage: 'Loading'} }}"
is-loading="loading === true"
/>

0 comments on commit fc1d146

Please sign in to comment.