Skip to content

Commit e3bd16d

Browse files
valkirilovclaude
andcommitted
feat(i18n): migrate database-analysis page (RI-8276)
Route the Database Analysis page through i18n under analytics.databaseAnalysis.* with Bulgarian translations: page title, header (report select, scan progress, new-report button + tooltip title/body), the Data Summary / Tips tabs, empty states (reports/keys/encrypt — the keys variant links Workbench via <Trans>), recommendations empty state and tutorial, summary-per-data and TTL sections, and the top-namespaces / top-keys tables (headers, tooltips, toggles). The no-namespaces empty state links Tree View via <Trans>. Also migrates the analytics navigation tabs (AnalyticsTabs: Overview / Database Analysis / Slow Log) under analytics.nav.*, and translates the new-report tooltip body at the use site (leaving the shared recommendations constant untouched for the still-English side panels). Extends analytics.units.* with %/B resolved through t(). The TTL tooltip seconds suffix stays literal to match the untranslated duration from the shared truncateNumberToDuration util (same rationale as cluster-details). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8270a97 commit e3bd16d

14 files changed

Lines changed: 273 additions & 103 deletions

File tree

redisinsight/ui/src/components/analytics-tabs/AnalyticsTabs.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import { useConnectionType } from 'uiSrc/components/hooks/useConnectionType'
2020
import { ONBOARDING_FEATURES } from 'uiSrc/components/onboarding-features'
2121
import Tabs, { TabInfo } from 'uiSrc/components/base/layout/tabs'
2222
import { Text } from 'uiSrc/components/base/text'
23+
import { useTranslation } from 'uiSrc/i18n'
2324

2425
const AnalyticsTabs = () => {
26+
const { t } = useTranslation()
2527
const { viewTab } = useAppSelector(analyticsSettingsSelector)
2628
const connectionType = useConnectionType()
2729
const { currentStep } = useAppSelector(appFeatureOnboardingSelector)
@@ -46,7 +48,7 @@ const AnalyticsTabs = () => {
4648
value: AnalyticsViewTab.DatabaseAnalysis,
4749
content: null,
4850
label: renderOnboardingTourWithChild(
49-
<Text>Database Analysis</Text>,
51+
<Text>{t('analytics.nav.databaseAnalysis')}</Text>,
5052
{
5153
options: ONBOARDING_FEATURES?.ANALYTICS_DATABASE_ANALYSIS,
5254
anchorPosition: 'downLeft',
@@ -59,7 +61,7 @@ const AnalyticsTabs = () => {
5961
value: AnalyticsViewTab.SlowLog,
6062
content: null,
6163
label: renderOnboardingTourWithChild(
62-
<Text>Slow Log</Text>,
64+
<Text>{t('analytics.nav.slowLog')}</Text>,
6365
{
6466
options: ONBOARDING_FEATURES?.ANALYTICS_SLOW_LOG,
6567
anchorPosition: 'downLeft',
@@ -75,7 +77,7 @@ const AnalyticsTabs = () => {
7577
value: AnalyticsViewTab.ClusterDetails,
7678
content: null,
7779
label: renderOnboardingTourWithChild(
78-
<Text>Overview</Text>,
80+
<Text>{t('analytics.nav.overview')}</Text>,
7981
{
8082
options: ONBOARDING_FEATURES?.ANALYTICS_OVERVIEW,
8183
anchorPosition: 'downLeft',
@@ -87,7 +89,7 @@ const AnalyticsTabs = () => {
8789
}
8890

8991
return visibleTabs
90-
}, [viewTab, connectionType])
92+
}, [t, viewTab, connectionType])
9193

9294
const handleTabChange = (id: string) => {
9395
if (viewTab === id) return

redisinsight/ui/src/i18n/locales/bg.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,58 @@
11
{
2+
"analytics.databaseAnalysis.empty.encrypt.text": "Не може да се декриптира. Проверете системния ключодържател или изпълнете отново генерирането на отчета.",
3+
"analytics.databaseAnalysis.empty.encrypt.title": "Криптирани данни",
4+
"analytics.databaseAnalysis.empty.keys.text": "<workbenchLink>Използвайте ръководствата и уроците на Работна среда</workbenchLink>, за да заредите бързо данните.",
5+
"analytics.databaseAnalysis.empty.keys.title": "Няма ключове за показване",
6+
"analytics.databaseAnalysis.empty.reports.text": "Щракнете върху „Анализирай“, за да генерирате първия отчет.",
7+
"analytics.databaseAnalysis.empty.reports.title": "Не са намерени отчети",
8+
"analytics.databaseAnalysis.expiration.showNoExpiry": "Показване на „Без изтичане“",
9+
"analytics.databaseAnalysis.expiration.title": "ПАМЕТ, КОЯТО ВЕРОЯТНО ЩЕ БЪДЕ ОСВОБОДЕНА С ВРЕМЕТО",
10+
"analytics.databaseAnalysis.extrapolateResults": "Екстраполиране на резултатите",
11+
"analytics.databaseAnalysis.header.newReport": "Нов отчет",
12+
"analytics.databaseAnalysis.header.newReportAria": "Нови отчети",
13+
"analytics.databaseAnalysis.header.reportGeneratedOn": "Отчетът е генериран на:",
14+
"analytics.databaseAnalysis.header.scanned": "Сканирани {{percentage}}",
15+
"analytics.databaseAnalysis.header.scannedKeys": "({{processed}}/{{total}} ключа)",
16+
"analytics.databaseAnalysis.header.tooltipContent": "Анализирайте до 10 000 ключа, за да получите преглед на вашите данни и съвети как да спестите памет и да оптимизирате използването на базата данни.",
17+
"analytics.databaseAnalysis.header.tooltipContentCluster": "Анализирайте до 10 000 ключа на шард, за да получите преглед на вашите данни и съвети как да спестите памет и да оптимизирате използването на базата данни.",
18+
"analytics.databaseAnalysis.header.tooltipTitle": "Анализ на базата данни",
19+
"analytics.databaseAnalysis.pageTitle": "{{dbName}} - Анализ на базата данни",
20+
"analytics.databaseAnalysis.recommendations.empty.line1": "Няма съвети в момента,",
21+
"analytics.databaseAnalysis.recommendations.empty.line2": "продължавайте в същия дух!",
22+
"analytics.databaseAnalysis.recommendations.empty.title": "ОТЛИЧНА РАБОТА!",
23+
"analytics.databaseAnalysis.recommendations.redisStackTooltip": "Redis Stack",
24+
"analytics.databaseAnalysis.recommendations.tutorial": "Урок",
25+
"analytics.databaseAnalysis.summaryPerData.keys": "Ключове",
26+
"analytics.databaseAnalysis.summaryPerData.memory": "Памет",
27+
"analytics.databaseAnalysis.summaryPerData.title": "ОБОБЩЕНИЕ ПО ТИП ДАННИ",
28+
"analytics.databaseAnalysis.tabs.dataSummary": "Обобщение на данните",
29+
"analytics.databaseAnalysis.tabs.tips": "Съвети",
30+
"analytics.databaseAnalysis.topKeys.byLength": "по дължина",
31+
"analytics.databaseAnalysis.topKeys.byMemory": "по памет",
32+
"analytics.databaseAnalysis.topKeys.considerSplitting": "Обмислете разделянето му на няколко ключа",
33+
"analytics.databaseAnalysis.topKeys.keyName": "Име на ключа",
34+
"analytics.databaseAnalysis.topKeys.keySize": "Размер на ключа",
35+
"analytics.databaseAnalysis.topKeys.keyType": "Тип на ключа",
36+
"analytics.databaseAnalysis.topKeys.length": "Дължина",
37+
"analytics.databaseAnalysis.topKeys.noLimit": "Без ограничение",
38+
"analytics.databaseAnalysis.topKeys.timeToLive": "Време на живот",
39+
"analytics.databaseAnalysis.topKeys.title": "НАЙ-ГОЛЕМИ КЛЮЧОВЕ",
40+
"analytics.databaseAnalysis.topKeys.titleMax": "НАЙ-ГОЛЕМИ {{max}} КЛЮЧА",
41+
"analytics.databaseAnalysis.topKeys.ttl": "TTL",
42+
"analytics.databaseAnalysis.topNamespaces.byMemory": "по памет",
43+
"analytics.databaseAnalysis.topNamespaces.byNumberOfKeys": "по брой ключове",
44+
"analytics.databaseAnalysis.topNamespaces.dataType": "Тип данни",
45+
"analytics.databaseAnalysis.topNamespaces.empty.text": "Конфигурирайте разделителя в <treeViewLink>Дървовиден изглед</treeViewLink>, за да персонализирате показваните именни пространства.",
46+
"analytics.databaseAnalysis.topNamespaces.empty.title": "Няма именни пространства за показване",
47+
"analytics.databaseAnalysis.topNamespaces.keyPattern": "Шаблон на ключа",
48+
"analytics.databaseAnalysis.topNamespaces.title": "НАЙ-ГОЛЕМИ ИМЕННИ ПРОСТРАНСТВА",
49+
"analytics.databaseAnalysis.topNamespaces.totalKeys": "Общо ключове",
50+
"analytics.databaseAnalysis.topNamespaces.totalMemory": "Обща памет",
51+
"analytics.nav.databaseAnalysis": "Анализ на базата данни",
52+
"analytics.nav.overview": "Преглед",
53+
"analytics.nav.slowLog": "Бавни команди",
54+
"analytics.units.bytes": "Б",
55+
"analytics.units.percent": "%",
256
"api.agreement.analytics.description": "Помогнете за подобряването на Redis Insight, като споделяте анонимни данни за употреба. Това ни помага да разберем използването на функциите и да направим приложението по-добро. Активирайки това, се съгласявате с нашата ",
357
"api.agreement.analytics.label": "Данни за употреба",
458
"api.agreement.notifications.description": "Изберете, за да се показват известия. В противен случай известията се показват в Центъра за известия.",

redisinsight/ui/src/i18n/locales/en.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,58 @@
11
{
2+
"analytics.databaseAnalysis.empty.encrypt.text": "Unable to decrypt. Check the system keychain or re-run the report generation.",
3+
"analytics.databaseAnalysis.empty.encrypt.title": "Encrypted data",
4+
"analytics.databaseAnalysis.empty.keys.text": "<workbenchLink>Use Workbench Guides and Tutorials</workbenchLink> to quickly load the data.",
5+
"analytics.databaseAnalysis.empty.keys.title": "No keys to display",
6+
"analytics.databaseAnalysis.empty.reports.text": "Click \"Analyze\" to generate the first report.",
7+
"analytics.databaseAnalysis.empty.reports.title": "No Reports found",
8+
"analytics.databaseAnalysis.expiration.showNoExpiry": "Show \"No Expiry\"",
9+
"analytics.databaseAnalysis.expiration.title": "MEMORY LIKELY TO BE FREED OVER TIME",
10+
"analytics.databaseAnalysis.extrapolateResults": "Extrapolate results",
11+
"analytics.databaseAnalysis.header.newReport": "New Report",
12+
"analytics.databaseAnalysis.header.newReportAria": "New reports",
13+
"analytics.databaseAnalysis.header.reportGeneratedOn": "Report generated on:",
14+
"analytics.databaseAnalysis.header.scanned": "Scanned {{percentage}}",
15+
"analytics.databaseAnalysis.header.scannedKeys": "({{processed}}/{{total}} keys)",
16+
"analytics.databaseAnalysis.header.tooltipContent": "Analyze up to 10 000 keys to get an overview of your data and tips on how to save memory and optimize the usage of your database.",
17+
"analytics.databaseAnalysis.header.tooltipContentCluster": "Analyze up to 10 000 keys per shard to get an overview of your data and tips on how to save memory and optimize the usage of your database.",
18+
"analytics.databaseAnalysis.header.tooltipTitle": "Database Analysis",
19+
"analytics.databaseAnalysis.pageTitle": "{{dbName}} - Database Analysis",
20+
"analytics.databaseAnalysis.recommendations.empty.line1": "No Tips at the moment,",
21+
"analytics.databaseAnalysis.recommendations.empty.line2": "keep up the good work!",
22+
"analytics.databaseAnalysis.recommendations.empty.title": "AMAZING JOB!",
23+
"analytics.databaseAnalysis.recommendations.redisStackTooltip": "Redis Stack",
24+
"analytics.databaseAnalysis.recommendations.tutorial": "Tutorial",
25+
"analytics.databaseAnalysis.summaryPerData.keys": "Keys",
26+
"analytics.databaseAnalysis.summaryPerData.memory": "Memory",
27+
"analytics.databaseAnalysis.summaryPerData.title": "SUMMARY PER DATA TYPE",
28+
"analytics.databaseAnalysis.tabs.dataSummary": "Data Summary",
29+
"analytics.databaseAnalysis.tabs.tips": "Tips",
30+
"analytics.databaseAnalysis.topKeys.byLength": "by Length",
31+
"analytics.databaseAnalysis.topKeys.byMemory": "by Memory",
32+
"analytics.databaseAnalysis.topKeys.considerSplitting": "Consider splitting it into multiple keys",
33+
"analytics.databaseAnalysis.topKeys.keyName": "Key Name",
34+
"analytics.databaseAnalysis.topKeys.keySize": "Key Size",
35+
"analytics.databaseAnalysis.topKeys.keyType": "Key Type",
36+
"analytics.databaseAnalysis.topKeys.length": "Length",
37+
"analytics.databaseAnalysis.topKeys.noLimit": "No limit",
38+
"analytics.databaseAnalysis.topKeys.timeToLive": "Time to Live",
39+
"analytics.databaseAnalysis.topKeys.title": "TOP KEYS",
40+
"analytics.databaseAnalysis.topKeys.titleMax": "TOP {{max}} KEYS",
41+
"analytics.databaseAnalysis.topKeys.ttl": "TTL",
42+
"analytics.databaseAnalysis.topNamespaces.byMemory": "by Memory",
43+
"analytics.databaseAnalysis.topNamespaces.byNumberOfKeys": "by Number of Keys",
44+
"analytics.databaseAnalysis.topNamespaces.dataType": "Data Type",
45+
"analytics.databaseAnalysis.topNamespaces.empty.text": "Configure the delimiter in <treeViewLink>Tree View</treeViewLink> to customize the namespaces displayed.",
46+
"analytics.databaseAnalysis.topNamespaces.empty.title": "No namespaces to display",
47+
"analytics.databaseAnalysis.topNamespaces.keyPattern": "Key Pattern",
48+
"analytics.databaseAnalysis.topNamespaces.title": "TOP NAMESPACES",
49+
"analytics.databaseAnalysis.topNamespaces.totalKeys": "Total Keys",
50+
"analytics.databaseAnalysis.topNamespaces.totalMemory": "Total Memory",
51+
"analytics.nav.databaseAnalysis": "Database Analysis",
52+
"analytics.nav.overview": "Overview",
53+
"analytics.nav.slowLog": "Slow Log",
54+
"analytics.units.bytes": "B",
55+
"analytics.units.percent": "%",
256
"api.agreement.analytics.description": "Help improve Redis Insight by sharing anonymous usage data. This helps us understand feature usage and make the app better. By enabling this, you agree to our ",
357
"api.agreement.analytics.label": "Usage Data",
458
"api.agreement.notifications.description": "Select to display notifications. Otherwise, notifications are shown in the Notification Center.",

redisinsight/ui/src/pages/database-analysis/DatabaseAnalysisPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ import {
2222
TelemetryPageView,
2323
} from 'uiSrc/telemetry'
2424
import { formatLongName, getDbIndex, setTitle } from 'uiSrc/utils'
25+
import { useTranslation } from 'uiSrc/i18n'
2526
import { DatabaseAnalysisPageView } from './DatabaseAnalysisPageView'
2627

2728
export const DatabaseAnalysisPage = () => {
29+
const { t } = useTranslation()
2830
const { viewTab } = useAppSelector(analyticsSettingsSelector)
2931
const { loading: analysisLoading, data } = useAppSelector(dbAnalysisSelector)
3032
const { data: reports, selectedAnalysis } = useAppSelector(
@@ -42,7 +44,7 @@ export const DatabaseAnalysisPage = () => {
4244

4345
const dispatch = useAppDispatch()
4446
const dbName = `${formatLongName(connectedInstanceName, 33, 0, '...')} ${getDbIndex(db)}`
45-
setTitle(`${dbName} - Database Analysis`)
47+
setTitle(t('analytics.databaseAnalysis.pageTitle', { dbName }))
4648

4749
useEffect(() => {
4850
dispatch(fetchDBAnalysisReportsHistory(instanceId))

redisinsight/ui/src/pages/database-analysis/components/analysis-ttl-view/ExpirationGroupsView.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { useAppDispatch, useAppSelector } from 'uiSrc/slices/hooks'
22
import React, { useEffect, useState } from 'react'
33
import AutoSizer from 'react-virtualized-auto-sizer'
44

5+
import { useTranslation } from 'uiSrc/i18n'
6+
57
import {
68
DEFAULT_EXTRAPOLATION,
79
SectionName,
@@ -49,6 +51,7 @@ export interface Props {
4951

5052
const ExpirationGroupsView = (props: Props) => {
5153
const { data, loading, extrapolation, onSwitchExtrapolation } = props
54+
const { t } = useTranslation()
5255
const { totalMemory, totalKeys } = data || {}
5356

5457
const { showNoExpiryGroup } = useAppSelector(dbAnalysisReportsSelector)
@@ -115,11 +118,11 @@ const ExpirationGroupsView = (props: Props) => {
115118
<SectionTitleWrapper>
116119
<TitleWrapper>
117120
<Title size="M" className="section-title">
118-
MEMORY LIKELY TO BE FREED OVER TIME
121+
{t('analytics.databaseAnalysis.expiration.title')}
119122
</Title>
120123
{extrapolation !== DEFAULT_EXTRAPOLATION && (
121124
<SwitchExtrapolateResults
122-
title="Extrapolate results"
125+
title={t('analytics.databaseAnalysis.extrapolateResults')}
123126
checked={isExtrapolated}
124127
onCheckedChange={(checked) => {
125128
setIsExtrapolated(checked)
@@ -133,7 +136,7 @@ const ExpirationGroupsView = (props: Props) => {
133136
)}
134137
</TitleWrapper>
135138
<Switch
136-
title={'Show "No Expiry"'}
139+
title={t('analytics.databaseAnalysis.expiration.showNoExpiry')}
137140
checked={showNoExpiryGroup}
138141
onCheckedChange={onSwitchChange}
139142
data-testid="show-no-expiry-switch"

redisinsight/ui/src/pages/database-analysis/components/data-nav-tabs/DatabaseAnalysisTabs.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useMemo } from 'react'
22
import { isNull } from 'lodash'
33
import { useAppDispatch, useAppSelector } from 'uiSrc/slices/hooks'
4+
import { useTranslation } from 'uiSrc/i18n'
45
import { EmptyMessage } from 'uiSrc/pages/database-analysis/constants'
56
import { EmptyAnalysisMessage } from 'uiSrc/pages/database-analysis/components'
67
import {
@@ -34,6 +35,7 @@ export interface Props {
3435
const DatabaseAnalysisTabs = (props: Props) => {
3536
const { loading, reports, data } = props
3637

38+
const { t } = useTranslation()
3739
const viewTab = useAppSelector(dbAnalysisViewTabSelector)
3840
const { id: instanceId = '', provider } = useAppSelector(
3941
connectedInstanceSelector,
@@ -47,14 +49,14 @@ const DatabaseAnalysisTabs = (props: Props) => {
4749
const tabs: TabInfo[] = useMemo(
4850
() => [
4951
{
50-
label: <Text>Data Summary</Text>,
52+
label: <Text>{t('analytics.databaseAnalysis.tabs.dataSummary')}</Text>,
5153
value: DatabaseAnalysisViewTab.DataSummary,
5254
content: <AnalysisDataView />,
5355
},
5456
{
5557
label: renderOnboardingTourWithChild(
5658
<Text>
57-
Tips{' '}
59+
{t('analytics.databaseAnalysis.tabs.tips')}{' '}
5860
{data?.recommendations?.length
5961
? `(${data.recommendations.length})`
6062
: ''}
@@ -70,7 +72,7 @@ const DatabaseAnalysisTabs = (props: Props) => {
7072
content: <Recommendations />,
7173
},
7274
],
73-
[viewTab, data?.recommendations],
75+
[t, viewTab, data?.recommendations],
7476
)
7577

7678
const handleTabChange = (id: string) => {

redisinsight/ui/src/pages/database-analysis/components/empty-analysis-message/EmptyAnalysisMessage.tsx

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom'
33
import { Text } from 'uiSrc/components/base/text'
44

55
import { Pages } from 'uiSrc/constants'
6+
import { Trans, useTranslation } from 'uiSrc/i18n'
67
import { EmptyMessage, Content } from 'uiSrc/pages/database-analysis/constants'
78
import { getRouterLinkProps } from 'uiSrc/services'
89

@@ -13,38 +14,40 @@ interface Props {
1314
name: EmptyMessage
1415
}
1516

16-
const emptyMessageContent: { [key in EmptyMessage]: Content } = {
17-
[EmptyMessage.Reports]: {
18-
title: 'No Reports found',
19-
text: () => 'Click "Analyze" to generate the first report.',
20-
},
21-
[EmptyMessage.Keys]: {
22-
title: 'No keys to display',
23-
text: (path) => (
24-
<>
25-
<Link
26-
{...getRouterLinkProps(path)}
27-
className={styles.summary}
28-
data-test-subj="workbench-page-btn"
29-
>
30-
Use Workbench Guides and Tutorials
31-
</Link>
32-
{' to quickly load the data.'}
33-
</>
34-
),
35-
},
36-
[EmptyMessage.Encrypt]: {
37-
title: 'Encrypted data',
38-
text: () =>
39-
'Unable to decrypt. Check the system keychain or re-run the report generation.',
40-
},
41-
}
42-
4317
const EmptyAnalysisMessage = (props: Props) => {
4418
const { name } = props
4519

20+
const { t } = useTranslation()
4621
const { instanceId = '' } = useParams<{ instanceId: string }>()
4722

23+
const emptyMessageContent: { [key in EmptyMessage]: Content } = {
24+
[EmptyMessage.Reports]: {
25+
title: t('analytics.databaseAnalysis.empty.reports.title'),
26+
text: () => t('analytics.databaseAnalysis.empty.reports.text'),
27+
},
28+
[EmptyMessage.Keys]: {
29+
title: t('analytics.databaseAnalysis.empty.keys.title'),
30+
text: (path) => (
31+
<Trans
32+
i18nKey="analytics.databaseAnalysis.empty.keys.text"
33+
components={{
34+
workbenchLink: (
35+
<Link
36+
{...getRouterLinkProps(path)}
37+
className={styles.summary}
38+
data-test-subj="workbench-page-btn"
39+
/>
40+
),
41+
}}
42+
/>
43+
),
44+
},
45+
[EmptyMessage.Encrypt]: {
46+
title: t('analytics.databaseAnalysis.empty.encrypt.title'),
47+
text: () => t('analytics.databaseAnalysis.empty.encrypt.text'),
48+
},
49+
}
50+
4851
const { text, title } = emptyMessageContent[name]
4952

5053
return (

0 commit comments

Comments
 (0)