[i18n] Translate inspector views (#29044)

* Translate inspector_views

* Add I18nProvider

* Replace intl.formatMessage to i18n.translate

* Remove redundant vars

* Update snapshot

* Resolve review comments

* Fix error in  inspector.tsx

* Fix test and update snapshot

* Resolve review comments

* Replace injecti18n to i18n.translate
This commit is contained in:
Nox911 2019-01-25 16:19:22 +03:00 committed by GitHub
parent 06a5ce7165
commit 50ec75f800
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 432 additions and 59 deletions

View file

@ -4,6 +4,7 @@
"server": "src/server",
"console": "src/legacy/core_plugins/console",
"inputControl": "src/legacy/core_plugins/input_control_vis",
"inspectorViews": "src/legacy/core_plugins/inspector_views",
"interpreter": "src/legacy/core_plugins/interpreter",
"kbn": "src/legacy/core_plugins/kibana",
"kbnVislibVisTypes": "src/legacy/core_plugins/kbn_vislib_vis_types",

View file

@ -15,6 +15,108 @@ exports[`Inspector Data View component should render empty state 1`] = `
},
}
}
intl={
Object {
"defaultFormats": Object {},
"defaultLocale": "en",
"formatDate": [Function],
"formatHTMLMessage": [Function],
"formatMessage": [Function],
"formatNumber": [Function],
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {
"date": Object {
"full": Object {
"day": "numeric",
"month": "long",
"weekday": "long",
"year": "numeric",
},
"long": Object {
"day": "numeric",
"month": "long",
"year": "numeric",
},
"medium": Object {
"day": "numeric",
"month": "short",
"year": "numeric",
},
"short": Object {
"day": "numeric",
"month": "numeric",
"year": "2-digit",
},
},
"number": Object {
"currency": Object {
"style": "currency",
},
"percent": Object {
"style": "percent",
},
},
"relative": Object {
"days": Object {
"units": "day",
},
"hours": Object {
"units": "hour",
},
"minutes": Object {
"units": "minute",
},
"months": Object {
"units": "month",
},
"seconds": Object {
"units": "second",
},
"years": Object {
"units": "year",
},
},
"time": Object {
"full": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"long": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"medium": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
},
"short": Object {
"hour": "numeric",
"minute": "numeric",
},
},
},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],
"getNumberFormat": [Function],
"getPluralFormat": [Function],
"getRelativeFormat": [Function],
},
"locale": "en",
"messages": Object {},
"now": [Function],
"onError": [Function],
"textComponent": Symbol(react.fragment),
"timeZone": null,
}
}
title="Test Data"
>
<InspectorView
@ -30,14 +132,22 @@ exports[`Inspector Data View component should render empty state 1`] = `
body={
<React.Fragment>
<p>
The element did not provide any data.
<FormattedMessage
defaultMessage="The element did not provide any data."
id="inspectorViews.data.noDataAvailableDescription"
values={Object {}}
/>
</p>
</React.Fragment>
}
iconColor="subdued"
title={
<h2>
No data available
<FormattedMessage
defaultMessage="No data available"
id="inspectorViews.data.noDataAvailableTitle"
values={Object {}}
/>
</h2>
}
>
@ -58,7 +168,13 @@ exports[`Inspector Data View component should render empty state 1`] = `
<h2
className="euiTitle euiTitle--medium"
>
No data available
<FormattedMessage
defaultMessage="No data available"
id="inspectorViews.data.noDataAvailableTitle"
values={Object {}}
>
No data available
</FormattedMessage>
</h2>
</EuiTitle>
<EuiSpacer
@ -76,7 +192,13 @@ exports[`Inspector Data View component should render empty state 1`] = `
className="euiText euiText--medium"
>
<p>
The element did not provide any data.
<FormattedMessage
defaultMessage="The element did not provide any data."
id="inspectorViews.data.noDataAvailableDescription"
values={Object {}}
>
The element did not provide any data.
</FormattedMessage>
</p>
</div>
</EuiText>
@ -103,6 +225,108 @@ exports[`Inspector Data View component should render loading state 1`] = `
},
}
}
intl={
Object {
"defaultFormats": Object {},
"defaultLocale": "en",
"formatDate": [Function],
"formatHTMLMessage": [Function],
"formatMessage": [Function],
"formatNumber": [Function],
"formatPlural": [Function],
"formatRelative": [Function],
"formatTime": [Function],
"formats": Object {
"date": Object {
"full": Object {
"day": "numeric",
"month": "long",
"weekday": "long",
"year": "numeric",
},
"long": Object {
"day": "numeric",
"month": "long",
"year": "numeric",
},
"medium": Object {
"day": "numeric",
"month": "short",
"year": "numeric",
},
"short": Object {
"day": "numeric",
"month": "numeric",
"year": "2-digit",
},
},
"number": Object {
"currency": Object {
"style": "currency",
},
"percent": Object {
"style": "percent",
},
},
"relative": Object {
"days": Object {
"units": "day",
},
"hours": Object {
"units": "hour",
},
"minutes": Object {
"units": "minute",
},
"months": Object {
"units": "month",
},
"seconds": Object {
"units": "second",
},
"years": Object {
"units": "year",
},
},
"time": Object {
"full": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"long": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
"timeZoneName": "short",
},
"medium": Object {
"hour": "numeric",
"minute": "numeric",
"second": "numeric",
},
"short": Object {
"hour": "numeric",
"minute": "numeric",
},
},
},
"formatters": Object {
"getDateTimeFormat": [Function],
"getMessageFormat": [Function],
"getNumberFormat": [Function],
"getPluralFormat": [Function],
"getRelativeFormat": [Function],
},
"locale": "en",
"messages": Object {},
"now": [Function],
"onError": [Function],
"textComponent": Symbol(react.fragment),
"timeZone": null,
}
}
title="Test Data"
>
<InspectorView
@ -178,7 +402,13 @@ exports[`Inspector Data View component should render loading state 1`] = `
className="euiText euiText--medium"
>
<p>
Gathering data
<FormattedMessage
defaultMessage="Gathering data"
id="inspectorViews.data.gatheringDataLabel"
values={Object {}}
>
Gathering data
</FormattedMessage>
</p>
</div>
</EuiText>

View file

@ -29,10 +29,12 @@ import {
EuiToolTip,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { DataDownloadOptions } from './download_options';
class DataTableFormat extends Component {
state = { };
static renderCell(col, value, isFormatted) {
@ -54,12 +56,17 @@ class DataTableFormat extends Component {
{ col.filter &&
<EuiToolTip
position="bottom"
content="Filter for value"
content={<FormattedMessage
id="inspectorViews.data.filterForValueButtonTooltip"
defaultMessage="Filter for value"
/>}
>
<EuiButtonIcon
iconType="plusInCircle"
color="text"
aria-label="Filter for value"
aria-label={i18n.translate('inspectorViews.data.filterForValueButtonAriaLabel', {
defaultMessage: 'Filter for value'
})}
data-test-subj="filterForInspectorCellValue"
className="insDataTableFormat__filter"
onClick={() => col.filter(value)}
@ -70,12 +77,17 @@ class DataTableFormat extends Component {
<EuiFlexItem grow={false}>
<EuiToolTip
position="bottom"
content="Filter out value"
content={<FormattedMessage
id="inspectorViews.data.filterOutValueButtonTooltip"
defaultMessage="Filter out value"
/>}
>
<EuiButtonIcon
iconType="minusInCircle"
color="text"
aria-label="Filter out value"
aria-label={i18n.translate('inspectorViews.data.filterOutValueButtonAriaLabel', {
defaultMessage: 'Filter out value'
})}
data-test-subj="filterOutInspectorCellValue"
className="insDataTableFormat__filter"
onClick={() => col.filterOut(value)}

View file

@ -35,6 +35,9 @@ import {
DataTableFormat,
} from './data_table';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
class DataViewComponent extends Component {
_isMounted = false;
@ -101,10 +104,22 @@ class DataViewComponent extends Component {
return (
<InspectorView useFlex={true}>
<EuiEmptyPrompt
title={<h2>No data available</h2>}
title={
<h2>
<FormattedMessage
id="inspectorViews.data.noDataAvailableTitle"
defaultMessage="No data available"
/>
</h2>
}
body={
<React.Fragment>
<p>The element did not provide any data.</p>
<p>
<FormattedMessage
id="inspectorViews.data.noDataAvailableDescription"
defaultMessage="The element did not provide any data."
/>
</p>
</React.Fragment>
}
/>
@ -124,7 +139,12 @@ class DataViewComponent extends Component {
<EuiLoadingChart size="m" />
<EuiSpacer size="s" />
<EuiText>
<p>Gathering data</p>
<p>
<FormattedMessage
id="inspectorViews.data.gatheringDataLabel"
defaultMessage="Gathering data"
/>
</p>
</EuiText>
</EuiPanel>
</EuiFlexItem>
@ -153,9 +173,13 @@ class DataViewComponent extends Component {
}
const DataView = {
title: 'Data',
title: i18n.translate('inspectorViews.data.dataTitle', {
defaultMessage: 'Data'
}),
order: 10,
help: `View the data behind the visualization`,
help: i18n.translate('inspectorViews.data.dataDescriptionTooltip', {
defaultMessage: 'View the data behind the visualization'
}),
shouldShow(adapters) {
return Boolean(adapters.data);
},

View file

@ -20,7 +20,7 @@
import React from 'react';
import { DataView } from './data_view';
import { DataAdapter } from 'ui/inspector/adapters';
import { mount } from 'enzyme';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
jest.mock('./lib/export_csv', () => ({
exportAsCsv: jest.fn(),
@ -43,7 +43,7 @@ describe('Inspector Data View', () => {
});
it('should render loading state', () => {
const component = mount(
const component = mountWithIntl(
<DataView.component
title="Test Data"
adapters={adapters}
@ -54,7 +54,7 @@ describe('Inspector Data View', () => {
});
it('should render empty state', async () => {
const component = mount(
const component = mountWithIntl(
<DataView.component
title="Test Data"
adapters={adapters}

View file

@ -27,6 +27,7 @@ import {
} from '@elastic/eui';
import { exportAsCsv } from './lib/export_csv';
import { FormattedMessage } from '@kbn/i18n/react';
class DataDownloadOptions extends Component {
@ -64,7 +65,10 @@ class DataDownloadOptions extends Component {
size="s"
onClick={this.exportCsv}
>
Download CSV
<FormattedMessage
id="inspectorViews.data.downloadCSVButtonLabel"
defaultMessage="Download CSV"
/>
</EuiButton>
);
}
@ -77,25 +81,40 @@ class DataDownloadOptions extends Component {
size="s"
onClick={this.onTogglePopover}
>
Download CSV
<FormattedMessage
id="inspectorViews.data.downloadCSVToggleButtonLabel"
defaultMessage="Download CSV"
/>
</EuiButton>
);
const items = [
<EuiContextMenuItem
key="csv"
onClick={this.exportFormattedCsv}
toolTipContent="Download the data in table format"
toolTipContent={<FormattedMessage
id="inspectorViews.data.formattedCSVButtonTooltip"
defaultMessage="Download the data in table format"
/>}
toolTipPosition="left"
>
Formatted CSV
<FormattedMessage
id="inspectorViews.data.formattedCSVButtonLabel"
defaultMessage="Formatted CSV"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
key="rawCsv"
onClick={this.exportFormattedAsRawCsv}
toolTipContent={`Download the data as provided, for example, dates as timestamps`}
toolTipContent={<FormattedMessage
id="inspectorViews.data.rawCSVButtonTooltip"
defaultMessage="Download the data as provided, for example, dates as timestamps"
/>}
toolTipPosition="left"
>
Raw CSV
<FormattedMessage
id="inspectorViews.data.rawCSVButtonLabel"
defaultMessage="Raw CSV"
/>
</EuiContextMenuItem>
];

View file

@ -27,9 +27,9 @@ import {
EuiTableRow,
EuiTableRowCell,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
class RequestDetailsStats extends Component {
static shouldShow = (request) => !!request.stats && Object.keys(request.stats).length;
renderStatRow = (stat) => {
@ -41,7 +41,9 @@ class RequestDetailsStats extends Component {
<span className="insRequestDetailsStats__icon">
{ stat.description &&
<EuiIconTip
aria-label="Description"
aria-label={i18n.translate('inspectorViews.requests.descriptionRowIconAriaLabel', {
defaultMessage: 'Description'
})}
type="questionInCircle"
color="subdued"
content={stat.description}

View file

@ -29,11 +29,30 @@ import {
RequestDetailsResponse,
RequestDetailsStats,
} from './details';
import { i18n } from '@kbn/i18n';
const DETAILS = [
{ name: 'Statistics', component: RequestDetailsStats },
{ name: 'Request', component: RequestDetailsRequest },
{ name: 'Response', component: RequestDetailsResponse },
{
name: 'Statistics',
label: i18n.translate('inspectorViews.requests.statisticsTabLabel', {
defaultMessage: 'Statistics'
}),
component: RequestDetailsStats
},
{
name: 'Request',
label: i18n.translate('inspectorViews.requests.requestTabLabel', {
defaultMessage: 'Request'
}),
component: RequestDetailsRequest
},
{
name: 'Response',
label: i18n.translate('inspectorViews.requests.responseTabLabel', {
defaultMessage: 'Response'
}),
component: RequestDetailsResponse
},
];
class RequestDetails extends Component {
@ -76,7 +95,7 @@ class RequestDetails extends Component {
onClick={() => this.selectDetailsTab(detail)}
data-test-subj={`inspectorRequestDetail${detail.name}`}
>
{detail.name}
{detail.label}
</EuiTab>
);
}

View file

@ -33,6 +33,9 @@ import {
EuiToolTip,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { RequestStatus } from 'ui/inspector/adapters';
class RequestSelector extends Component {
@ -70,11 +73,16 @@ class RequestSelector extends Component {
>
<EuiTextColor color={hasFailed ? 'danger' : 'default'}>
{request.name}
{ hasFailed && ' (failed)' }
{ hasFailed && <FormattedMessage
id="inspectorViews.requests.failedLabel"
defaultMessage=" (failed)"
/>}
{ inProgress &&
<EuiLoadingSpinner
size="s"
aria-label="Request in progress"
aria-label={i18n.translate('inspectorViews.requests.requestInProgressAriaLabel', {
defaultMessage: 'Request in progress'
})}
className="insRequestSelector__menuSpinner"
/>
}
@ -123,7 +131,12 @@ class RequestSelector extends Component {
<EuiFlexItem
grow={false}
>
<strong>Request:</strong>
<strong>
<FormattedMessage
id="inspectorViews.requests.requestLabel"
defaultMessage="Request:"
/>
</strong>
</EuiFlexItem>
<EuiFlexItem grow={true}>
{requests.length <= 1 &&
@ -137,21 +150,41 @@ class RequestSelector extends Component {
{ selectedRequest.status !== RequestStatus.PENDING &&
<EuiToolTip
position="left"
title={selectedRequest.status === RequestStatus.OK ? 'Request succeeded' : 'Request failed'}
content="The total time the request took."
title={selectedRequest.status === RequestStatus.OK ?
<FormattedMessage
id="inspectorViews.requests.requestSucceededTooltipTitle"
defaultMessage="Request succeeded"
/> :
<FormattedMessage
id="inspectorViews.requests.requestFailedTooltipTitle"
defaultMessage="Request failed"
/>
}
content={<FormattedMessage
id="inspectorViews.requests.requestTooltipDescription"
defaultMessage="The total time the request took."
/>}
>
<EuiBadge
color={selectedRequest.status === RequestStatus.OK ? 'secondary' : 'danger'}
iconType={selectedRequest.status === RequestStatus.OK ? 'check' : 'cross'}
>
{selectedRequest.time}ms
<FormattedMessage
id="inspectorViews.requests.requestTimeLabel"
defaultMessage="{requestTime}ms"
values={{ requestTime: selectedRequest.time }}
/>
</EuiBadge>
</EuiToolTip>
}
{ selectedRequest.status === RequestStatus.PENDING &&
<EuiLoadingSpinner
size="m"
aria-label="Request in progress"
aria-label={this.props.intl.formatMessage({
id: 'inspectorViews.requests.requestInProgressAriaLabel',
defaultMessage: 'Request in progress'
})}
/>
}
</EuiFlexItem>

View file

@ -31,6 +31,8 @@ import { RequestStatus } from 'ui/inspector/adapters';
import { RequestSelector } from './request_selector';
import { RequestDetails } from './request_details';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
class RequestsViewComponent extends Component {
@ -69,13 +71,28 @@ class RequestsViewComponent extends Component {
<InspectorView useFlex={true}>
<EuiEmptyPrompt
data-test-subj="inspectorNoRequestsMessage"
title={<h2>No requests logged</h2>}
title={
<h2>
<FormattedMessage
id="inspectorViews.requests.noRequestsLoggedTitle"
defaultMessage="No requests logged"
/>
</h2>
}
body={
<React.Fragment>
<p>The element hasn&apos;t logged any requests (yet).</p>
<p>
This usually means that there was no need to fetch any data or
that the element has not yet started fetching data.
<FormattedMessage
id="inspectorViews.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText"
defaultMessage="The element hasn't logged any requests (yet)."
/>
</p>
<p>
<FormattedMessage
id="inspectorViews.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText"
defaultMessage="This usually means that there was no need to fetch any data or
that the element has not yet started fetching data."
/>
</p>
</React.Fragment>
}
@ -97,18 +114,24 @@ class RequestsViewComponent extends Component {
<InspectorView>
<EuiText size="xs">
<p role="status" aria-live="polite" aria-atomic="true">
{this.state.requests.length}
{this.state.requests.length !== 1 ? ' requests were' : ' request was'} made
{failedCount > 0 &&
<React.Fragment>
, {' '}
<EuiTextColor
color="danger"
>
{failedCount} had a failure
</EuiTextColor>
</React.Fragment>
}
<FormattedMessage
id="inspectorViews.requests.requestWasMadeDescription"
defaultMessage="{requestsCount, plural, one {# request was} other {# requests were} } made{failedRequests}"
values={{
requestsCount: this.state.requests.length,
failedRequests: (
failedCount > 0 ? (
<EuiTextColor color="danger">
<FormattedMessage
id="inspectorViews.requests.requestWasMadeDescription.requestHadFailureText"
defaultMessage=", {failedCount} had a failure"
values={{ failedCount }}
/>
</EuiTextColor>
) : ''
)
}}
/>
</p>
</EuiText>
<EuiSpacer size="xs"/>
@ -139,9 +162,13 @@ RequestsViewComponent.propTypes = {
};
const RequestsView = {
title: 'Requests',
title: i18n.translate('inspectorViews.requests.requestsTitle', {
defaultMessage: 'Requests'
}),
order: 20,
help: `View the requests that collected the data`,
help: i18n.translate('inspectorViews.requests.requestsDescriptionTooltip', {
defaultMessage: 'View the requests that collected the data'
}),
shouldShow(adapters) {
return Boolean(adapters.requests);
},

View file

@ -19,6 +19,7 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { I18nProvider } from '@kbn/i18n/react';
import { FlyoutSession, openFlyout } from 'ui/flyout';
import { Adapters } from './types';
import { InspectorPanel } from './ui/inspector_panel';
@ -72,10 +73,15 @@ function open(adapters: Adapters, options: InspectorOptions = {}): InspectorSess
if an inspector can be shown.`);
}
return openFlyout(<InspectorPanel views={views} adapters={adapters} title={options.title} />, {
'data-test-subj': 'inspectorPanel',
closeButtonAriaLabel: closeButtonLabel,
});
return openFlyout(
<I18nProvider>
<InspectorPanel views={views} adapters={adapters} title={options.title} />
</I18nProvider>,
{
'data-test-subj': 'inspectorPanel',
closeButtonAriaLabel: closeButtonLabel,
}
);
}
const Inspector = {