From aa0842a1e733574bffcb7e53f425d441c72b65fb Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Thu, 15 May 2025 14:31:49 +0100 Subject: [PATCH] Internationalisation: Mark up azure monitor plugin (#105262) * start marking up azuremonitor * more markup * finish mark up * add package and correct imports * convert to functional component + use correct t import * fix duplicate key + typo * add extract config + fake french translations to test * run prettier and fix unit tests * use nx * enable i18n lint rules for azure monitor * remove fake french translations * await initPluginTranslations * top level await breaks unit tests * leave as class component for now --- Makefile | 2 + eslint.config.js | 14 +- package.json | 1 + .../ArgQueryEditor/SubscriptionField.tsx | 15 +- .../AppRegistrationCredentials.tsx | 39 ++- .../ConfigEditor/AzureCredentialsForm.tsx | 11 +- .../ConfigEditor/BasicLogsToggle.tsx | 34 ++- .../components/ConfigEditor/ConfigEditor.tsx | 8 +- .../CurrentUserFallbackCredentials.tsx | 66 ++-- .../ConfigEditor/DefaultSubscription.tsx | 8 +- .../LogsQueryBuilder/AggregateItem.tsx | 17 +- .../LogsQueryBuilder/AggregationSection.tsx | 9 +- .../LogsQueryBuilder/FilterItem.tsx | 14 +- .../LogsQueryBuilder/FilterSection.tsx | 17 +- .../LogsQueryBuilder/FuzzySearch.tsx | 14 +- .../LogsQueryBuilder/GroupByItem.tsx | 11 +- .../LogsQueryBuilder/GroupBySection.tsx | 11 +- .../LogsQueryBuilder/KQLPreview.tsx | 8 +- .../LogsQueryBuilder/LimitSection.tsx | 13 +- .../LogsQueryBuilder/LogsQueryBuilder.tsx | 10 +- .../LogsQueryBuilder/OrderBySection.tsx | 17 +- .../LogsQueryBuilder/TableSection.tsx | 14 +- .../AdvancedResourcePicker.tsx | 30 +- .../LogsQueryEditor/AzureCheatSheet.tsx | 42 ++- .../LogsQueryEditor/AzureCheatSheetModal.tsx | 9 +- .../LogsQueryEditor/LogsManagement.tsx | 17 +- .../LogsQueryEditor/LogsQueryEditor.tsx | 5 +- .../components/LogsQueryEditor/RawQuery.tsx | 4 +- .../LogsQueryEditor/TimeManagement.tsx | 28 +- .../AdvancedResourcePicker.tsx | 48 ++- .../MetricsQueryEditor/AggregationField.tsx | 4 +- .../DimensionFields.test.tsx | 6 +- .../MetricsQueryEditor/DimensionFields.tsx | 22 +- .../MetricsQueryEditor/LegendFormatField.tsx | 6 +- .../MetricsQueryEditor/MetricNameField.tsx | 7 +- .../MetricNamespaceField.tsx | 4 +- .../MetricsQueryEditor/TimeGrainField.tsx | 4 +- .../MetricsQueryEditor/TopField.tsx | 4 +- .../components/QueryEditor/QueryEditor.tsx | 68 +++-- .../components/QueryEditor/QueryHeader.tsx | 14 +- .../ResourceField/ResourceField.tsx | 12 +- .../ResourcePicker/AdvancedMulti.tsx | 4 +- .../components/ResourcePicker/NestedRow.tsx | 7 +- .../ResourcePicker/ResourcePicker.test.tsx | 8 +- .../ResourcePicker/ResourcePicker.tsx | 43 ++- .../components/ResourcePicker/Search.tsx | 6 +- .../components/TracesQueryEditor/Filter.tsx | 14 +- .../components/TracesQueryEditor/Filters.tsx | 4 +- .../TracesQueryEditor/TraceTypeField.tsx | 6 +- .../TracesQueryEditor/TracesQueryEditor.tsx | 4 +- .../GrafanaTemplateVariableFn.tsx | 15 +- .../VariableEditor/VariableEditor.test.tsx | 72 ++--- .../VariableEditor/VariableEditor.tsx | 82 +++-- .../components/shared/FormatAsField.tsx | 7 +- .../grafana-azure-monitor-datasource.json | 289 ++++++++++++++++++ .../locales/i18next-parser.config.cjs | 12 + .../plugins/datasource/azuremonitor/module.ts | 3 + .../datasource/azuremonitor/package.json | 5 +- .../datasource/azuremonitor/plugin.json | 23 +- .../datasource/azuremonitor/webpack.config.ts | 2 +- public/locales/en-US/grafana.json | 6 - public/locales/i18next-parser.config.cjs | 1 + yarn.lock | 4 +- 63 files changed, 1001 insertions(+), 303 deletions(-) create mode 100644 public/app/plugins/datasource/azuremonitor/locales/en-US/grafana-azure-monitor-datasource.json create mode 100644 public/app/plugins/datasource/azuremonitor/locales/i18next-parser.config.cjs diff --git a/Makefile b/Makefile index d8cfafb4f7b..062bdd3036a 100644 --- a/Makefile +++ b/Makefile @@ -139,6 +139,8 @@ endif i18n-extract: i18n-extract-enterprise @echo "Extracting i18n strings for OSS" yarn run i18next --config public/locales/i18next-parser.config.cjs + @echo "Extracting i18n strings for plugins" + yarn run plugin:i18n-extract ##@ Building .PHONY: gen-cue diff --git a/eslint.config.js b/eslint.config.js index f0a2ecd0407..dc732177d64 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -19,6 +19,7 @@ const getEnvConfig = require('./scripts/webpack/env-util'); const envConfig = getEnvConfig(); const enableBettererRules = envConfig.frontend_dev_betterer_eslint_rules; +const pluginsToTranslate = ['public/app/plugins/datasource/azuremonitor']; /** * @type {Array} @@ -287,15 +288,12 @@ module.exports = [ plugins: { '@grafana': grafanaPlugin, }, - files: ['public/**/*.{ts,tsx,js,jsx}', 'packages/grafana-ui/**/*.{ts,tsx,js,jsx}'], - ignores: [ - 'public/app/plugins/**', - '**/*.story.tsx', - '**/*.{test,spec}.{ts,tsx}', - '**/__mocks__/', - 'public/test', - '**/spec/**/*.{ts,tsx}', + files: [ + 'public/app/!(plugins)/**/*.{ts,tsx,js,jsx}', + 'packages/grafana-ui/**/*.{ts,tsx,js,jsx}', + ...pluginsToTranslate.map((plugin) => `${plugin}/**/*.{ts,tsx,js,jsx}`), ], + ignores: ['**/*.story.tsx', '**/*.{test,spec}.{ts,tsx}', '**/__mocks__/', 'public/test', '**/spec/**/*.{ts,tsx}'], rules: { '@grafana/no-untranslated-strings': 'error', '@grafana/no-translation-top-level': 'error', diff --git a/package.json b/package.json index a58d31044e2..819c087d2a2 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "plugin:build": "nx run-many -t build --projects='tag:scope:plugin'", "plugin:build:commit": "nx run-many -t build:commit --projects='tag:scope:plugin'", "plugin:build:dev": "nx run-many -t dev --projects='tag:scope:plugin' --maxParallel=100", + "plugin:i18n-extract": "nx run-many -t i18n-extract --projects='tag:scope:plugin'", "process-specs": "node --experimental-strip-types scripts/process-specs.ts", "generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts", "generate:api-client": "NODE_OPTIONS='--experimental-strip-types' plop --plopfile public/app/api/generator/plopfile.ts" diff --git a/public/app/plugins/datasource/azuremonitor/components/ArgQueryEditor/SubscriptionField.tsx b/public/app/plugins/datasource/azuremonitor/components/ArgQueryEditor/SubscriptionField.tsx index e99b1dcaf77..3fa6eae2999 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ArgQueryEditor/SubscriptionField.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ArgQueryEditor/SubscriptionField.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo, useState } from 'react'; import { SelectableValue } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { FieldValidationMessage, MultiSelect } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; @@ -17,6 +18,7 @@ const SubscriptionField = ({ query, subscriptions, variableOptionGroup, onQueryC const [error, setError] = useState(false); const [values, setValues] = useState>>([]); const options = useMemo(() => [...subscriptions, variableOptionGroup], [subscriptions, variableOptionGroup]); + const { t } = useTranslate(); useEffect(() => { if (query.subscriptions && query.subscriptions.length > 0) { @@ -54,7 +56,10 @@ const SubscriptionField = ({ query, subscriptions, variableOptionGroup, onQueryC }; return ( - + <> - {error ? At least one subscription must be chosen. : null} + {error ? ( + + + At least one subscription must be chosen. + + + ) : null} ); diff --git a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/AppRegistrationCredentials.tsx b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/AppRegistrationCredentials.tsx index 1bdce1de9a0..079ca77c760 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/AppRegistrationCredentials.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/AppRegistrationCredentials.tsx @@ -2,6 +2,7 @@ import { ChangeEvent } from 'react'; import { AzureClientSecretCredentials, AzureCredentials } from '@grafana/azure-sdk'; import { SelectableValue } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { Field, Select, Input, Button } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; @@ -15,6 +16,7 @@ export interface AppRegistrationCredentialsProps { export const AppRegistrationCredentials = (props: AppRegistrationCredentialsProps) => { const { azureCloudOptions, disabled, credentials, onCredentialsChange } = props; + const { t } = useTranslate(); const onAzureCloudChange = (selected: SelectableValue) => { const updated: AzureCredentials = { @@ -60,14 +62,14 @@ export const AppRegistrationCredentials = (props: AppRegistrationCredentialsProp <> {azureCloudOptions && ( {!disabled && (typeof credentials.clientSecret === 'symbol' ? ( - +
) : ( { workloadIdentityEnabled, userIdentityEnabled, } = props; + const { t } = useTranslate(); const authTypeOptions = useMemo(() => { let opts: Array> = [ @@ -98,11 +100,14 @@ export const AzureCredentialsForm = (props: Props) => { }; return ( - + {authTypeOptions.length > 1 && ( diff --git a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/BasicLogsToggle.tsx b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/BasicLogsToggle.tsx index 79ad74c1e3e..defb0cd403a 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/BasicLogsToggle.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/BasicLogsToggle.tsx @@ -1,7 +1,8 @@ import { css } from '@emotion/css'; import * as React from 'react'; -import { Field, Switch, useTheme2 } from '@grafana/ui'; +import { Trans, useTranslate } from '@grafana/i18n'; +import { Field, Switch, TextLink, useTheme2 } from '@grafana/ui'; import { AzureMonitorDataSourceJsonData } from '../../types'; @@ -12,6 +13,7 @@ export interface Props { export const BasicLogsToggle = (props: Props) => { const { options, onBasicLogsEnabledChange } = props; + const { t } = useTranslate(); const theme = useTheme2(); const styles = { @@ -31,21 +33,29 @@ export const BasicLogsToggle = (props: Props) => { const onChange = (e: React.ChangeEvent) => onBasicLogsEnabledChange(e.target.checked); const description = (

- Enabling this feature incurs Azure Monitor per-query costs on dashboard panels that query tables configured for{' '} - - Basic Logs - - . + + Enabling this feature incurs Azure Monitor per-query costs on dashboard panels that query tables configured for{' '} + + Basic Logs + + . +

); return ( - +
- +
); diff --git a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/ConfigEditor.tsx b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/ConfigEditor.tsx index bdf2129e11d..9e273f101be 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/ConfigEditor.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/ConfigEditor.tsx @@ -1,6 +1,7 @@ import { PureComponent } from 'react'; import { DataSourcePluginOptionsEditorProps, SelectableValue, updateDatasourcePluginOption } from '@grafana/data'; +import { t } from '@grafana/i18n/internal'; import { ConfigSection, DataSourceDescription } from '@grafana/plugin-ui'; import { getBackendSrv, getTemplateSrv, isFetchError, TemplateSrv, config } from '@grafana/runtime'; import { Alert, Divider, SecureSocksProxySettings } from '@grafana/ui'; @@ -118,8 +119,11 @@ export class ConfigEditor extends PureComponent { <> diff --git a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/CurrentUserFallbackCredentials.tsx b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/CurrentUserFallbackCredentials.tsx index d13296dc1c5..8d98d7cca06 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/CurrentUserFallbackCredentials.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/CurrentUserFallbackCredentials.tsx @@ -2,9 +2,10 @@ import { useMemo } from 'react'; import { AadCurrentUserCredentials, AzureCredentials, instanceOfAzureCredential } from '@grafana/azure-sdk'; import { SelectableValue } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { ConfigSection } from '@grafana/plugin-ui'; import { config } from '@grafana/runtime'; -import { Select, Field, RadioButtonGroup, Alert, Stack } from '@grafana/ui'; +import { Select, Field, RadioButtonGroup, Alert, Stack, TextLink } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; @@ -29,6 +30,7 @@ export const CurrentUserFallbackCredentials = (props: Props) => { managedIdentityEnabled, workloadIdentityEnabled, } = props; + const { t } = useTranslate(); type FallbackCredentialAuthTypeOptions = 'clientsecret' | 'msi' | 'workloadidentity'; const authTypeOptions = useMemo(() => { @@ -90,45 +92,66 @@ export const CurrentUserFallbackCredentials = (props: Props) => { if (!config.azure.userIdentityFallbackCredentialsEnabled) { return ( - - <> + + Fallback credentials have been disabled. As user-based authentication only inherently supports requests with a user in scope, features such as alerting, recorded queries, or reporting will not function as expected. Please review the{' '} - documentation - {' '} + {' '} for more details. - + ); } return ( - - + +
- User-based authentication does not inherently support Grafana features that make requests to the data source - without a users details available to the request. An example of this is alerting. If you wish to ensure that - features that do not have a user in the context of the request still function, please provide fallback - credentials below. + + User-based authentication does not inherently support Grafana features that make requests to the data + source without a users details available to the request. An example of this is alerting. If you wish to + ensure that features that do not have a user in the context of the request still function, please provide + fallback credentials below. +
- Note: Features like alerting will be restricted to the access level of the fallback credentials rather - than the user. This may present confusion for users and should be clarified. + + Note: Features like alerting will be restricted to the access level of the fallback credentials rather + than the user. This may present confusion for users and should be clarified. +
{ <> {authTypeOptions.length > 0 && ( diff --git a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/DefaultSubscription.tsx b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/DefaultSubscription.tsx index dc2af88ab78..9c86a372e5b 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/DefaultSubscription.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ConfigEditor/DefaultSubscription.tsx @@ -2,6 +2,7 @@ import { useEffect, useReducer } from 'react'; import { AzureCredentials, isCredentialsComplete } from '@grafana/azure-sdk'; import { SelectableValue } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { Select, Button, Field } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; @@ -29,6 +30,7 @@ export const DefaultSubscription = (props: Props) => { } = props; const hasRequiredFields = isCredentialsComplete(credentials); const [loadSubscriptionsClicked, onLoadSubscriptions] = useReducer((val) => val + 1, 0); + const { t } = useTranslate(); useEffect(() => { if (!getSubscriptions || !hasRequiredFields) { @@ -69,14 +71,14 @@ export const DefaultSubscription = (props: Props) => { return ( <>
= ({ } }} /> - + ) : ( <> @@ -134,7 +138,7 @@ const AggregateItem: React.FC = ({ {!isCountAggregate ? ( !usedColumns.includes(opt.value!))} onChange={(e) => e.value && onChange(groupIndex, 'property', e.value, filterIndex)} /> updateFuzzySearch(selectedColumn, e.currentTarget.value)} /> - + ); }; diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/GroupBySection.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/GroupBySection.tsx index 11bf30f6354..0f8e0d191ef 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/GroupBySection.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/GroupBySection.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState, useRef } from 'react'; import { SelectableValue } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { EditorField, EditorFieldGroup, EditorList, EditorRow, InputGroup } from '@grafana/plugin-ui'; import { Button } from '@grafana/ui'; @@ -27,6 +28,7 @@ export const GroupBySection: React.FC = ({ allColumns, templateVariableOptions, }) => { + const { t } = useTranslate(); const builderQuery = query.azureLogAnalytics?.builderQuery; const prevTable = useRef(builderQuery?.from?.property.name || null); const [groupBys, setGroupBys] = useState( @@ -88,11 +90,12 @@ export const GroupBySection: React.FC = ({ {groupBys.length > 0 ? ( diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/KQLPreview.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/KQLPreview.tsx index 2d57d415b96..91819122f17 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/KQLPreview.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/KQLPreview.tsx @@ -3,6 +3,7 @@ import Prism from 'prismjs'; import React, { useEffect } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { EditorField, EditorFieldGroup, EditorRow } from '@grafana/plugin-ui'; import { Button, useStyles2 } from '@grafana/ui'; @@ -17,6 +18,7 @@ interface KQLPreviewProps { const KQLPreview: React.FC = ({ query, hidden, setHidden }) => { const styles = useStyles2(getStyles); + const { t } = useTranslate(); useEffect(() => { Prism.highlightAll(); @@ -25,10 +27,10 @@ const KQLPreview: React.FC = ({ query, hidden, setHidden }) => return ( - + <> diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LimitSection.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LimitSection.tsx index deea6a23396..aa7c9761f36 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LimitSection.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LimitSection.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; +import { useTranslate } from '@grafana/i18n'; import { EditorRow, EditorFieldGroup, EditorField } from '@grafana/plugin-ui'; import { Input } from '@grafana/ui'; @@ -11,6 +12,7 @@ interface LimitSectionProps { export const LimitSection: React.FC = (props) => { const { buildAndUpdateQuery } = props; + const { t } = useTranslate(); const [limit, setLimit] = useState(1000); const handleQueryLimitUpdate = (newLimit: number) => { @@ -22,11 +24,18 @@ export const LimitSection: React.FC = (props) => { return ( - + ) => { const newValue = e.target.value.replace(/[^0-9]/g, ''); diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LogsQueryBuilder.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LogsQueryBuilder.tsx index 1d08f93cc52..1e4ddec8216 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LogsQueryBuilder.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/LogsQueryBuilder.tsx @@ -1,6 +1,7 @@ import React, { useMemo, useState, useCallback } from 'react'; import { SelectableValue, TimeRange } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { EditorRows } from '@grafana/plugin-ui'; import { Alert } from '@grafana/ui'; @@ -48,6 +49,7 @@ interface LogsQueryBuilderProps { export const LogsQueryBuilder: React.FC = (props) => { const { query, onQueryChange, schema, datasource, timeRange, isLoadingSchema } = props; const [isKQLPreviewHidden, setIsKQLPreviewHidden] = useState(true); + const { t } = useTranslate(); const tables: AzureLogAnalyticsMetadataTable[] = useMemo(() => { return schema?.database?.tables || []; @@ -136,7 +138,13 @@ export const LogsQueryBuilder: React.FC = (props) => { {schema && tables.length === 0 && ( - + )} = ({ query, allColumn const builderQuery = query.azureLogAnalytics?.builderQuery; const prevTable = useRef(builderQuery?.from?.property.name || null); const hasLoadedOrderBy = useRef(false); + const { t } = useTranslate(); const [orderBy, setOrderBy] = useState( builderQuery?.orderBy?.expressions || [] @@ -109,24 +111,29 @@ export const OrderBySection: React.FC = ({ query, allColumn <> {orderBy.length > 0 ? ( orderBy.map((entry, index) => ( o.value === entry.order) || null} options={orderOptions} diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/TableSection.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/TableSection.tsx index bd879f00d53..43ccb24b1c7 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/TableSection.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/TableSection.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { SelectableValue } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { EditorField, EditorFieldGroup, EditorRow, InputGroup } from '@grafana/plugin-ui'; import { Button, Select } from '@grafana/ui'; @@ -26,6 +27,7 @@ interface TableSectionProps { export const TableSection: React.FC = (props) => { const { allColumns, query, tables, buildAndUpdateQuery, templateVariableOptions, isLoadingSchema } = props; + const { t } = useTranslate(); const builderQuery = query.azureLogAnalytics?.builderQuery; const selectedColumns = query.azureLogAnalytics?.builderQuery?.columns?.columns || []; @@ -139,27 +141,27 @@ export const TableSection: React.FC = (props) => { return ( - + { resources: T[]; @@ -16,6 +17,7 @@ const getStyles = (theme: GrafanaTheme2) => ({ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps) => { const styles = useStyles2(getStyles); + const { t } = useTranslate(); useEffect(() => { // Ensure there is at least one resource @@ -44,20 +46,19 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps
))} - ); diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx index bb6bbc043d8..4d74d73d574 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx @@ -2,6 +2,7 @@ import { css } from '@emotion/css'; import { useEffect, useMemo, useState } from 'react'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { reportInteraction } from '@grafana/runtime'; import { Button, @@ -41,6 +42,7 @@ const AzureCheatSheet = (props: AzureCheatSheetProps) => { const [isLoading, setIsLoading] = useState(true); const [searchInputValue, setSearchInputValue] = useState(''); const styles = useStyles2(getStyles); + const { t } = useTranslate(); const lang = { grammar: tokenizer, name: 'kql' }; const dropdownMenu = useMemo(() => { if (cheatsheetQueries) { @@ -144,17 +146,20 @@ const AzureCheatSheet = (props: AzureCheatSheetProps) => { const filteredQueries = filterQueriesBySearch(e.currentTarget.value); setVisibleQueries(filteredQueries); }} - placeholder="Search Logs queries" + placeholder={t('components.azure-cheat-sheet.placeholder-search-logs', 'Search Logs queries')} width={40} /> - + ({ }); const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps) => { + const { t } = useTranslate(); const styles = useStyles2(getStyles); useEffect(() => { @@ -60,7 +62,7 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps onCommonPropChange({ subscription: event.currentTarget.value })} - placeholder="aaaaaaaa-bbbb-cccc-dddd-eeeeeeee" + // eslint-disable-next-line @grafana/no-untranslated-strings + placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX" /> onCommonPropChange({ metricNamespace: event.currentTarget.value })} + // eslint-disable-next-line @grafana/no-untranslated-strings placeholder="Microsoft.Insights/metricNamespaces" /> onCommonPropChange({ region: event.currentTarget.value })} + // eslint-disable-next-line @grafana/no-untranslated-strings placeholder="northeurope" />
{resources.map((resource, index) => (
- {resources.length !== 1 && } + {resources.length !== 1 && ( + + )} onResourceChange(index, { ...resource, resourceGroup: event.currentTarget.value }) } + // eslint-disable-next-line @grafana/no-untranslated-strings placeholder="resource-group" /> removeResource(index)} @@ -139,7 +157,7 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps onResourceChange(index, { ...resource, resourceName: event.currentTarget.value })} - placeholder="name" + placeholder={t('components.advanced-resource-picker.placeholder-resource-name', 'name')} />
))}
- ); diff --git a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/AggregationField.tsx b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/AggregationField.tsx index b148263d631..06961437de3 100644 --- a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/AggregationField.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/AggregationField.tsx @@ -1,6 +1,7 @@ import { useCallback } from 'react'; import { SelectableValue } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { Select } from '@grafana/ui'; import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types'; @@ -21,6 +22,7 @@ const AggregationField = ({ aggregationOptions, isLoading, }: AggregationFieldProps) => { + const { t } = useTranslate(); const handleChange = useCallback( (change: SelectableValue) => { if (!change.value) { @@ -36,7 +38,7 @@ const AggregationField = ({ const options = addValueToOptions(aggregationOptions, variableOptionGroup, query.azureMonitor?.aggregation); return ( - + onFieldChange('dimension', item, e.value ?? '', onChange)} /> )} - + ); }; return ( - + ); diff --git a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/LegendFormatField.tsx b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/LegendFormatField.tsx index 143cf2bc94f..e4b30705a15 100644 --- a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/LegendFormatField.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/LegendFormatField.tsx @@ -1,6 +1,7 @@ import { useCallback, useState } from 'react'; import * as React from 'react'; +import { useTranslate } from '@grafana/i18n'; import { Input } from '@grafana/ui'; import { AzureQueryEditorFieldProps } from '../../types'; @@ -10,6 +11,7 @@ import { setLegendAlias } from './setQueryValue'; const LegendFormatField = ({ onQueryChange, query }: AzureQueryEditorFieldProps) => { const [value, setValue] = useState(query.azureMonitor?.alias ?? ''); + const { t } = useTranslate(); // As calling onQueryChange initiates a the datasource refresh, we only want to call it once // the field loses focus @@ -25,10 +27,10 @@ const LegendFormatField = ({ onQueryChange, query }: AzureQueryEditorFieldProps) }, [onQueryChange, query, value]); return ( - + { + const { t } = useTranslate(); const handleChange = useCallback( (change: SelectableValue) => { if (!change.value) { @@ -30,7 +32,10 @@ const MetricNameField = ({ metricNames, query, variableOptionGroup, onQueryChang const options = addValueToOptions(metricNames, variableOptionGroup, query.azureMonitor?.metricName); return ( - + { + const { t } = useTranslate(); const handleChange = useCallback( (change: SelectableValue) => { if (!change.value) { @@ -50,7 +52,7 @@ const TimeGrainField = ({ query, timeGrainOptions, variableOptionGroup, onQueryC }, [timeGrainOptions, variableOptionGroup, query.azureMonitor?.timeGrain]); return ( - + debounce(baseOnRunQuery, 500), [baseOnRunQuery]); const [azureLogsCheatSheetModalOpen, setAzureLogsCheatSheetModalOpen] = useState(false); const [defaultSubscriptionId, setDefaultSubscriptionId] = useState(''); + const { t } = useTranslate(); const onQueryChange = useCallback( (newQuery: AzureMonitorQuery) => { @@ -119,7 +121,13 @@ const QueryEditor = ({ {errorMessage && ( <> - + {errorMessage instanceof Error ? errorMessage.message : errorMessage} @@ -149,6 +157,7 @@ const EditorForQueryType = ({ onQueryChange, range, }: EditorForQueryTypeProps) => { + const { t } = useTranslate(); switch (query.queryType) { case AzureQueryType.AzureMonitor: return ( @@ -207,18 +216,19 @@ const EditorForQueryType = ({ default: const type = query.queryType as unknown; return ( - + {(type === 'Application Insights' || type === 'Insights Analytics') && ( <> - {type} was deprecated in Grafana 9. See the{' '} - - deprecation notice - {' '} - to get more information about how to migrate your queries. This is the current query definition: + + {{ type }} was deprecated in Grafana 9. See the{' '} + + deprecation notice + {' '} + to get more information about how to migrate your queries. This is the current query definition: + )} @@ -228,42 +238,48 @@ const EditorForQueryType = ({ }; const UserAuthAlert = () => { + const { t } = useTranslate(); return ( - - <> + + Usage of this data source requires you to be authenticated via Azure Entra (formerly Azure Active Directory). Please review the{' '} - documentation - {' '} + {' '} for more information. - + ); }; const UserAuthFallbackAlert = () => { + const { t } = useTranslate(); return ( - <> + Data source backend features (such as alerting) require service credentials to function. This data source is configured without service credential fallback, or the fallback functionality is disabled. Please review the{' '} - documentation - {' '} + {' '} for more information. - + ); }; diff --git a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryHeader.tsx b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryHeader.tsx index b13e76acead..91424d9a619 100644 --- a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryHeader.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryHeader.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { CoreApp, LoadingState, PanelData, SelectableValue } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { EditorHeader, FlexItem, InlineSelect } from '@grafana/plugin-ui'; import { config, reportInteraction } from '@grafana/runtime'; import { Button, ConfirmModal, RadioButtonGroup } from '@grafana/ui'; @@ -35,6 +36,7 @@ export const QueryHeader = ({ const [showModeSwitchWarning, setShowModeSwitchWarning] = useState(false); const [pendingModeChange, setPendingModeChange] = useState(null); + const { t } = useTranslate(); const currentMode = query.azureLogAnalytics?.mode; @@ -109,7 +111,7 @@ export const QueryHeader = ({ {query.queryType === AzureQueryType.LogAnalytics && query.azureLogAnalytics?.mode === LogsEditorMode.Raw && ( )} @@ -173,7 +175,7 @@ export const QueryHeader = ({ onClick={onRunQuery} data-testid={selectors.components.queryEditor.logsQueryEditor.runQuery.button} > - Run query + Run query )} diff --git a/public/app/plugins/datasource/azuremonitor/components/ResourceField/ResourceField.tsx b/public/app/plugins/datasource/azuremonitor/components/ResourceField/ResourceField.tsx index 7a9d0b3553f..968e14986df 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ResourceField/ResourceField.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ResourceField/ResourceField.tsx @@ -2,6 +2,7 @@ import { cx } from '@emotion/css'; import { useCallback, useEffect, useState } from 'react'; import * as React from 'react'; +import { Trans, useTranslate } from '@grafana/i18n'; import { Button, Icon, Modal, useStyles2, IconName } from '@grafana/ui'; import Datasource from '../../datasource'; @@ -42,6 +43,7 @@ const ResourceField = ({ }: Props) => { const styles = useStyles2(getStyles); const [pickerIsOpen, setPickerIsOpen] = useState(false); + const { t } = useTranslate(); const handleOpenPicker = useCallback(() => { setPickerIsOpen(true); @@ -63,7 +65,7 @@ const ResourceField = ({ - + @@ -104,7 +110,7 @@ const ResourceLabel = ({ resources, datasource }: ResourceLabelPropsSelect a resource; + return Select a resource; } return ; diff --git a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx index d300576a03a..d89488f22e6 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import * as React from 'react'; +import { useTranslate } from '@grafana/i18n'; import { Collapse, Space } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; @@ -14,12 +15,13 @@ export interface ResourcePickerProps { const AdvancedMulti = ({ resources, onChange, renderAdvanced }: ResourcePickerProps) => { const [isAdvancedOpen, setIsAdvancedOpen] = useState(!!resources.length && JSON.stringify(resources).includes('$')); + const { t } = useTranslate(); return (
setIsAdvancedOpen(!isAdvancedOpen)} > diff --git a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/NestedRow.tsx b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/NestedRow.tsx index 730f726d812..36821f816c2 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/NestedRow.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/NestedRow.tsx @@ -1,6 +1,7 @@ import { cx } from '@emotion/css'; import { useEffect, useState } from 'react'; +import { useTranslate } from '@grafana/i18n'; import { FadeTransition, LoadingPlaceholder, useStyles2 } from '@grafana/ui'; import { NestedEntry } from './NestedEntry'; @@ -31,6 +32,7 @@ const NestedRow = ({ }: NestedRowProps) => { const styles = useStyles2(getStyles); const [rowStatus, setRowStatus] = useState<'open' | 'closed' | 'loading'>('closed'); + const { t } = useTranslate(); const isSelected = !!selectedRows.find((v) => v.uri.toLowerCase() === row.uri.toLowerCase()); const isDisabled = !isSelected && disableRow(row, selectedRows); @@ -101,7 +103,10 @@ const NestedRow = ({ - + diff --git a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.test.tsx b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.test.tsx index 44095e1d20c..69ada6687fa 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.test.tsx @@ -260,7 +260,7 @@ describe('AzureMonitor ResourcePicker', () => { const searchRow1 = screen.queryByLabelText('search-result'); expect(searchRow1).not.toBeInTheDocument(); - const searchField = await screen.findByLabelText('resource search'); + const searchField = await screen.findByLabelText('Resource search'); expect(searchField).toBeInTheDocument(); await userEvent.type(searchField, 'sea'); @@ -275,7 +275,7 @@ describe('AzureMonitor ResourcePicker', () => { render(); - const searchField = await screen.findByLabelText('resource search'); + const searchField = await screen.findByLabelText('Resource search'); expect(searchField).toBeInTheDocument(); await userEvent.type(searchField, 'some search that has no results'); @@ -294,7 +294,7 @@ describe('AzureMonitor ResourcePicker', () => { render(); - const searchField = await screen.findByLabelText('resource search'); + const searchField = await screen.findByLabelText('Resource search'); expect(searchField).toBeInTheDocument(); await userEvent.type(searchField, 'sear'); @@ -320,7 +320,7 @@ describe('AzureMonitor ResourcePicker', () => { const searchRow1 = screen.queryByLabelText('search-result'); expect(searchRow1).not.toBeInTheDocument(); - const searchField = await screen.findByLabelText('resource search'); + const searchField = await screen.findByLabelText('Resource search'); expect(searchField).toBeInTheDocument(); await userEvent.type(searchField, 'sea'); diff --git a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.tsx b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.tsx index e5ed0431bd3..2a127bb4c58 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/ResourcePicker.tsx @@ -3,6 +3,7 @@ import { useCallback, useEffect, useState } from 'react'; import * as React from 'react'; import { useEffectOnce } from 'react-use'; +import { Trans, useTranslate } from '@grafana/i18n'; import { Alert, Button, LoadingPlaceholder, Modal, useStyles2, Space } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; @@ -42,6 +43,7 @@ const ResourcePicker = ({ selectionNotice, }: ResourcePickerProps) => { const styles = useStyles2(getStyles); + const { t } = useTranslate(); const [isLoading, setIsLoading] = useState(false); const [rows, setRows] = useState([]); @@ -173,7 +175,14 @@ const ResourcePicker = ({ <> {shouldShowLimitFlag ? ( -

Showing first {resourcePickerData.resultLimit} results

+

+ + Showing first {'{{numResults}}'} results + +

) : ( )} @@ -181,9 +190,15 @@ const ResourcePicker = ({ - - - + + +
ScopeTypeLocation + Scope + + Type + + Location +
@@ -194,14 +209,14 @@ const ResourcePicker = ({ {isLoading && ( - + )} {!isLoading && rows.length === 0 && ( - No resources found + No resources found )} @@ -226,7 +241,9 @@ const ResourcePicker = ({