diff --git a/.i18nrc.json b/.i18nrc.json index ead573b89d500..807b1eb3c8ae2 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -22,6 +22,7 @@ "xpack.idxMgmt": "x-pack/plugins/index_management", "xpack.infra": "x-pack/plugins/infra", "xpack.licenseMgmt": "x-pack/plugins/license_management", + "xpack.ml": "x-pack/plugins/ml", "xpack.logstash": "x-pack/plugins/logstash", "xpack.monitoring": "x-pack/plugins/monitoring", "xpack.reporting": "x-pack/plugins/reporting", diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/delete_job_modal/delete_job_modal.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/delete_job_modal/delete_job_modal.js index fd148bd37ea9a..7b80a3678dd09 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/delete_job_modal/delete_job_modal.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/delete_job_modal/delete_job_modal.js @@ -19,6 +19,7 @@ import { } from '@elastic/eui'; import { deleteJobs } from '../utils'; +import { FormattedMessage } from '@kbn/i18n/react'; export class DeleteJobModal extends Component { constructor(props) { @@ -79,10 +80,21 @@ export class DeleteJobModal extends Component { if (this.el && this.state.deleting === true) { // work around to disable the modal's buttons if the jobs are being deleted this.el.confirmButton.style.display = 'none'; - this.el.cancelButton.textContent = 'Close'; + this.el.cancelButton.textContent = (); } - const title = `Delete ${(this.state.jobs.length > 1) ? `${this.state.jobs.length} jobs` : this.state.jobs[0].id}`; + const title = ( + ); modal = ( )} + confirmButtonText={()} buttonColor="danger" defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON} className="eui-textBreakWord" > {(this.state.deleting === true) &&
- Deleting jobs +
@@ -108,10 +129,22 @@ export class DeleteJobModal extends Component { {(this.state.deleting === false) && -

Are you sure you want to delete {(this.state.jobs.length > 1) ? 'these jobs' : 'this job'}?

+

+ +

{(this.state.jobs.length > 1) && -

Deleting multiple jobs can be time consuming. - They will be deleted in the background and may not disappear from the jobs list instantly +

+

}
diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js index 734e38844bd73..51eb70098aebe 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js @@ -32,8 +32,9 @@ import { isValidCustomUrls } from '../validate_job'; import { mlMessageBarService } from '../../../../components/messagebar/messagebar_service'; import { toastNotifications } from 'ui/notify'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; -export class EditJobFlyout extends Component { +class EditJobFlyoutUI extends Component { constructor(props) { super(props); @@ -134,7 +135,10 @@ export class EditJobFlyout extends Component { if (jobDetails.jobGroups !== undefined) { if (jobDetails.jobGroups.some(j => this.props.allJobIds.includes(j))) { - jobGroupsValidationError = 'A job with this ID already exists. Groups and jobs cannot use the same ID.'; + jobGroupsValidationError = this.props.intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.groupsAndJobsHasSameIdErrorMessage', + defaultMessage: 'A job with this ID already exists. Groups and jobs cannot use the same ID.' + }); } else { jobGroupsValidationError = validateGroupNames(jobDetails.jobGroups).message; } @@ -185,13 +189,21 @@ export class EditJobFlyout extends Component { saveJob(this.state.job, newJobData) .then(() => { - toastNotifications.addSuccess(`Changes to ${this.state.job.job_id} saved`); + toastNotifications.addSuccess(this.props.intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.changesSavedNotificationMessage', + defaultMessage: 'Changes to {jobId} saved' }, { + jobId: this.state.job.job_id } + )); this.refreshJobs(); this.closeFlyout(); }) .catch((error) => { console.error(error); - toastNotifications.addDanger(`Could not save changes to ${this.state.job.job_id}`); + toastNotifications.addDanger(this.props.intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.changesNotSavedNotificationMessage', + defaultMessage: 'Could not save changes to {jobId}' }, { + jobId: this.state.job.job_id } + )); mlMessageBarService.notify.error(error); }); } @@ -219,9 +231,14 @@ export class EditJobFlyout extends Component { isValidJobCustomUrls, } = this.state; + const { intl } = this.props; + const tabs = [{ id: 'job-details', - name: 'Job details', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.jobDetailsTitle', + defaultMessage: 'Job details' + }), content: , }, { id: 'detectors', - name: 'Detectors', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.detectorsTitle', + defaultMessage: 'Detectors' + }), content: , }, { id: 'datafeed', - name: 'Datafeed', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.datafeedTitle', + defaultMessage: 'Datafeed' + }), content: , }, { id: 'custom-urls', - name: 'Custom URLs', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.customUrlsTitle', + defaultMessage: 'Custom URLs' + }), content:

- Edit {job.id} +

@@ -290,7 +320,10 @@ export class EditJobFlyout extends Component { onClick={this.closeFlyout} flush="left" > - Close + @@ -299,7 +332,10 @@ export class EditJobFlyout extends Component { fill isDisabled={(isValidJobDetails === false) || (isValidJobCustomUrls === false)} > - Save + @@ -317,9 +353,11 @@ export class EditJobFlyout extends Component { } } -EditJobFlyout.propTypes = { +EditJobFlyoutUI.propTypes = { setShowFunction: PropTypes.func.isRequired, unsetShowFunction: PropTypes.func.isRequired, refreshJobs: PropTypes.func.isRequired, allJobIds: PropTypes.array.isRequired, }; + +export const EditJobFlyout = injectI18n(EditJobFlyoutUI); diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/custom_urls.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/custom_urls.js index 1a8e36b539099..49964514a21fa 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/custom_urls.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/custom_urls.js @@ -37,10 +37,12 @@ import { loadIndexPatterns, } from '../edit_utils'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; + const MAX_NUMBER_DASHBOARDS = 1000; const MAX_NUMBER_INDEX_PATTERNS = 1000; -export class CustomUrls extends Component { +class CustomUrlsUI extends Component { constructor(props) { super(props); @@ -65,13 +67,18 @@ export class CustomUrls extends Component { } componentDidMount() { + const { intl } = this.props; + loadSavedDashboards(MAX_NUMBER_DASHBOARDS) .then((dashboards)=> { this.setState({ dashboards }); }) .catch((resp) => { console.log('Error loading list of dashboards:', resp); - toastNotifications.addDanger('An error occurred loading the list of saved Kibana dashboards'); + toastNotifications.addDanger(intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.customUrls.loadSavedDashboardsErrorNotificationMessage', + defaultMessage: 'An error occurred loading the list of saved Kibana dashboards' + })); }); loadIndexPatterns(MAX_NUMBER_INDEX_PATTERNS) @@ -80,7 +87,10 @@ export class CustomUrls extends Component { }) .catch((resp) => { console.log('Error loading list of dashboards:', resp); - toastNotifications.addDanger('An error occurred loading the list of saved index patterns'); + toastNotifications.addDanger(intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.customUrls.loadIndexPatternsErrorNotificationMessage', + defaultMessage: 'An error occurred loading the list of saved index patterns' + })); }); } @@ -111,12 +121,16 @@ export class CustomUrls extends Component { }) .catch((resp) => { console.log('Error building custom URL from settings:', resp); - toastNotifications.addDanger('An error occurred building the new custom URL from the supplied settings'); + toastNotifications.addDanger(this.props.intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.customUrls.addNewUrlErrorNotificationMessage', + defaultMessage: 'An error occurred building the new custom URL from the supplied settings' + })); }); } onTestButtonClick = () => { const job = this.props.job; + const { intl } = this.props; buildCustomUrlFromSettings(this.state.editorSettings, job) .then((customUrl) => { getTestUrl(job, customUrl) @@ -125,12 +139,18 @@ export class CustomUrls extends Component { }) .catch((resp) => { console.log('Error obtaining URL for test:', resp); - toastNotifications.addWarning('An error occurred obtaining the URL to test the configuration'); + toastNotifications.addWarning(intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.customUrls.getTestUrlErrorNotificationMessage', + defaultMessage: 'An error occurred obtaining the URL to test the configuration' + })); }); }) .catch((resp) => { console.log('Error building custom URL from settings:', resp); - toastNotifications.addWarning('An error occurred building the custom URL for testing from the supplied settings'); + toastNotifications.addWarning(intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.customUrls.buildUrlErrorNotificationMessage', + defaultMessage: 'An error occurred building the custom URL for testing from the supplied settings' + })); }); } @@ -160,7 +180,10 @@ export class CustomUrls extends Component { size="s" onClick={() => this.editNewCustomUrl()} > - Add custom URL + ) : ( @@ -170,7 +193,10 @@ export class CustomUrls extends Component { color="text" onClick={() => this.closeEditor()} iconType="cross" - aria-label="Close custom URL editor" + aria-label={this.props.intl.formatMessage({ + id: 'xpack.ml.jobsList.editJobFlyout.customUrls.closeEditorAriaLabel', + defaultMessage: 'Close custom URL editor' + })} className="close-editor-button" /> this.addNewCustomUrl()} isDisabled={!isValidEditorSettings} > - Add + @@ -198,7 +227,10 @@ export class CustomUrls extends Component { onClick={() => this.onTestButtonClick()} isDisabled={!isValidEditorSettings} > - Test + @@ -217,8 +249,10 @@ export class CustomUrls extends Component { ); } } -CustomUrls.propTypes = { +CustomUrlsUI.propTypes = { job: PropTypes.object.isRequired, jobCustomUrls: PropTypes.array.isRequired, setCustomUrls: PropTypes.func.isRequired, }; + +export const CustomUrls = injectI18n(CustomUrlsUI); diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/datafeed.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/datafeed.js index 4d1dca27389ad..c9e431dee846e 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/datafeed.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/edit_job_flyout/tabs/datafeed.js @@ -22,6 +22,7 @@ import { calculateDatafeedFrequencyDefaultSeconds } from 'plugins/ml/../common/u import { newJobDefaults } from 'plugins/ml/jobs/new_job/utils/new_job_defaults'; import { parseInterval } from 'plugins/ml/../common/util/parse_interval'; import { MLJobEditor } from '../../ml_job_editor'; +import { FormattedMessage } from '@kbn/i18n/react'; function getDefaults(bucketSpan, jobDefaults) { const bucketSpanSeconds = (bucketSpan !== undefined) ? parseInterval(bucketSpan).asSeconds() : ''; @@ -91,7 +92,10 @@ export class Datafeed extends Component { )} style={{ maxWidth: 'inherit' }} > )} > )} > )} > )} > )} isInvalid={(groupsValidationError !== '')} error={groupsValidationError} > )} isInvalid={(mmlValidationError !== '')} error={mmlValidationError} > @@ -155,9 +168,11 @@ export class JobDetails extends Component { ); } } -JobDetails.propTypes = { +JobDetailsUI.propTypes = { jobDescription: PropTypes.string.isRequired, jobGroups: PropTypes.array.isRequired, jobModelMemoryLimit: PropTypes.string.isRequired, setJobDetails: PropTypes.func.isRequired, }; + +export const JobDetails = injectI18n(JobDetailsUI); diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/management.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/management.js index 4aa47dfd177fa..7234e3f9252d9 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/management.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/management.js @@ -15,6 +15,7 @@ import { isStoppable, isClosable, } from '../utils'; +import { i18n } from '@kbn/i18n'; export function actionsMenuContent(showEditJobFlyout, showDeleteJobModal, showStartDatafeedModal, refreshJobs) { const canCreateJob = (checkPermission('canCreateJob') && mlNodesAvailable()); @@ -26,8 +27,12 @@ export function actionsMenuContent(showEditJobFlyout, showDeleteJobModal, showSt return [ { - name: 'Start datafeed', - description: 'Start datafeed', + name: i18n.translate('xpack.ml.jobsList.managementActions.startDatafeedLabel', { + defaultMessage: 'Start datafeed' + }), + description: i18n.translate('xpack.ml.jobsList.managementActions.startDatafeedDescription', { + defaultMessage: 'Start datafeed' + }), icon: 'play', enabled: () => (canStartStopDatafeed), available: (item) => (isStartable([item])), @@ -36,8 +41,12 @@ export function actionsMenuContent(showEditJobFlyout, showDeleteJobModal, showSt closeMenu(); } }, { - name: 'Stop datafeed', - description: 'Stop datafeed', + name: i18n.translate('xpack.ml.jobsList.managementActions.stopDatafeedLabel', { + defaultMessage: 'Stop datafeed' + }), + description: i18n.translate('xpack.ml.jobsList.managementActions.stopDatafeedDescription', { + defaultMessage: 'Stop datafeed' + }), icon: 'stop', enabled: () => (canStartStopDatafeed), available: (item) => (isStoppable([item])), @@ -46,8 +55,12 @@ export function actionsMenuContent(showEditJobFlyout, showDeleteJobModal, showSt closeMenu(true); } }, { - name: 'Close job', - description: 'Close job', + name: i18n.translate('xpack.ml.jobsList.managementActions.closeJobLabel', { + defaultMessage: 'Close job' + }), + description: i18n.translate('xpack.ml.jobsList.managementActions.closeJobDescription', { + defaultMessage: 'Close job' + }), icon: 'cross', enabled: () => (canCloseJob), available: (item) => (isClosable([item])), @@ -56,8 +69,12 @@ export function actionsMenuContent(showEditJobFlyout, showDeleteJobModal, showSt closeMenu(true); } }, { - name: 'Clone job', - description: 'Clone job', + name: i18n.translate('xpack.ml.jobsList.managementActions.cloneJobLabel', { + defaultMessage: 'Clone job' + }), + description: i18n.translate('xpack.ml.jobsList.managementActions.cloneJobDescription', { + defaultMessage: 'Clone job' + }), icon: 'copy', enabled: () => (canCreateJob), onClick: (item) => { @@ -65,8 +82,12 @@ export function actionsMenuContent(showEditJobFlyout, showDeleteJobModal, showSt closeMenu(true); } }, { - name: 'Edit job', - description: 'Edit job', + name: i18n.translate('xpack.ml.jobsList.managementActions.editJobLabel', { + defaultMessage: 'Edit job' + }), + description: i18n.translate('xpack.ml.jobsList.managementActions.editJobDescription', { + defaultMessage: 'Edit job' + }), icon: 'pencil', enabled: () => (canUpdateJob && canUpdateDatafeed), onClick: (item) => { @@ -74,8 +95,12 @@ export function actionsMenuContent(showEditJobFlyout, showDeleteJobModal, showSt closeMenu(); } }, { - name: 'Delete job', - description: 'Delete job', + name: i18n.translate('xpack.ml.jobsList.managementActions.deleteJobLabel', { + defaultMessage: 'Delete job' + }), + description: i18n.translate('xpack.ml.jobsList.managementActions.deleteJobDescription', { + defaultMessage: 'Delete job' + }), icon: 'trash', color: 'danger', enabled: () => (canDeleteJob), diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/results.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/results.js index 486d27e8b9dbb..054ce12f65935 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/results.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/job_actions/results.js @@ -18,6 +18,7 @@ import moment from 'moment'; const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; import { mlJobService } from 'plugins/ml/services/job_service'; +import { injectI18n } from '@kbn/i18n/react'; function getLink(location, jobs) { let from = 0; @@ -38,8 +39,19 @@ function getLink(location, jobs) { return `${chrome.getBasePath()}/app/${url}`; } -export function ResultLinks({ jobs }) { - const tooltipJobs = (jobs.length === 1) ? jobs[0].id : `${jobs.length} jobs`; +function ResultLinksUI({ jobs, intl }) { + const openJobsInSingleMetricViewerText = intl.formatMessage({ + id: 'xpack.ml.jobsList.resultActions.openJobsInSingleMetricViewerText', + defaultMessage: 'Open {jobsCount, plural, one {{jobId}} other {# jobs}} in Single Metric Viewer' }, { + jobsCount: jobs.length, + jobId: jobs[0].id + }); + const openJobsInAnomalyExplorerText = intl.formatMessage({ + id: 'xpack.ml.jobsList.resultActions.openJobsInAnomalyExplorerText', + defaultMessage: 'Open {jobsCount, plural, one {{jobId}} other {# jobs}} in Anomaly Explorer' }, { + jobsCount: jobs.length, + jobId: jobs[0].id + }); const singleMetricVisible = (jobs.length < 2); const singleMetricEnabled = (jobs.length === 1 && jobs[0].isSingleMetricViewerJob); return ( @@ -47,12 +59,12 @@ export function ResultLinks({ jobs }) { {(singleMetricVisible) && @@ -60,12 +72,12 @@ export function ResultLinks({ jobs }) { } @@ -73,7 +85,8 @@ export function ResultLinks({ jobs }) { ); } -ResultLinks.propTypes = { +ResultLinksUI.propTypes = { jobs: PropTypes.array.isRequired, }; +export const ResultLinks = injectI18n(ResultLinksUI); diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list/jobs_list.js index 0165b26095b16..5a495352bdb6f 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -22,12 +22,13 @@ import { EuiBasicTable, EuiButtonIcon, } from '@elastic/eui'; +import { injectI18n } from '@kbn/i18n/react'; const PAGE_SIZE = 10; const PAGE_SIZE_OPTIONS = [10, 25, 50]; const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; -export class JobsList extends Component { +class JobsListUI extends Component { constructor(props) { super(props); @@ -97,9 +98,13 @@ export class JobsList extends Component { } render() { + const { intl } = this.props; const selectionControls = { selectable: () => true, - selectableMessage: (selectable) => (!selectable) ? 'Cannot select job' : undefined, + selectableMessage: (selectable) => (!selectable) ? intl.formatMessage({ + id: 'xpack.ml.jobsList.cannotSelectJobTooltip', + defaultMessage: 'Cannot select job' }) + : undefined, onSelectionChange: this.props.selectJobChange }; @@ -110,13 +115,26 @@ export class JobsList extends Component { this.toggleRow(item)} iconType={this.state.itemIdToExpandedRowMap[item.id] ? 'arrowDown' : 'arrowRight'} - aria-label={`${this.state.itemIdToExpandedRowMap[item.id] ? 'Hide' : 'Show'} details for ${item.id}`} + aria-label={this.state.itemIdToExpandedRowMap[item.id] + ? intl.formatMessage({ + id: 'xpack.ml.jobsList.collapseJobDetailsAriaLabel', + defaultMessage: 'Hide details for {itemId}' }, + { itemId: item.id } + ) + : intl.formatMessage({ + id: 'xpack.ml.jobsList.expandJobDetailsAriaLabel', + defaultMessage: 'Show details for {itemId}' }, + { itemId: item.id } + )} data-row-id={item.id} /> ) }, { field: 'id', - name: 'ID', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.idLabel', + defaultMessage: 'ID' + }), sortable: true, truncateText: false, @@ -127,7 +145,10 @@ export class JobsList extends Component { ) }, { - name: 'Description', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.descriptionLabel', + defaultMessage: 'Description' + }), sortable: true, field: 'description', render: (description, item) => ( @@ -135,28 +156,43 @@ export class JobsList extends Component { ) }, { field: 'processed_record_count', - name: 'Processed records', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.processedRecordsLabel', + defaultMessage: 'Processed records' + }), sortable: true, truncateText: false, dataType: 'number', render: count => toLocaleString(count) }, { field: 'memory_status', - name: 'Memory status', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.memoryStatusLabel', + defaultMessage: 'Memory status' + }), sortable: true, truncateText: false, }, { field: 'jobState', - name: 'Job state', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.jobStateLabel', + defaultMessage: 'Job state' + }), sortable: true, truncateText: false, }, { field: 'datafeedState', - name: 'Datafeed state', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.datafeedStateLabel', + defaultMessage: 'Datafeed state' + }), sortable: true, truncateText: false, }, { - name: 'Latest timestamp', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.latestTimestampLabel', + defaultMessage: 'Latest timestamp' + }), truncateText: false, field: 'latestTimestampSortValue', sortable: true, @@ -168,7 +204,10 @@ export class JobsList extends Component { ) }, { - name: 'Actions', + name: intl.formatMessage({ + id: 'xpack.ml.jobsList.actionsLabel', + defaultMessage: 'Actions' + }), render: (item) => ( ) @@ -228,7 +267,7 @@ export class JobsList extends Component { ); } } -JobsList.propTypes = { +JobsListUI.propTypes = { jobsSummaryList: PropTypes.array.isRequired, fullJobsList: PropTypes.object.isRequired, itemIdToExpandedRowMap: PropTypes.object.isRequired, @@ -240,3 +279,5 @@ JobsList.propTypes = { refreshJobs: PropTypes.func.isRequired, selectedJobsCount: PropTypes.number.isRequired, }; + +export const JobsList = injectI18n(JobsListUI); diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js index 79cc8bf8ee861..5af623cb82449 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js @@ -9,16 +9,53 @@ import { JOB_STATE, DATAFEED_STATE } from 'plugins/ml/../common/constants/states import PropTypes from 'prop-types'; import React from 'react'; +import { i18n } from '@kbn/i18n'; function createJobStats(jobsSummaryList) { const jobStats = { - activeNodes: { label: 'Active ML Nodes', value: 0, show: true }, - total: { label: 'Total jobs', value: 0, show: true }, - open: { label: 'Open jobs', value: 0, show: true }, - closed: { label: 'Closed jobs', value: 0, show: true }, - failed: { label: 'Failed jobs', value: 0, show: false }, - activeDatafeeds: { label: 'Active datafeeds', value: 0, show: true } + activeNodes: { + label: i18n.translate('xpack.ml.jobsList.statsBar.activeMLNodesLabel', { + defaultMessage: 'Active ML Nodes' + }), + value: 0, + show: true + }, + total: { + label: i18n.translate('xpack.ml.jobsList.statsBar.totalJobsLabel', { + defaultMessage: 'Total jobs' + }), + value: 0, + show: true + }, + open: { + label: i18n.translate('xpack.ml.jobsList.statsBar.openJobsLabel', { + defaultMessage: 'Open jobs' + }), + value: 0, + show: true + }, + closed: { + label: i18n.translate('xpack.ml.jobsList.statsBar.closedJobsLabel', { + defaultMessage: 'Closed jobs' + }), + value: 0, + show: true + }, + failed: { + label: i18n.translate('xpack.ml.jobsList.statsBar.failedJobsLabel', { + defaultMessage: 'Failed jobs' + }), + value: 0, + show: false + }, + activeDatafeeds: { + label: i18n.translate('xpack.ml.jobsList.statsBar.activeDatafeedsLabel', { + defaultMessage: 'Active datafeeds' + }), + value: 0, + show: true + } }; if (jobsSummaryList === undefined) { diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/actions_menu.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/actions_menu.js index c4fbc39eba7c7..571203b5fa99e 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/actions_menu.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/actions_menu.js @@ -26,8 +26,9 @@ import { isStoppable, isClosable, } from '../utils'; +import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; -export class MultiJobActionsMenu extends Component { +class MultiJobActionsMenuUI extends Component { constructor(props) { super(props); @@ -58,13 +59,15 @@ export class MultiJobActionsMenu extends Component { size="s" onClick={this.onButtonClick} iconType="gear" - aria-label="Management actions" + aria-label={this.props.intl.formatMessage({ + id: 'xpack.ml.jobsList.multiJobActionsMenu.managementActionsAriaLabel', + defaultMessage: 'Management actions' + })} color="text" disabled={(this.canDeleteJob === false && this.canStartStopDatafeed === false)} /> ); - const s = (this.props.jobs.length > 1) ? 's' : ''; const items = [ ( { this.props.showDeleteJobModal(this.props.jobs); this.closePopover(); }} > - Delete job{s} + ) ]; @@ -86,7 +93,11 @@ export class MultiJobActionsMenu extends Component { disabled={(this.canCloseJob === false)} onClick={() => { closeJobs(this.props.jobs); this.closePopover(); }} > - Close job{s} + ); } @@ -99,7 +110,11 @@ export class MultiJobActionsMenu extends Component { disabled={(this.canStartStopDatafeed === false)} onClick={() => { stopDatafeeds(this.props.jobs, this.props.refreshJobs); this.closePopover(); }} > - Stop datafeed{s} + ); } @@ -112,7 +127,11 @@ export class MultiJobActionsMenu extends Component { disabled={(this.canStartStopDatafeed === false)} onClick={() => { this.props.showStartDatafeedModal(this.props.jobs); this.closePopover(); }} > - Start datafeed{s} + ); } @@ -132,9 +151,11 @@ export class MultiJobActionsMenu extends Component { ); } } -MultiJobActionsMenu.propTypes = { +MultiJobActionsMenuUI.propTypes = { jobs: PropTypes.array.isRequired, showStartDatafeedModal: PropTypes.func.isRequired, showDeleteJobModal: PropTypes.func.isRequired, refreshJobs: PropTypes.func.isRequired, }; + +export const MultiJobActionsMenu = injectI18n(MultiJobActionsMenuUI); diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/multi_job_actions.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/multi_job_actions.js index 4089b2b3e5881..26017bad23ea6 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/multi_job_actions.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/multi_job_actions/multi_job_actions.js @@ -13,6 +13,7 @@ import React, { import { ResultLinks } from '../job_actions'; import { MultiJobActionsMenu } from './actions_menu'; import { GroupSelector } from './group_selector'; +import { FormattedMessage } from '@kbn/i18n/react'; export class MultiJobActions extends Component { constructor(props) { @@ -22,13 +23,18 @@ export class MultiJobActions extends Component { } render() { - const s = (this.props.selectedJobs.length > 1) ? 's' : ''; const jobsSelected = (this.props.selectedJobs.length > 0); return (
{jobsSelected && - {this.props.selectedJobs.length} job{s} selected + + +
diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/new_job_button/new_job_button.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/new_job_button/new_job_button.js index df61ddf6decdb..6c56b4c8b1076 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/new_job_button/new_job_button.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/new_job_button/new_job_button.js @@ -14,6 +14,7 @@ import React from 'react'; import { EuiButton, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; function newJob() { window.location.href = `#/jobs/new_job`; @@ -29,7 +30,10 @@ export function NewJobButton() { fill iconType="plusInCircle" > - Create new job + ); } diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/node_available_warning/node_available_warning.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/node_available_warning/node_available_warning.js index 7e0d39ffd4c07..b8bf465eea576 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/node_available_warning/node_available_warning.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/node_available_warning/node_available_warning.js @@ -14,6 +14,7 @@ import { EuiLink, EuiSpacer, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; export function NodeAvailableWarning() { const isCloud = false; // placeholder for future specific cloud functionality @@ -23,18 +24,40 @@ export function NodeAvailableWarning() { return ( )} color="warning" iconType="alert" >

- There are no ML nodes available.
- You will not be able to create or run jobs. - {isCloud && - -  This can be configured in Cloud here. - - } +
+ + + + ) + }} + /> + : '', + }} + />

diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/refresh_jobs_list_button/refresh_jobs_list_button.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/refresh_jobs_list_button/refresh_jobs_list_button.js index 3aecf95d55f56..72338915cc1fc 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/refresh_jobs_list_button/refresh_jobs_list_button.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/refresh_jobs_list_button/refresh_jobs_list_button.js @@ -12,6 +12,7 @@ import PropTypes from 'prop-types'; import { EuiButtonEmpty, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; export const RefreshJobsListButton = ({ onRefreshClick, isRefreshing }) => ( @@ -19,7 +20,10 @@ export const RefreshJobsListButton = ({ onRefreshClick, isRefreshing }) => ( onClick={onRefreshClick} isLoading={isRefreshing} > - Refresh + ); diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js index f37db8d6309fc..6617f3cdc81ae 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js @@ -30,6 +30,8 @@ import { forceStartDatafeeds } from '../utils'; import { TimeRangeSelector } from './time_range_selector'; +import { FormattedMessage } from '@kbn/i18n/react'; + export class StartDatafeedModal extends Component { constructor(props) { super(props); @@ -136,7 +138,14 @@ export class StartDatafeedModal extends Component { > - Start {(startableJobs.length > 1) ? `${startableJobs.length} jobs` : startableJobs[0].id} + @@ -154,7 +163,10 @@ export class StartDatafeedModal extends Component { )} checked={createWatch} onChange={this.setCreateWatch} /> @@ -166,7 +178,10 @@ export class StartDatafeedModal extends Component { - Cancel + - Start + diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js b/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js index d3b815ac253d5..d7b2b5ddf18ea 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/components/start_datafeed_modal/time_range_selector/time_range_selector.js @@ -15,6 +15,7 @@ import { } from '@elastic/eui'; import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n/react'; const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; @@ -78,8 +79,34 @@ export class TimeRangeSelector extends Component { // the job has seen any data yet const showContinueLabels = (this.latestTimestamp.valueOf() > 0); const startLabels = (showContinueLabels === true) ? - [`Continue from ${formattedStartTime}`, 'Continue from now', 'Continue from specified time'] : - ['Start at beginning of data', 'Start from now', 'Specify start time']; + [ + (), + (), + () + ] : [ + (), + (), + () + ]; const startItems = [ { index: 0, label: startLabels[0] }, @@ -96,8 +123,19 @@ export class TimeRangeSelector extends Component { }, ]; const endItems = [ - { index: 0, label: 'No end time (Real-time search)' }, - { index: 1, label: 'Specify end time', + { + index: 0, + label: () + }, + { + index: 1, + label: (), body: (
)} items={startItems} switchState={this.state.startTab} switchFunc={this.setStartTab} /> )} items={endItems} switchState={this.state.endTab} switchFunc={this.setEndTab} diff --git a/x-pack/plugins/ml/public/jobs/jobs_list/directive.js b/x-pack/plugins/ml/public/jobs/jobs_list/directive.js index 84164c5213e70..030524baff642 100644 --- a/x-pack/plugins/ml/public/jobs/jobs_list/directive.js +++ b/x-pack/plugins/ml/public/jobs/jobs_list/directive.js @@ -34,6 +34,7 @@ uiRoutes }); import { JobsPage } from './jobs'; +import { I18nProvider } from '@kbn/i18n/react'; module.directive('jobsPage', function () { return { @@ -41,7 +42,7 @@ module.directive('jobsPage', function () { restrict: 'E', link: (scope, element) => { ReactDOM.render( - React.createElement(JobsPage), + {React.createElement(JobsPage)}, element[0] ); }