diff --git a/.i18nrc.json b/.i18nrc.json index 3ced22fa9cd7..0c43772b6882 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -16,6 +16,7 @@ "timelion": "src/legacy/core_plugins/timelion", "tagCloud": "src/legacy/core_plugins/tagcloud", "tsvb": "src/legacy/core_plugins/metrics", + "xpack.apm": "x-pack/plugins/apm", "xpack.beatsManagement": "x-pack/plugins/beats_management", "xpack.graph": "x-pack/plugins/graph", "xpack.grokDebugger": "x-pack/plugins/grokdebugger", diff --git a/x-pack/plugins/apm/index.js b/x-pack/plugins/apm/index.js index b9965ccac70e..e91aff597854 100644 --- a/x-pack/plugins/apm/index.js +++ b/x-pack/plugins/apm/index.js @@ -13,6 +13,7 @@ import { initStatusApi } from './server/routes/status_check'; import { initTracesApi } from './server/routes/traces'; import mappings from './mappings'; import { makeApmUsageCollector } from './server/lib/apm_telemetry'; +import { i18n } from '@kbn/i18n'; export function apm(kibana) { return new kibana.Plugin({ @@ -24,7 +25,9 @@ export function apm(kibana) { uiExports: { app: { title: 'APM', - description: 'APM for the Elastic Stack', + description: i18n.translate('xpack.apm.apmForESDescription', { + defaultMessage: 'APM for the Elastic Stack' + }), main: 'plugins/apm/index', icon: 'plugins/apm/icon.svg', euiIconType: 'apmApp', diff --git a/x-pack/plugins/apm/public/register_feature.js b/x-pack/plugins/apm/public/register_feature.js index 5b75f189c2e2..c176d6ab1bdd 100644 --- a/x-pack/plugins/apm/public/register_feature.js +++ b/x-pack/plugins/apm/public/register_feature.js @@ -11,13 +11,15 @@ import { } from 'ui/registry/feature_catalogue'; if (chrome.getInjected('apmUiEnabled')) { - FeatureCatalogueRegistryProvider.register(() => { + FeatureCatalogueRegistryProvider.register(i18n => { return { id: 'apm', title: 'APM', - description: - 'Automatically collect in-depth performance metrics and ' + - 'errors from inside your applications.', + description: i18n('xpack.apm.apmDescription', { + defaultMessage: + 'Automatically collect in-depth performance metrics and ' + + 'errors from inside your applications.' + }), icon: 'apmApp', path: '/app/apm', showOnHomePage: true, diff --git a/x-pack/plugins/apm/public/utils/formatters.ts b/x-pack/plugins/apm/public/utils/formatters.ts index 08551aa43b7a..dc0592b1f858 100644 --- a/x-pack/plugins/apm/public/utils/formatters.ts +++ b/x-pack/plugins/apm/public/utils/formatters.ts @@ -5,10 +5,18 @@ */ import numeral from '@elastic/numeral'; +import { i18n } from '@kbn/i18n'; import { memoize } from 'lodash'; const SECONDS_CUT_OFF = 10 * 1000000; // 10 seconds (in microseconds) const MILLISECONDS_CUT_OFF = 10 * 1000; // 10 milliseconds (in microseconds) +const SPACE = ' '; +const notAvailableLabel: string = i18n.translate( + 'xpack.apm.formatters.notAvailableLabel', + { + defaultMessage: 'N/A' + } +); /* * value: time in microseconds @@ -23,45 +31,57 @@ interface FormatterOptions { export function asSeconds( value: FormatterValue, - { withUnit = true, defaultValue = 'N/A' }: FormatterOptions = {} + { withUnit = true, defaultValue = notAvailableLabel }: FormatterOptions = {} ) { if (value == null) { return defaultValue; } + const secondsLabel = + SPACE + + i18n.translate('xpack.apm.formatters.secondsTimeUnitLabel', { + defaultMessage: 's' + }); const formatted = asDecimal(value / 1000000); - return `${formatted}${withUnit ? ' s' : ''}`; + return `${formatted}${withUnit ? secondsLabel : ''}`; } export function asMillis( value: FormatterValue, - { withUnit = true, defaultValue = 'N/A' }: FormatterOptions = {} + { withUnit = true, defaultValue = notAvailableLabel }: FormatterOptions = {} ) { if (value == null) { return defaultValue; } + const millisLabel = + SPACE + + i18n.translate('xpack.apm.formatters.millisTimeUnitLabel', { + defaultMessage: 'ms' + }); const formatted = asInteger(value / 1000); - return `${formatted}${withUnit ? ' ms' : ''}`; + return `${formatted}${withUnit ? millisLabel : ''}`; } export function asMicros( value: FormatterValue, - { withUnit = true, defaultValue = 'N/A' }: FormatterOptions = {} + { withUnit = true, defaultValue = notAvailableLabel }: FormatterOptions = {} ) { if (value == null) { return defaultValue; } + const microsLabel = + SPACE + + i18n.translate('xpack.apm.formatters.microsTimeUnitLabel', { + defaultMessage: 'μs' + }); const formatted = asInteger(value); - return `${formatted}${withUnit ? ' μs' : ''}`; + return `${formatted}${withUnit ? microsLabel : ''}`; } type TimeFormatter = ( max: number -) => ( - value: FormatterValue, - { withUnit, defaultValue }: FormatterOptions -) => string; +) => (value: FormatterValue, options: FormatterOptions) => string; export const getTimeFormatter: TimeFormatter = memoize((max: number) => { const unit = timeUnit(max); @@ -87,7 +107,7 @@ export function timeUnit(max: number) { export function asTime( value: FormatterValue, - { withUnit = true, defaultValue = 'N/A' }: FormatterOptions = {} + { withUnit = true, defaultValue = notAvailableLabel }: FormatterOptions = {} ) { if (value == null) { return defaultValue; @@ -105,7 +125,13 @@ export function asInteger(value: number) { } export function tpmUnit(type: string) { - return type === 'request' ? 'rpm' : 'tpm'; + return type === 'request' + ? i18n.translate('xpack.apm.formatters.requestsPerMinLabel', { + defaultMessage: 'rpm' + }) + : i18n.translate('xpack.apm.formatters.transactionsPerMinLabel', { + defaultMessage: 'tpm' + }); } export function asPercent( diff --git a/x-pack/plugins/apm/public/utils/timepicker/index.js b/x-pack/plugins/apm/public/utils/timepicker/index.js index b605fb6847fe..505682d177db 100644 --- a/x-pack/plugins/apm/public/utils/timepicker/index.js +++ b/x-pack/plugins/apm/public/utils/timepicker/index.js @@ -37,38 +37,48 @@ export function initTimepicker(history, dispatch) { uiModules .get('app/apm', []) - .controller('TimePickerController', ($scope, globalState, $rootScope) => { - // Add APM feedback menu - // TODO: move this somewhere else - $scope.topNavMenu = []; - $scope.topNavMenu.push({ - key: 'APM feedback', - description: 'APM feedback', - tooltip: 'Provide feedback on APM', - template: require('../../templates/feedback_menu.html') - }); + .controller( + 'TimePickerController', + ($scope, globalState, $rootScope, i18n) => { + // Add APM feedback menu + // TODO: move this somewhere else + $scope.topNavMenu = []; + $scope.topNavMenu.push({ + key: 'APM feedback', + label: i18n('xpack.apm.topNav.apmFeedbackLabel', { + defaultMessage: 'APM feedback' + }), + description: i18n('xpack.apm.topNav.apmFeedbackDescription', { + defaultMessage: 'APM feedback' + }), + tooltip: i18n('xpack.apm.topNav.apmFeedbackTooltip', { + defaultMessage: 'Provide feedback on APM' + }), + template: require('../../templates/feedback_menu.html') + }); - history.listen(() => { + history.listen(() => { + updateRefreshRate(dispatch); + globalState.fetch(); // ensure global state is updated when url changes + }); + + // ensure that redux is notified after timefilter has updated + $scope.$listen(timefilter, 'timeUpdate', () => + dispatch(updateTimePickerAction()) + ); + + // ensure that timepicker updates when global state changes + registerTimefilterWithGlobalState(globalState, $rootScope); + + timefilter.enableTimeRangeSelector(); + timefilter.enableAutoRefreshSelector(); + + dispatch(updateTimePickerAction()); updateRefreshRate(dispatch); - globalState.fetch(); // ensure global state is updated when url changes - }); - // ensure that redux is notified after timefilter has updated - $scope.$listen(timefilter, 'timeUpdate', () => - dispatch(updateTimePickerAction()) - ); - - // ensure that timepicker updates when global state changes - registerTimefilterWithGlobalState(globalState, $rootScope); - - timefilter.enableTimeRangeSelector(); - timefilter.enableAutoRefreshSelector(); - - dispatch(updateTimePickerAction()); - updateRefreshRate(dispatch); - - Promise.all([waitForAngularReady]).then(resolve); - }); + Promise.all([waitForAngularReady]).then(resolve); + } + ); }); } diff --git a/x-pack/plugins/apm/public/utils/url.tsx b/x-pack/plugins/apm/public/utils/url.tsx index f2b0dfc9a34f..18b68728c090 100644 --- a/x-pack/plugins/apm/public/utils/url.tsx +++ b/x-pack/plugins/apm/public/utils/url.tsx @@ -5,6 +5,7 @@ */ import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import createHistory from 'history/createHashHistory'; import { get, isPlainObject, mapValues } from 'lodash'; import qs from 'querystring'; @@ -26,6 +27,13 @@ const DEFAULT_KIBANA_TIME_RANGE = { } }; +const viewJobLabel: string = i18n.translate( + 'xpack.apm.viewMLJob.viewJobLabel', + { + defaultMessage: 'View Job' + } +); + interface ViewMlJobArgs { serviceName: string; transactionType: string; @@ -36,7 +44,7 @@ export const ViewMLJob: React.SFC = ({ serviceName, transactionType, location, - children = 'View Job' + children = viewJobLabel }) => { const pathname = '/app/ml'; const hash = '/timeseriesexplorer';