diff --git a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json index d7c9b1f6b1b..7885c3d457c 100644 --- a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json @@ -225,6 +225,29 @@ "step": "Step: {{value}}", "type": "Type: {{value}}" }, + "get-placeholders": { + "browse": "Search metrics by name", + "include-null-metadata": "Include results with no metadata", + "metadata-search-switch": "Include description in search", + "set-use-backend": "Enable regex search", + "type": "Filter by type" + }, + "get-prom-types": { + "description-counter": "A cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.", + "description-gauge": "A metric that represents a single numerical value that can arbitrarily go up and down.", + "description-histogram": "A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets.", + "description-native-histogram": "Native histograms are different from classic Prometheus histograms in a number of ways: Native histogram bucket boundaries are calculated by a formula that depends on the scale (resolution) of the native histogram, and are not user defined.", + "description-no-type": "These metrics have no defined type in the metadata.", + "description-summary": "A summary samples observations (usually things like request durations and response sizes) and can calculate configurable quantiles over a sliding time window.", + "description-unknown": "These metrics have been given the type unknown in the metadata.", + "label-counter": "Counter", + "label-gauge": "Gauge", + "label-histogram": "Histogram", + "label-native-histogram": "Native histogram", + "label-no-type": "No type", + "label-summary": "Summary", + "label-unknown": "Unknown" + }, "handle-function": { "text": { "query-parsing-is-ambiguous": "Query parsing is ambiguous." @@ -368,6 +391,9 @@ "results-table": { "content-descriptive-type": "When creating a {{descriptiveType}}, Prometheus exposes multiple series with the type counter. ", "description": "Description", + "message-expand-label-filters": "There are no metrics found. Try to expand your label filters.", + "message-expand-search": "There are no metrics found. Try to expand your search and filters.", + "message-no-metrics-found": "There are no metrics found in the data source.", "name": "Name", "select": "Select", "type": "Type" diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/AdditionalSettings.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/AdditionalSettings.tsx index a60a070b5b4..4ce04117dd9 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/AdditionalSettings.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/AdditionalSettings.tsx @@ -7,7 +7,7 @@ import { Icon, Switch, Tooltip, useTheme2 } from '@grafana/ui'; import { metricsModaltestIds } from './shared/testIds'; import { AdditionalSettingsProps } from './shared/types'; -import { placeholders } from './state/helpers'; +import { getPlaceholders } from './state/helpers'; export function AdditionalSettings(props: AdditionalSettingsProps) { const { state, onChangeFullMetaSearch, onChangeIncludeNullMetadata, onChangeDisableTextWrap, onChangeUseBackend } = @@ -16,6 +16,8 @@ export function AdditionalSettings(props: AdditionalSettingsProps) { const theme = useTheme2(); const styles = getStyles(theme); + const placeholders = getPlaceholders(); + return ( <>
diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx index 3d2c500e795..f598cf0951d 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx @@ -32,8 +32,8 @@ import { calculatePageList, calculateResultsPerPage, displayedMetrics, - placeholders, - promTypes, + getPlaceholders, + getPromTypes, setMetrics, tracking, } from './state/helpers'; @@ -55,6 +55,8 @@ export const MetricsModal = (props: MetricsModalProps) => { const theme = useTheme2(); const styles = getStyles(theme, state.disableTextWrap); + const placeholders = getPlaceholders(); + const promTypes = getPromTypes(); /** * loads metrics and metadata on opening modal and switching off useBackend @@ -88,7 +90,7 @@ export const MetricsModal = (props: MetricsModalProps) => { const typeOptions: SelectableValue[] = promTypes.map((t: PromFilterOption) => { return { value: t.value, - label: t.value, + label: t.label, description: t.description, }; }); diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx index 47c990c091f..1e179d336ee 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx @@ -4,7 +4,7 @@ import { ReactElement } from 'react'; import Highlighter from 'react-highlight-words'; import { GrafanaTheme2 } from '@grafana/data'; -import { Trans } from '@grafana/i18n'; +import { t, Trans } from '@grafana/i18n'; import { Button, Icon, Tooltip, useTheme2 } from '@grafana/ui'; import { docsTip } from '../../../configuration/shared/utils'; @@ -106,15 +106,24 @@ export function ResultsTable(props: ResultsTableProps) { let message; if (!state.fuzzySearchQuery) { - message = 'There are no metrics found in the data source.'; + message = t( + 'grafana-prometheus.querybuilder.results-table.message-no-metrics-found', + 'There are no metrics found in the data source.' + ); } if (query.labels.length > 0) { - message = 'There are no metrics found. Try to expand your label filters.'; + message = t( + 'grafana-prometheus.querybuilder.results-table.message-expand-label-filters', + 'There are no metrics found. Try to expand your label filters.' + ); } if (state.fuzzySearchQuery || state.selectedTypes.length > 0) { - message = 'There are no metrics found. Try to expand your search and filters.'; + message = t( + 'grafana-prometheus.querybuilder.results-table.message-expand-search', + 'There are no metrics found. Try to expand your search and filters.' + ); } return ( diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/helpers.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/helpers.ts index 66fa83e2624..88d5f9e27bf 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/helpers.ts +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/helpers.ts @@ -1,6 +1,7 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/helpers.ts import { AnyAction } from '@reduxjs/toolkit'; +import { t } from '@grafana/i18n'; import { reportInteraction } from '@grafana/runtime'; import { PrometheusDatasource } from '../../../../datasource'; @@ -232,45 +233,75 @@ export function tracking(event: string, state?: MetricsModalState | null, metric } } -export const promTypes: PromFilterOption[] = [ +export const getPromTypes: () => PromFilterOption[] = () => [ { value: 'counter', - description: - 'A cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-counter', 'Counter'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-counter', + 'A cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.' + ), }, { value: 'gauge', - description: 'A metric that represents a single numerical value that can arbitrarily go up and down.', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-gauge', 'Gauge'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-gauge', + 'A metric that represents a single numerical value that can arbitrarily go up and down.' + ), }, { value: 'histogram', - description: - 'A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets.', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-histogram', 'Histogram'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-histogram', + 'A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets.' + ), }, { value: 'native histogram', - description: - 'Native histograms are different from classic Prometheus histograms in a number of ways: Native histogram bucket boundaries are calculated by a formula that depends on the scale (resolution) of the native histogram, and are not user defined.', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-native-histogram', 'Native histogram'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-native-histogram', + 'Native histograms are different from classic Prometheus histograms in a number of ways: Native histogram bucket boundaries are calculated by a formula that depends on the scale (resolution) of the native histogram, and are not user defined.' + ), }, { value: 'summary', - description: - 'A summary samples observations (usually things like request durations and response sizes) and can calculate configurable quantiles over a sliding time window.', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-summary', 'Summary'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-summary', + 'A summary samples observations (usually things like request durations and response sizes) and can calculate configurable quantiles over a sliding time window.' + ), }, { value: 'unknown', - description: 'These metrics have been given the type unknown in the metadata.', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-unknown', 'Unknown'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-unknown', + 'These metrics have been given the type unknown in the metadata.' + ), }, { value: 'no type', - description: 'These metrics have no defined type in the metadata.', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-no-type', 'No type'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-no-type', + 'These metrics have no defined type in the metadata.' + ), }, ]; -export const placeholders = { - browse: 'Search metrics by name', - metadataSearchSwitch: 'Include description in search', - type: 'Filter by type', - includeNullMetadata: 'Include results with no metadata', - setUseBackend: 'Enable regex search', -}; +export const getPlaceholders = () => ({ + browse: t('grafana-prometheus.querybuilder.get-placeholders.browse', 'Search metrics by name'), + metadataSearchSwitch: t( + 'grafana-prometheus.querybuilder.get-placeholders.metadata-search-switch', + 'Include description in search' + ), + type: t('grafana-prometheus.querybuilder.get-placeholders.type', 'Filter by type'), + includeNullMetadata: t( + 'grafana-prometheus.querybuilder.get-placeholders.include-null-metadata', + 'Include results with no metadata' + ), + setUseBackend: t('grafana-prometheus.querybuilder.get-placeholders.set-use-backend', 'Enable regex search'), +}); diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/types.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/types.ts index 233f3765e8a..88d39441eb3 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/types.ts +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/types.ts @@ -9,6 +9,7 @@ export type MetricData = { export type PromFilterOption = { value: string; + label: string; description: string; }; diff --git a/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx b/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx index 808e5245ae0..7bcdbb6852a 100644 --- a/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx +++ b/packages/grafana-ui/src/components/TagsInput/TagsInput.tsx @@ -3,7 +3,7 @@ import { useCallback, useState, forwardRef } from 'react'; import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { Trans } from '@grafana/i18n'; +import { t, Trans } from '@grafana/i18n'; import { useStyles2, useTheme2 } from '../../themes/ThemeContext'; import { Button } from '../Button/Button'; @@ -32,7 +32,7 @@ export interface Props { export const TagsInput = forwardRef( ( { - placeholder = 'New tag (enter key to add)', + placeholder: placeholderProp, tags = [], onChange, width, @@ -45,6 +45,7 @@ export const TagsInput = forwardRef( }, ref ) => { + const placeholder = placeholderProp ?? t('grafana-ui.tags-input.placeholder-new-tag', 'New tag (enter key to add)'); const [newTagName, setNewTagName] = useState(''); const styles = useStyles2(getStyles); const theme = useTheme2(); diff --git a/public/app/features/annotations/components/AnnotationResultMapper.tsx b/public/app/features/annotations/components/AnnotationResultMapper.tsx index 475659c7620..b572ab13e40 100644 --- a/public/app/features/annotations/components/AnnotationResultMapper.tsx +++ b/public/app/features/annotations/components/AnnotationResultMapper.tsx @@ -13,7 +13,7 @@ import { import { Trans, t } from '@grafana/i18n'; import { Select, Tooltip, Icon } from '@grafana/ui'; -import { annotationEventNames, AnnotationFieldInfo } from '../standardAnnotationSupport'; +import { getAnnotationEventNames, AnnotationFieldInfo } from '../standardAnnotationSupport'; import { AnnotationQueryResponse } from '../types'; // const valueOptions: Array> = [ @@ -204,7 +204,7 @@ export class AnnotationFieldMapper extends PureComponent { - {annotationEventNames.map((row) => { + {getAnnotationEventNames().map((row) => { return this.renderRow(row, mappings[row.key] || {}, first); })} diff --git a/public/app/features/annotations/standardAnnotationSupport.ts b/public/app/features/annotations/standardAnnotationSupport.ts index a2caca1d583..90d28715c7f 100644 --- a/public/app/features/annotations/standardAnnotationSupport.ts +++ b/public/app/features/annotations/standardAnnotationSupport.ts @@ -17,6 +17,7 @@ import { KeyValue, standardTransformers, } from '@grafana/data'; +import { t } from '@grafana/i18n'; import { config } from 'app/core/config'; export const standardAnnotationSupport: AnnotationSupport = { @@ -97,22 +98,44 @@ export interface AnnotationFieldInfo { } // These fields get added to the standard UI -export const annotationEventNames: AnnotationFieldInfo[] = [ +export const getAnnotationEventNames: () => AnnotationFieldInfo[] = () => [ { key: 'time', field: (frame: DataFrame) => frame.fields.find((f) => f.type === FieldType.time), - placeholder: 'time, or the first time field', + placeholder: t( + 'annotations.get-annotation-event-names.placeholder.time-or-the-first-field', + '{{defaultField}}, or the first time field', + { defaultField: 'time' } + ), + }, + { + key: 'timeEnd', + // label: 'end time', + help: t( + 'annotations.get-annotation-event-names.help.annotation-treated-as-range', + 'When this field is defined, the annotation will be treated as a range' + ), }, - { key: 'timeEnd', label: 'end time', help: 'When this field is defined, the annotation will be treated as a range' }, { key: 'title', }, { key: 'text', field: (frame: DataFrame) => frame.fields.find((f) => f.type === FieldType.string), - placeholder: 'text, or the first text field', + placeholder: t( + 'annotations.get-annotation-event-names.placeholder.text-or-the-first-field', + '{{defaultField}}, or the first text field', + { defaultField: 'text' } + ), + }, + { + key: 'tags', + split: ',', + help: t( + 'annotations.get-annotation-event-names.help.results-split-on-comma', + 'The results will be split on comma (,)' + ), }, - { key: 'tags', split: ',', help: 'The results will be split on comma (,)' }, { key: 'id', }, @@ -134,7 +157,7 @@ export const publicDashboardEventNames: AnnotationFieldInfo[] = [ // pipeline, but include fields that should not be exposed generally const alertEventAndAnnotationFields: AnnotationFieldInfo[] = [ ...(config.publicDashboardAccessToken ? publicDashboardEventNames : []), - ...annotationEventNames, + ...getAnnotationEventNames(), { key: 'userId' }, { key: 'login' }, { key: 'email' }, diff --git a/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx b/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx index 9c2f783c49e..00001fac1f8 100644 --- a/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx +++ b/public/app/features/dashboard-scene/saving/SaveDashboardAsForm.tsx @@ -118,7 +118,10 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
onSave(false))}> } invalid={!!errors.title} error={errors.title?.message}> <> [ { - label: 'All panels', + label: t('dashboard.get-panel-filters.label.all-panels', 'All panels'), value: PanelFilterType.AllPanels, - description: 'Send the annotation data to all panels that support annotations', + description: t( + 'dashboard.get-panel-filters.description.annotation-panels-support-annotations', + 'Send the annotation data to all panels that support annotations' + ), }, { - label: 'Selected panels', + label: t('dashboard.get-panel-filters.label.selected-panels', 'Selected panels'), value: PanelFilterType.IncludePanels, - description: 'Send the annotations to the explicitly listed panels', + description: t( + 'dashboard.get-panel-filters.description.annotations-explicitly-listed-panels', + 'Send the annotations to the explicitly listed panels' + ), }, { - label: 'All panels except', + label: t('dashboard.get-panel-filters.label.all-panels-except', 'All panels except'), value: PanelFilterType.ExcludePanels, - description: 'Do not send annotation data to the following panels', + description: t( + 'dashboard.get-panel-filters.description.annotation-following-panels', + 'Do not send annotation data to the following panels' + ), }, ]; diff --git a/public/app/features/manage-dashboards/services/ValidationSrv.ts b/public/app/features/manage-dashboards/services/ValidationSrv.ts index 24d41c1c048..48ca75c417b 100644 --- a/public/app/features/manage-dashboards/services/ValidationSrv.ts +++ b/public/app/features/manage-dashboards/services/ValidationSrv.ts @@ -1,3 +1,4 @@ +import { t } from '@grafana/i18n'; import { getGrafanaSearcher } from 'app/features/search/service/searcher'; class ValidationError extends Error { @@ -13,14 +14,24 @@ export class ValidationSrv { rootName = 'general'; validateNewDashboardName(folderUID: string, name: string) { - return this.validate(folderUID, name, 'A dashboard or a folder with the same name already exists'); + return this.validate( + folderUID, + name, + t( + 'manage-dashboards.validation-srv.message-same-name', + 'A dashboard or a folder with the same name already exists' + ) + ); } validateNewFolderName(name?: string) { return this.validate( this.rootName, name, - 'A folder or dashboard in the general folder with the same name already exists' + t( + 'manage-dashboards.validation-srv.message-same-name-general', + 'A folder or dashboard in the general folder with the same name already exists' + ) ); } @@ -29,11 +40,20 @@ export class ValidationSrv { const nameLowerCased = name.toLowerCase(); if (name.length === 0) { - throw new ValidationError('REQUIRED', 'Name is required'); + throw new ValidationError( + 'REQUIRED', + t('manage-dashboards.validation-srv.message-name-required', 'Name is required') + ); } if (nameLowerCased === this.rootName) { - throw new ValidationError('EXISTING', 'This is a reserved name and cannot be used for a folder.'); + throw new ValidationError( + 'EXISTING', + t( + 'manage-dashboards.validation-srv.message-reserved-name', + 'This is a reserved name and cannot be used for a folder.' + ) + ); } const searcher = getGrafanaSearcher(); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index ab3d742f57d..6daba435bae 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -3143,6 +3143,16 @@ "info-box-content-2": "Checkout the <2>Annotations documentation for more information.", "title": "There are no custom annotation queries added yet" }, + "get-annotation-event-names": { + "help": { + "annotation-treated-as-range": "When this field is defined, the annotation will be treated as a range", + "results-split-on-comma": "The results will be split on comma (,)" + }, + "placeholder": { + "text-or-the-first-field": "{{defaultField}}, or the first text field", + "time-or-the-first-field": "{{defaultField}}, or the first time field" + } + }, "standard-annotation-query-editor": { "events-found": "{{numEvents}} events (from {{numFields}} fields)", "no-events-found": "No events found", @@ -4676,6 +4686,18 @@ "transform-data": "Transform data" } }, + "get-panel-filters": { + "description": { + "annotation-following-panels": "Do not send annotation data to the following panels", + "annotation-panels-support-annotations": "Send the annotation data to all panels that support annotations", + "annotations-explicitly-listed-panels": "Send the annotations to the explicitly listed panels" + }, + "label": { + "all-panels": "All panels", + "all-panels-except": "All panels except", + "selected-panels": "Selected panels" + } + }, "get-panel-frame-category": { "descriptor": { "title": { @@ -5581,6 +5603,40 @@ "row-height-options": "Row height options" } }, + "get-editable-variables": { + "description": { + "add-keyvalue-filters-on-the-fly": "Add key/value filters on the fly", + "dynamically-switch-source-multiple-panels": "Dynamically switch the data source for multiple panels", + "group": "Add keys to group by on the fly", + "hidden-constant-variable": "A hidden constant variable, useful for metric prefixes in dashboards you want to share", + "users-enter-arbitrary-strings-textbox": "Users can enter any arbitrary strings in a textbox", + "values-are-static-and-defined-manually": "Values are static and defined manually", + "values-fetched-source-query": "Values are fetched from a data source query", + "values-timespans": "Values are timespans, ex 1m, 1h, 1d" + }, + "name": { + "ad-hoc-filters": "Ad hoc filters", + "constant": "Constant", + "custom": "Custom", + "data-source": "Data source", + "group-by": "Group by", + "interval": "Interval", + "query": "Query", + "textbox": "Textbox" + } + }, + "get-panel-filters": { + "description": { + "annotation-following-panels": "Do not send annotation data to the following panels", + "annotation-panels-support-annotations": "Send the annotation data to all panels that support annotations", + "annotations-explicitly-listed-panels": "Send the annotations to the explicitly listed panels" + }, + "label": { + "all-panels": "All panels", + "all-panels-except": "All panels except", + "selected-panels": "Selected panels" + } + }, "get-panel-frame-options": { "descriptor": { "title": { @@ -5814,7 +5870,8 @@ "label-folder": "Folder", "render-footer": { "title-failed-to-save-dashboard": "Failed to save dashboard" - } + }, + "required": "Required" }, "save-dashboard-drawer": { "tabs": { @@ -8022,6 +8079,7 @@ }, "tags-input": { "add": "Add", + "placeholder-new-tag": "New tag (enter key to add)", "remove": "Remove tag: {{name}}" }, "time-sync-button": { @@ -8968,6 +9026,12 @@ "text": { "import-dashboard": "Import dashboard" } + }, + "validation-srv": { + "message-name-required": "Name is required", + "message-reserved-name": "This is a reserved name and cannot be used for a folder.", + "message-same-name": "A dashboard or a folder with the same name already exists", + "message-same-name-general": "A folder or dashboard in the general folder with the same name already exists" } }, "metric-select": {