mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Navigation: Ensure command palette is correctly translated (#61103)
* Navigation: Translate nav tree in store * Remove usage of getNavTitle * properly translate children * add pubdash translation * remove extraneous calls to translation in PageHeader * empty commit * correctly allow subtitle to be overrwitten (thank you tests!) --------- Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
parent
bc67edbff8
commit
504eabbe80
@ -44,6 +44,7 @@ export interface NavModelItem extends NavLinkDTO {
|
||||
highlightId?: string;
|
||||
tabSuffix?: ComponentType<{ className?: string }>;
|
||||
hideFromBreadcrumbs?: boolean;
|
||||
emptyMessage?: string;
|
||||
}
|
||||
|
||||
export enum NavSection {
|
||||
|
@ -123,6 +123,10 @@ export class GrafanaApp {
|
||||
setPanelDataErrorView(PanelDataErrorView);
|
||||
setLocationSrv(locationService);
|
||||
setTimeZoneResolver(() => config.bootData.user.timezone);
|
||||
|
||||
// We must wait for translations to load because some preloaded store state requires translating
|
||||
await initI18nPromise;
|
||||
|
||||
// Important that extension reducers are initialized before store
|
||||
addExtensionReducers();
|
||||
configureStore();
|
||||
@ -177,12 +181,8 @@ export class GrafanaApp {
|
||||
const modalManager = new ModalManager();
|
||||
modalManager.init();
|
||||
|
||||
await Promise.all([
|
||||
initI18nPromise,
|
||||
|
||||
// Preload selected app plugins
|
||||
await preloadPlugins(config.apps),
|
||||
]);
|
||||
// Preload selected app plugins
|
||||
await preloadPlugins(config.apps);
|
||||
|
||||
// initialize chrome service
|
||||
const queryParams = locationService.getSearchObject();
|
||||
|
@ -6,8 +6,6 @@ import { useStyles2 } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { useNavModel } from 'app/core/hooks/useNavModel';
|
||||
|
||||
import { getNavTitle, getNavSubTitle } from '../NavBar/navBarItem-translations';
|
||||
|
||||
import { NavLandingPageCard } from './NavLandingPageCard';
|
||||
|
||||
interface Props {
|
||||
@ -28,8 +26,8 @@ export function NavLandingPage({ navId }: Props) {
|
||||
{children?.map((child) => (
|
||||
<NavLandingPageCard
|
||||
key={child.id}
|
||||
description={getNavSubTitle(child.id) ?? child.subTitle}
|
||||
text={getNavTitle(child.id) ?? child.text}
|
||||
description={child.subTitle}
|
||||
text={child.text}
|
||||
url={child.url ?? ''}
|
||||
/>
|
||||
))}
|
||||
|
@ -6,7 +6,6 @@ import { useLocation } from 'react-router-dom';
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { Menu, MenuItem, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { getNavTitle } from '../../NavBar/navBarItem-translations';
|
||||
import { enrichConfigItems, enrichWithInteractionTracking } from '../../NavBar/utils';
|
||||
|
||||
export interface TopNavBarMenuProps {
|
||||
@ -30,24 +29,23 @@ export function TopNavBarMenu({ node: nodePlain }: TopNavBarMenuProps) {
|
||||
// see https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-static-element-interactions.md#case-the-event-handler-is-only-being-used-to-capture-bubbled-events
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div onClick={(e) => e.stopPropagation()} className={styles.header}>
|
||||
<div>{getNavTitle(node.id) ?? node.text}</div>
|
||||
<div>{node.text}</div>
|
||||
{node.subTitle && <div className={styles.subTitle}>{node.subTitle}</div>}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{node.children?.map((item) => {
|
||||
const itemText = getNavTitle(item.id) ?? item.text;
|
||||
const showExternalLinkIcon = /^https?:\/\//.test(item.url || '');
|
||||
return item.url ? (
|
||||
<MenuItem
|
||||
url={item.url}
|
||||
label={itemText}
|
||||
label={item.text}
|
||||
icon={showExternalLinkIcon ? 'external-link-alt' : undefined}
|
||||
target={item.target}
|
||||
key={item.id}
|
||||
/>
|
||||
) : (
|
||||
<MenuItem icon={item.icon} onClick={item.onClick} label={itemText} key={item.id} />
|
||||
<MenuItem icon={item.icon} onClick={item.onClick} label={item.text} key={item.id} />
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { NavModelItem } from '@grafana/data';
|
||||
|
||||
import { getNavTitle } from '../NavBar/navBarItem-translations';
|
||||
|
||||
import { Breadcrumb } from './types';
|
||||
|
||||
export function buildBreadcrumbs(sectionNav: NavModelItem, pageNav?: NavModelItem, homeNav?: NavModelItem) {
|
||||
@ -19,10 +17,10 @@ export function buildBreadcrumbs(sectionNav: NavModelItem, pageNav?: NavModelIte
|
||||
|
||||
if (!foundHome && !node.hideFromBreadcrumbs) {
|
||||
if (homeNav && urlToMatch === homeNav.url) {
|
||||
crumbs.unshift({ text: getNavTitle(homeNav.id) ?? homeNav.text, href: node.url ?? '' });
|
||||
crumbs.unshift({ text: homeNav.text, href: node.url ?? '' });
|
||||
foundHome = true;
|
||||
} else {
|
||||
crumbs.unshift({ text: getNavTitle(node.id) ?? node.text, href: node.url ?? '' });
|
||||
crumbs.unshift({ text: node.text, href: node.url ?? '' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import React from 'react';
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { toIconName, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { getNavTitle } from '../NavBar/navBarItem-translations';
|
||||
import { isMatchOrChildMatch } from '../NavBar/utils';
|
||||
|
||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||
@ -21,12 +20,11 @@ export function NavBarMenuItemWrapper({
|
||||
}) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
if (link.emptyMessageId && !linkHasChildren(link)) {
|
||||
const emptyMessageTranslated = getNavTitle(link.emptyMessageId);
|
||||
if (link.emptyMessage && !linkHasChildren(link)) {
|
||||
return (
|
||||
<NavBarMenuSection link={link}>
|
||||
<ul className={styles.children}>
|
||||
<div className={styles.emptyMessage}>{emptyMessageTranslated}</div>
|
||||
<div className={styles.emptyMessage}>{link.emptyMessage}</div>
|
||||
</ul>
|
||||
</NavBarMenuSection>
|
||||
);
|
||||
@ -52,7 +50,7 @@ export function NavBarMenuItemWrapper({
|
||||
target={childLink.target}
|
||||
url={childLink.url}
|
||||
>
|
||||
{getNavTitle(childLink.id) ?? childLink.text}
|
||||
{childLink.text}
|
||||
</NavBarMenuItem>
|
||||
)
|
||||
);
|
||||
|
@ -7,7 +7,6 @@ import { Button, Icon, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { NavBarItemIcon } from '../NavBar/NavBarItemIcon';
|
||||
import { NavFeatureHighlight } from '../NavBar/NavFeatureHighlight';
|
||||
import { getNavTitle } from '../NavBar/navBarItem-translations';
|
||||
import { hasChildMatch } from '../NavBar/utils';
|
||||
|
||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||
@ -53,7 +52,7 @@ export function NavBarMenuSection({
|
||||
<FeatureHighlightWrapper>
|
||||
<NavBarItemIcon link={link} />
|
||||
</FeatureHighlightWrapper>
|
||||
{getNavTitle(link.id) ?? link.text}
|
||||
{link.text}
|
||||
</div>
|
||||
</NavBarMenuItem>
|
||||
{children && (
|
||||
|
@ -11,7 +11,6 @@ import { NavBarItemMenuTrigger } from './NavBarItemMenuTrigger';
|
||||
import { getNavBarItemWithoutMenuStyles } from './NavBarItemWithoutMenu';
|
||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||
import { useNavBarContext } from './context';
|
||||
import { getNavTitle } from './navBarItem-translations';
|
||||
import { getNavModelItemKey } from './utils';
|
||||
|
||||
export interface Props {
|
||||
@ -53,14 +52,12 @@ const NavBarItem = ({ isActive = false, className, reverseMenuDirection = false,
|
||||
}
|
||||
};
|
||||
|
||||
const linkText = getNavTitle(link.id) ?? link.text;
|
||||
|
||||
return (
|
||||
<li className={cx(styles.container, { [styles.containerHover]: section.id === menuIdOpen }, className)}>
|
||||
<NavBarItemMenuTrigger
|
||||
item={section}
|
||||
isActive={isActive}
|
||||
label={linkText}
|
||||
label={link.text}
|
||||
reverseMenuDirection={reverseMenuDirection}
|
||||
>
|
||||
<NavBarItemMenu
|
||||
@ -72,7 +69,6 @@ const NavBarItem = ({ isActive = false, className, reverseMenuDirection = false,
|
||||
onNavigate={onNavigate}
|
||||
>
|
||||
{(item: NavModelItem) => {
|
||||
const itemText = getNavTitle(item.id) ?? item.text;
|
||||
const isSection = item.menuItemType === NavMenuItemType.Section;
|
||||
const iconName = item.icon ? toIconName(item.icon) : undefined;
|
||||
const icon = item.showIconInNavbar && !isSection ? iconName : undefined;
|
||||
@ -83,7 +79,7 @@ const NavBarItem = ({ isActive = false, className, reverseMenuDirection = false,
|
||||
isDivider={!isSection && item.divider}
|
||||
icon={icon}
|
||||
target={item.target}
|
||||
text={itemText}
|
||||
text={item.text}
|
||||
url={item.url}
|
||||
onClick={item.onClick}
|
||||
styleOverrides={cx(styles.primaryText, { [styles.header]: isSection })}
|
||||
|
@ -10,7 +10,6 @@ import { CustomScrollbar, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { NavBarItemMenuItem } from './NavBarItemMenuItem';
|
||||
import { useNavBarItemMenuContext } from './context';
|
||||
import { getNavTitle } from './navBarItem-translations';
|
||||
import { getNavModelItemKey } from './utils';
|
||||
|
||||
export interface NavBarItemMenuProps extends SpectrumMenuProps<NavModelItem> {
|
||||
@ -58,11 +57,10 @@ export function NavBarItemMenu(props: NavBarItemMenuProps): ReactElement | null
|
||||
<NavBarItemMenuItem key={getNavModelItemKey(item.value)} item={item} state={state} onNavigate={onNavigate} />
|
||||
));
|
||||
|
||||
if (itemComponents.length === 0 && section.value.emptyMessageId) {
|
||||
const emptyMessageTranslated = getNavTitle(section.value.emptyMessageId);
|
||||
if (itemComponents.length === 0 && section.value.emptyMessage) {
|
||||
itemComponents.push(
|
||||
<div key="empty-message" className={styles.emptyMessage}>
|
||||
{emptyMessageTranslated}
|
||||
{section.value.emptyMessage}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
|
||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||
import { NavBarToggle } from './NavBarToggle';
|
||||
import { NavFeatureHighlight } from './NavFeatureHighlight';
|
||||
import { getNavTitle } from './navBarItem-translations';
|
||||
import { isMatchOrChildMatch } from './utils';
|
||||
|
||||
const MENU_WIDTH = '350px';
|
||||
@ -256,7 +255,7 @@ export function NavItem({
|
||||
}}
|
||||
styleOverrides={styles.item}
|
||||
target={childLink.target}
|
||||
text={getNavTitle(childLink.id) ?? childLink.text}
|
||||
text={childLink.text}
|
||||
url={childLink.url}
|
||||
isMobile={true}
|
||||
/>
|
||||
@ -266,12 +265,11 @@ export function NavItem({
|
||||
</ul>
|
||||
</CollapsibleNavItem>
|
||||
);
|
||||
} else if (link.emptyMessageId) {
|
||||
const emptyMessageTranslated = getNavTitle(link.emptyMessageId);
|
||||
} else if (link.emptyMessage) {
|
||||
return (
|
||||
<CollapsibleNavItem onClose={onClose} link={link} isActive={isMatchOrChildMatch(link, activeItem)}>
|
||||
<ul className={styles.children}>
|
||||
<div className={styles.emptyMessage}>{emptyMessageTranslated}</div>
|
||||
<div className={styles.emptyMessage}>{link.emptyMessage}</div>
|
||||
</ul>
|
||||
</CollapsibleNavItem>
|
||||
);
|
||||
@ -297,7 +295,7 @@ export function NavItem({
|
||||
<NavBarItemIcon link={link} />
|
||||
</FeatureHighlightWrapper>
|
||||
</div>
|
||||
<span className={styles.linkText}>{getNavTitle(link.id) ?? link.text}</span>
|
||||
<span className={styles.linkText}>{link.text}</span>
|
||||
</div>
|
||||
</NavBarItemWithoutMenu>
|
||||
</li>
|
||||
@ -398,7 +396,7 @@ function CollapsibleNavItem({
|
||||
contentClassName={styles.collapseContent}
|
||||
label={
|
||||
<div className={cx(styles.labelWrapper, { [styles.primary]: isActive })}>
|
||||
<span className={styles.linkText}>{getNavTitle(link.id) ?? link.text}</span>
|
||||
<span className={styles.linkText}>{link.text}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
// Maps the ID of the nav item to a translated phrase to later pass to <Trans />
|
||||
// Because the navigation content is dynamic (defined in the backend), we can not use
|
||||
// the normal inline message definition method.
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { t } from 'app/core/internationalization';
|
||||
|
||||
// see pkg/api/index.go
|
||||
export function getNavTitle(navId: string | undefined) {
|
||||
// the switch cases must match the ID of the navigation item, as defined in the backend nav model
|
||||
switch (navId) {
|
||||
@ -38,6 +39,8 @@ export function getNavTitle(navId: string | undefined) {
|
||||
return t('nav.snapshots.title', 'Snapshots');
|
||||
case 'dashboards/library-panels':
|
||||
return t('nav.library-panels.title', 'Library panels');
|
||||
case 'dashboards/public':
|
||||
return t('nav.public.title', 'Public dashboards');
|
||||
case 'dashboards/new':
|
||||
return t('nav.new-dashboard.title', 'New dashboard');
|
||||
case 'dashboards/folder/new':
|
||||
|
@ -3,7 +3,6 @@ import React, { FC } from 'react';
|
||||
|
||||
import { NavModelItem, NavModelBreadcrumb, GrafanaTheme2 } from '@grafana/data';
|
||||
import { Tab, TabsBar, Icon, useStyles2, toIconName } from '@grafana/ui';
|
||||
import { getNavTitle, getNavSubTitle } from 'app/core/components/NavBar/navBarItem-translations';
|
||||
import { PanelHeaderMenuItem } from 'app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem';
|
||||
|
||||
import { PageInfoItem } from '../Page/types';
|
||||
@ -72,7 +71,7 @@ const Navigation = ({ children }: { children: NavModelItem[] }) => {
|
||||
return (
|
||||
!child.hideFromTabs && (
|
||||
<Tab
|
||||
label={getNavTitle(child.id) ?? child.text}
|
||||
label={child.text}
|
||||
active={child.active}
|
||||
key={`${child.url}-${index}`}
|
||||
icon={child.icon}
|
||||
@ -97,18 +96,19 @@ export const PageHeader: FC<Props> = ({ navItem: model, renderTitle, actions, in
|
||||
const renderHeader = (main: NavModelItem) => {
|
||||
const marginTop = main.icon === 'grafana' ? 12 : 14;
|
||||
const icon = main.icon && toIconName(main.icon);
|
||||
const sub = subTitle ?? getNavSubTitle(main.id) ?? main.subTitle;
|
||||
const text = getNavTitle(main.id) ?? main.text;
|
||||
const sub = subTitle ?? main.subTitle;
|
||||
|
||||
return (
|
||||
<div className="page-header__inner">
|
||||
<span className="page-header__logo">
|
||||
{icon && <Icon name={icon} size="xxxl" style={{ marginTop }} />}
|
||||
{main.img && <img className="page-header__img" src={main.img} alt={`logo of ${text}`} />}
|
||||
{main.img && <img className="page-header__img" src={main.img} alt="" />}
|
||||
</span>
|
||||
|
||||
<div className={cx('page-header__info-block', styles.headerText)}>
|
||||
{renderTitle ? renderTitle(text) : renderHeaderTitle(text, main.breadcrumbs ?? [], main.highlightText)}
|
||||
{renderTitle
|
||||
? renderTitle(main.text)
|
||||
: renderHeaderTitle(main.text, main.breadcrumbs ?? [], main.highlightText)}
|
||||
{info && <PageInfo info={info} />}
|
||||
{sub && <div className="page-header__sub-title">{sub}</div>}
|
||||
{actions && <div className={styles.actions}>{actions}</div>}
|
||||
|
@ -4,7 +4,6 @@ import React from 'react';
|
||||
import { NavModelItem, GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { getNavSubTitle, getNavTitle } from '../NavBar/navBarItem-translations';
|
||||
import { PageInfoItem } from '../Page/types';
|
||||
import { PageInfo } from '../PageInfo/PageInfo';
|
||||
|
||||
@ -18,10 +17,9 @@ export interface Props {
|
||||
|
||||
export function PageHeader({ navItem, renderTitle, actions, info, subTitle }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const sub = subTitle ?? getNavSubTitle(navItem.id) ?? navItem.subTitle;
|
||||
const sub = subTitle ?? navItem.subTitle;
|
||||
|
||||
const title = getNavTitle(navItem.id) ?? navItem.text;
|
||||
const titleElement = renderTitle ? renderTitle(title) : <h1 className={styles.pageTitle}>{title}</h1>;
|
||||
const titleElement = renderTitle ? renderTitle(navItem.text) : <h1 className={styles.pageTitle}>{navItem.text}</h1>;
|
||||
|
||||
return (
|
||||
<div className={styles.pageHeader}>
|
||||
|
@ -6,8 +6,6 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { useStyles2, Icon } from '@grafana/ui';
|
||||
|
||||
import { getNavTitle } from '../NavBar/navBarItem-translations';
|
||||
|
||||
export interface Props {
|
||||
item: NavModelItem;
|
||||
isSectionRoot?: boolean;
|
||||
@ -55,7 +53,7 @@ export function SectionNavItem({ item, isSectionRoot = false }: Props) {
|
||||
aria-selected={item.active}
|
||||
>
|
||||
{isSectionRoot && icon}
|
||||
{getNavTitle(item.id) ?? item.text}
|
||||
{item.text}
|
||||
{item.tabSuffix && <item.tabSuffix className={styles.suffix} />}
|
||||
</a>
|
||||
{children?.map((child, index) => (
|
||||
|
@ -22,7 +22,14 @@ const loadTranslations: BackendModule = {
|
||||
export function initializeI18n(language: string) {
|
||||
const validLocale = VALID_LANGUAGES.includes(language) ? language : DEFAULT_LANGUAGE;
|
||||
|
||||
i18n
|
||||
// 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
|
||||
t(
|
||||
'_comment',
|
||||
'Do not manually edit this file, or update these source phrases in Crowdin. The source of truth for English strings are in the code source'
|
||||
);
|
||||
|
||||
return i18n
|
||||
.use(loadTranslations)
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
@ -37,13 +44,6 @@ export function initializeI18n(language: string) {
|
||||
|
||||
pluralSeparator: '__',
|
||||
});
|
||||
|
||||
// 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
|
||||
t(
|
||||
'_comment',
|
||||
'Do not manually edit this file, or update these source phrases in Crowdin. The source of truth for English strings are in the code source'
|
||||
);
|
||||
}
|
||||
|
||||
export function changeLanguage(locale: string) {
|
||||
|
@ -3,14 +3,30 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { NavModelItem } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { getNavSubTitle, getNavTitle } from '../components/NavBar/navBarItem-translations';
|
||||
|
||||
export const initialState: NavModelItem[] = config.bootData?.navTree ?? [];
|
||||
|
||||
function translateNav(navTree: NavModelItem[]): NavModelItem[] {
|
||||
return navTree.map((navItem) => {
|
||||
const children = navItem.children && translateNav(navItem.children);
|
||||
|
||||
return {
|
||||
...navItem,
|
||||
children: children,
|
||||
text: getNavTitle(navItem.id) ?? navItem.text,
|
||||
subTitle: getNavSubTitle(navItem.id) ?? navItem.subTitle,
|
||||
emptyMessage: getNavTitle(navItem.emptyMessageId),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// this matches the prefix set in the backend navtree
|
||||
export const ID_PREFIX = 'starred/';
|
||||
|
||||
const navTreeSlice = createSlice({
|
||||
name: 'navBarTree',
|
||||
initialState,
|
||||
initialState: () => translateNav(config.bootData?.navTree ?? []),
|
||||
reducers: {
|
||||
setStarred: (state, action: PayloadAction<{ id: string; title: string; url: string; isStarred: boolean }>) => {
|
||||
const starredItems = state.find((navItem) => navItem.id === 'starred');
|
||||
|
@ -4,6 +4,8 @@ import { cloneDeep } from 'lodash';
|
||||
import { NavIndex, NavModel, NavModelItem } from '@grafana/data';
|
||||
import config from 'app/core/config';
|
||||
|
||||
import { getNavSubTitle, getNavTitle } from '../components/NavBar/navBarItem-translations';
|
||||
|
||||
export const HOME_NAV_ID = 'home';
|
||||
|
||||
export function buildInitialState(): NavIndex {
|
||||
@ -16,22 +18,37 @@ export function buildInitialState(): NavIndex {
|
||||
buildNavIndex(navIndex, [homeNav]);
|
||||
}
|
||||
// set home as parent for the other rootNodes
|
||||
buildNavIndex(navIndex, otherRootNodes, homeNav);
|
||||
// need to use the translated home node from the navIndex
|
||||
buildNavIndex(navIndex, otherRootNodes, navIndex[HOME_NAV_ID]);
|
||||
|
||||
return navIndex;
|
||||
}
|
||||
|
||||
function buildNavIndex(navIndex: NavIndex, children: NavModelItem[], parentItem?: NavModelItem) {
|
||||
const translatedChildren: NavModelItem[] = [];
|
||||
|
||||
for (const node of children) {
|
||||
node.parentItem = parentItem;
|
||||
const translatedNode: NavModelItem = {
|
||||
...node,
|
||||
text: getNavTitle(node.id) ?? node.text,
|
||||
subTitle: getNavSubTitle(node.id) ?? node.subTitle,
|
||||
emptyMessage: getNavTitle(node.emptyMessageId),
|
||||
parentItem: parentItem,
|
||||
};
|
||||
|
||||
if (node.id) {
|
||||
navIndex[node.id] = node;
|
||||
if (translatedNode.id) {
|
||||
navIndex[translatedNode.id] = translatedNode;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
buildNavIndex(navIndex, node.children, node);
|
||||
if (translatedNode.children) {
|
||||
buildNavIndex(navIndex, translatedNode.children, translatedNode);
|
||||
}
|
||||
translatedChildren.push(translatedNode);
|
||||
}
|
||||
|
||||
// need to update the parentItem children with the new translated children
|
||||
if (parentItem) {
|
||||
parentItem.children = translatedChildren;
|
||||
}
|
||||
|
||||
navIndex['not-found'] = { ...buildWarningNav('Page not found', '404 Error').node };
|
||||
|
@ -287,6 +287,9 @@
|
||||
"title": "Einstellungen"
|
||||
},
|
||||
"profile/switch-org": "Organisation wechseln",
|
||||
"public": {
|
||||
"title": ""
|
||||
},
|
||||
"scenes": {
|
||||
"title": "Szenen"
|
||||
},
|
||||
|
@ -287,6 +287,9 @@
|
||||
"title": "Profile"
|
||||
},
|
||||
"profile/switch-org": "Switch organization",
|
||||
"public": {
|
||||
"title": "Public dashboards"
|
||||
},
|
||||
"scenes": {
|
||||
"title": "Scenes"
|
||||
},
|
||||
|
@ -287,6 +287,9 @@
|
||||
"title": "Preferencias"
|
||||
},
|
||||
"profile/switch-org": "Cambiar de organización",
|
||||
"public": {
|
||||
"title": ""
|
||||
},
|
||||
"scenes": {
|
||||
"title": "Escenas"
|
||||
},
|
||||
|
@ -287,6 +287,9 @@
|
||||
"title": "Préférences"
|
||||
},
|
||||
"profile/switch-org": "Passer à une autre organisation",
|
||||
"public": {
|
||||
"title": ""
|
||||
},
|
||||
"scenes": {
|
||||
"title": "Scènes"
|
||||
},
|
||||
|
@ -287,6 +287,9 @@
|
||||
"title": "Přőƒįľę"
|
||||
},
|
||||
"profile/switch-org": "Ŝŵįŧčĥ őřģäʼnįžäŧįőʼn",
|
||||
"public": {
|
||||
"title": "Pūþľįč đäşĥþőäřđş"
|
||||
},
|
||||
"scenes": {
|
||||
"title": "Ŝčęʼnęş"
|
||||
},
|
||||
|
@ -287,6 +287,9 @@
|
||||
"title": "首选项"
|
||||
},
|
||||
"profile/switch-org": "切换组织",
|
||||
"public": {
|
||||
"title": ""
|
||||
},
|
||||
"scenes": {
|
||||
"title": "场景"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user