Skip to content

Commit 9e752b7

Browse files
nccursoragent
authored andcommitted
Extend ru-RU core catalog for dashboard UI.
Add Russian strings for query editor, vis builder, and related core plugins used across the Wazuh dashboard shell. Signed-off-by: svn <svn@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 950624a commit 9e752b7

25 files changed

Lines changed: 668 additions & 243 deletions

File tree

src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/public/i18n/i18n_eui_mapping.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,14 @@ export const getEuiContextMapping = () => {
430430
}),
431431
'euiRelativeTab.relativeDate': ({ position }: EuiValues) =>
432432
i18n.translate('core.euiRelativeTab.relativeDate', {
433-
defaultMessage: '{position} date',
433+
defaultMessage:
434+
'{position, select, Start {Start date} End {End date} other {{position} date}}',
434435
values: { position },
435436
}),
436437
'euiRelativeTab.roundingLabel': ({ unit }: EuiValues) =>
437438
i18n.translate('core.euiRelativeTab.roundingLabel', {
438-
defaultMessage: 'Round to the {unit}',
439+
defaultMessage:
440+
'{unit, select, second {Round to the second} minute {Round to the minute} hour {Round to the hour} day {Round to the day} week {Round to the week} month {Round to the month} year {Round to the year} other {Round to the {unit}}}',
439441
values: { unit },
440442
}),
441443
'euiRelativeTab.unitInputLabel': i18n.translate('core.euiRelativeTab.unitInputLabel', {

src/core/public/i18n/i18n_service.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,32 @@
2929
*/
3030

3131
import React from 'react';
32+
import moment from 'moment';
3233
import { EuiContext } from '@elastic/eui';
34+
import { i18n } from '@osd/i18n';
3335
import { I18nProvider } from '@osd/i18n/react';
3436

37+
import 'moment/locale/ru';
38+
3539
import { getEuiContextMapping } from './i18n_eui_mapping';
3640

41+
function toMomentLocale(osdLocale: string): string {
42+
const lower = (osdLocale || 'en').toLowerCase();
43+
if (lower.startsWith('ru')) {
44+
return 'ru';
45+
}
46+
if (lower === 'zh-cn' || lower.startsWith('zh-cn')) {
47+
return 'zh-cn';
48+
}
49+
if (lower === 'zh-tw' || lower.startsWith('zh-tw')) {
50+
return 'zh-tw';
51+
}
52+
if (lower.startsWith('pt')) {
53+
return 'pt';
54+
}
55+
return osdLocale.split('-')[0] || 'en';
56+
}
57+
3758
/**
3859
* Service that is responsible for i18n capabilities.
3960
* @internal
@@ -49,6 +70,8 @@ export class I18nService {
4970
*/
5071
public getContext(): I18nStart {
5172
const euiContextMapping = getEuiContextMapping();
73+
const locale = toMomentLocale(typeof i18n.getLocale === 'function' ? i18n.getLocale() : 'en');
74+
moment.locale(locale);
5275

5376
const mapping = {
5477
...euiContextMapping,
@@ -57,7 +80,7 @@ export class I18nService {
5780
Context: function I18nContext({ children }) {
5881
return (
5982
<I18nProvider>
60-
<EuiContext i18n={{ mapping }}>{children}</EuiContext>
83+
<EuiContext i18n={{ mapping, locale }}>{children}</EuiContext>
6184
</I18nProvider>
6285
);
6386
},

src/plugins/advanced_settings/public/header_user_theme_menu.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,21 @@ export const HeaderUserThemeMenu = () => {
3535
const colorModeOptions = [
3636
{
3737
value: 'light',
38-
text: 'Light mode',
38+
text: i18n.translate('advancedSettings.headerGlobalNav.colorMode.light', {
39+
defaultMessage: 'Light mode',
40+
}),
3941
},
4042
{
4143
value: 'dark',
42-
text: 'Dark mode',
44+
text: i18n.translate('advancedSettings.headerGlobalNav.colorMode.dark', {
45+
defaultMessage: 'Dark mode',
46+
}),
4347
},
4448
{
4549
value: 'automatic',
46-
text: 'Use browser settings',
50+
text: i18n.translate('advancedSettings.headerGlobalNav.colorMode.automatic', {
51+
defaultMessage: 'Use browser settings',
52+
}),
4753
},
4854
];
4955
const defaultThemeVersion = uiSettings.getDefault('theme:version');
@@ -145,25 +151,38 @@ export const HeaderUserThemeMenu = () => {
145151
</EuiToolTip>
146152
);
147153

148-
// TODO: make i18n, check all translation ids
154+
const defaultColorModeLabel =
155+
colorModeOptions.find((t) => {
156+
const defaultValue = defaultIsDarkMode ? 'dark' : 'light';
157+
return defaultValue === t.value;
158+
})?.text ?? '';
159+
149160
// TODO: fix focus behavior
150161
const appearanceContent = (
151162
<div style={{ maxWidth: 300 }}>
152-
<EuiCompressedFormRow label="Theme version" helpText={`Default: ${defaultThemeVersion}`}>
163+
<EuiCompressedFormRow
164+
label={i18n.translate('advancedSettings.headerGlobalNav.themeVersionLabel', {
165+
defaultMessage: 'Theme version',
166+
})}
167+
helpText={i18n.translate('advancedSettings.field.defaultValueText', {
168+
defaultMessage: 'Default: {value}',
169+
values: { value: defaultThemeVersion },
170+
})}
171+
>
153172
<EuiCompressedSelect
154173
options={themeVersionOptions}
155174
value={selectedThemeVersion}
156175
onChange={handleThemeVersionChange}
157176
/>
158177
</EuiCompressedFormRow>
159178
<EuiCompressedFormRow
160-
label="Screen mode"
161-
helpText={`Default: ${
162-
colorModeOptions.find((t) => {
163-
const defaultValue = defaultIsDarkMode ? 'dark' : 'light';
164-
return defaultValue === t.value;
165-
})?.text
166-
}`}
179+
label={i18n.translate('advancedSettings.headerGlobalNav.screenModeLabel', {
180+
defaultMessage: 'Screen mode',
181+
})}
182+
helpText={i18n.translate('advancedSettings.field.defaultValueText', {
183+
defaultMessage: 'Default: {value}',
184+
values: { value: defaultColorModeLabel },
185+
})}
167186
>
168187
<EuiCompressedSelect
169188
options={colorModeOptions}
@@ -178,15 +197,19 @@ export const HeaderUserThemeMenu = () => {
178197
target="_blank"
179198
href="https://forum.opensearch.org/t/feedback-on-dark-mode-experience/15725"
180199
>
181-
Theme feedback
200+
{i18n.translate('advancedSettings.headerGlobalNav.themeFeedbackLink', {
201+
defaultMessage: 'Theme feedback',
202+
})}
182203
</EuiLink>
183204
</EuiCompressedFormRow>
184205
</EuiFlexItem>
185206
<EuiFlexItem grow={false}>
186207
<EuiCompressedFormRow hasEmptyLabelSpace>
187208
{/* TODO: disable submit until changes */}
188209
<EuiSmallButton onClick={applyAppearanceSettings} type="submit">
189-
Apply
210+
{i18n.translate('advancedSettings.headerGlobalNav.applyButtonLabel', {
211+
defaultMessage: 'Apply',
212+
})}
190213
</EuiSmallButton>
191214
</EuiCompressedFormRow>
192215
</EuiFlexItem>

src/plugins/advanced_settings/public/management_app/user_settings.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ const sectionRender = (contents: Content[]) => {
3232
export const setupUserSettingsPage = (contentManagement?: ContentManagementPluginSetup) => {
3333
contentManagement?.registerPage({
3434
id: 'user_settings',
35-
title: 'User Settings',
35+
title: i18n.translate('advancedSettings.userSettingsLabel', {
36+
defaultMessage: 'User settings',
37+
}),
3638
sections: [
3739
{
3840
id: 'user_profile',
3941
order: 1000,
40-
title: `User's profile`,
42+
title: i18n.translate('advancedSettings.userSettings.profileSectionTitle', {
43+
defaultMessage: "User's profile",
44+
}),
4145
kind: 'custom',
4246
render: sectionRender,
4347
},

src/plugins/dashboard/public/application/utils/get_no_items_message.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55

66
import React, { Fragment } from 'react';
77
import { FormattedMessage } from '@osd/i18n/react';
8+
import { i18n } from '@osd/i18n';
89
import { EuiSmallButton, EuiEmptyPrompt, EuiLink, EuiText } from '@elastic/eui';
910
import { ApplicationStart } from 'opensearch-dashboards/public';
1011

11-
const appName = 'Wazuh dashboard';
12+
const appName = i18n.translate('dashboard.listing.appName', {
13+
defaultMessage: 'Wazuh dashboard',
14+
});
1215
export const getNoItemsMessage = (
1316
hideWriteControls: boolean,
1417
createItem: () => void,

src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
import { UI_SETTINGS } from '../../../common';
3535
import { getQueryLog, PersistedLog, QueryStatus } from '../../query';
3636
import { NoDataPopover } from './no_data_popover';
37+
import { toDatePickerLocale, translateQuickRangeLabel } from '../query_string_input/timepicker_i18n';
3738
import QueryEditorUI from './query_editor';
3839
import { useCancelButtonTiming } from '../hooks';
3940

@@ -395,7 +396,7 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) {
395396
return {
396397
start: from,
397398
end: to,
398-
label: display,
399+
label: translateQuickRangeLabel(from, to, display),
399400
};
400401
});
401402

@@ -423,6 +424,7 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) {
423424
className="osdQueryEditor__datePicker"
424425
data-test-subj="osdQueryEditorDatePicker"
425426
compressed={true}
427+
locale={toDatePickerLocale()}
426428
/>
427429
</EuiFlexItem>
428430
);

src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import QueryStringInputUI from './query_string_input';
5757
import { doesKueryExpressionHaveLuceneSyntaxError, UI_SETTINGS } from '../../../common';
5858
import { PersistedLog, getQueryLog } from '../../query';
5959
import { NoDataPopover } from './no_data_popover';
60+
import { toDatePickerLocale, translateQuickRangeLabel } from './timepicker_i18n';
6061

6162
const QueryStringInput = withOpenSearchDashboards(QueryStringInputUI);
6263

@@ -343,7 +344,7 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) {
343344
return {
344345
start: from,
345346
end: to,
346-
label: display,
347+
label: translateQuickRangeLabel(from, to, display),
347348
};
348349
});
349350

@@ -371,6 +372,7 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) {
371372
className="osdQueryBar__datePicker"
372373
data-test-subj="osdQueryBarDatePicker"
373374
compressed={true}
375+
locale={toDatePickerLocale()}
374376
/>
375377
</EuiFlexItem>
376378
);
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { i18n } from '@osd/i18n';
7+
8+
interface QuickRangeLabel {
9+
from: string;
10+
to: string;
11+
id: string;
12+
defaultMessage: string;
13+
}
14+
15+
/**
16+
* Default timepicker:quickRanges from data.advancedSettings.timepicker.*.
17+
* Labels are stored in uiSettings as English on first write; re-translate at render.
18+
*/
19+
const QUICK_RANGE_LABELS: QuickRangeLabel[] = [
20+
{
21+
from: 'now/d',
22+
to: 'now/d',
23+
id: 'data.advancedSettings.timepicker.today',
24+
defaultMessage: 'Today',
25+
},
26+
{
27+
from: 'now/w',
28+
to: 'now/w',
29+
id: 'data.advancedSettings.timepicker.thisWeek',
30+
defaultMessage: 'This week',
31+
},
32+
{
33+
from: 'now-15m',
34+
to: 'now',
35+
id: 'data.advancedSettings.timepicker.last15Minutes',
36+
defaultMessage: 'Last 15 minutes',
37+
},
38+
{
39+
from: 'now-30m',
40+
to: 'now',
41+
id: 'data.advancedSettings.timepicker.last30Minutes',
42+
defaultMessage: 'Last 30 minutes',
43+
},
44+
{
45+
from: 'now-1h',
46+
to: 'now',
47+
id: 'data.advancedSettings.timepicker.last1Hour',
48+
defaultMessage: 'Last 1 hour',
49+
},
50+
{
51+
from: 'now-24h',
52+
to: 'now',
53+
id: 'data.advancedSettings.timepicker.last24Hours',
54+
defaultMessage: 'Last 24 hours',
55+
},
56+
{
57+
from: 'now-7d',
58+
to: 'now',
59+
id: 'data.advancedSettings.timepicker.last7Days',
60+
defaultMessage: 'Last 7 days',
61+
},
62+
{
63+
from: 'now-30d',
64+
to: 'now',
65+
id: 'data.advancedSettings.timepicker.last30Days',
66+
defaultMessage: 'Last 30 days',
67+
},
68+
{
69+
from: 'now-90d',
70+
to: 'now',
71+
id: 'data.advancedSettings.timepicker.last90Days',
72+
defaultMessage: 'Last 90 days',
73+
},
74+
{
75+
from: 'now-1y',
76+
to: 'now',
77+
id: 'data.advancedSettings.timepicker.last1Year',
78+
defaultMessage: 'Last 1 year',
79+
},
80+
];
81+
82+
const byFromTo = new Map(QUICK_RANGE_LABELS.map((range) => [`${range.from}|${range.to}`, range]));
83+
const byDisplay = new Map(QUICK_RANGE_LABELS.map((range) => [range.defaultMessage, range]));
84+
85+
export function translateQuickRangeLabel(from: string, to: string, display: string): string {
86+
const match = byFromTo.get(`${from}|${to}`) || byDisplay.get(display);
87+
if (!match) {
88+
return display;
89+
}
90+
return i18n.translate(match.id, { defaultMessage: match.defaultMessage });
91+
}
92+
93+
export function toDatePickerLocale(osdLocale?: string): string {
94+
const locale =
95+
osdLocale || (typeof i18n.getLocale === 'function' ? i18n.getLocale() : 'en') || 'en';
96+
const lower = locale.toLowerCase();
97+
if (lower.startsWith('ru')) {
98+
return 'ru';
99+
}
100+
if (lower === 'zh-cn' || lower.startsWith('zh-cn')) {
101+
return 'zh-cn';
102+
}
103+
if (lower === 'zh-tw' || lower.startsWith('zh-tw')) {
104+
return 'zh-tw';
105+
}
106+
return locale.split('-')[0] || 'en';
107+
}

src/plugins/data_source_management/public/plugin.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { toMountPoint } from '../../../../src/plugins/opensearch_dashboards_reac
1919
import { DashboardDirectQuerySyncBanner } from './components/direct_query_data_sources_components/direct_query_sync/direct_query_sync_banner';
2020
import { parseUrlHash } from '../../opensearch_dashboards_utils/public';
2121

22-
import { PLUGIN_NAME } from '../common';
2322
import { createDataSourceSelector } from './components/data_source_selector/create_data_source_selector';
2423

2524
import { ManagementSetup } from '../../management/public';
@@ -148,9 +147,13 @@ export class DataSourceManagementPlugin
148147

149148
this.featureFlagStatus = !!dataSource;
150149

150+
const dataSourcesTitle = i18n.translate('dataSourcesManagement.dataSources.listBreadcrumb', {
151+
defaultMessage: 'Data sources',
152+
});
153+
151154
opensearchDashboardsSection.registerApp({
152155
id: DSM_APP_ID,
153-
title: PLUGIN_NAME,
156+
title: dataSourcesTitle,
154157
order: 1,
155158
// @ts-expect-error TS2322 TODO(ts-error): fixme
156159
mount: async (params: AppMountParameters) => {
@@ -169,7 +172,7 @@ export class DataSourceManagementPlugin
169172
if (core.chrome.navGroup.getNavGroupEnabled()) {
170173
core.application.register({
171174
id: DSM_APP_ID,
172-
title: PLUGIN_NAME,
175+
title: dataSourcesTitle,
173176
order: 100,
174177
description: i18n.translate('dataSourcesManagement.description', {
175178
defaultMessage: 'Create and manage data source connections.',

0 commit comments

Comments
 (0)