diff --git a/.betterer.eslint.config.js b/.betterer.eslint.config.js
index 1be44601a3c..349ee612837 100644
--- a/.betterer.eslint.config.js
+++ b/.betterer.eslint.config.js
@@ -113,6 +113,7 @@ module.exports = [
ignores: ['public/app/plugins/**', '**/*.story.tsx', '**/*.{test,spec}.{ts,tsx}', '**/__mocks__/', 'public/test'],
rules: {
'@grafana/no-untranslated-strings': 'error',
+ '@grafana/no-translation-top-level': 'error',
},
},
];
diff --git a/.betterer.results b/.betterer.results
index d5ac5c018d3..1e0cc343e3b 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -549,6 +549,11 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
+ "packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.tsx:5381": [
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "0"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "1"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "2"]
+ ],
"packages/grafana-ui/src/components/DataSourceSettings/types.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
@@ -3044,7 +3049,8 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"]
],
"public/app/features/connections/tabs/ConnectData/Search/Search.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "0"],
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
],
"public/app/features/connections/tabs/ConnectData/Search/index.tsx:5381": [
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"]
@@ -4568,9 +4574,18 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with ", "3"]
],
+ "public/app/features/explore/RichHistory/RichHistorySettingsTab.tsx:5381": [
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "0"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "1"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "2"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "3"]
+ ],
"public/app/features/explore/RichHistory/RichHistoryStarredTab.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
],
+ "public/app/features/explore/ShortLinkButtonMenu.tsx:5381": [
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "0"]
+ ],
"public/app/features/explore/SupplementaryResultError.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with ", "0"]
],
@@ -4929,8 +4944,14 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with ", "4"]
],
"public/app/features/inspector/InspectJSONTab.tsx:5381": [
- [0, 0, 0, "No untranslated strings. Wrap text with ", "0"],
- [0, 0, 0, "No untranslated strings. Wrap text with ", "1"]
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "0"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "1"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "2"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "3"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "4"],
+ [0, 0, 0, "Do not use the t() function outside of a component or function", "5"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "6"],
+ [0, 0, 0, "No untranslated strings. Wrap text with ", "7"]
],
"public/app/features/inspector/InspectStatsTab.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
diff --git a/packages/grafana-eslint-rules/README.md b/packages/grafana-eslint-rules/README.md
index 2f73d46a529..e57e2ec09a1 100644
--- a/packages/grafana-eslint-rules/README.md
+++ b/packages/grafana-eslint-rules/README.md
@@ -156,7 +156,18 @@ const SearchTitle = ({ term }) => (
#### How to translate props or attributes
-Right now, we only check if a string is wrapped up by the `Trans` tag. We currently do not apply this rule to props, attributes or similar, but we also ask for them to be translated with the `t()` function.
+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`
+- `text`
+- `tooltip`
```tsx
// Bad ❌
@@ -168,3 +179,50 @@ return ;
```
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