diff --git a/contribute/internationalization.md b/contribute/internationalization.md index 8729f8175f5..331c36b1710 100644 --- a/contribute/internationalization.md +++ b/contribute/internationalization.md @@ -17,10 +17,10 @@ Grafana uses the [i18next](https://www.i18next.com/) framework for managing tran ### JSX -1. For JSX children, use the `` component from `app/core/internationalization` with the `i18nKey`, ensuring it conforms to the following guidelines, with the default English translation. For example: +1. For JSX children, use the `` component from `@grafana/i18n` with the `i18nKey`, ensuring it conforms to the following guidelines, with the default English translation. For example: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; const SearchTitle = ({ term }) => Results for {{ term }}; ``` @@ -32,7 +32,7 @@ There may be cases where you need to interpolate variables inside other componen If the nested component is displaying the variable only (e.g. to add emphasis or color), the best solution is to create a new wrapping component: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; import { Text } from '@grafana/ui'; const SearchTerm = ({ term }) => {term}; @@ -47,7 +47,7 @@ const SearchTitle = ({ term }) => ( However there are also cases where the nested component might be displaying additional text which also needs to be translated. In this case, you can use the `values` prop to explicitly pass variables to the translation, and reference them as templated strings in the markup. For example: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; import { Text } from '@grafana/ui'; const SearchTitle = ({ term }) => ( @@ -75,8 +75,9 @@ const ErrorMessage = ({ id, message }) => There Sometimes you may need to translate a string cannot be represented in JSX, such as `placeholder` props. Use the `t` macro for this. ```jsx -import { t } from "app/core/internationalization" +import { useTranslate } from "@grafana/i18n" +const { t } = useTranslate(); const placeholder = t('form.username-placeholder','Username'); return @@ -146,7 +147,7 @@ Refer to the [i18next](https://www.i18next.com/) and [react-i18next](https://rea For fixed phrases: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; Hello user!; ``` @@ -154,7 +155,7 @@ import { Trans } from 'app/core/internationalization'; To interpolate a variable, include it as an object child. It's a weird syntax, but `Trans` will do its magic to make it work: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; Hello {{ name: user.name }}!; @@ -165,7 +166,7 @@ const userName = user.name; Variables must be strings (or, must support calling `.toString()`, which we almost never want). For example: ```jsx -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; // This will not work const userName = user.name; @@ -183,7 +184,7 @@ const userName = user.name; Both HTML tags and React components can be included in a phase. The `Trans` function handles interpolating for its children. ```js -import { Trans } from "app/core/internationalization" +import { Trans } from "@grafana/i18n" Click to learn more. @@ -202,7 +203,7 @@ import { Trans } from "app/core/internationalization" Plurals require special handling to make sure they can be translated according to the rules of each locale (which may be more complex than you think). Use either the `` component or the `t` function, with the `count` prop to provide a singular form. For example: ```js -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; You got {{ count: messages.length }} messages @@ -210,8 +211,9 @@ import { Trans } from 'app/core/internationalization'; ``` ```js -import { t } from 'app/core/internationalization'; +import { useTranslate } from '@grafana/i18n'; +const { t } = useTranslate(); const translatedString = t('inbox.heading', 'You got {{count}} messages', { count: messages.length }); ``` diff --git a/packages/grafana-i18n/src/i18n.tsx b/packages/grafana-i18n/src/i18n.tsx index a0459c73e26..02f2396c403 100644 --- a/packages/grafana-i18n/src/i18n.tsx +++ b/packages/grafana-i18n/src/i18n.tsx @@ -119,7 +119,8 @@ export function getNamespaces() { } export async function changeLanguage(language?: string) { - await getI18nInstance().changeLanguage(language ?? DEFAULT_LANGUAGE); + const validLanguage = LANGUAGES.find((lang) => lang.code === language)?.code ?? DEFAULT_LANGUAGE; + await getI18nInstance().changeLanguage(validLanguage); } type ResourceKey = string; diff --git a/packages/grafana-i18n/src/internal/index.ts b/packages/grafana-i18n/src/internal/index.ts index 5e1174e6437..ad39e9cb4d1 100644 --- a/packages/grafana-i18n/src/internal/index.ts +++ b/packages/grafana-i18n/src/internal/index.ts @@ -9,6 +9,13 @@ * preventing the code from being importable by plugins or other npm packages making it truly "internal". * */ +import { t } from '../i18n'; + +type TFunction = typeof t; + +export type { TFunction }; +export { t }; + export { addResourceBundle, changeLanguage, @@ -16,5 +23,4 @@ export { getLanguage, getResolvedLanguage, initTranslations, - t, } from '../i18n'; diff --git a/public/app/api/clients/provisioning/index.ts b/public/app/api/clients/provisioning/index.ts index 5695306ad6e..bbddc1ae363 100644 --- a/public/app/api/clients/provisioning/index.ts +++ b/public/app/api/clients/provisioning/index.ts @@ -1,7 +1,7 @@ +import { t } from '@grafana/i18n/internal'; import { isFetchError } from '@grafana/runtime'; import { notifyApp } from 'app/core/actions'; import { createSuccessNotification, createErrorNotification } from 'app/core/copy/appNotification'; -import { t } from 'app/core/internationalization'; import { generatedAPI, diff --git a/public/app/app.ts b/public/app/app.ts index 6244e79df41..4fbfe064bf8 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -18,6 +18,7 @@ import { standardFieldConfigEditorRegistry, standardTransformersRegistry, } from '@grafana/data'; +import { initTranslations } from '@grafana/i18n/internal'; import { locationService, registerEchoBackend, @@ -61,7 +62,8 @@ import { getAllOptionEditors, getAllStandardFieldConfigs } from './core/componen import { PluginPage } from './core/components/Page/PluginPage'; import { GrafanaContextType, useReturnToPreviousInternal } from './core/context/GrafanaContext'; import { initializeCrashDetection } from './core/crash'; -import { initializeI18n } from './core/internationalization'; +import { NAMESPACES } from './core/internationalization/constants'; +import { loadTranslations } from './core/internationalization/loadTranslations'; import { postInitTasks, preInitTasks } from './core/lifecycle-hooks'; import { setMonacoEnv } from './core/monacoEnv'; import { interceptLinkClicks } from './core/navigation/patch/interceptLinkClicks'; @@ -124,7 +126,14 @@ export class GrafanaApp { // Let iframe container know grafana has started loading window.parent.postMessage('GrafanaAppInit', '*'); - const initI18nPromise = initializeI18n(config.bootData.user.language); + // This is a placeholder so we can put a 'comment' in the message json files. + // Starts with an underscore so it's sorted to the top of the file. Even though it is in a comment the following line is still extracted + // t('_comment', 'The code is the source of truth for English phrases. They should be updated in the components directly, and additional plurals specified in this file.'); + const initI18nPromise = initTranslations({ + language: config.bootData.user.language, + ns: NAMESPACES, + module: loadTranslations, + }); initI18nPromise.then(({ language }) => updateConfig({ language })); setBackendSrv(backendSrv); diff --git a/public/app/core/components/AccessControl/AddPermission.tsx b/public/app/core/components/AccessControl/AddPermission.tsx index 3a7b8628ff9..3b2cde7838b 100644 --- a/public/app/core/components/AccessControl/AddPermission.tsx +++ b/public/app/core/components/AccessControl/AddPermission.tsx @@ -1,11 +1,12 @@ import { useEffect, useMemo, useState } from 'react'; +import { Trans } from '@grafana/i18n'; +import { t } from '@grafana/i18n/internal'; import { Button, Select, Stack } from '@grafana/ui'; import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; import { ServiceAccountPicker } from 'app/core/components/Select/ServiceAccountPicker'; import { TeamPicker } from 'app/core/components/Select/TeamPicker'; import { UserPicker } from 'app/core/components/Select/UserPicker'; -import { Trans, t } from 'app/core/internationalization'; import { OrgRole } from 'app/types/acl'; import { Assignments, PermissionTarget, SetPermission } from './types'; diff --git a/public/app/core/components/AccessControl/PermissionList.tsx b/public/app/core/components/AccessControl/PermissionList.tsx index 9468a4bf5a1..8fcef15408a 100644 --- a/public/app/core/components/AccessControl/PermissionList.tsx +++ b/public/app/core/components/AccessControl/PermissionList.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; -import { Trans } from 'app/core/internationalization'; +import { Trans } from '@grafana/i18n'; import { PermissionListItem } from './PermissionListItem'; import { ResourcePermission } from './types'; diff --git a/public/app/core/components/AccessControl/PermissionListItem.tsx b/public/app/core/components/AccessControl/PermissionListItem.tsx index 0b9930cbc07..5a18bd566b6 100644 --- a/public/app/core/components/AccessControl/PermissionListItem.tsx +++ b/public/app/core/components/AccessControl/PermissionListItem.tsx @@ -1,8 +1,8 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; +import { Trans, useTranslate } from '@grafana/i18n'; import { Box, Button, Icon, Select, Tooltip, useStyles2 } from '@grafana/ui'; -import { t, Trans } from 'app/core/internationalization'; import { ResourcePermission } from './types'; @@ -16,7 +16,7 @@ interface Props { export const PermissionListItem = ({ item, permissionLevels, canSet, onRemove, onChange }: Props) => { const styles = useStyles2(getStyles); - + const { t } = useTranslate(); return ( {getAvatar(item)} diff --git a/public/app/core/components/AccessControl/Permissions.tsx b/public/app/core/components/AccessControl/Permissions.tsx index 939c179901e..36b32270160 100644 --- a/public/app/core/components/AccessControl/Permissions.tsx +++ b/public/app/core/components/AccessControl/Permissions.tsx @@ -4,9 +4,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { Trans } from '@grafana/i18n'; +import { t } from '@grafana/i18n/internal'; import { Text, Box, Button, useStyles2, Space } from '@grafana/ui'; import { SlideDown } from 'app/core/components/Animations/SlideDown'; -import { Trans, t } from 'app/core/internationalization'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { DescendantCount } from 'app/features/browse-dashboards/components/BrowseActions/DescendantCount'; diff --git a/public/app/core/components/AppChrome/AppChrome.tsx b/public/app/core/components/AppChrome/AppChrome.tsx index 6497973d7ae..bc017c618b8 100644 --- a/public/app/core/components/AppChrome/AppChrome.tsx +++ b/public/app/core/components/AppChrome/AppChrome.tsx @@ -4,11 +4,11 @@ import { Resizable } from 're-resizable'; import { PropsWithChildren, useEffect } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { Trans } from '@grafana/i18n'; import { locationSearchToObject, locationService, useScopes } from '@grafana/runtime'; import { ErrorBoundaryAlert, getDragStyles, LinkButton, useStyles2 } from '@grafana/ui'; import { useGrafana } from 'app/core/context/GrafanaContext'; import { useMediaQueryMinWidth } from 'app/core/hooks/useMediaQueryMinWidth'; -import { Trans } from 'app/core/internationalization'; import store from 'app/core/store'; import { CommandPalette } from 'app/features/commandPalette/CommandPalette'; import { ScopesDashboards } from 'app/features/scopes/dashboards/ScopesDashboards'; diff --git a/public/app/core/components/AppChrome/AppChromeService.tsx b/public/app/core/components/AppChrome/AppChromeService.tsx index f3de83d38a5..68a98eef634 100644 --- a/public/app/core/components/AppChrome/AppChromeService.tsx +++ b/public/app/core/components/AppChrome/AppChromeService.tsx @@ -2,9 +2,9 @@ import { useObservable } from 'react-use'; import { BehaviorSubject } from 'rxjs'; import { AppEvents, NavModel, NavModelItem, PageLayoutType, UrlQueryValue } from '@grafana/data'; +import { t } from '@grafana/i18n/internal'; import { config, locationService, reportInteraction } from '@grafana/runtime'; import appEvents from 'app/core/app_events'; -import { t } from 'app/core/internationalization'; import store from 'app/core/store'; import { isShallowEqual } from 'app/core/utils/isShallowEqual'; import { KioskMode } from 'app/types'; diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx index c8c638ea512..10144d70031 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionToolbarItem.tsx @@ -2,8 +2,8 @@ import { css, cx } from '@emotion/css'; import { useCallback } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { Dropdown, Menu, ToolbarButton, useTheme2 } from '@grafana/ui'; -import { t } from 'app/core/internationalization'; import { NavToolbarSeparator } from '../NavToolbar/NavToolbarSeparator'; @@ -17,6 +17,7 @@ export function ExtensionToolbarItem() { const styles = getStyles(useTheme2()); const { availableComponents, dockedComponentId, setDockedComponentId, isOpen, isEnabled } = useExtensionSidebarContext(); + const { t } = useTranslate(); let dockedComponentTitle = ''; if (dockedComponentId) { @@ -72,7 +73,7 @@ export function ExtensionToolbarItem() { /> ); }, - [setDockedComponentId, styles.button, styles.buttonActive] + [setDockedComponentId, styles.button, styles.buttonActive, t] ); if (components.length === 1) { diff --git a/public/app/core/components/AppChrome/History/HistoryContainer.tsx b/public/app/core/components/AppChrome/History/HistoryContainer.tsx index 95d95e708b2..d2e677f3b12 100644 --- a/public/app/core/components/AppChrome/History/HistoryContainer.tsx +++ b/public/app/core/components/AppChrome/History/HistoryContainer.tsx @@ -3,9 +3,9 @@ import { useEffect } from 'react'; import { useToggle } from 'react-use'; import { GrafanaTheme2, store } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { Drawer, ToolbarButton, useStyles2 } from '@grafana/ui'; import { appEvents } from 'app/core/app_events'; -import { t } from 'app/core/internationalization'; import { RecordHistoryEntryEvent } from 'app/types/events'; import { HISTORY_LOCAL_STORAGE_KEY } from '../AppChromeService'; @@ -47,6 +47,7 @@ export function HistoryContainer() { }; }); }, []); + const { t } = useTranslate(); return ( <> diff --git a/public/app/core/components/AppChrome/History/HistoryWrapper.tsx b/public/app/core/components/AppChrome/History/HistoryWrapper.tsx index 53aec15c77a..479580d6cfb 100644 --- a/public/app/core/components/AppChrome/History/HistoryWrapper.tsx +++ b/public/app/core/components/AppChrome/History/HistoryWrapper.tsx @@ -3,8 +3,8 @@ import moment from 'moment'; import { useState } from 'react'; import { FieldType, GrafanaTheme2, store } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { Button, Card, IconButton, Space, Stack, Text, useStyles2, Box, Sparkline, useTheme2, Icon } from '@grafana/ui'; -import { t } from 'app/core/internationalization'; import { formatDate } from 'app/core/internationalization/dates'; import { HISTORY_LOCAL_STORAGE_KEY } from '../AppChromeService'; @@ -13,6 +13,7 @@ import { HistoryEntry } from '../types'; import { logClickUnifiedHistoryEntryEvent, logUnifiedHistoryShowMoreEvent } from './eventsTracking'; export function HistoryWrapper({ onClose }: { onClose: () => void }) { + const { t } = useTranslate(); const history = store.getObject(HISTORY_LOCAL_STORAGE_KEY, []).filter((entry) => { return moment(entry.time).isAfter(moment().subtract(2, 'day').startOf('day')); }); @@ -88,6 +89,7 @@ function HistoryEntryAppView({ entry, isSelected, onClick }: ItemProps) { const styles = useStyles2(getStyles); const theme = useTheme2(); const [isExpanded, setIsExpanded] = useState(isSelected && entry.views.length > 0); + const { t } = useTranslate(); const { breadcrumbs, views, time, url, sparklineData } = entry; const expandedLabel = isExpanded ? t('nav.history-wrapper.collapse', 'Collapse') diff --git a/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx b/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx index 88d61ad0665..4e68c1b8f18 100644 --- a/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx +++ b/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx @@ -5,10 +5,10 @@ import { useLocation } from 'react-router-dom-v5-compat'; import { GrafanaTheme2, NavModelItem } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; +import { useTranslate } from '@grafana/i18n'; import { config, reportInteraction } from '@grafana/runtime'; import { ScrollContainer, useStyles2 } from '@grafana/ui'; import { useGrafana } from 'app/core/context/GrafanaContext'; -import { t } from 'app/core/internationalization'; import { setBookmark } from 'app/core/reducers/navBarTree'; import { usePatchUserPreferencesMutation } from 'app/features/preferences/api/index'; import { useDispatch, useSelector } from 'app/types'; @@ -37,7 +37,7 @@ export const MegaMenu = memo( const state = chrome.useState(); const [patchPreferences] = usePatchUserPreferencesMutation(); const pinnedItems = usePinnedItems(); - + const { t } = useTranslate(); // Remove profile + help from tree const navItems = navTree .filter((item) => item.id !== 'profile' && item.id !== 'help') diff --git a/public/app/core/components/AppChrome/MegaMenu/MegaMenuHeader.tsx b/public/app/core/components/AppChrome/MegaMenu/MegaMenuHeader.tsx index 91f476bdef1..b44c3e62745 100644 --- a/public/app/core/components/AppChrome/MegaMenu/MegaMenuHeader.tsx +++ b/public/app/core/components/AppChrome/MegaMenu/MegaMenuHeader.tsx @@ -1,9 +1,9 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { IconButton, Stack, ToolbarButton, useTheme2 } from '@grafana/ui'; import { useGrafana } from 'app/core/context/GrafanaContext'; -import { t } from 'app/core/internationalization'; import { Branding } from '../../Branding/Branding'; import { OrganizationSwitcher } from '../OrganizationSwitcher/OrganizationSwitcher'; @@ -22,6 +22,7 @@ export function MegaMenuHeader({ handleMegaMenu, handleDockedMenu, onClose }: Pr const theme = useTheme2(); const { chrome } = useGrafana(); const state = chrome.useState(); + const { t } = useTranslate(); const styles = getStyles(theme); return ( diff --git a/public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx b/public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx index a64b26d474d..fe99a1efad8 100644 --- a/public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx +++ b/public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx @@ -5,10 +5,10 @@ import { useLocation } from 'react-router-dom-v5-compat'; import { useLocalStorage } from 'react-use'; import { FeatureState, GrafanaTheme2, NavModelItem, toIconName } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { useStyles2, Text, IconButton, Icon, Stack, FeatureBadge } from '@grafana/ui'; import { useGrafana } from 'app/core/context/GrafanaContext'; -import { t } from '../../../internationalization'; import { Indent } from '../../Indent/Indent'; import { FeatureHighlight } from './FeatureHighlight'; @@ -58,6 +58,7 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi }); } }, [isActive]); + const { t } = useTranslate(); if (!link.url) { return null; diff --git a/public/app/core/components/AppChrome/MegaMenu/MegaMenuItemText.tsx b/public/app/core/components/AppChrome/MegaMenu/MegaMenuItemText.tsx index b6d18b50d9d..1b9434aa313 100644 --- a/public/app/core/components/AppChrome/MegaMenu/MegaMenuItemText.tsx +++ b/public/app/core/components/AppChrome/MegaMenu/MegaMenuItemText.tsx @@ -3,9 +3,9 @@ import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; +import { useTranslate } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { Icon, IconButton, Link, useTheme2 } from '@grafana/ui'; -import { t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; export interface Props { @@ -20,6 +20,7 @@ export interface Props { export function MegaMenuItemText({ children, isActive, onClick, target, url, onPin, isPinned }: Props) { const theme = useTheme2(); + const { t } = useTranslate(); const styles = getStyles(theme, isActive); const LinkComponent = !target && url.startsWith('/') ? Link : 'a'; diff --git a/public/app/core/components/AppChrome/MegaMenu/utils.ts b/public/app/core/components/AppChrome/MegaMenu/utils.ts index 178d6604dca..55ec4af5389 100644 --- a/public/app/core/components/AppChrome/MegaMenu/utils.ts +++ b/public/app/core/components/AppChrome/MegaMenu/utils.ts @@ -1,9 +1,9 @@ import { useEffect } from 'react'; import { NavModelItem } from '@grafana/data'; +import { t } from '@grafana/i18n/internal'; import { config, reportInteraction } from '@grafana/runtime'; import { MEGA_MENU_TOGGLE_ID } from 'app/core/constants'; -import { t } from 'app/core/internationalization'; import { HOME_NAV_ID } from 'app/core/reducers/navModel'; import { ShowModalReactEvent } from '../../../../types/events'; diff --git a/public/app/core/components/AppChrome/News/NewsDrawer.tsx b/public/app/core/components/AppChrome/News/NewsDrawer.tsx index 71aef1bac3b..d582a5267b4 100644 --- a/public/app/core/components/AppChrome/News/NewsDrawer.tsx +++ b/public/app/core/components/AppChrome/News/NewsDrawer.tsx @@ -2,8 +2,8 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; +import { useTranslate } from '@grafana/i18n'; import { IconButton, Drawer, useStyles2, Text } from '@grafana/ui'; -import { t } from 'app/core/internationalization'; import { DEFAULT_FEED_URL } from 'app/plugins/panel/news/constants'; import grotNewsSvg from 'img/grot-news.svg'; @@ -16,7 +16,7 @@ interface NewsContainerProps { export function NewsContainer({ onClose }: NewsContainerProps) { const styles = useStyles2(getStyles); - + const { t } = useTranslate(); return ( { getNews(); }, [getNews]); + const { t } = useTranslate(); if (state.error) { return
{state.error && state.error.message}
; diff --git a/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSelect.tsx b/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSelect.tsx index 8f0f9b89574..b75688a9c8c 100644 --- a/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSelect.tsx +++ b/public/app/core/components/AppChrome/OrganizationSwitcher/OrganizationSelect.tsx @@ -2,16 +2,16 @@ import { css } from '@emotion/css'; import { useMemo, useState } from 'react'; import { SelectableValue, GrafanaTheme2 } from '@grafana/data'; +import { useTranslate } from '@grafana/i18n'; import { Icon, Select, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { UserOrg } from 'app/types'; -import { t } from '../../../internationalization'; - import { OrganizationBaseProps } from './types'; export function OrganizationSelect({ orgs, onSelectChange }: OrganizationBaseProps) { const styles = useStyles2(getStyles); + const { t } = useTranslate(); const { orgId } = contextSrv.user; const options = useMemo( diff --git a/public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx b/public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx index 269ce8f9471..74b8749144a 100644 --- a/public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx +++ b/public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx @@ -1,11 +1,11 @@ import { useMemo, useState } from 'react'; +import { useTranslate } from '@grafana/i18n'; import { reportInteraction } from '@grafana/runtime'; import { Menu, Dropdown, ToolbarButton } from '@grafana/ui'; import { getExternalUserMngLinkUrl } from 'app/features/users/utils'; import { useSelector } from 'app/types'; -import { t } from '../../../internationalization'; import { performInviteUserClick, shouldRenderInviteUserButton } from '../../InviteUserButton/utils'; import { NavToolbarSeparator } from '../NavToolbar/NavToolbarSeparator'; @@ -16,6 +16,7 @@ export interface Props {} export const QuickAdd = ({}: Props) => { const navBarTree = useSelector((state) => state.navBarTree); const [isOpen, setIsOpen] = useState(false); + const { t } = useTranslate(); const createActions = useMemo(() => { const actions = findCreateActions(navBarTree); @@ -32,7 +33,7 @@ export const QuickAdd = ({}: Props) => { } return actions; - }, [navBarTree]); + }, [navBarTree, t]); const showQuickAdd = createActions.length > 0; if (!showQuickAdd) { diff --git a/public/app/core/components/AppChrome/ReturnToPrevious/DismissableButton.tsx b/public/app/core/components/AppChrome/ReturnToPrevious/DismissableButton.tsx index fbc0dcc3803..b618f69c291 100644 --- a/public/app/core/components/AppChrome/ReturnToPrevious/DismissableButton.tsx +++ b/public/app/core/components/AppChrome/ReturnToPrevious/DismissableButton.tsx @@ -2,8 +2,8 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; +import { useTranslate } from '@grafana/i18n'; import { Button, ButtonGroup, useStyles2 } from '@grafana/ui'; -import { t } from 'app/core/internationalization'; export interface DismissableButtonProps { label: string; @@ -13,7 +13,7 @@ export interface DismissableButtonProps { export const DismissableButton = ({ label, onClick, onDismiss }: DismissableButtonProps) => { const styles = useStyles2(getStyles); - + const { t } = useTranslate(); return (