Skip to content

Commit 3d6e6be

Browse files
committed
feat: i18n key
1 parent 6829a2d commit 3d6e6be

12 files changed

Lines changed: 430 additions & 108 deletions

File tree

src/components/stateless/SettingDrawer/index.tsx

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react'
22
import { Drawer, Switch, Divider, Tooltip, theme, ColorPicker } from 'antd'
33
import { CheckOutlined } from '@ant-design/icons'
44
import { useProThemeContext, ThemeSettings } from '@/theme/hooks'
5+
import { useTranslation } from 'react-i18next'
56

67
const { useToken } = theme
78

@@ -85,34 +86,35 @@ const ThemeColor = ({
8586

8687
const SettingDrawer: React.FC<SettingDrawerProps> = ({ open, onClose }) => {
8788
const { themeSettings, updateSettings } = useProThemeContext()
89+
const { t } = useTranslation()
8890

8991
const changeSetting = <K extends keyof ThemeSettings>(key: K, value: ThemeSettings[K]) => {
9092
updateSettings({ [key]: value } as Partial<ThemeSettings>)
9193
}
9294

9395
return (
9496
<Drawer
95-
title="偏好设置"
97+
title={t('settingDrawer.title')}
9698
placement="right"
9799
onClose={onClose}
98100
open={open}
99101
size={300}
100102
styles={{ body: { padding: '20px 24px' } }}
101103
>
102104
<div className="mb-6">
103-
<h3 className="mb-4 text-sm font-bold">整体风格设置</h3>
105+
<h3 className="mb-4 text-sm font-bold">{t('settingDrawer.sections.overallStyle')}</h3>
104106
<div className="flex gap-4">
105107
<BlockCheckbox
106108
list={
107109
[
108110
{
109111
key: 'light',
110-
title: '亮色菜单风格',
112+
title: t('settingDrawer.navTheme.light'),
111113
url: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgPHJlY3Qgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4IiBmaWxsPSIjRjBGMkY1Ii8+CiAgICA8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iNDgiIGZpbGw9IiNGRkYiLz4KICAgIDxyZWN0IHdpZHRoPSI0OCIgaGVpZ2h0PSIxMiIgZmlsbD0iI0ZGRiIvPgogIDwvZz4KPC9zdmc+',
112114
},
113115
{
114116
key: 'dark',
115-
title: '暗色菜单风格',
117+
title: t('settingDrawer.navTheme.dark'),
116118
url: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgPHJlY3Qgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4IiBmaWxsPSIjRjBGMkY1Ii8+CiAgICA8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iNDgiIGZpbGw9IiMwMDE1MjkiLz4KICAgIDxyZWN0IHdpZHRoPSI0OCIgaGVpZ2h0PSIxMiIgZmlsbD0iI0ZGRiIvPgogIDwvZz4KPC9zdmc+',
117119
},
118120
] as const
@@ -122,7 +124,7 @@ const SettingDrawer: React.FC<SettingDrawerProps> = ({ open, onClose }) => {
122124
/>
123125
</div>
124126
<div className="mt-4 flex items-center justify-between">
125-
<span className="text-sm">开启暗黑模式</span>
127+
<span className="text-sm">{t('settingDrawer.enableDarkMode')}</span>
126128
<Switch
127129
checked={themeSettings.themeMode === 'dark'}
128130
onChange={(checked) => changeSetting('themeMode', checked ? 'dark' : 'light')}
@@ -131,23 +133,23 @@ const SettingDrawer: React.FC<SettingDrawerProps> = ({ open, onClose }) => {
131133
</div>
132134

133135
<div className="mb-6">
134-
<h3 className="mb-4 text-sm font-bold">主题色</h3>
136+
<h3 className="mb-4 text-sm font-bold">{t('settingDrawer.sections.themeColor')}</h3>
135137
<ThemeColor
136138
colors={[
137-
{ key: '拂晓蓝 (默认)', color: '#1677ff' },
138-
{ key: '薄暮', color: '#F5222D' },
139-
{ key: '火山', color: '#FA541C' },
140-
{ key: '日暮', color: '#FAAD14' },
141-
{ key: '明青', color: '#13C2C2' },
142-
{ key: '极光绿', color: '#52C41A' },
143-
{ key: 'Geek Blue', color: '#2F54EB' },
144-
{ key: '酱紫', color: '#722ED1' },
139+
{ key: t('settingDrawer.colors.dawnBlueDefault'), color: '#1677ff' },
140+
{ key: t('settingDrawer.colors.dustRed'), color: '#F5222D' },
141+
{ key: t('settingDrawer.colors.volcano'), color: '#FA541C' },
142+
{ key: t('settingDrawer.colors.sunset'), color: '#FAAD14' },
143+
{ key: t('settingDrawer.colors.cyan'), color: '#13C2C2' },
144+
{ key: t('settingDrawer.colors.auroraGreen'), color: '#52C41A' },
145+
{ key: t('settingDrawer.colors.geekBlue'), color: '#2F54EB' },
146+
{ key: t('settingDrawer.colors.purple'), color: '#722ED1' },
145147
]}
146148
value={themeSettings.colorPrimary}
147149
onChange={(val) => changeSetting('colorPrimary', val)}
148150
/>
149151
<div style={{ display: 'flex', alignItems: 'center', marginTop: '10px' }}>
150-
<span className="mr-2 text-sm">自定义颜色:</span>
152+
<span className="mr-2 text-sm">{t('settingDrawer.customColor')}:</span>
151153
<ColorPicker
152154
value={themeSettings.colorPrimary}
153155
onChange={(color) => changeSetting('colorPrimary', color.toHexString())}
@@ -159,23 +161,23 @@ const SettingDrawer: React.FC<SettingDrawerProps> = ({ open, onClose }) => {
159161
<Divider />
160162

161163
<div className="mb-6">
162-
<h3 className="mb-4 text-sm font-bold">导航模式</h3>
164+
<h3 className="mb-4 text-sm font-bold">{t('settingDrawer.sections.navigationMode')}</h3>
163165
<BlockCheckbox
164166
list={
165167
[
166168
{
167169
key: 'side',
168-
title: '侧边菜单布局',
170+
title: t('settingDrawer.layout.side'),
169171
url: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgPHJlY3Qgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4IiBmaWxsPSIjRjBGMkY1Ii8+CiAgICA8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iNDgiIGZpbGw9IiMwMDE1MjkiLz4KICAgIDxyZWN0IHdpZHRoPSI0OCIgaGVpZ2h0PSIxMiIgZmlsbD0iI0ZGRiIvPgogIDwvZz4KPC9zdmc+',
170172
},
171173
{
172174
key: 'top',
173-
title: '顶部菜单布局',
175+
title: t('settingDrawer.layout.top'),
174176
url: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgPHJlY3Qgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4IiBmaWxsPSIjRjBGMkY1Ii8+CiAgICA8cmVjdCB3aWR0aD0iNDgiIGhlaWdodD0iMTIiIGZpbGw9IiMwMDE1MjkiLz4KICA8L2c+Cjwvc3ZnPg==',
175177
},
176178
{
177179
key: 'mix',
178-
title: '混合菜单布局',
180+
title: t('settingDrawer.layout.mix'),
179181
url: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgPHJlY3Qgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4IiBmaWxsPSIjRjBGMkY1Ii8+CiAgICA8cmVjdCB3aWR0aD0iNDgiIGhlaWdodD0iMTIiIGZpbGw9IiMwMDE1MjkiLz4KICAgIDxyZWN0IHg9IjAiIHk9IjEyIiB3aWR0aD0iMTYiIGhlaWdodD0iMzYiIGZpbGw9IiNGRkYiLz4KICA8L2c+Cjwvc3ZnPg==',
180182
},
181183
] as const
@@ -186,12 +188,12 @@ const SettingDrawer: React.FC<SettingDrawerProps> = ({ open, onClose }) => {
186188
</div>
187189

188190
<div className="mb-6">
189-
<h3 className="mb-4 text-sm font-bold">内容区域宽度</h3>
191+
<h3 className="mb-4 text-sm font-bold">{t('settingDrawer.sections.contentWidth')}</h3>
190192
<div className="flex justify-between">
191-
<span>内容区域宽度</span>
193+
<span>{t('settingDrawer.contentWidth')}</span>
192194
<Switch
193-
checkedChildren="Fixed"
194-
unCheckedChildren="Fluid"
195+
checkedChildren={t('settingDrawer.contentWidthFixed')}
196+
unCheckedChildren={t('settingDrawer.contentWidthFluid')}
195197
checked={themeSettings.contentWidth === 'Fixed'}
196198
onChange={(checked) => changeSetting('contentWidth', checked ? 'Fixed' : 'Fluid')}
197199
disabled={themeSettings.layout === 'side'}
@@ -202,42 +204,42 @@ const SettingDrawer: React.FC<SettingDrawerProps> = ({ open, onClose }) => {
202204
<Divider />
203205

204206
<div className="mb-6">
205-
<h3 className="mb-4 text-sm font-bold">其他设置</h3>
207+
<h3 className="mb-4 text-sm font-bold">{t('settingDrawer.sections.other')}</h3>
206208

207209
<div className="mb-3 flex justify-between">
208-
<span>色弱模式</span>
210+
<span>{t('settingDrawer.other.colorWeak')}</span>
209211
<Switch checked={themeSettings.colorWeak} onChange={(checked) => changeSetting('colorWeak', checked)} />
210212
</div>
211213
<div className="mb-3 flex justify-between">
212-
<span>灰色模式</span>
214+
<span>{t('settingDrawer.other.grayMode')}</span>
213215
<Switch checked={themeSettings.grayMode} onChange={(checked) => changeSetting('grayMode', checked)} />
214216
</div>
215217
<div className="mb-3 flex justify-between">
216-
<span>紧凑模式</span>
218+
<span>{t('settingDrawer.other.compactMode')}</span>
217219
<Switch
218220
checked={themeSettings.compactAlgorithm}
219221
onChange={(checked) => changeSetting('compactAlgorithm', checked)}
220222
/>
221223
</div>
222224
<div className="mb-3 flex justify-between">
223-
<span>固定 Header</span>
225+
<span>{t('settingDrawer.other.fixedHeader')}</span>
224226
<Switch checked={themeSettings.fixedHeader} onChange={(checked) => changeSetting('fixedHeader', checked)} />
225227
</div>
226228
<div className="mb-3 flex justify-between">
227-
<span>固定侧边菜单</span>
229+
<span>{t('settingDrawer.other.fixedSider')}</span>
228230
<Switch checked={themeSettings.fixSiderbar} onChange={(checked) => changeSetting('fixSiderbar', checked)} />
229231
</div>
230232
</div>
231233

232234
<Divider />
233235
<div className="mb-6">
234-
<h3 className="mb-4 text-sm font-bold">视觉特效</h3>
236+
<h3 className="mb-4 text-sm font-bold">{t('settingDrawer.sections.visualEffects')}</h3>
235237
<div className="mb-3 flex justify-between">
236-
<span>指针跟随</span>
238+
<span>{t('settingDrawer.effects.pointerFollow')}</span>
237239
<Switch checked={themeSettings.pointerMove} onChange={(checked) => changeSetting('pointerMove', checked)} />
238240
</div>
239241
<div className="mb-3 flex justify-between">
240-
<span>指针轨迹</span>
242+
<span>{t('settingDrawer.effects.pointerTrail')}</span>
241243
<Switch checked={themeSettings.magicTrail} onChange={(checked) => changeSetting('magicTrail', checked)} />
242244
</div>
243245
</div>

src/config/menu.config.jsx

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,85 +34,100 @@ import {
3434
ThunderboltOutlined,
3535
ToolOutlined,
3636
} from '@ant-design/icons'
37-
import { t } from 'i18next' // 注意:这里可能需要处理 i18n
3837

3938
// 静态菜单配置
4039
// 这里的 key 对应路由 path
4140
const rawMainLayoutMenu = [
4241
{ label: 'home', i18nKey: 'home', key: '/', icon: <HomeOutlined /> },
4342
{ label: 'demo', i18nKey: 'demo', key: '/demo', icon: <GlobalOutlined /> },
44-
{ label: 'Motion', key: '/motion', icon: <RocketOutlined /> },
45-
{ label: 'Mermaid', key: '/mermaid', icon: <ProjectOutlined /> },
46-
{ label: 'Topology', key: '/topology', icon: <DeploymentUnitOutlined /> },
47-
{ label: '权限示例', key: '/permission', icon: <LockOutlined /> },
48-
{ label: 'PH Bar', key: '/ph-bar', icon: <BarChartOutlined /> },
49-
{ label: 'ChatGPT', key: '/markmap', icon: <RobotOutlined /> },
50-
{ label: 'React Tilt', key: '/tilt', icon: <ExperimentOutlined /> },
51-
{ label: 'Music', key: '/music', icon: <SoundOutlined /> },
52-
{ label: 'Crypto', key: '/crypto', icon: <LockOutlined /> },
53-
{ label: 'Video', key: '/video', icon: <VideoCameraOutlined /> },
54-
{ label: 'Big Screen', key: '/big-screen', icon: <FundProjectionScreenOutlined /> },
55-
{ label: 'Echarts', key: '/echarts', icon: <PieChartOutlined /> },
56-
{ label: 'Qr Generate', key: '/qrcode', icon: <QrcodeOutlined /> },
57-
{ label: 'Business', key: '/business', icon: <BankOutlined /> },
58-
{ label: 'Prism Render', key: '/prism', icon: <HighlightOutlined /> },
59-
{ label: 'Post Message', key: '/postmessage', icon: <SendOutlined /> },
60-
{ label: 'Geo Chart', key: '/geo', icon: <EnvironmentOutlined /> },
61-
{ label: 'D3 Chart', key: '/d3-chart', icon: <DotChartOutlined /> },
62-
{ label: 'Print', key: '/print', icon: <PrinterOutlined /> },
63-
{ label: 'Profile', key: '/profile', icon: <UserOutlined /> },
64-
{ label: 'Contact', key: '/contact', icon: <ContactsOutlined /> },
43+
{ label: 'Motion', i18nKey: 'menu.motion', key: '/motion', icon: <RocketOutlined /> },
44+
{ label: 'Mermaid', i18nKey: 'menu.mermaid', key: '/mermaid', icon: <ProjectOutlined /> },
45+
{ label: 'Topology', i18nKey: 'menu.topology', key: '/topology', icon: <DeploymentUnitOutlined /> },
46+
{ label: '权限示例', i18nKey: 'menu.permissionExample', key: '/permission', icon: <LockOutlined /> },
47+
{ label: 'PH Bar', i18nKey: 'menu.phBar', key: '/ph-bar', icon: <BarChartOutlined /> },
48+
{ label: 'ChatGPT', i18nKey: 'menu.chatgpt', key: '/markmap', icon: <RobotOutlined /> },
49+
{ label: 'React Tilt', i18nKey: 'menu.reactTilt', key: '/tilt', icon: <ExperimentOutlined /> },
50+
{ label: 'Music', i18nKey: 'menu.music', key: '/music', icon: <SoundOutlined /> },
51+
{ label: 'Crypto', i18nKey: 'menu.crypto', key: '/crypto', icon: <LockOutlined /> },
52+
{ label: 'Video', i18nKey: 'menu.video', key: '/video', icon: <VideoCameraOutlined /> },
53+
{ label: 'Big Screen', i18nKey: 'menu.bigScreen', key: '/big-screen', icon: <FundProjectionScreenOutlined /> },
54+
{ label: 'Echarts', i18nKey: 'menu.echarts', key: '/echarts', icon: <PieChartOutlined /> },
55+
{ label: 'Qr Generate', i18nKey: 'menu.qrGenerate', key: '/qrcode', icon: <QrcodeOutlined /> },
56+
{ label: 'Business', i18nKey: 'menu.business', key: '/business', icon: <BankOutlined /> },
57+
{ label: 'Prism Render', i18nKey: 'menu.prismRender', key: '/prism', icon: <HighlightOutlined /> },
58+
{ label: 'Post Message', i18nKey: 'menu.postMessage', key: '/postmessage', icon: <SendOutlined /> },
59+
{ label: 'Geo Chart', i18nKey: 'menu.geoChart', key: '/geo', icon: <EnvironmentOutlined /> },
60+
{ label: 'D3 Chart', i18nKey: 'menu.d3Chart', key: '/d3-chart', icon: <DotChartOutlined /> },
61+
{ label: 'Print', i18nKey: 'menu.print', key: '/print', icon: <PrinterOutlined /> },
62+
{ label: 'Profile', i18nKey: 'menu.profile', key: '/profile', icon: <UserOutlined /> },
63+
{ label: 'Contact', i18nKey: 'menu.contact', key: '/contact', icon: <ContactsOutlined /> },
6564
{
6665
label: '前端技术栈',
66+
i18nKey: 'menu.frontendTechStack',
6767
key: '/tech/frontend',
6868
icon: <DeploymentUnitOutlined />,
6969
children: [
70-
{ label: 'React', key: '/tech/frontend/react', icon: <CodeOutlined /> },
70+
{ label: 'React', i18nKey: 'menu.react', key: '/tech/frontend/react', icon: <CodeOutlined /> },
7171
{
7272
label: 'Vue',
73+
i18nKey: 'menu.vue',
7374
key: '/tech/frontend/vue',
7475
icon: <CodeOutlined />,
7576
children: [
7677
{
7778
label: 'Vue 插件',
79+
i18nKey: 'menu.vuePlugins',
7880
key: '/tech/frontend/plugins',
7981
icon: <AppstoreOutlined />,
8082
children: [
81-
{ label: 'Vue3 API', key: '/tech/frontend/plugins/vue3', icon: <FileTextOutlined /> },
82-
{ label: '性能优化', key: '/tech/frontend/plugins/perf', icon: <ThunderboltOutlined /> },
83+
{
84+
label: 'Vue3 API',
85+
i18nKey: 'menu.vue3Api',
86+
key: '/tech/frontend/plugins/vue3',
87+
icon: <FileTextOutlined />,
88+
},
89+
{
90+
label: '性能优化',
91+
i18nKey: 'menu.performanceOptimization',
92+
key: '/tech/frontend/plugins/perf',
93+
icon: <ThunderboltOutlined />,
94+
},
8395
],
8496
},
8597
],
8698
},
87-
{ label: 'Angular', key: '/tech/frontend/angular', icon: <Html5Outlined /> },
88-
{ label: 'Node', key: '/tech/frontend/node', icon: <CloudServerOutlined /> },
99+
{ label: 'Angular', i18nKey: 'menu.angular', key: '/tech/frontend/angular', icon: <Html5Outlined /> },
100+
{ label: 'Node', i18nKey: 'menu.node', key: '/tech/frontend/node', icon: <CloudServerOutlined /> },
89101
],
90102
},
91103
{
92104
label: '后端技术栈',
105+
i18nKey: 'menu.backendTechStack',
93106
key: '/tech/backend',
94107
icon: <CloudServerOutlined />,
95108
children: [
96-
{ label: 'Node', key: '/tech/backend/node', icon: <CodeOutlined /> },
97-
{ label: 'Java', key: '/tech/backend/java', icon: <CodeOutlined /> },
98-
{ label: 'Go', key: '/tech/backend/go', icon: <CodeOutlined /> },
109+
{ label: 'Node', i18nKey: 'menu.node', key: '/tech/backend/node', icon: <CodeOutlined /> },
110+
{ label: 'Java', i18nKey: 'menu.java', key: '/tech/backend/java', icon: <CodeOutlined /> },
111+
{ label: 'Go', i18nKey: 'menu.go', key: '/tech/backend/go', icon: <CodeOutlined /> },
99112
],
100113
},
101114
{
102115
label: '构建工具',
116+
i18nKey: 'menu.buildTools',
103117
key: '/build',
104118
icon: <ApartmentOutlined />,
105119
children: [
106-
{ label: 'Webpack', key: '/build/webpack', icon: <ToolOutlined /> },
107-
{ label: 'Vite', key: '/build/vite', icon: <ThunderboltOutlined /> },
120+
{ label: 'Webpack', i18nKey: 'menu.webpack', key: '/build/webpack', icon: <ToolOutlined /> },
121+
{ label: 'Vite', i18nKey: 'menu.vite', key: '/build/vite', icon: <ThunderboltOutlined /> },
108122
],
109123
},
110124

111125
{
112126
label: 'Error',
127+
i18nKey: 'menu.error',
113128
key: '/sub-error',
114129
icon: <QuestionCircleOutlined />,
115-
children: [{ label: 'ErrorBoundary', key: '/error' }],
130+
children: [{ label: 'ErrorBoundary', i18nKey: 'menu.errorBoundary', key: '/error' }],
116131
},
117132
]
118133

src/i18n/menuI18nKey.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { mainLayoutMenu } from '../config/menu.config.jsx'
2+
3+
const normalize = (value) => {
4+
if (value === undefined || value === null) return ''
5+
return String(value).split('?')[0].trim()
6+
}
7+
8+
const buildMenuPathToI18nKeyMap = () => {
9+
const map = new Map()
10+
11+
const walk = (items) => {
12+
if (!Array.isArray(items)) return
13+
for (const item of items) {
14+
if (!item || typeof item !== 'object') continue
15+
16+
const i18nKey = item.i18nKey
17+
const key = normalize(item.key)
18+
const path = normalize(item.path)
19+
20+
if (i18nKey) {
21+
if (key) map.set(key, i18nKey)
22+
if (path) map.set(path, i18nKey)
23+
}
24+
25+
if (Array.isArray(item.children) && item.children.length > 0) {
26+
walk(item.children)
27+
}
28+
}
29+
}
30+
31+
walk(mainLayoutMenu)
32+
return map
33+
}
34+
35+
// Build once at module load; menu config is static.
36+
const MENU_PATH_TO_I18N_KEY = buildMenuPathToI18nKeyMap()
37+
38+
export const getMenuI18nKeyByPath = (pathOrKey) => {
39+
const normalized = normalize(pathOrKey)
40+
if (!normalized) return undefined
41+
42+
// exact match first
43+
const direct = MENU_PATH_TO_I18N_KEY.get(normalized)
44+
if (direct) return direct
45+
46+
// fallback: strip trailing slash
47+
if (normalized.endsWith('/')) {
48+
const noSlash = normalized.replace(/\/+$/, '')
49+
return MENU_PATH_TO_I18N_KEY.get(noSlash)
50+
}
51+
52+
return undefined
53+
}

0 commit comments

Comments
 (0)