I18n: Move eslint rule to package (#105860)

pull/106209/head
Tom Ratcliffe 4 weeks ago committed by GitHub
parent 1de0cd5d68
commit abb885c585
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .betterer.eslint.config.js
  2. 6
      eslint.config.js
  3. 181
      packages/grafana-eslint-rules/README.md
  4. 4
      packages/grafana-eslint-rules/index.cjs
  5. 4
      packages/grafana-i18n/package.json
  6. 185
      packages/grafana-i18n/src/eslint/README.md
  7. 9
      packages/grafana-i18n/src/eslint/index.cjs
  8. 2
      packages/grafana-i18n/src/eslint/no-translation-top-level/no-translation-top-level.cjs
  9. 34
      packages/grafana-i18n/src/eslint/no-translation-top-level/no-translation-top-level.test.js
  10. 7
      packages/grafana-i18n/src/eslint/no-untranslated-strings/no-untranslated-strings.cjs
  11. 43
      packages/grafana-i18n/src/eslint/no-untranslated-strings/no-untranslated-strings.test.js
  12. 0
      packages/grafana-i18n/src/eslint/no-untranslated-strings/translation-utils.cjs
  13. 1
      packages/grafana-ui/src/components/Combobox/MultiCombobox.tsx
  14. 2
      packages/grafana-ui/src/components/DataSourceSettings/CustomHeadersSettings.tsx
  15. 2
      packages/grafana-ui/src/components/DataSourceSettings/TLSAuthSettings.tsx
  16. 4
      packages/grafana-ui/src/components/ThemeDemos/BorderRadius.tsx
  17. 2
      packages/grafana-ui/src/components/ThemeDemos/EmotionPerfTest.tsx
  18. 2
      packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx
  19. 2
      packages/grafana-ui/src/components/UsersIndicator/UsersIndicator.tsx
  20. 2
      packages/grafana-ui/src/options/builder/axis.tsx
  21. 2
      public/app/core/components/AppChrome/History/HistoryWrapper.tsx
  22. 2
      public/app/features/actions/ActionVariablesEditor.tsx
  23. 4
      public/app/features/admin/UpgradePage.tsx
  24. 8
      public/app/features/admin/ldap/LdapDrawer.tsx
  25. 1
      public/app/features/alerting/unified/components/expressions/Expression.tsx
  26. 2
      public/app/features/alerting/unified/components/notification-policies/PromDurationDocs.tsx
  27. 16
      public/app/features/alerting/unified/components/receivers/editor/templateDataSuggestions.ts
  28. 2
      public/app/features/alerting/unified/components/receivers/form/fields/SubformArrayField.tsx
  29. 2
      public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.tsx
  30. 4
      public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx
  31. 4
      public/app/features/alerting/unified/components/rules/RuleDetails.tsx
  32. 2
      public/app/features/alerting/unified/components/rules/RulesTable.tsx
  33. 2
      public/app/features/alerting/unified/components/silences/SilencesFilter.tsx
  34. 2
      public/app/features/alerting/unified/components/silences/SilencesTable.tsx
  35. 1
      public/app/features/alerting/unified/testSetup/plugins.ts
  36. 8
      public/app/features/auth-config/fields.tsx
  37. 3
      public/app/features/canvas/runtime/ables.tsx
  38. 2
      public/app/features/canvas/runtime/frame.tsx
  39. 1
      public/app/features/commandPalette/ResultItem.tsx
  40. 4
      public/app/features/commandPalette/actions/staticActions.ts
  41. 2
      public/app/features/correlations/Forms/ConfigureCorrelationSourceForm.tsx
  42. 2
      public/app/features/correlations/Forms/TransformationEditorRow.tsx
  43. 2
      public/app/features/dashboard-scene/embedding/EmbeddedDashboardTestPage.tsx
  44. 1
      public/app/features/dashboard-scene/settings/variables/VariableSetEditableElement.tsx
  45. 2
      public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx
  46. 2
      public/app/features/dashboard-scene/settings/variables/components/DataSourceVariableForm.tsx
  47. 4
      public/app/features/dashboard-scene/settings/variables/components/IntervalVariableForm.tsx
  48. 2
      public/app/features/dashboard-scene/settings/variables/components/QueryVariableForm.tsx
  49. 2
      public/app/features/dashboard-scene/settings/variables/editors/QueryVariableEditor.tsx
  50. 2
      public/app/features/dashboard/components/DashboardSettings/TimePickerSettings.tsx
  51. 2
      public/app/features/dashboard/components/ShareModal/SharePublicDashboard/ConfigPublicDashboard/EmailSharingConfiguration.tsx
  52. 2
      public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx
  53. 2
      public/app/features/dimensions/editors/ResourceDimensionEditor.tsx
  54. 5
      public/app/features/dimensions/editors/ThresholdsEditor/ThresholdsEditor.tsx
  55. 10
      public/app/features/dimensions/editors/ValueMappingsEditor/ValueMappingEditRow.tsx
  56. 2
      public/app/features/explore/ExploreRunQueryButton.tsx
  57. 6
      public/app/features/explore/Logs/LogsMetaRow.tsx
  58. 4
      public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx
  59. 4
      public/app/features/explore/TraceView/components/TracePageHeader/TracePageHeader.tsx
  60. 6
      public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx
  61. 6
      public/app/features/explore/extensions/getExploreExtensionConfigs.tsx
  62. 2
      public/app/features/invites/SignupInvited.tsx
  63. 2
      public/app/features/manage-dashboards/DashboardImportPage.tsx
  64. 2
      public/app/features/migrate-to-cloud/shared/AlertWithTraceID.tsx
  65. 2
      public/app/features/org/UserInviteForm.tsx
  66. 2
      public/app/features/plugins/admin/components/PluginDetailsPanel.tsx
  67. 4
      public/app/features/plugins/components/PluginPageContext.tsx
  68. 2
      public/app/features/plugins/extensions/utils.tsx
  69. 11
      public/app/features/provisioning/Shared/TokenPermissionsInfo.tsx
  70. 1
      public/app/features/provisioning/Wizard/ProvisioningWizard.tsx
  71. 1
      public/app/features/query/components/QueryEditorRowHeader.tsx
  72. 12
      public/app/features/query/components/QueryGroupOptions.tsx
  73. 2
      public/app/features/sandbox/TestStuffPage.tsx
  74. 2
      public/app/features/teams/CreateTeam.tsx
  75. 2
      public/app/features/teams/TeamSettings.tsx
  76. 7
      public/app/features/transformers/editors/CalculateFieldTransformerEditor/UnaryOperationEditor.tsx
  77. 6
      public/app/features/transformers/editors/ConvertFieldTypeTransformerEditor.tsx
  78. 2
      public/app/features/transformers/editors/FormatTimeTransformerEditor.tsx
  79. 2
      public/app/features/transformers/editors/JoinByFieldTransformerEditor.tsx
  80. 4
      public/app/features/transformers/extractFields/ExtractFieldsTransformerEditor.tsx
  81. 1
      public/app/features/transformers/joinByLabels/JoinByLabelsTransformerEditor.tsx
  82. 2
      public/app/features/variables/interval/reducer.ts
  83. 2
      public/app/features/visualization/data-hover/DataHoverRows.tsx
  84. 6
      public/app/plugins/datasource/azuremonitor/components/ConfigEditor/AppRegistrationCredentials.tsx
  85. 2
      public/app/plugins/datasource/azuremonitor/components/ConfigEditor/ConfigEditor.tsx
  86. 2
      public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AdvancedResourcePicker.tsx
  87. 8
      public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/AdvancedResourcePicker.tsx
  88. 12
      public/app/plugins/datasource/mssql/CheatSheet.tsx
  89. 6
      public/app/plugins/datasource/mssql/azureauth/AzureCredentialsForm.tsx
  90. 6
      public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx
  91. 10
      public/app/plugins/datasource/mssql/configuration/Kerberos.tsx
  92. 1
      public/locales/en-US/grafana.json
  93. 2
      public/swagger/SwaggerPage.tsx

@ -10,6 +10,7 @@ const testingLibraryPlugin = require('eslint-plugin-testing-library');
const grafanaConfig = require('@grafana/eslint-config/flat'); const grafanaConfig = require('@grafana/eslint-config/flat');
const grafanaPlugin = require('@grafana/eslint-plugin'); const grafanaPlugin = require('@grafana/eslint-plugin');
const grafanaI18nPlugin = require('@grafana/i18n/eslint-plugin');
// Include the Grafana config and remove the rules, // Include the Grafana config and remove the rules,
// as we just want to pull in all of the necessary configuration but not run the rules // as we just want to pull in all of the necessary configuration but not run the rules
@ -65,6 +66,7 @@ module.exports = [
'no-barrel-files': barrelPlugin, 'no-barrel-files': barrelPlugin,
'@grafana': grafanaPlugin, '@grafana': grafanaPlugin,
'testing-library': testingLibraryPlugin, 'testing-library': testingLibraryPlugin,
'@grafana/i18n': grafanaI18nPlugin,
}, },
linterOptions: { linterOptions: {
// This reports unused disable directives that we can clean up but // This reports unused disable directives that we can clean up but

@ -13,6 +13,7 @@ const unicornPlugin = require('eslint-plugin-unicorn');
const grafanaConfig = require('@grafana/eslint-config/flat'); const grafanaConfig = require('@grafana/eslint-config/flat');
const grafanaPlugin = require('@grafana/eslint-plugin'); const grafanaPlugin = require('@grafana/eslint-plugin');
const grafanaI18nPlugin = require('@grafana/i18n/eslint-plugin');
const bettererConfig = require('./.betterer.eslint.config'); const bettererConfig = require('./.betterer.eslint.config');
const getEnvConfig = require('./scripts/webpack/env-util'); const getEnvConfig = require('./scripts/webpack/env-util');
@ -294,6 +295,7 @@ module.exports = [
name: 'grafana/i18n-overrides', name: 'grafana/i18n-overrides',
plugins: { plugins: {
'@grafana': grafanaPlugin, '@grafana': grafanaPlugin,
'@grafana/i18n': grafanaI18nPlugin,
}, },
files: [ files: [
'public/app/!(plugins)/**/*.{ts,tsx,js,jsx}', 'public/app/!(plugins)/**/*.{ts,tsx,js,jsx}',
@ -308,8 +310,8 @@ module.exports = [
'**/mock*.{ts,tsx}', '**/mock*.{ts,tsx}',
], ],
rules: { rules: {
'@grafana/no-untranslated-strings': ['error', { calleesToIgnore: ['^css$', 'use[A-Z].*'] }], '@grafana/i18n/no-untranslated-strings': ['error', { calleesToIgnore: ['^css$', 'use[A-Z].*'] }],
'@grafana/no-translation-top-level': 'error', '@grafana/i18n/no-translation-top-level': 'error',
}, },
}, },
{ {

@ -111,184 +111,3 @@ const getStyles = (theme: GrafanaTheme2) => ({
### `theme-token-usage` ### `theme-token-usage`
Used to find all instances of `theme` tokens being used in the codebase and emit the counts as metrics. Should **not** be used as an actual lint rule! Used to find all instances of `theme` tokens being used in the codebase and emit the counts as metrics. Should **not** be used as an actual lint rule!
### `no-untranslated-strings`
Check if strings are marked for translation inside JSX Elements, in certain JSX props, and in certain object properties.
### Options
#### `forceFix`
Allows specifying directories that, if the file is present within, then the rule will automatically fix the errors. This is primarily a workaround to allow for automatic mark up of new violations as the rule evolves.
#### Example:
```ts
{
'@grafana/no-untranslated-strings': ['error', { forceFix: ['app/features/some-feature'] }],
}
```
#### `calleesToIgnore`
Allows specifying regexes for methods that should be ignored when checking if object properties are untranslated.
This is particularly useful to exclude references to properties such as `label` inside `css()` calls.
#### Example:
```ts
{
'@grafana/no-untranslated-strings': ['error', { calleesToIgnore: ['^css$'] }],
}
// The below would not be reported as an error
const foo = css({
label: 'test',
});
// The below would still be reported as an error
const bar = {
label: 'test',
};
```
#### JSXText
```tsx
// Bad ❌
<InlineToast placement="top" referenceElement={buttonRef.current}>
Copied
</InlineToast>
// Good ✅
<InlineToast placement="top" referenceElement={buttonRef.current}>
<Trans i18nKey="clipboard-button.inline-toast.success">Copied</Trans>
</InlineToast>
```
#### JSXAttributes
```tsx
// Bad ❌
<div title="foo bar" />
// Good ✅
<div title={t('some.key.foo-bar', 'foo bar')} />
```
#### Object properties
```tsx
// Bad ❌
const someConfig = {
label: 'Some label',
};
// Good ✅
const getSomeConfig = () => ({
label: t('some.key.label', 'Some label'),
});
```
#### Passing variables to translations
```tsx
// Bad ❌
const SearchTitle = ({ term }) => <div>Results for {term}</div>;
// Good ✅
const SearchTitle = ({ term }) => <Trans i18nKey="search-page.results-title">Results for {{ term }}</Trans>;
// Good ✅ (if you need to interpolate variables inside nested components)
const SearchTerm = ({ term }) => <Text color="success">{term}</Text>;
const SearchTitle = ({ term }) => (
<Trans i18nKey="search-page.results-title">
Results for <SearchTerm term={term} />
</Trans>
);
// Good ✅ (if you need to interpolate variables and additional translated strings inside nested components)
const SearchTitle = ({ term }) => (
<Trans i18nKey="search-page.results-title" values={{ myVariable: term }}>
Results for <Text color="success">{'{{ myVariable }}'} and this translated text is also in green</Text>
</Trans>
);
```
#### How to translate props or attributes
This rule checks if a string is wrapped up by the `Trans` tag, or if certain props contain untranslated strings.
We ask for such props to be translated with the `t()` function.
The below props are checked for untranslated strings:
- `label`
- `description`
- `placeholder`
- `aria-label`
- `title`
- `subTitle`
- `text`
- `tooltip`
- `message`
- `name`
```tsx
// Bad ❌
<input type="value" placeholder={'Username'} />;
// Good ✅
const placeholder = t('form.username-placeholder', 'Username');
return <input type="value" placeholder={placeholder} />;
```
Check more info about how translations work in Grafana in [Internationalization.md](https://github.com/grafana/grafana/blob/main/contribute/internationalization.md)
### `no-translation-top-level`
Ensure that `t()` translation method is not used at the top level of a file, outside of a component of method.
This is to prevent calling the translation method before it's been instantiated.
This does not cause an error if a file is lazily loaded, but refactors can cause errors, and it can cause problems in tests.
Fix the
```tsx
// Bad ❌
const someTranslatedText = t('some.key', 'Some text');
const SomeComponent = () => {
return <div title={someTranslatedText} />;
};
// Good ✅
const SomeComponent = () => {
const someTranslatedText = t('some.key', 'Some text');
return <div title={someTranslatedText} />;
};
// Bad ❌
const someConfigThatHasToBeShared = [{ foo: t('some.key', 'Some text') }];
const SomeComponent = () => {
return (
<div>
{someConfigThatHasToBeShared.map((cfg) => {
return <div>{cfg.foo}</div>;
})}
</div>
);
};
// Good ✅
const someConfigThatHasToBeShared = () => [{ foo: t('some.key', 'Some text') }];
const SomeComponent = () => {
const configs = someConfigThatHasToBeShared();
return (
<div>
{configs.map((cfg) => {
return <div>{cfg.foo}</div>;
})}
</div>
);
};
```

@ -1,8 +1,6 @@
const noAriaLabelSelectors = require('./rules/no-aria-label-e2e-selectors.cjs'); const noAriaLabelSelectors = require('./rules/no-aria-label-e2e-selectors.cjs');
const noBorderRadiusLiteral = require('./rules/no-border-radius-literal.cjs'); const noBorderRadiusLiteral = require('./rules/no-border-radius-literal.cjs');
const noUnreducedMotion = require('./rules/no-unreduced-motion.cjs'); const noUnreducedMotion = require('./rules/no-unreduced-motion.cjs');
const noUntranslatedStrings = require('./rules/no-untranslated-strings.cjs');
const noTranslationTopLevel = require('./rules/no-translation-top-level.cjs');
const themeTokenUsage = require('./rules/theme-token-usage.cjs'); const themeTokenUsage = require('./rules/theme-token-usage.cjs');
const noRestrictedImgSrcs = require('./rules/no-restricted-img-srcs.cjs'); const noRestrictedImgSrcs = require('./rules/no-restricted-img-srcs.cjs');
@ -12,8 +10,6 @@ module.exports = {
'no-aria-label-selectors': noAriaLabelSelectors, 'no-aria-label-selectors': noAriaLabelSelectors,
'no-border-radius-literal': noBorderRadiusLiteral, 'no-border-radius-literal': noBorderRadiusLiteral,
'theme-token-usage': themeTokenUsage, 'theme-token-usage': themeTokenUsage,
'no-untranslated-strings': noUntranslatedStrings,
'no-translation-top-level': noTranslationTopLevel,
'no-restricted-img-srcs': noRestrictedImgSrcs, 'no-restricted-img-srcs': noRestrictedImgSrcs,
}, },
}; };

@ -26,6 +26,10 @@
"./internal": { "./internal": {
"import": "./src/internal/index.ts", "import": "./src/internal/index.ts",
"require": "./src/internal/index.ts" "require": "./src/internal/index.ts"
},
"./eslint-plugin": {
"import": "./src/eslint/index.cjs",
"require": "./src/eslint/index.cjs"
} }
}, },
"publishConfig": { "publishConfig": {

@ -0,0 +1,185 @@
# Grafana Internationalization ESLint Rules
This package also contains custom i18n eslint rules for use within the Grafana codebase and plugins.
## Rules
### `no-untranslated-strings`
Check if strings are marked for translation inside JSX Elements, in certain JSX props, and in certain object properties.
### Options
#### `forceFix`
Allows specifying directories that, if the file is present within, then the rule will automatically fix the errors. This is primarily a workaround to allow for automatic mark up of new violations as the rule evolves.
#### Example:
```ts
{
'@grafana/i18n/no-untranslated-strings': ['error', { forceFix: ['app/features/some-feature'] }],
}
```
#### `calleesToIgnore`
Allows specifying regexes for methods that should be ignored when checking if object properties are untranslated.
This is particularly useful to exclude references to properties such as `label` inside `css()` calls.
#### Example:
```ts
{
'@grafana/i18n/no-untranslated-strings': ['error', { calleesToIgnore: ['^css$'] }],
}
// The below would not be reported as an error
const foo = css({
label: 'test',
});
// The below would still be reported as an error
const bar = {
label: 'test',
};
```
#### JSXText
```tsx
// Bad ❌
<InlineToast placement="top" referenceElement={buttonRef.current}>
Copied
</InlineToast>
// Good ✅
<InlineToast placement="top" referenceElement={buttonRef.current}>
<Trans i18nKey="clipboard-button.inline-toast.success">Copied</Trans>
</InlineToast>
```
#### JSXAttributes
```tsx
// Bad ❌
<div title="foo bar" />
// Good ✅
<div title={t('some.key.foo-bar', 'foo bar')} />
```
#### Object properties
```tsx
// Bad ❌
const someConfig = {
label: 'Some label',
};
// Good ✅
const getSomeConfig = () => ({
label: t('some.key.label', 'Some label'),
});
```
#### Passing variables to translations
```tsx
// Bad ❌
const SearchTitle = ({ term }) => <div>Results for {term}</div>;
// Good ✅
const SearchTitle = ({ term }) => <Trans i18nKey="search-page.results-title">Results for {{ term }}</Trans>;
// Good ✅ (if you need to interpolate variables inside nested components)
const SearchTerm = ({ term }) => <Text color="success">{term}</Text>;
const SearchTitle = ({ term }) => (
<Trans i18nKey="search-page.results-title">
Results for <SearchTerm term={term} />
</Trans>
);
// Good ✅ (if you need to interpolate variables and additional translated strings inside nested components)
const SearchTitle = ({ term }) => (
<Trans i18nKey="search-page.results-title" values={{ myVariable: term }}>
Results for <Text color="success">{'{{ myVariable }}'} and this translated text is also in green</Text>
</Trans>
);
```
#### How to translate props or attributes
This rule checks if a string is wrapped up by the `Trans` tag, or if certain props contain untranslated strings.
We ask for such props to be translated with the `t()` function.
The below props are checked for untranslated strings:
- `label`
- `description`
- `placeholder`
- `aria-label`
- `title`
- `subTitle`
- `text`
- `tooltip`
- `message`
- `name`
```tsx
// Bad ❌
<input type="value" placeholder={'Username'} />;
// Good ✅
const placeholder = t('form.username-placeholder', 'Username');
return <input type="value" placeholder={placeholder} />;
```
Check more info about how translations work in Grafana in [Internationalization.md](https://github.com/grafana/grafana/blob/main/contribute/internationalization.md)
### `no-translation-top-level`
Ensure that `t()` translation method is not used at the top level of a file, outside of a component of method.
This is to prevent calling the translation method before it's been instantiated.
This does not cause an error if a file is lazily loaded, but refactors can cause errors, and it can cause problems in tests.
```tsx
// Bad ❌
const someTranslatedText = t('some.key', 'Some text');
const SomeComponent = () => {
return <div title={someTranslatedText} />;
};
// Good ✅
const SomeComponent = () => {
const someTranslatedText = t('some.key', 'Some text');
return <div title={someTranslatedText} />;
};
// Bad ❌
const someConfigThatHasToBeShared = [{ foo: t('some.key', 'Some text') }];
const SomeComponent = () => {
return (
<div>
{someConfigThatHasToBeShared.map((cfg) => {
return <div>{cfg.foo}</div>;
})}
</div>
);
};
// Good ✅
const getSomeConfigThatHasToBeShared = () => [{ foo: t('some.key', 'Some text') }];
const SomeComponent = () => {
const configs = getSomeConfigThatHasToBeShared();
return (
<div>
{configs.map((cfg) => {
return <div>{cfg.foo}</div>;
})}
</div>
);
};
```

@ -0,0 +1,9 @@
const noUntranslatedStrings = require('./no-untranslated-strings/no-untranslated-strings.cjs');
const noTranslationTopLevel = require('./no-translation-top-level/no-translation-top-level.cjs');
module.exports = {
rules: {
'no-untranslated-strings': noUntranslatedStrings,
'no-translation-top-level': noTranslationTopLevel,
},
};

@ -7,7 +7,7 @@ const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
*/ */
const createRule = ESLintUtils.RuleCreator( const createRule = ESLintUtils.RuleCreator(
(name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}` (name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-i18n/src/eslint/README.md#${name}`
); );
/** /**

@ -1,8 +1,8 @@
import { RuleTester } from 'eslint'; import { RuleTester } from 'eslint';
import noTranslationTopLevel from '../rules/no-translation-top-level.cjs'; import noTranslationTopLevel from './no-translation-top-level.cjs';
RuleTester.setDefaultConfig({ const ruleTester = new RuleTester({
languageOptions: { languageOptions: {
ecmaVersion: 2018, ecmaVersion: 2018,
sourceType: 'module', sourceType: 'module',
@ -14,13 +14,11 @@ RuleTester.setDefaultConfig({
}, },
}); });
const expectedErrorMessage = 'Do not use the t() function outside of a component or function'; // @ts-ignore
const ruleTester = new RuleTester();
ruleTester.run('eslint no-translation-top-level', noTranslationTopLevel, { ruleTester.run('eslint no-translation-top-level', noTranslationTopLevel, {
valid: [ valid: [
{ {
name: 'invocation inside component',
code: ` code: `
function Component() { function Component() {
return <div>{t('some.key', 'Some text')}</div>; return <div>{t('some.key', 'Some text')}</div>;
@ -28,31 +26,37 @@ function Component() {
`, `,
}, },
{ {
name: 'invocation inside function',
code: `const foo = () => t('some.key', 'Some text');`, code: `const foo = () => t('some.key', 'Some text');`,
}, },
{ {
code: `const foo = ttt('some.key', 'Some text');`, name: 'invocation inside class component',
code: `class Component {
render() {
return t('some.key', 'Some text');
}
}`,
}, },
{ {
code: `class Component { name: 'invocation of something not named t at top level',
render() { code: `const foo = ttt('some.key', 'Some text');`,
return t('some.key', 'Some text');
}
}`,
}, },
], ],
invalid: [ invalid: [
{ {
name: 'invocation at top level',
code: `const thing = t('some.key', 'Some text');`, code: `const thing = t('some.key', 'Some text');`,
errors: [{ message: expectedErrorMessage }], errors: 1,
}, },
{ {
name: 'invocation in array',
code: `const things = [t('some.key', 'Some text')];`, code: `const things = [t('some.key', 'Some text')];`,
errors: [{ message: expectedErrorMessage }], errors: 1,
}, },
{ {
name: 'invocation in object',
code: `const objectThings = [{foo: t('some.key', 'Some text')}];`, code: `const objectThings = [{foo: t('some.key', 'Some text')}];`,
errors: [{ message: expectedErrorMessage }], errors: 1,
}, },
], ],
}); });

@ -18,7 +18,7 @@ const {
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils'); const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
const createRule = ESLintUtils.RuleCreator( const createRule = ESLintUtils.RuleCreator(
(name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}` (name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-i18n/src/eslint/README.md#${name}`
); );
/** /**
@ -224,7 +224,10 @@ const noUntranslatedStrings = createRule({
// We don't want to report if the parent has a text node, // We don't want to report if the parent has a text node,
// as we'd end up doing it twice. This makes it awkward for us to auto fix // as we'd end up doing it twice. This makes it awkward for us to auto fix
const parentHasText = parentHasChildren const parentHasText = parentHasChildren
? parent.children.some((child) => child.type === AST_NODE_TYPES.JSXText && getNodeValue(child).trim()) ? parent.children.some((child) => {
const childValue = getNodeValue(child).trim();
return child.type === AST_NODE_TYPES.JSXText && childValue && !isStringNonAlphanumeric(childValue);
})
: false; : false;
if (untranslatedTextNodes.length && !parentHasText) { if (untranslatedTextNodes.length && !parentHasText) {

@ -1,8 +1,17 @@
import { RuleTester } from 'eslint'; import { RuleTester } from 'eslint';
import noUntranslatedStrings from '../rules/no-untranslated-strings.cjs'; import noUntranslatedStrings from './no-untranslated-strings.cjs';
RuleTester.setDefaultConfig({ const filename = 'public/app/features/some-feature/nested/SomeFile.tsx';
const packageName = '@grafana/i18n';
const TRANS_IMPORT = `import { Trans } from '${packageName}';`;
const T_IMPORT = `import { t } from '${packageName}/internal';`;
const USE_TRANSLATE_IMPORT = `import { useTranslate } from '${packageName}';`;
const TRANS_AND_USE_TRANSLATE_IMPORT = `import { Trans, useTranslate } from '${packageName}';`;
const ruleTester = new RuleTester({
languageOptions: { languageOptions: {
ecmaVersion: 2018, ecmaVersion: 2018,
sourceType: 'module', sourceType: 'module',
@ -14,17 +23,7 @@ RuleTester.setDefaultConfig({
}, },
}); });
const filename = 'public/app/features/some-feature/nested/SomeFile.tsx'; //@ts-ignore
const packageName = '@grafana/i18n';
const TRANS_IMPORT = `import { Trans } from '${packageName}';`;
const T_IMPORT = `import { t } from '${packageName}/internal';`;
const USE_TRANSLATE_IMPORT = `import { useTranslate } from '${packageName}';`;
const TRANS_AND_USE_TRANSLATE_IMPORT = `import { Trans, useTranslate } from '${packageName}';`;
const ruleTester = new RuleTester();
ruleTester.run('eslint no-untranslated-strings', noUntranslatedStrings, { ruleTester.run('eslint no-untranslated-strings', noUntranslatedStrings, {
test: [], test: [],
valid: [ valid: [
@ -212,6 +211,24 @@ const Foo = () => <div><Trans i18nKey="some-feature.foo.untranslated-text">Untra
], ],
}, },
{
name: 'non-alphanumeric characters outside child element',
code: `
const Foo = () => {
return (
<>
<div>
something untranslated but i'm a naughty dev and
I put a bunch of non-alphanumeric characters outside of the div
</div>
.?!;
</>
)
}`,
filename,
errors: 1,
},
{ {
name: 'Text inside JSXElement, not in a function', name: 'Text inside JSXElement, not in a function',
code: ` code: `

@ -267,7 +267,6 @@ export const MultiCombobox = <T extends string | number>(props: MultiComboboxPro
))} ))}
{selectedItems.length > visibleItems.length && ( {selectedItems.length > visibleItems.length && (
<Box display="flex" direction="row" marginLeft={0.5} gap={1} ref={counterMeasureRef}> <Box display="flex" direction="row" marginLeft={0.5} gap={1} ref={counterMeasureRef}>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
<Text>...</Text> <Text>...</Text>
<Tooltip <Tooltip
interactive interactive

@ -61,7 +61,7 @@ const CustomHeaderRow = ({ header, onBlur, onChange, onRemove, onReset }: Custom
<FormField <FormField
label={t('grafana-ui.data-source-settings.custom-headers-header', 'Header')} label={t('grafana-ui.data-source-settings.custom-headers-header', 'Header')}
name="name" name="name"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="X-Custom-Header" placeholder="X-Custom-Header"
labelWidth={5} labelWidth={5}
value={header.name || ''} value={header.name || ''}

@ -98,7 +98,7 @@ export const TLSAuthSettings = ({ dataSourceConfig, onChange }: HttpSettingsBase
label={t('grafana-ui.data-source-settings.tls-server-name-label', 'ServerName')} label={t('grafana-ui.data-source-settings.tls-server-name-label', 'ServerName')}
labelWidth={7} labelWidth={7}
inputWidth={30} inputWidth={30}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="domain.example.com" placeholder="domain.example.com"
value={hasServerName && dataSourceConfig.jsonData.serverName} value={hasServerName && dataSourceConfig.jsonData.serverName}
onChange={onServerNameLabelChange} onChange={onServerNameLabelChange}

@ -24,14 +24,14 @@ export const BorderRadiusContainer = ({
return ( return (
<Stack direction="column" alignItems="center" gap={4}> <Stack direction="column" alignItems="center" gap={4}>
<Stack alignItems="center"> <Stack alignItems="center">
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Text variant="code">getInternalRadius</Text> <Text variant="code">getInternalRadius</Text>
<div className={styles.baseForInternal}> <div className={styles.baseForInternal}>
<div className={styles.internalContainer} /> <div className={styles.internalContainer} />
</div> </div>
</Stack> </Stack>
<Stack alignItems="center"> <Stack alignItems="center">
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Text variant="code">getExternalRadius</Text> <Text variant="code">getExternalRadius</Text>
<div className={styles.externalContainer}> <div className={styles.externalContainer}>
<div className={styles.baseForExternal} /> <div className={styles.baseForExternal} />

@ -1,4 +1,4 @@
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
/** @jsxImportSource @emotion/react */ /** @jsxImportSource @emotion/react */
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import classnames from 'classnames'; import classnames from 'classnames';

@ -1,4 +1,4 @@
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import { useState } from 'react'; import { useState } from 'react';
import * as React from 'react'; import * as React from 'react';

@ -36,7 +36,7 @@ export const UsersIndicator = ({ users, onClick, limit = 4 }: UsersIndicatorProp
{limitReached && ( {limitReached && (
<UserIcon onClick={onClick} userView={{ user: { name: 'Extra users' }, lastActiveAt: '' }} showTooltip={false}> <UserIcon onClick={onClick} userView={{ user: { name: 'Extra users' }, lastActiveAt: '' }} showTooltip={false}>
{tooManyUsers {tooManyUsers
? // eslint-disable-next-line @grafana/no-untranslated-strings ? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
'...' '...'
: `+${extraUsers}`} : `+${extraUsers}`}
</UserIcon> </UserIcon>

@ -179,7 +179,7 @@ export const ScaleDistributionEditor = ({ value, onChange }: StandardEditorProps
{type === ScaleDistribution.Symlog && ( {type === ScaleDistribution.Symlog && (
<Field label={t('grafana-ui.axis-builder.linear-threshold', 'Linear threshold')} style={{ marginBottom: 0 }}> <Field label={t('grafana-ui.axis-builder.linear-threshold', 'Linear threshold')} style={{ marginBottom: 0 }}>
<Input <Input
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="1" placeholder="1"
value={value?.linearThreshold} value={value?.linearThreshold}
onChange={(v) => { onChange={(v) => {

@ -139,7 +139,7 @@ function HistoryEntryAppView({ entry, isSelected, onClick }: ItemProps) {
<Text key={index}> <Text key={index}>
{breadcrumb.text}{' '} {breadcrumb.text}{' '}
{index !== breadcrumbs.length - 1 {index !== breadcrumbs.length - 1
? // eslint-disable-next-line @grafana/no-untranslated-strings ? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
'> ' '> '
: ''} : ''}
</Text> </Text>

@ -56,7 +56,7 @@ export const ActionVariablesEditor = ({ value, onChange }: Props) => {
const variableTypeOptions: ComboboxOption[] = [ const variableTypeOptions: ComboboxOption[] = [
{ {
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label: 'string', label: 'string',
value: ActionVariableType.String, value: ActionVariableType.String,
}, },

@ -200,7 +200,7 @@ const FeatureListing = () => {
</Item> </Item>
<Item title={t('admin.feature-listing.title-enterprise-plugins', 'Enterprise plugins')}> <Item title={t('admin.feature-listing.title-enterprise-plugins', 'Enterprise plugins')}>
<List nested={true}> <List nested={true}>
{/* eslint-disable @grafana/no-untranslated-strings */} {/* eslint-disable @grafana/i18n/no-untranslated-strings */}
<Item title="Oracle" /> <Item title="Oracle" />
<Item title="Splunk" /> <Item title="Splunk" />
<Item title="Service Now" /> <Item title="Service Now" />
@ -216,7 +216,7 @@ const FeatureListing = () => {
<Item title="Salesforce" /> <Item title="Salesforce" />
<Item title="Snowflake" /> <Item title="Snowflake" />
<Item title="Wavefront" /> <Item title="Wavefront" />
{/* eslint-enable @grafana/no-untranslated-strings */} {/* eslint-enable @grafana/i18n/no-untranslated-strings */}
</List> </List>
</Item> </Item>
</List> </List>

@ -99,7 +99,7 @@ export const LdapDrawerComponent = ({
<Trans i18nKey="ldap-drawer.extra-security-section.use-ssl-tooltip"> <Trans i18nKey="ldap-drawer.extra-security-section.use-ssl-tooltip">
For a complete list of supported ciphers and TLS versions, refer to: For a complete list of supported ciphers and TLS versions, refer to:
</Trans>{' '} </Trans>{' '}
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<TextLink style={{ fontSize: 'inherit' }} href="https://go.dev/src/crypto/tls/cipher_suites.go" external> <TextLink style={{ fontSize: 'inherit' }} href="https://go.dev/src/crypto/tls/cipher_suites.go" external>
https://go.dev/src/crypto/tls/cipher_suites.go https://go.dev/src/crypto/tls/cipher_suites.go
</TextLink> </TextLink>
@ -145,7 +145,7 @@ export const LdapDrawerComponent = ({
> >
<Input <Input
id="port" id="port"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="389" placeholder="389"
type="number" type="number"
{...register(`${serverConfig}.port`, { valueAsNumber: true })} {...register(`${serverConfig}.port`, { valueAsNumber: true })}
@ -160,7 +160,7 @@ export const LdapDrawerComponent = ({
> >
<Input <Input
id="timeout" id="timeout"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="10" placeholder="10"
type="number" type="number"
{...register(`${serverConfig}.timeout`, { valueAsNumber: true })} {...register(`${serverConfig}.timeout`, { valueAsNumber: true })}
@ -414,7 +414,7 @@ export const LdapDrawerComponent = ({
<Field label={t('ldap-drawer.extra-security-section.client-cert-label', 'Client certificate path')}> <Field label={t('ldap-drawer.extra-security-section.client-cert-label', 'Client certificate path')}>
<Input <Input
id="client-cert" id="client-cert"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/path/to/client_cert.pem" placeholder="/path/to/client_cert.pem"
type="text" type="text"
{...register(`${serverConfig}.client_cert`)} {...register(`${serverConfig}.client_cert`)}

@ -413,7 +413,6 @@ interface FrameProps extends Pick<ExpressionProps, 'isAlertCondition'> {
const OpeningBracket = () => <span>{'{'}</span>; const OpeningBracket = () => <span>{'{'}</span>;
const ClosingBracket = () => <span>{'}'}</span>; const ClosingBracket = () => <span>{'}'}</span>;
// eslint-disable-next-line @grafana/no-untranslated-strings
const Quote = () => <span>&quot;</span>; const Quote = () => <span>&quot;</span>;
const Equals = () => <span>{'='}</span>; const Equals = () => <span>{'='}</span>;

@ -39,7 +39,7 @@ export function PromDurationDocs() {
<div> <div>
<Trans i18nKey="alerting.prom-duration-docs.multiple-units-combined">Multiple units combined</Trans> <Trans i18nKey="alerting.prom-duration-docs.multiple-units-combined">Multiple units combined</Trans>
</div> </div>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<code>1m30s, 2h30m20s, 1w2d</code> <code>1m30s, 2h30m20s, 1w2d</code>
</div> </div>
</div> </div>

@ -17,7 +17,7 @@ import { SuggestionDefinition } from './suggestionDefinition';
export function getGlobalSuggestions(monaco: Monaco): SuggestionDefinition[] { export function getGlobalSuggestions(monaco: Monaco): SuggestionDefinition[] {
const kind = monaco.languages.CompletionItemKind.Field; const kind = monaco.languages.CompletionItemKind.Field;
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
return [ return [
{ {
label: 'Alerts', label: 'Alerts',
@ -39,14 +39,14 @@ export function getGlobalSuggestions(monaco: Monaco): SuggestionDefinition[] {
{ label: 'GroupKey', kind, detail: 'string' }, { label: 'GroupKey', kind, detail: 'string' },
{ label: 'TruncatedAlerts', kind, detail: 'integer' }, { label: 'TruncatedAlerts', kind, detail: 'integer' },
]; ];
/* eslint-enable @grafana/no-untranslated-strings */ /* eslint-enable @grafana/i18n/no-untranslated-strings */
} }
// Suggestions that are valid only in the scope of an alert (e.g. in the .Alerts loop) // Suggestions that are valid only in the scope of an alert (e.g. in the .Alerts loop)
export function getAlertSuggestions(monaco: Monaco): SuggestionDefinition[] { export function getAlertSuggestions(monaco: Monaco): SuggestionDefinition[] {
const kind = monaco.languages.CompletionItemKind.Field; const kind = monaco.languages.CompletionItemKind.Field;
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
return [ return [
{ {
label: { label: 'Status', detail: '(Alert)', description: 'string' }, label: { label: 'Status', detail: '(Alert)', description: 'string' },
@ -148,26 +148,26 @@ export function getAlertSuggestions(monaco: Monaco): SuggestionDefinition[] {
), ),
}, },
]; ];
/* eslint-enable @grafana/no-untranslated-strings */ /* eslint-enable @grafana/i18n/no-untranslated-strings */
} }
// Suggestions for .Alerts // Suggestions for .Alerts
export function getAlertsSuggestions(monaco: Monaco): SuggestionDefinition[] { export function getAlertsSuggestions(monaco: Monaco): SuggestionDefinition[] {
const kind = monaco.languages.CompletionItemKind.Field; const kind = monaco.languages.CompletionItemKind.Field;
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
return [ return [
{ label: 'Firing', kind, detail: 'Alert[]' }, { label: 'Firing', kind, detail: 'Alert[]' },
{ label: 'Resolved', kind, detail: 'Alert[]' }, { label: 'Resolved', kind, detail: 'Alert[]' },
]; ];
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-enable @grafana/i18n/no-untranslated-strings */
} }
// Suggestions for the KeyValue types // Suggestions for the KeyValue types
export function getKeyValueSuggestions(monaco: Monaco): SuggestionDefinition[] { export function getKeyValueSuggestions(monaco: Monaco): SuggestionDefinition[] {
const kind = monaco.languages.CompletionItemKind.Field; const kind = monaco.languages.CompletionItemKind.Field;
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
return [ return [
{ label: 'SortedPairs', kind, detail: '[]KeyValue' }, { label: 'SortedPairs', kind, detail: '[]KeyValue' },
{ label: 'Names', kind, detail: '[]string' }, { label: 'Names', kind, detail: '[]string' },
@ -178,7 +178,7 @@ export function getKeyValueSuggestions(monaco: Monaco): SuggestionDefinition[] {
kind: monaco.languages.CompletionItemKind.Method, kind: monaco.languages.CompletionItemKind.Method,
}, },
]; ];
/* eslint-enable @grafana/no-untranslated-strings */ /* eslint-enable @grafana/i18n/no-untranslated-strings */
} }
export const snippets = { export const snippets = {

@ -39,7 +39,7 @@ export const SubformArrayField = ({
<div className={styles.wrapper}> <div className={styles.wrapper}>
<CollapsibleSection <CollapsibleSection
className={styles.collapsibleSection} className={styles.collapsibleSection}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label={`${option.label} (${fields.length})`} label={`${option.label} (${fields.length})`}
description={option.description} description={option.description}
> >

@ -182,7 +182,7 @@ const AnnotationsStep = () => {
{...register(`annotations.${index}.value`)} {...register(`annotations.${index}.value`)}
placeholder={ placeholder={
isUrl isUrl
? // eslint-disable-next-line @grafana/no-untranslated-strings ? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
'https://' 'https://'
: (annotationField.key && : (annotationField.key &&
t('alerting.annotations-step.placeholder-value-input', 'Enter a {{key}}...', { t('alerting.annotations-step.placeholder-value-input', 'Enter a {{key}}...', {

@ -49,7 +49,7 @@ export const GroupAndNamespaceFields = ({ rulesSourceName }: Props) => {
label={t('alerting.group-and-namespace-fields.namespace-picker-label-namespace', 'Namespace')} label={t('alerting.group-and-namespace-fields.namespace-picker-label-namespace', 'Namespace')}
// Disable translations as we don't intend to use this dropdown longterm, // Disable translations as we don't intend to use this dropdown longterm,
// so avoiding us adding translations for the sake of it // so avoiding us adding translations for the sake of it
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
description="Type to search for an existing namespace or create a new one" description="Type to search for an existing namespace or create a new one"
error={errors.namespace?.message} error={errors.namespace?.message}
invalid={!!errors.namespace?.message} invalid={!!errors.namespace?.message}
@ -82,7 +82,7 @@ export const GroupAndNamespaceFields = ({ rulesSourceName }: Props) => {
label={t('alerting.group-and-namespace-fields.group-picker-label-group', 'Group')} label={t('alerting.group-and-namespace-fields.group-picker-label-group', 'Group')}
// Disable translations as we don't intend to use this dropdown longterm, // Disable translations as we don't intend to use this dropdown longterm,
// so avoiding us adding translations for the sake of it // so avoiding us adding translations for the sake of it
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
description="Type to search for an existing group or create a new one" description="Type to search for an existing group or create a new one"
error={errors.group?.message} error={errors.group?.message}
invalid={!!errors.group?.message} invalid={!!errors.group?.message}

@ -118,7 +118,7 @@ const EvaluationBehaviorSummary = ({ rule }: EvaluationBehaviorSummaryProps) =>
> >
<Tooltip <Tooltip
placement="top" placement="top"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
content={`${dateTimeFormat(lastEvaluation, { format: 'YYYY-MM-DD HH:mm:ss' })}`} content={`${dateTimeFormat(lastEvaluation, { format: 'YYYY-MM-DD HH:mm:ss' })}`}
theme="info" theme="info"
> >
@ -138,7 +138,7 @@ const EvaluationBehaviorSummary = ({ rule }: EvaluationBehaviorSummaryProps) =>
> >
<Tooltip <Tooltip
placement="top" placement="top"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
content={`${lastEvaluationDuration}s`} content={`${lastEvaluationDuration}s`}
theme="info" theme="info"
> >

@ -268,7 +268,7 @@ function useColumns(
nextEvalInfo && ( nextEvalInfo && (
<Tooltip <Tooltip
placement="top" placement="top"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
content={`${nextEvalInfo?.fullDate}`} content={`${nextEvalInfo?.fullDate}`}
theme="info" theme="info"
> >

@ -59,7 +59,7 @@ export const SilencesFilter = () => {
</Trans> </Trans>
</div> </div>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<pre>severity=critical, env=production</pre> <pre>severity=critical, env=production</pre>
</> </>
} }

@ -332,7 +332,7 @@ function useColumns(alertManagerSourceName: string) {
<span data-testid="alerts"> <span data-testid="alerts">
{Array.isArray(silencedAlerts) {Array.isArray(silencedAlerts)
? silencedAlerts.length ? silencedAlerts.length
: // eslint-disable-next-line @grafana/no-untranslated-strings : // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
'-'} '-'}
</span> </span>
); );

@ -1,4 +1,3 @@
/* eslint-disable @grafana/no-untranslated-strings */
import { PluginLoadingStrategy, PluginMeta, PluginType } from '@grafana/data'; import { PluginLoadingStrategy, PluginMeta, PluginType } from '@grafana/data';
import { AppPluginConfig, setPluginComponentsHook, setPluginLinksHook } from '@grafana/runtime'; import { AppPluginConfig, setPluginComponentsHook, setPluginLinksHook } from '@grafana/runtime';
import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges'; import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges';

@ -433,13 +433,13 @@ export function fieldMap(provider: string): Record<string, FieldData> {
), ),
multi: false, multi: false,
options: [ options: [
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
{ value: 'AutoDetect', label: 'AutoDetect' }, { value: 'AutoDetect', label: 'AutoDetect' },
{ value: 'InParams', label: 'InParams' }, { value: 'InParams', label: 'InParams' },
{ value: 'InHeader', label: 'InHeader' }, { value: 'InHeader', label: 'InHeader' },
], ],
defaultValue: { value: 'AutoDetect', label: 'AutoDetect' }, defaultValue: { value: 'AutoDetect', label: 'AutoDetect' },
/* eslint-enable @grafana/no-untranslated-strings */ /* eslint-enable @grafana/i18n/no-untranslated-strings */
}, },
tokenUrl: { tokenUrl: {
label: tokenURLLabel, label: tokenURLLabel,
@ -914,7 +914,7 @@ function orgMappingDescription(provider: string): string {
function clientAuthenticationOptions(provider: string): Array<SelectableValue<string>> { function clientAuthenticationOptions(provider: string): Array<SelectableValue<string>> {
// Other options are purposefully not translated // Other options are purposefully not translated
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
switch (provider) { switch (provider) {
case 'azuread': case 'azuread':
return [ return [
@ -930,5 +930,5 @@ function clientAuthenticationOptions(provider: string): Array<SelectableValue<st
{ value: 'client_secret_post', label: 'Client secret' }, { value: 'client_secret_post', label: 'Client secret' },
]; ];
} }
/* eslint-enable @grafana/no-untranslated-strings */ /* eslint-enable @grafana/i18n/no-untranslated-strings */
} }

@ -34,7 +34,6 @@ export const settingsViewable = (scene: Scene) => ({
const rect = moveable.getRect(); const rect = moveable.getRect();
return ( return (
// eslint-disable-next-line @grafana/no-untranslated-strings
<div <div
key={'settings-viewable'} key={'settings-viewable'}
className={'moveable-settings'} className={'moveable-settings'}
@ -70,7 +69,7 @@ export const dimensionViewable = {
render(moveable: MoveableManagerInterface<unknown, unknown>, React: Renderer) { render(moveable: MoveableManagerInterface<unknown, unknown>, React: Renderer) {
const rect = moveable.getRect(); const rect = moveable.getRect();
return ( return (
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
<div <div
key={'dimension-viewable'} key={'dimension-viewable'}
className={'moveable-dimension'} className={'moveable-dimension'}

@ -29,7 +29,7 @@ export const frameItemDummy: CanvasElementItem = {
display: () => { display: () => {
// never shown to end user // never shown to end user
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
return <div>FRAME!</div>; return <div>FRAME!</div>;
}, },
}; };

@ -54,7 +54,6 @@ export const ResultItem = React.forwardRef(
{!hasCommandOrLink(ancestor) && ( {!hasCommandOrLink(ancestor) && (
<> <>
<span className={styles.breadcrumbAncestor}>{ancestor.name}</span> <span className={styles.breadcrumbAncestor}>{ancestor.name}</span>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
<span className={styles.breadcrumbSeparator}>&rsaquo;</span> <span className={styles.breadcrumbSeparator}>&rsaquo;</span>
</> </>
)} )}

@ -103,7 +103,7 @@ function getGlobalActions(): CommandPaletteAction[] {
]; ];
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
// eslint-disable @grafana/no-untranslated-strings // eslint-disable @grafana/i18n/no-untranslated-strings
const section = 'Dev tooling'; const section = 'Dev tooling';
const currentState = currentMockApiState(); const currentState = currentMockApiState();
const mockApiAction = currentState ? 'Disable' : 'Enable'; const mockApiAction = currentState ? 'Disable' : 'Enable';
@ -128,7 +128,7 @@ function getGlobalActions(): CommandPaletteAction[] {
togglePseudoLocale(); togglePseudoLocale();
}, },
}); });
// eslint-enable @grafana/no-untranslated-strings // eslint-enable @grafana/i18n/no-untranslated-strings
} }
return actions; return actions;

@ -76,7 +76,7 @@ export const ConfigureCorrelationSourceForm = () => {
<span className={styles.variable} key={i}> <span className={styles.variable} key={i}>
{name} {name}
{i < variables.length - 1 {i < variables.length - 1
? // eslint-disable-next-line @grafana/no-untranslated-strings ? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
', ' ', '
: ''} : ''}
</span> </span>

@ -138,7 +138,7 @@ const TransformationEditorRow = (props: Props) => {
<Label htmlFor={`config.transformations.${defaultValue.id}.expression`}> <Label htmlFor={`config.transformations.${defaultValue.id}.expression`}>
<Trans i18nKey="correlations.transform-row.expression-label">Expression</Trans> <Trans i18nKey="correlations.transform-row.expression-label">Expression</Trans>
{getSupportedTransTypeDetails(watch(`config.transformations.${index}.type`)).expressionDetails.required {getSupportedTransTypeDetails(watch(`config.transformations.${index}.type`)).expressionDetails.required
? // eslint-disable-next-line @grafana/no-untranslated-strings ? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
' *' ' *'
: ''} : ''}
</Label> </Label>

@ -24,7 +24,7 @@ export function EmbeddedDashboardTestPage() {
layout={PageLayoutType.Canvas} layout={PageLayoutType.Canvas}
> >
{/* this is a test page, no need to translate */} {/* this is a test page, no need to translate */}
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Box paddingY={2}>Internal url state: {state}</Box> <Box paddingY={2}>Internal url state: {state}</Box>
<EmbeddedDashboard uid="lVE-2YFMz" initialState={state} onStateChange={setState} /> <EmbeddedDashboard uid="lVE-2YFMz" initialState={state} onStateChange={setState} />
</Page> </Page>

@ -80,7 +80,6 @@ function VariableList({ set }: { set: SceneVariableSet }) {
// TODO fix keyboard a11y here // TODO fix keyboard a11y here
// eslint-disable-next-line jsx-a11y/no-static-element-interactions,jsx-a11y/click-events-have-key-events // eslint-disable-next-line jsx-a11y/no-static-element-interactions,jsx-a11y/click-events-have-key-events
<div className={styles.variableItem} key={variable.state.name} onClick={() => onEditVariable(variable)}> <div className={styles.variableItem} key={variable.state.name} onClick={() => onEditVariable(variable)}>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
<Text>${variable.state.name}</Text> <Text>${variable.state.name}</Text>
<Stack direction="row" gap={1} alignItems="center"> <Stack direction="row" gap={1} alignItems="center">
<Button variant="primary" size="sm" fill="outline"> <Button variant="primary" size="sm" fill="outline">

@ -48,7 +48,7 @@ export function CustomVariableForm({
<VariableTextAreaField <VariableTextAreaField
name="Values separated by comma" name="Values separated by comma"
defaultValue={query} defaultValue={query}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="1, 10, mykey : myvalue, myvalue, escaped\,value" placeholder="1, 10, mykey : myvalue, myvalue, escaped\,value"
onBlur={onQueryChange} onBlur={onQueryChange}
required required

@ -60,7 +60,7 @@ export function DataSourceVariableForm({
<VariableTextField <VariableTextField
defaultValue={regex} defaultValue={regex}
name="Instance name filter" name="Instance name filter"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/.*-(.*)-.*/" placeholder="/.*-(.*)-.*/"
onBlur={onRegExBlur} onBlur={onRegExBlur}
description={ description={

@ -52,7 +52,7 @@ export function IntervalVariableForm({
<VariableTextField <VariableTextField
defaultValue={intervals} defaultValue={intervals}
name="Values" name="Values"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="1m,10m,1h,6h,1d,7d" placeholder="1m,10m,1h,6h,1d,7d"
onBlur={onIntervalsChange} onBlur={onIntervalsChange}
testId={selectors.pages.Dashboard.Settings.Variables.Edit.IntervalVariable.intervalsValueInput} testId={selectors.pages.Dashboard.Settings.Variables.Edit.IntervalVariable.intervalsValueInput}
@ -91,7 +91,7 @@ export function IntervalVariableForm({
'dashboard-scene.interval-variable-form.description-calculated-value-below-threshold', 'dashboard-scene.interval-variable-form.description-calculated-value-below-threshold',
'The calculated value will not go below this threshold' 'The calculated value will not go below this threshold'
)} )}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="10s" placeholder="10s"
onChange={onAutoMinIntervalChanged} onChange={onAutoMinIntervalChanged}
width={11} width={11}

@ -125,7 +125,7 @@ export function QueryVariableEditorForm({
</Trans> </Trans>
</div> </div>
} }
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/" placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/"
onBlur={onRegExChange} onBlur={onRegExChange}
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2} testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2}

@ -246,7 +246,7 @@ export function Editor({ variable }: { variable: QueryVariable }) {
</Trans> </Trans>
</div> </div>
} }
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/" placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/"
onBlur={onRegExChange} onBlur={onRegExChange}
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2} testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2}

@ -106,7 +106,7 @@ export class TimePickerSettings extends PureComponent<Props, State> {
<Input <Input
id="now-delay-input" id="now-delay-input"
invalid={!this.state.isNowDelayValid} invalid={!this.state.isNowDelayValid}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="0m" placeholder="0m"
onChange={this.onNowDelayChange} onChange={this.onNowDelayChange}
defaultValue={this.props.nowDelay} defaultValue={this.props.nowDelay}

@ -190,7 +190,7 @@ export const EmailSharingConfiguration = ({ dashboard }: { dashboard: DashboardM
<div className={styles.emailContainer}> <div className={styles.emailContainer}>
<Input <Input
className={styles.emailInput} className={styles.emailInput}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="me@example.com" placeholder="me@example.com"
autoCapitalize="none" autoCapitalize="none"
{...register('email', { {...register('email', {

@ -217,7 +217,7 @@ export const TransformationOperationRow = ({
<QueryOperationRow <QueryOperationRow
id={id} id={id}
index={index} index={index}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
title={`${index + 1} - ${uiConfig.name}`} title={`${index + 1} - ${uiConfig.name}`}
draggable draggable
actions={renderActions} actions={renderActions}

@ -128,7 +128,7 @@ export const ResourceDimensionEditor = (
labelWidth={labelWidth} labelWidth={labelWidth}
grow={true} grow={true}
> >
{/* eslint-disable-next-line @grafana/no-untranslated-strings*/} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings*/}
<div>TODO mappings editor!</div> <div>TODO mappings editor!</div>
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>

@ -191,10 +191,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
enableNamedColors={true} enableNamedColors={true}
/> />
</div> </div>
{isPercent && ( {isPercent && <div className={styles.percentIcon}>%</div>}
// eslint-disable-next-line @grafana/no-untranslated-strings
<div className={styles.percentIcon}>%</div>
)}
</div> </div>
} }
suffix={ suffix={

@ -123,7 +123,7 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
const specialMatchOptions: Array<SelectableValue<SpecialValueMatch>> = [ const specialMatchOptions: Array<SelectableValue<SpecialValueMatch>> = [
{ {
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label: 'Null', label: 'Null',
value: SpecialValueMatch.Null, value: SpecialValueMatch.Null,
description: t( description: t(
@ -132,7 +132,7 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
), ),
}, },
{ {
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label: 'NaN', label: 'NaN',
value: SpecialValueMatch.NaN, value: SpecialValueMatch.NaN,
description: t( description: t(
@ -141,7 +141,7 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
), ),
}, },
{ {
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label: 'Null + NaN', label: 'Null + NaN',
value: SpecialValueMatch.NullAndNaN, value: SpecialValueMatch.NullAndNaN,
description: t( description: t(
@ -150,7 +150,7 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
), ),
}, },
{ {
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label: 'True', label: 'True',
value: SpecialValueMatch.True, value: SpecialValueMatch.True,
description: t( description: t(
@ -159,7 +159,7 @@ export function ValueMappingEditRow({ mapping, index, onChange, onRemove, onDupl
), ),
}, },
{ {
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label: 'False', label: 'False',
value: SpecialValueMatch.False, value: SpecialValueMatch.False,
description: t( description: t(

@ -108,7 +108,7 @@ export function ExploreRunQueryButton({
runQuery(pane[0]); runQuery(pane[0]);
onClick?.(); onClick?.();
}} }}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label={`${paneLabel}: ${buttonText.translation}`} label={`${paneLabel}: ${buttonText.translation}`}
disabled={isInvalid || pane[0] === undefined} disabled={isInvalid || pane[0] === undefined}
/> />

@ -78,11 +78,11 @@ export const LogsMetaRow = memo(
const downloadMenu = ( const downloadMenu = (
<Menu> <Menu>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Menu.Item label="txt" onClick={() => download(DownloadFormat.Text)} /> <Menu.Item label="txt" onClick={() => download(DownloadFormat.Text)} />
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Menu.Item label="json" onClick={() => download(DownloadFormat.Json)} /> <Menu.Item label="json" onClick={() => download(DownloadFormat.Json)} />
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Menu.Item label="csv" onClick={() => download(DownloadFormat.CSV)} /> <Menu.Item label="csv" onClick={() => download(DownloadFormat.CSV)} />
</Menu> </Menu>
); );

@ -216,7 +216,7 @@ export const SpanFilters = memo((props: SpanFilterProps) => {
ariaLabel="Select min span duration" ariaLabel="Select min span duration"
onChange={(val) => setSpanFiltersSearch({ ...search, from: val })} onChange={(val) => setSpanFiltersSearch({ ...search, from: val })}
isInvalidError="Invalid duration" isInvalidError="Invalid duration"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="e.g. 100ms, 1.2s" placeholder="e.g. 100ms, 1.2s"
width={18} width={18}
value={search.from || ''} value={search.from || ''}
@ -233,7 +233,7 @@ export const SpanFilters = memo((props: SpanFilterProps) => {
ariaLabel="Select max span duration" ariaLabel="Select max span duration"
onChange={(val) => setSpanFiltersSearch({ ...search, to: val })} onChange={(val) => setSpanFiltersSearch({ ...search, to: val })}
isInvalidError="Invalid duration" isInvalidError="Invalid duration"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="e.g. 100ms, 1.2s" placeholder="e.g. 100ms, 1.2s"
width={18} width={18}
value={search.to || ''} value={search.to || ''}

@ -157,7 +157,7 @@ export const TracePageHeader = memo((props: TracePageHeaderProps) => {
)} )}
{method && method.length > 0 && ( {method && method.length > 0 && (
<Tooltip <Tooltip
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
content="http.method" content="http.method"
interactive={true} interactive={true}
> >
@ -168,7 +168,7 @@ export const TracePageHeader = memo((props: TracePageHeaderProps) => {
)} )}
{status && status.length > 0 && ( {status && status.length > 0 && (
<Tooltip <Tooltip
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
content="http.status_code" content="http.status_code"
interactive={true} interactive={true}
> >

@ -108,7 +108,11 @@ export default function AccordianLogs({
return ( return (
<div className={styles.AccordianLogs}> <div className={styles.AccordianLogs}>
<HeaderComponent className={styles.AccordianLogsHeader} {...headerProps}> <HeaderComponent className={styles.AccordianLogsHeader} {...headerProps}>
{arrow} <strong>Events</strong> ({logs.length}) {arrow}{' '}
<strong>
<Trans i18nKey="explore.accordian-logs.events">Events</Trans>
</strong>{' '}
({logs.length})
</HeaderComponent> </HeaderComponent>
{isOpen && ( {isOpen && (
<div className={styles.AccordianLogsContent}> <div className={styles.AccordianLogsContent}>

@ -17,7 +17,7 @@ export function getExploreExtensionConfigs(): PluginExtensionAddedLinkConfig[] {
return [ return [
createAddedLinkConfig<PluginExtensionExploreContext>({ createAddedLinkConfig<PluginExtensionExploreContext>({
// This is called at the top level, so will break if we add a translation here 😱 // This is called at the top level, so will break if we add a translation here 😱
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
title: 'Add to dashboard', title: 'Add to dashboard',
description: 'Use the query and panel from explore and create/add it to a dashboard', description: 'Use the query and panel from explore and create/add it to a dashboard',
targets: [PluginExtensionPoints.ExploreToolbarAction], targets: [PluginExtensionPoints.ExploreToolbarAction],
@ -44,9 +44,9 @@ export function getExploreExtensionConfigs(): PluginExtensionAddedLinkConfig[] {
}), }),
createAddedLinkConfig<PluginExtensionExploreContext>({ createAddedLinkConfig<PluginExtensionExploreContext>({
// This is called at the top level, so will break if we add a translation here 😱 // This is called at the top level, so will break if we add a translation here 😱
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
title: 'Add correlation', title: 'Add correlation',
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
description: 'Create a correlation from this query', description: 'Create a correlation from this query',
targets: [PluginExtensionPoints.ExploreToolbarAction], targets: [PluginExtensionPoints.ExploreToolbarAction],
icon: 'link', icon: 'link',

@ -106,7 +106,7 @@ export const SignupInvitedPage = () => {
label={t('invites.signup-invited-page.label-email', 'Email')} label={t('invites.signup-invited-page.label-email', 'Email')}
> >
<Input <Input
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="email@example.com" placeholder="email@example.com"
{...register('email', { {...register('email', {
required: 'Email is required', required: 'Email is required',

@ -165,7 +165,7 @@ class UnthemedDashboardImport extends PureComponent<Props> {
const styles = importStyles(this.props.theme); const styles = importStyles(this.props.theme);
const GcomDashboardsLink = () => ( const GcomDashboardsLink = () => (
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
<TextLink variant="bodySmall" href="https://grafana.com/grafana/dashboards/" external> <TextLink variant="bodySmall" href="https://grafana.com/grafana/dashboards/" external>
grafana.com/dashboards grafana.com/dashboards
</TextLink> </TextLink>

@ -17,7 +17,7 @@ export function AlertWithTraceID(props: AlertWithTraceIDProps) {
{traceID && ( {traceID && (
/* Deliberately don't want to translate 'Trace ID' */ /* Deliberately don't want to translate 'Trace ID' */
/* eslint-disable-next-line @grafana/no-untranslated-strings */ /* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */
<Text element="p" color="secondary" variant="bodySmall"> <Text element="p" color="secondary" variant="bodySmall">
Trace ID: {traceID} Trace ID: {traceID}
</Text> </Text>

@ -79,7 +79,7 @@ export const UserInviteForm = () => {
error={!!errors.loginOrEmail ? 'Email or username is required' : undefined} error={!!errors.loginOrEmail ? 'Email or username is required' : undefined}
label={t('org.user-invite-form.label-email-or-username', 'Email or username')} label={t('org.user-invite-form.label-email-or-username', 'Email or username')}
> >
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Input {...register('loginOrEmail', { required: true })} placeholder="email@example.com" /> <Input {...register('loginOrEmail', { required: true })} placeholder="email@example.com" />
</Field> </Field>
<Field invalid={!!errors.name} label={t('org.user-invite-form.label-name', 'Name')}> <Field invalid={!!errors.name} label={t('org.user-invite-form.label-name', 'Name')}>

@ -246,7 +246,7 @@ export function PluginDetailsPanel(props: Props): React.ReactElement | null {
This feature is for reporting malicious or harmful behaviour within plugins. For plugin concerns, email This feature is for reporting malicious or harmful behaviour within plugins. For plugin concerns, email
us at:{' '} us at:{' '}
</Trans> </Trans>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<TextLink href="mailto:integrations+report-plugin@grafana.com">integrations@grafana.com</TextLink> <TextLink href="mailto:integrations+report-plugin@grafana.com">integrations@grafana.com</TextLink>
</Text> </Text>
<Text> <Text>

@ -13,9 +13,9 @@ PluginPageContext.displayName = 'PluginPageContext';
function getInitialPluginPageContext(): PluginPageContextType { function getInitialPluginPageContext(): PluginPageContextType {
return { return {
sectionNav: { sectionNav: {
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
main: { text: 'Plugin page' }, main: { text: 'Plugin page' },
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
node: { text: 'Plugin page' }, node: { text: 'Plugin page' },
}, },
}; };

@ -377,7 +377,7 @@ export function createExtensionSubMenu(extensions: PluginExtensionLink[]): Panel
if (uncategorized.length > 0) { if (uncategorized.length > 0) {
if (subMenu.length > 0) { if (subMenu.length > 0) {
subMenu.push({ subMenu.push({
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
text: 'divider', text: 'divider',
type: 'divider', type: 'divider',
}); });

@ -10,7 +10,7 @@ export function TokenPermissionsInfo() {
return ( return (
<div className={styles.container}> <div className={styles.container}>
{/* GitHub UI is English only, so these strings are not translated */} {/* GitHub UI is English only, so these strings are not translated */}
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable @grafana/i18n/no-untranslated-strings */}
<Stack gap={0.5} wrap={'wrap'}> <Stack gap={0.5} wrap={'wrap'}>
<Trans i18nKey="provisioning.token-permissions-info.go-to">Go to</Trans> <Trans i18nKey="provisioning.token-permissions-info.go-to">Go to</Trans>
<TextLink external href="https://github.com/settings/personal-access-tokens/new"> <TextLink external href="https://github.com/settings/personal-access-tokens/new">
@ -20,21 +20,22 @@ export function TokenPermissionsInfo() {
<strong>"Fine-grained token".</strong> <strong>"Fine-grained token".</strong>
<Trans i18nKey="provisioning.token-permissions-info.make-sure">Make sure to include these permissions</Trans>: <Trans i18nKey="provisioning.token-permissions-info.make-sure">Make sure to include these permissions</Trans>:
</Stack> </Stack>
{/* eslint-enable @grafana/i18n/no-untranslated-strings */}
<ul className={styles.permissionsList}> <ul className={styles.permissionsList}>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<li> <li>
Content: <span className={styles.accessLevel}>Read and write</span> Content: <span className={styles.accessLevel}>Read and write</span>
</li> </li>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<li> <li>
Metadata: <span className={styles.accessLevel}>Read only</span> Metadata: <span className={styles.accessLevel}>Read only</span>
</li> </li>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<li> <li>
Pull requests: <span className={styles.accessLevel}>Read and write</span> Pull requests: <span className={styles.accessLevel}>Read and write</span>
</li> </li>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<li> <li>
Webhooks: <span className={styles.accessLevel}>Read and write</span> Webhooks: <span className={styles.accessLevel}>Read and write</span>
</li> </li>

@ -237,7 +237,6 @@ export function ProvisioningWizard({ type }: { type: RepoType }) {
<FormPrompt onDiscard={handleCancel} confirmRedirect={isDirty && activeStep !== 'finish' && !isCancelling} /> <FormPrompt onDiscard={handleCancel} confirmRedirect={isDirty && activeStep !== 'finish' && !isCancelling} />
<Stack direction="column"> <Stack direction="column">
<Box marginBottom={2}> <Box marginBottom={2}>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
<Text element="h2"> <Text element="h2">
{currentStepIndex + 1}. {currentStepConfig?.title} {currentStepIndex + 1}. {currentStepConfig?.title}
</Text> </Text>

@ -138,7 +138,6 @@ const renderDataSource = <TQuery extends DataQuery>(
const { alerting, dataSource, onChangeDataSource } = props; const { alerting, dataSource, onChangeDataSource } = props;
if (!onChangeDataSource) { if (!onChangeDataSource) {
// eslint-disable-next-line @grafana/no-untranslated-strings
return <em className={styles.contextInfo}>({dataSource.name})</em>; return <em className={styles.contextInfo}>({dataSource.name})</em>;
} }

@ -169,7 +169,7 @@ export const QueryGroupOptionsEditor = React.memo(({ options, dataSource, data,
<Input <Input
id="cache-timeout-id" id="cache-timeout-id"
type="text" type="text"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="60" placeholder="60"
spellCheck={false} spellCheck={false}
onBlur={onCacheTimeoutBlur} onBlur={onCacheTimeoutBlur}
@ -193,7 +193,7 @@ export const QueryGroupOptionsEditor = React.memo(({ options, dataSource, data,
</InlineLabel> </InlineLabel>
<Input <Input
type="number" type="number"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder={`${dataSource.cachingConfig.TTLMs}`} placeholder={`${dataSource.cachingConfig.TTLMs}`}
spellCheck={false} spellCheck={false}
onBlur={onQueryCachingTTLBlur} onBlur={onQueryCachingTTLBlur}
@ -226,7 +226,7 @@ export const QueryGroupOptionsEditor = React.memo(({ options, dataSource, data,
<Input <Input
id="max-data-points-input" id="max-data-points-input"
type="number" type="number"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder={`${realMd}`} placeholder={`${realMd}`}
spellCheck={false} spellCheck={false}
onBlur={onMaxDataPointsBlur} onBlur={onMaxDataPointsBlur}
@ -267,7 +267,7 @@ export const QueryGroupOptionsEditor = React.memo(({ options, dataSource, data,
<Input <Input
id="min-interval-input" id="min-interval-input"
type="text" type="text"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder={`${minIntervalOnDs}`} placeholder={`${minIntervalOnDs}`}
spellCheck={false} spellCheck={false}
onBlur={onMinIntervalBlur} onBlur={onMinIntervalBlur}
@ -359,7 +359,7 @@ export const QueryGroupOptionsEditor = React.memo(({ options, dataSource, data,
<Input <Input
id="relative-time-input" id="relative-time-input"
type="text" type="text"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="1h" placeholder="1h"
onChange={onRelativeTimeChange} onChange={onRelativeTimeChange}
onBlur={onOverrideTime} onBlur={onOverrideTime}
@ -385,7 +385,7 @@ export const QueryGroupOptionsEditor = React.memo(({ options, dataSource, data,
<Input <Input
id="time-shift-input" id="time-shift-input"
type="text" type="text"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="1h" placeholder="1h"
onChange={onTimeShiftChange} onChange={onTimeShiftChange}
onBlur={onTimeShift} onBlur={onTimeShift}

@ -1,4 +1,4 @@
/* eslint-disable @grafana/no-untranslated-strings */ /* eslint-disable @grafana/i18n/no-untranslated-strings */
import { NavModelItem } from '@grafana/data'; import { NavModelItem } from '@grafana/data';
import { usePluginLinks } from '@grafana/runtime'; import { usePluginLinks } from '@grafana/runtime';
import { Button, LinkButton, Stack, Text } from '@grafana/ui'; import { Button, LinkButton, Stack, Text } from '@grafana/ui';

@ -81,7 +81,7 @@ export const CreateTeam = (): JSX.Element => {
'This is optional and is primarily used for allowing custom team avatars' 'This is optional and is primarily used for allowing custom team avatars'
)} )}
> >
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Input {...register('email')} type="email" id="team-email" placeholder="email@test.com" /> <Input {...register('email')} type="email" id="team-email" placeholder="email@test.com" />
</Field> </Field>
</FieldSet> </FieldSet>

@ -78,7 +78,7 @@ export const TeamSettings = ({ team, updateTeam }: Props) => {
)} )}
disabled={!canWriteTeamSettings} disabled={!canWriteTeamSettings}
> >
{/* eslint-disable-next-line @grafana/no-untranslated-strings */} {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */}
<Input {...register('email')} placeholder="team@email.com" type="email" id="email-input" /> <Input {...register('email')} placeholder="team@email.com" type="email" id="email-input" />
</Field> </Field>
<Button type="submit" disabled={!canWriteTeamSettings}> <Button type="submit" disabled={!canWriteTeamSettings}>

@ -59,11 +59,7 @@ export const UnaryOperationEditor = (props: {
> >
<Select options={ops} value={unary?.operator ?? ops[0].value} onChange={onUnaryOperationChanged} /> <Select options={ops} value={unary?.operator ?? ops[0].value} onChange={onUnaryOperationChanged} />
</InlineField> </InlineField>
<InlineField <InlineField label="(" labelWidth={2}>
// eslint-disable-next-line @grafana/no-untranslated-strings
label="("
labelWidth={2}
>
<Select <Select
placeholder={t('transformers.unary-operation-editor.placeholder-field', 'Field')} placeholder={t('transformers.unary-operation-editor.placeholder-field', 'Field')}
className="min-width-11" className="min-width-11"
@ -72,7 +68,6 @@ export const UnaryOperationEditor = (props: {
onChange={onUnaryValueChanged} onChange={onUnaryValueChanged}
/> />
</InlineField> </InlineField>
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
<InlineLabel width={2}>)</InlineLabel> <InlineLabel width={2}>)</InlineLabel>
</InlineFieldRow> </InlineFieldRow>
</> </>

@ -174,7 +174,7 @@ export const ConvertFieldTypeTransformerEditor = ({
> >
<Input <Input
value={c.dateFormat} value={c.dateFormat}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder={'e.g. YYYY-MM-DD'} placeholder={'e.g. YYYY-MM-DD'}
onChange={onInputFormat(idx)} onChange={onInputFormat(idx)}
width={24} width={24}
@ -193,7 +193,7 @@ export const ConvertFieldTypeTransformerEditor = ({
> >
<Input <Input
value={c.joinWith} value={c.joinWith}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder={'JSON'} placeholder={'JSON'}
onChange={onJoinWithChange(idx)} onChange={onJoinWithChange(idx)}
width={16} width={16}
@ -211,7 +211,7 @@ export const ConvertFieldTypeTransformerEditor = ({
> >
<Input <Input
value={c.dateFormat} value={c.dateFormat}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder={'e.g. YYYY-MM-DD'} placeholder={'e.g. YYYY-MM-DD'}
onChange={onInputFormat(idx)} onChange={onInputFormat(idx)}
width={24} width={24}

@ -81,7 +81,7 @@ export function FormatTimeTransfomerEditor({
value={options.timeField} value={options.timeField}
onChange={onSelectField} onChange={onSelectField}
/* don't translate here as this references a field name */ /* don't translate here as this references a field name */
/* eslint-disable-next-line @grafana/no-untranslated-strings */ /* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */
placeholder="time" placeholder="time"
isClearable isClearable
/> />

@ -90,7 +90,7 @@ export function SeriesToFieldsTransformerEditor({ input, options, onChange }: Tr
value={options.byField} value={options.byField}
onChange={onSelectField} onChange={onSelectField}
/* don't translate here as this references a field name */ /* don't translate here as this references a field name */
/* eslint-disable-next-line @grafana/no-untranslated-strings */ /* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */
placeholder="time" placeholder="time"
isClearable isClearable
/> />

@ -118,7 +118,7 @@ export const extractFieldsTransformerEditor = ({
{options.format === FieldExtractorID.RegExp && ( {options.format === FieldExtractorID.RegExp && (
<InlineFieldRow> <InlineFieldRow>
<InlineField <InlineField
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
label="RegExp" label="RegExp"
labelWidth={16} labelWidth={16}
interactive={true} interactive={true}
@ -127,7 +127,7 @@ export const extractFieldsTransformerEditor = ({
})} })}
> >
<Input <Input
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/(?<NewField>.*)/" placeholder="/(?<NewField>.*)/"
value={options.regExp} value={options.regExp}
onChange={onRegexpChange} onChange={onRegexpChange}

@ -147,7 +147,6 @@ export function JoinByLabelsTransformerEditor({ input, options, onChange }: Prop
{Boolean(info.addOptions.length && idx === options.join!.length - 1) && ( {Boolean(info.addOptions.length && idx === options.join!.length - 1) && (
<ValuePicker <ValuePicker
icon="plus" icon="plus"
// eslint-disable-next-line @grafana/no-untranslated-strings
label={''} label={''}
options={info.addOptions} options={info.addOptions}
onChange={addJoin} onChange={addJoin}

@ -37,7 +37,7 @@ export const intervalVariableSlice = createSlice({
// add auto option if missing // add auto option if missing
if (options.length && options[0].text !== 'auto') { if (options.length && options[0].text !== 'auto') {
options.unshift({ options.unshift({
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
text: 'auto', text: 'auto',
value: '$__auto_interval_' + instanceState.name, value: '$__auto_interval_' + instanceState.name,
selected: false, selected: false,

@ -87,7 +87,6 @@ export const generateLabel = (feature: FeatureLike, idx: number): string | React
if (first) { if (first) {
return ( return (
// eslint-disable-next-line @grafana/no-untranslated-strings
<span> <span>
{first}: {renderValue(props[first])} {first}: {renderValue(props[first])}
</span> </span>
@ -98,7 +97,6 @@ export const generateLabel = (feature: FeatureLike, idx: number): string | React
const v = props[k]; const v = props[k];
if (isString(v)) { if (isString(v)) {
return ( return (
// eslint-disable-next-line @grafana/no-untranslated-strings
<span> <span>
{k}: {renderValue(v)} {k}: {renderValue(v)}
</span> </span>

@ -88,7 +88,7 @@ export const AppRegistrationCredentials = (props: AppRegistrationCredentialsProp
<Input <Input
aria-label={t('components.app-registration-credentials.aria-label-tenant-id', 'Tenant ID')} aria-label={t('components.app-registration-credentials.aria-label-tenant-id', 'Tenant ID')}
className="width-30" className="width-30"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.tenantId || ''} value={credentials.tenantId || ''}
onChange={onTenantIdChange} onChange={onTenantIdChange}
@ -106,7 +106,7 @@ export const AppRegistrationCredentials = (props: AppRegistrationCredentialsProp
<Input <Input
className="width-30" className="width-30"
aria-label={t('components.app-registration-credentials.aria-label-client-id', 'Client ID')} aria-label={t('components.app-registration-credentials.aria-label-client-id', 'Client ID')}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientId || ''} value={credentials.clientId || ''}
onChange={onClientIdChange} onChange={onClientIdChange}
@ -150,7 +150,7 @@ export const AppRegistrationCredentials = (props: AppRegistrationCredentialsProp
<Input <Input
className="width-30" className="width-30"
aria-label={t('components.app-registration-credentials.aria-label-client-secret', 'Client Secret')} aria-label={t('components.app-registration-credentials.aria-label-client-secret', 'Client Secret')}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientSecret || ''} value={credentials.clientSecret || ''}
onChange={onClientSecretChange} onChange={onClientSecretChange}

@ -154,7 +154,7 @@ export class ConfigEditor extends PureComponent<Props, State> {
value={options.jsonData.timeout} value={options.jsonData.timeout}
type="number" type="number"
className="width-15" className="width-15"
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="30" placeholder="30"
onChange={onTimeoutChange} onChange={onTimeoutChange}
/> />

@ -74,7 +74,7 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps<str
id={`input-advanced-resource-picker-${index + 1}`} id={`input-advanced-resource-picker-${index + 1}`}
value={resource} value={resource}
onChange={(event) => onResourceChange(index, event.currentTarget.value)} onChange={(event) => onResourceChange(index, event.currentTarget.value)}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="ex: /subscriptions/$subId" placeholder="ex: /subscriptions/$subId"
data-testid={`input-advanced-resource-picker-${index + 1}`} data-testid={`input-advanced-resource-picker-${index + 1}`}
/> />

@ -73,7 +73,7 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps<Azu
id={`input-advanced-resource-picker-subscription`} id={`input-advanced-resource-picker-subscription`}
value={resources[0]?.subscription ?? ''} value={resources[0]?.subscription ?? ''}
onChange={(event) => onCommonPropChange({ subscription: event.currentTarget.value })} onChange={(event) => onCommonPropChange({ subscription: event.currentTarget.value })}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX"
/> />
</InlineField> </InlineField>
@ -91,7 +91,7 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps<Azu
id={`input-advanced-resource-picker-metricNamespace`} id={`input-advanced-resource-picker-metricNamespace`}
value={resources[0]?.metricNamespace ?? ''} value={resources[0]?.metricNamespace ?? ''}
onChange={(event) => onCommonPropChange({ metricNamespace: event.currentTarget.value })} onChange={(event) => onCommonPropChange({ metricNamespace: event.currentTarget.value })}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="Microsoft.Insights/metricNamespaces" placeholder="Microsoft.Insights/metricNamespaces"
/> />
</InlineField> </InlineField>
@ -111,7 +111,7 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps<Azu
id={`input-advanced-resource-picker-region`} id={`input-advanced-resource-picker-region`}
value={resources[0]?.region ?? ''} value={resources[0]?.region ?? ''}
onChange={(event) => onCommonPropChange({ region: event.currentTarget.value })} onChange={(event) => onCommonPropChange({ region: event.currentTarget.value })}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="northeurope" placeholder="northeurope"
/> />
</InlineField> </InlineField>
@ -142,7 +142,7 @@ const AdvancedResourcePicker = ({ resources, onChange }: ResourcePickerProps<Azu
onChange={(event) => onChange={(event) =>
onResourceChange(index, { ...resource, resourceGroup: event.currentTarget.value }) onResourceChange(index, { ...resource, resourceGroup: event.currentTarget.value })
} }
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="resource-group" placeholder="resource-group"
/> />
<AccessoryButton <AccessoryButton

@ -56,7 +56,7 @@ export function CheatSheet() {
</li> </li>
</ul> </ul>
<Trans i18nKey="cheat-sheet.macros">Macros:</Trans> <Trans i18nKey="cheat-sheet.macros">Macros:</Trans>
{/* eslint-disable @grafana/no-untranslated-strings */} {/* eslint-disable @grafana/i18n/no-untranslated-strings */}
<ul className={styles.ulPadding}> <ul className={styles.ulPadding}>
<li>$__time(column) -&gt; column AS time</li> <li>$__time(column) -&gt; column AS time</li>
<li>$__timeEpoch(column) -&gt; DATEDIFF(second, &apos;1970-01-01&apos;, column) AS time</li> <li>$__timeEpoch(column) -&gt; DATEDIFF(second, &apos;1970-01-01&apos;, column) AS time</li>
@ -84,13 +84,13 @@ export function CheatSheet() {
<li>$__unixEpochGroup(column,&apos;5m&apos;) -&gt; FLOOR(column/300)*300</li> <li>$__unixEpochGroup(column,&apos;5m&apos;) -&gt; FLOOR(column/300)*300</li>
<li>$__unixEpochGroupAlias(column,&apos;5m&apos;) -&gt; FLOOR(column/300)*300 AS [time]</li> <li>$__unixEpochGroupAlias(column,&apos;5m&apos;) -&gt; FLOOR(column/300)*300 AS [time]</li>
</ul> </ul>
{/* eslint-enable @grafana/no-untranslated-strings */} {/* eslint-enable @grafana/i18n/no-untranslated-strings */}
<p> <p>
<Trans i18nKey="cheat-sheet.example-time-group" values={{ timeGroupMacro: '$__timeGroup' }}> <Trans i18nKey="cheat-sheet.example-time-group" values={{ timeGroupMacro: '$__timeGroup' }}>
Example of group by and order by with {'{{timeGroupMacro}}'}: Example of group by and order by with {'{{timeGroupMacro}}'}:
</Trans> </Trans>
</p> </p>
{/* eslint-disable @grafana/no-untranslated-strings */} {/* eslint-disable @grafana/i18n/no-untranslated-strings */}
<pre> <pre>
<code> <code>
SELECT $__timeGroup(date_time_col, &apos;1h&apos;) AS time, sum(value) as value <br /> SELECT $__timeGroup(date_time_col, &apos;1h&apos;) AS time, sum(value) as value <br />
@ -102,11 +102,11 @@ export function CheatSheet() {
<br /> <br />
</code> </code>
</pre> </pre>
{/* eslint-enable @grafana/no-untranslated-strings */} {/* eslint-enable @grafana/i18n/no-untranslated-strings */}
<Trans i18nKey="cheat-sheet.condtional-macros"> <Trans i18nKey="cheat-sheet.condtional-macros">
Or build your own conditionals using these macros which just return the values: Or build your own conditionals using these macros which just return the values:
</Trans> </Trans>
{/* eslint-disable @grafana/no-untranslated-strings */} {/* eslint-disable @grafana/i18n/no-untranslated-strings */}
<ul className={styles.ulPadding}> <ul className={styles.ulPadding}>
<li>$__timeFrom() -&gt; &apos;2017-04-21T05:01:17Z&apos;</li> <li>$__timeFrom() -&gt; &apos;2017-04-21T05:01:17Z&apos;</li>
<li>$__timeTo() -&gt; &apos;2017-04-21T05:01:17Z&apos;</li> <li>$__timeTo() -&gt; &apos;2017-04-21T05:01:17Z&apos;</li>
@ -115,7 +115,7 @@ export function CheatSheet() {
<li>$__unixEpochNanoFrom() -&gt; 1494410783152415214</li> <li>$__unixEpochNanoFrom() -&gt; 1494410783152415214</li>
<li>$__unixEpochNanoTo() -&gt; 1494497183142514872</li> <li>$__unixEpochNanoTo() -&gt; 1494497183142514872</li>
</ul> </ul>
{/* eslint-enable @grafana/no-untranslated-strings */} {/* eslint-enable @grafana/i18n/no-untranslated-strings */}
</div> </div>
); );
} }

@ -180,7 +180,7 @@ export const AzureCredentialsForm = (props: Props) => {
> >
<Input <Input
width={45} width={45}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.tenantId || ''} value={credentials.tenantId || ''}
onChange={onTenantIdChange} onChange={onTenantIdChange}
@ -197,7 +197,7 @@ export const AzureCredentialsForm = (props: Props) => {
> >
<Input <Input
width={45} width={45}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientId || ''} value={credentials.clientId || ''}
onChange={onClientIdChange} onChange={onClientIdChange}
@ -242,7 +242,7 @@ export const AzureCredentialsForm = (props: Props) => {
<Input <Input
width={45} width={45}
aria-label={t('azureauth.azure-credentials-form.aria-label-client-secret', 'Client Secret')} aria-label={t('azureauth.azure-credentials-form.aria-label-client-secret', 'Client Secret')}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" placeholder="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
value={credentials.clientSecret || ''} value={credentials.clientSecret || ''}
onChange={onClientSecretChange} onChange={onClientSecretChange}

@ -162,7 +162,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
name="host" name="host"
type="text" type="text"
value={dsSettings.url || ''} value={dsSettings.url || ''}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="localhost:1433" placeholder="localhost:1433"
onChange={onDSOptionChanged('url')} onChange={onDSOptionChanged('url')}
/> />
@ -367,7 +367,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
value={dsSettings.user || ''} value={dsSettings.user || ''}
placeholder={ placeholder={
jsonData.authenticationType === MSSQLAuthenticationType.kerberosRaw jsonData.authenticationType === MSSQLAuthenticationType.kerberosRaw
? // eslint-disable-next-line @grafana/no-untranslated-strings ? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
'name@EXAMPLE.COM' 'name@EXAMPLE.COM'
: t('configuration.configuration-editor.placeholder-user', 'user') : t('configuration.configuration-editor.placeholder-user', 'user')
} }
@ -434,7 +434,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
> >
<Input <Input
width={LONG_WIDTH} width={LONG_WIDTH}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="1m" placeholder="1m"
value={jsonData.timeInterval || ''} value={jsonData.timeInterval || ''}
onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')} onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')}

@ -62,7 +62,7 @@ export const KerberosConfig = (props: DataSourcePluginOptionsEditorProps<MssqlOp
error={'Keytab file path is required'} error={'Keytab file path is required'}
> >
<Input <Input
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/home/grot/grot.keytab" placeholder="/home/grot/grot.keytab"
onChange={onKeytabFileChanged} onChange={onKeytabFileChanged}
width={LONG_WIDTH} width={LONG_WIDTH}
@ -85,7 +85,7 @@ export const KerberosConfig = (props: DataSourcePluginOptionsEditorProps<MssqlOp
)} )}
> >
<Input <Input
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/tmp/krb5cc_1000" placeholder="/tmp/krb5cc_1000"
onChange={onCredentialCacheChanged} onChange={onCredentialCacheChanged}
width={LONG_WIDTH} width={LONG_WIDTH}
@ -124,7 +124,7 @@ export const KerberosConfig = (props: DataSourcePluginOptionsEditorProps<MssqlOp
)} )}
> >
<Input <Input
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="/home/grot/cache.json" placeholder="/home/grot/cache.json"
onChange={onCredentialCacheFileChanged} onChange={onCredentialCacheFileChanged}
width={LONG_WIDTH} width={LONG_WIDTH}
@ -179,7 +179,7 @@ export const KerberosAdvancedSettings = (props: DataSourcePluginOptionsEditorPro
<Input <Input
type="text" type="text"
width={LONG_WIDTH} width={LONG_WIDTH}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="0" placeholder="0"
defaultValue={jsonData.UDPConnectionLimit} defaultValue={jsonData.UDPConnectionLimit}
onChange={(e) => { onChange={(e) => {
@ -207,7 +207,7 @@ export const KerberosAdvancedSettings = (props: DataSourcePluginOptionsEditorPro
<Input <Input
type="text" type="text"
width={LONG_WIDTH} width={LONG_WIDTH}
// eslint-disable-next-line @grafana/no-untranslated-strings // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
placeholder="true" placeholder="true"
defaultValue={jsonData.enableDNSLookupKDC} defaultValue={jsonData.enableDNSLookupKDC}
onChange={onDNSLookupKDCChanged} onChange={onDNSLookupKDCChanged}

@ -5798,6 +5798,7 @@
}, },
"explore": { "explore": {
"accordian-logs": { "accordian-logs": {
"events": "Events",
"footer": "Event timestamps are relative to the start time of the full trace." "footer": "Event timestamps are relative to the start time of the full trace."
}, },
"accordian-references": { "accordian-references": {

@ -127,8 +127,6 @@ export const Page = () => {
persistAuthorization={false} persistAuthorization={false}
/> />
)} )}
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
{!url?.value && <div>...{/** TODO, we can make an api docs loading page here */}</div>} {!url?.value && <div>...{/** TODO, we can make an api docs loading page here */}</div>}
</NamespaceContext.Provider> </NamespaceContext.Provider>
</ThemeProvider> </ThemeProvider>

Loading…
Cancel
Save