search plugin support i18n

Signed-off-by: mario ma <mario.ma.node@gmail.com>
This commit is contained in:
mario ma
2025-03-26 23:10:41 +08:00
parent 714e86684e
commit fa485943e4
18 changed files with 182 additions and 24 deletions
+3
View File
@@ -284,3 +284,6 @@ export default createFrontendPlugin({
root: rootRouteRef,
}),
});
/** @alpha */
export { searchTranslationRef } from './translation';
@@ -39,6 +39,8 @@ import { useNavigate } from 'react-router-dom';
import { rootRouteRef } from '../../plugin';
import { SearchResultSet } from '@backstage/plugin-search-common';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
import { searchTranslationRef } from '../../translation';
/**
* @public
@@ -121,6 +123,7 @@ export const Modal = ({
const navigate = useNavigate();
const { transitions } = useTheme();
const { focusContent } = useContent();
const { t } = useTranslationRef(searchTranslationRef);
const searchRootRoute = useRouteRef(rootRouteRef)();
const searchBarRef = useRef<HTMLInputElement | null>(null);
@@ -171,7 +174,7 @@ export const Modal = ({
onClick={handleSearchBarSubmit}
disableRipple
>
View Full Results
{t('searchModal.viewFullResults')}
</Button>
</Grid>
</Grid>
@@ -31,6 +31,8 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Typography from '@material-ui/core/Typography';
import AllIcon from '@material-ui/icons/FontDownload';
import useAsync from 'react-use/esm/useAsync';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
import { searchTranslationRef } from '../../translation';
const useStyles = makeStyles(theme => ({
icon: {
@@ -83,6 +85,7 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => {
const searchApi = useApi(searchApiRef);
const [expanded, setExpanded] = useState(true);
const { defaultValue, name, showCounts, types: givenTypes } = props;
const { t } = useTranslationRef(searchTranslationRef);
const toggleExpanded = () => setExpanded(prevState => !prevState);
const handleClick = (type: string) => {
@@ -103,7 +106,7 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => {
const definedTypes = [
{
value: '',
name: 'All',
name: t('searchType.accordion.allTitle'),
icon: <AllIcon />,
},
...givenTypes,
@@ -117,7 +120,7 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => {
const counts = await Promise.all(
definedTypes
.map(t => t.value)
.map(_t => _t.value)
.map(async type => {
const { numberOfResults } = await searchApi.query({
term,
@@ -130,9 +133,10 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => {
return [
type,
numberOfResults !== undefined
? `${
numberOfResults >= 10000 ? `>10000` : numberOfResults
} results`
? t('searchType.accordion.numberOfResults', {
number:
numberOfResults >= 10000 ? `>10000` : `${numberOfResults}`,
})
: ' -- ',
];
}),
@@ -160,8 +164,8 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => {
IconButtonProps={{ size: 'small' }}
>
{expanded
? 'Collapse'
: definedTypes.filter(t => t.value === selected)[0]!.name}
? t('searchType.accordion.collapse')
: definedTypes.filter(_t => _t.value === selected)[0]!.name}
</AccordionSummary>
<AccordionDetails classes={{ root: classes.accordionDetails }}>
<List
@@ -20,6 +20,8 @@ import Tab from '@material-ui/core/Tab';
import Tabs from '@material-ui/core/Tabs';
import { makeStyles } from '@material-ui/core/styles';
import { Theme } from '@material-ui/core/styles';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
import { searchTranslationRef } from '../../translation';
const useStyles = makeStyles((theme: Theme) => ({
tabs: {
@@ -49,6 +51,7 @@ export const SearchTypeTabs = (props: SearchTypeTabsProps) => {
const classes = useStyles();
const { setPageCursor, setTypes, types } = useSearch();
const { defaultValue, types: givenTypes } = props;
const { t } = useTranslationRef(searchTranslationRef);
const changeTab = (_: ChangeEvent<{}>, newType: string) => {
setTypes(newType !== '' ? [newType] : []);
@@ -66,7 +69,7 @@ export const SearchTypeTabs = (props: SearchTypeTabsProps) => {
const definedTypes = [
{
value: '',
name: 'All',
name: t('searchType.tabs.allTitle'),
},
...givenTypes,
];
@@ -29,6 +29,8 @@ import {
} from './SearchType.Accordion';
import { SearchTypeTabs, SearchTypeTabsProps } from './SearchType.Tabs';
import { useSearch } from '@backstage/plugin-search-react';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
import { searchTranslationRef } from '../../translation';
const useStyles = makeStyles(theme => ({
label: {
@@ -63,6 +65,7 @@ const SearchType = (props: SearchTypeProps) => {
const { className, defaultValue, name, values = [] } = props;
const classes = useStyles();
const { types, setTypes } = useSearch();
const { t } = useTranslationRef(searchTranslationRef);
useEffectOnce(() => {
if (!types.length) {
@@ -94,7 +97,7 @@ const SearchType = (props: SearchTypeProps) => {
variant="outlined"
value={types}
onChange={handleChange}
placeholder="All Results"
placeholder={t('searchType.allResults')}
renderValue={selected => (
<div className={classes.chips}>
{(selected as string[]).map(value => (
@@ -22,6 +22,8 @@ import {
SearchModalProvider,
useSearchModal,
} from '../SearchModal';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
import { searchTranslationRef } from '../../translation';
/**
* Props for {@link SidebarSearchModal}.
@@ -38,13 +40,14 @@ export type SidebarSearchModalProps = Pick<
const SidebarSearchModalContent = (props: SidebarSearchModalProps) => {
const { state, toggleModal } = useSearchModal();
const Icon = props.icon ? props.icon : SearchIcon;
const { t } = useTranslationRef(searchTranslationRef);
return (
<>
<SidebarItem
className="search-icon"
icon={Icon}
text="Search"
text={t('sidebarSearchModal.title')}
onClick={toggleModal}
/>
<SearchModal
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createTranslationRef } from '@backstage/core-plugin-api/alpha';
export const searchTranslationRef = createTranslationRef({
id: 'search',
messages: {
searchModal: {
viewFullResults: 'View Full Results',
},
searchType: {
allResults: 'All Results',
tabs: {
allTitle: 'All',
},
accordion: {
allTitle: 'All',
collapse: 'Collapse',
numberOfResults: '{{number}} results',
},
},
sidebarSearchModal: {
title: 'Search',
},
},
});