-
+ {/*
= ({
showTooltip={false}
url={canExplore ? exploreUrl : undefined}
/>
-
+ */}
+ {/* DODO changed start 44120742 */}
+ {editMode && (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+ {!editMode && locale !== 'ru' && (
+
+
+
+ )}
+ {!editMode && locale === 'ru' && (
+
+
+
+ )}
+ {/* DODO changed stop 44120742 */}
{!!Object.values(annotationQuery).length && (
{/*
diff --git a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx
index d31e240e4f70d..0a9eba6f3fe8f 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx
@@ -233,6 +233,22 @@ const ChartHolder: React.FC
= ({
[component, updateComponents],
);
+ // DODO added 44120742
+ const handleUpdateSliceNameRU = useCallback(
+ (nextName: string) => {
+ updateComponents({
+ [component.id]: {
+ ...component,
+ meta: {
+ ...component.meta,
+ sliceNameOverrideRU: nextName,
+ },
+ },
+ });
+ },
+ [component, updateComponents],
+ );
+
const handleToggleFullSize = useCallback(() => {
setFullSizeChartId(isFullSize ? null : chartId);
}, [chartId, isFullSize, setFullSizeChartId]);
@@ -308,9 +324,17 @@ const ChartHolder: React.FC = ({
sliceName={
component.meta.sliceNameOverride ||
component.meta.sliceName ||
- ''
+ '--' // DODO changed 44120742
+ }
+ // DODO added 44120742
+ sliceNameRU={
+ component.meta.sliceNameOverrideRU ||
+ component.meta.sliceNameRU ||
+ component.meta.sliceName ||
+ '--'
}
updateSliceName={handleUpdateSliceName}
+ updateSliceNameRU={handleUpdateSliceNameRU} // DODO added 44120742
isComponentVisible={isComponentVisible}
handleToggleFullSize={handleToggleFullSize}
isFullSize={isFullSize}
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
index eaa8ca00ab4b3..6538077b67759 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
@@ -33,6 +33,10 @@ import DragDroppable, {
} from 'src/dashboard/components/dnd/DragDroppable';
import { componentShape } from 'src/dashboard/util/propShapes';
import { TAB_TYPE } from 'src/dashboard/util/componentTypes';
+import {
+ LanguageIndicator,
+ LanguageIndicatorWrapper,
+} from 'src/DodoExtensions/Common';
export const RENDER_TAB = 'RENDER_TAB';
export const RENDER_TAB_CONTENT = 'RENDER_TAB_CONTENT';
@@ -103,15 +107,29 @@ class Tab extends PureComponent {
this.props.setDirectPathToChild(pathToTabIndex);
}
- handleChangeText(nextTabText) {
+ // handleChangeText(nextTabText) {
+ // const { updateComponents, component } = this.props;
+ // if (nextTabText && nextTabText !== component.meta.text) {
+ // updateComponents({
+ // [component.id]: {
+ // ...component,
+ // meta: {
+ // ...component.meta,
+ // text: nextTabText,
+ // },
+ // },
+ // });
+ // }
+ // }
+ handleChangeText(nextTabText, property) {
const { updateComponents, component } = this.props;
- if (nextTabText && nextTabText !== component.meta.text) {
+ if (nextTabText && nextTabText !== component.meta[property]) {
updateComponents({
[component.id]: {
...component,
meta: {
...component.meta,
- text: nextTabText,
+ [property]: nextTabText,
},
},
});
@@ -268,6 +286,7 @@ class Tab extends PureComponent {
editMode,
isFocused,
isHighlighted,
+ locale,
} = this.props;
return (
@@ -288,15 +307,58 @@ class Tab extends PureComponent {
className="dragdroppable-tab"
ref={dragSourceRef}
>
-
+ {/* DODO changed 44120742 */}
+ {editMode && (
+
+
+
+ this.handleChangeText(nextTabText, 'text')
+ }
+ showTooltip={false}
+ editing={editMode && isFocused}
+ />
+
+ )}
+ {/* DODO added 44120742 */}
+ {editMode && (
+
+
+
+ this.handleChangeText(nextTabText, 'textRU')
+ }
+ showTooltip={false}
+ editing={editMode && isFocused}
+ />
+
+ )}
+ {!editMode && (
+
+ this.handleChangeText(nextTabText, 'text')
+ }
+ showTooltip={false}
+ editing={editMode && isFocused}
+ />
+ )}
{!editMode && (
}
>
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx
index 119763a22ef3e..e4c8eb3978708 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/ScopingModal/ChartsScopingListPanel.tsx
@@ -123,6 +123,11 @@ export const ChartsScopingListPanel = ({
chartLayoutItem?.meta.sliceNameOverride ||
chartLayoutItem?.meta.sliceName ||
'',
+ // DODO added 44120742
+ labelRU:
+ chartLayoutItem?.meta.sliceNameOverrideRU ||
+ chartLayoutItem?.meta.sliceNameRU ||
+ '',
};
});
}, [chartConfigs, layout]);
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts b/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts
index 36f0fd1925dd9..50df0168b0161 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts
@@ -157,13 +157,17 @@ const getRejectedColumns = (chart: any): Set =>
),
);
+type IndicatorDodoExtended = {
+ nameRU?: string; // DODO added 44120742
+};
+
export type Indicator = {
column?: QueryFormColumn;
name: string;
value?: any;
status?: IndicatorStatus;
path?: string[];
-};
+} & IndicatorDodoExtended;
export type CrossFilterIndicator = Indicator & { emitterId: number };
@@ -188,6 +192,10 @@ export const getCrossFilterIndicator = (
dashboardLayoutItem?.meta?.sliceNameOverride ||
dashboardLayoutItem?.meta?.sliceName ||
'',
+ nameRU:
+ dashboardLayoutItem?.meta?.sliceNameOverrideRU ||
+ dashboardLayoutItem?.meta?.sliceNameRU ||
+ '',
path: [
...(dashboardLayoutItem?.parents ?? []),
dashboardLayoutItem?.id || '',
diff --git a/superset-frontend/src/dashboard/containers/Chart.jsx b/superset-frontend/src/dashboard/containers/Chart.jsx
index 9f00bd0bf7096..f905ee6a72524 100644
--- a/superset-frontend/src/dashboard/containers/Chart.jsx
+++ b/superset-frontend/src/dashboard/containers/Chart.jsx
@@ -18,6 +18,7 @@
*/
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
+import { bootstrapData } from 'src/preamble'; // DODO added 44120742
import {
toggleExpandSlice,
setFocusedFilterField,
@@ -40,6 +41,8 @@ import Chart from 'src/dashboard/components/gridComponents/Chart';
import { PLACEHOLDER_DATASOURCE } from 'src/dashboard/constants';
import { enforceSharedLabelsColorsArray } from 'src/utils/colorScheme';
+const locale = bootstrapData?.common?.locale || 'en'; // DODO added 44120742
+
const EMPTY_OBJECT = {};
function mapStateToProps(
@@ -112,6 +115,7 @@ function mapStateToProps(
setControlValue,
datasetsStatus,
emitCrossFilters: !!dashboardInfo.crossFiltersEnabled,
+ locale, // DODO added 44120742
};
}
diff --git a/superset-frontend/src/dashboard/containers/DashboardComponent.jsx b/superset-frontend/src/dashboard/containers/DashboardComponent.jsx
index bf92c5dcecaeb..c2cb3b754179c 100644
--- a/superset-frontend/src/dashboard/containers/DashboardComponent.jsx
+++ b/superset-frontend/src/dashboard/containers/DashboardComponent.jsx
@@ -20,6 +20,7 @@ import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
+import { bootstrapData } from 'src/preamble'; // DODO added 44120742
import { logEvent } from 'src/logger/actions';
import { addDangerToast } from 'src/components/MessageToasts/actions';
import { componentLookup } from 'src/dashboard/components/gridComponents';
@@ -39,6 +40,8 @@ import {
setFullSizeChartId,
} from 'src/dashboard/actions/dashboardState';
+const locale = bootstrapData?.common?.locale || 'en'; // DODO added 44120742
+
const propTypes = {
id: PropTypes.string,
parentId: PropTypes.string,
@@ -58,6 +61,9 @@ const propTypes = {
directPathLastUpdated: PropTypes.number,
dashboardId: PropTypes.number.isRequired,
isComponentVisible: PropTypes.bool,
+
+ // DODO extended
+ locale: PropTypes.string, // DODO added 44120742
};
const defaultProps = {
@@ -121,7 +127,7 @@ class DashboardComponent extends PureComponent {
render() {
const { component } = this.props;
const Component = component ? componentLookup[component.type] : null;
- return Component ? : null;
+ return Component ? : null;
}
}
diff --git a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
index cc05916dccdee..f9c7aa2b5b450 100644
--- a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
+++ b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
@@ -75,6 +75,10 @@ function mapStateToProps({
dashboardTitle: (
(undoableLayout.present[DASHBOARD_HEADER_ID] || {}).meta || {}
).text,
+ // DODO added 44120742
+ dashboardTitleRU: (
+ (undoableLayout.present[DASHBOARD_HEADER_ID] || {}).meta || {}
+ ).textRU,
expandedSlices: dashboardState.expandedSlices,
refreshFrequency: dashboardState.refreshFrequency,
shouldPersistRefreshFrequency:
diff --git a/superset-frontend/src/dashboard/containers/DashboardPage.tsx b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
index e8c9283462069..8bc2abfd286fb 100644
--- a/superset-frontend/src/dashboard/containers/DashboardPage.tsx
+++ b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
@@ -21,6 +21,7 @@ import { Global } from '@emotion/react';
import { useHistory } from 'react-router-dom';
import { t, useTheme } from '@superset-ui/core';
import { useDispatch, useSelector } from 'react-redux';
+import { bootstrapData } from 'src/preamble'; // DODO added 44120742
import { useToasts } from 'src/components/MessageToasts/withToasts';
import Loading from 'src/components/Loading';
import {
@@ -55,6 +56,8 @@ import SyncDashboardState, {
getDashboardContextLocalStorage,
} from '../components/SyncDashboardState';
+const locale = bootstrapData?.common?.locale || 'en'; // DODO added 44120742
+
export const DashboardPageIdContext = createContext('');
const DashboardBuilder = lazy(
@@ -67,6 +70,7 @@ const DashboardBuilder = lazy(
);
const originalDocumentTitle = document.title;
+const fallBackPageTitle = 'Superset dashboard'; // DODO added 44120742
type PageProps = {
idOrSlug: string;
@@ -84,18 +88,22 @@ export const DashboardPage: FC = ({ idOrSlug }: PageProps) => {
const { addDangerToast } = useToasts();
const { result: dashboard, error: dashboardApiError } =
useDashboard(idOrSlug);
- const { result: charts, error: chartsApiError } =
- useDashboardCharts(idOrSlug);
+ const { result: charts, error: chartsApiError } = useDashboardCharts(
+ idOrSlug,
+ locale, // DODO added 44120742
+ );
const {
result: datasets,
error: datasetsApiError,
status,
- } = useDashboardDatasets(idOrSlug);
+ // } = useDashboardDatasets(idOrSlug);
+ } = useDashboardDatasets(idOrSlug, locale); // DODO changed 44120742
const isDashboardHydrated = useRef(false);
const error = dashboardApiError || chartsApiError;
const readyToRender = Boolean(dashboard && charts);
- const { dashboard_title, css, id = 0 } = dashboard || {};
+ // const { dashboard_title, css, id = 0 } = dashboard || {};
+ const { dashboard_title, dashboard_title_RU, css, id = 0 } = dashboard || {}; // DODO changed 44120742
useEffect(() => {
// mark tab id as redundant when user closes browser tab - a new id will be
@@ -163,14 +171,30 @@ export const DashboardPage: FC = ({ idOrSlug }: PageProps) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [readyToRender]);
+ // useEffect(() => {
+ // if (dashboard_title) {
+ // document.title = dashboard_title;
+ // }
+ // return () => {
+ // document.title = originalDocumentTitle;
+ // };
+ // }, [dashboard_title]);
+
+ // DODO changed 44120742
useEffect(() => {
- if (dashboard_title) {
- document.title = dashboard_title;
- }
+ const localisedTitle =
+ locale === 'ru' ? dashboard_title_RU : dashboard_title;
+
+ document.title =
+ localisedTitle ||
+ dashboard_title ||
+ dashboard_title_RU ||
+ fallBackPageTitle;
+
return () => {
document.title = originalDocumentTitle;
};
- }, [dashboard_title]);
+ }, [dashboard_title, dashboard_title_RU]);
useEffect(() => {
if (typeof css === 'string') {
diff --git a/superset-frontend/src/dashboard/reducers/dashboardLayout.js b/superset-frontend/src/dashboard/reducers/dashboardLayout.js
index 6ba297be9147e..436e4337d1301 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardLayout.js
+++ b/superset-frontend/src/dashboard/reducers/dashboardLayout.js
@@ -300,6 +300,7 @@ const actionHandlers = {
meta: {
...state[DASHBOARD_HEADER_ID].meta,
text: action.text,
+ textRU: action.textRU, // DODO added 44120742
},
},
};
diff --git a/superset-frontend/src/dashboard/types.ts b/superset-frontend/src/dashboard/types.ts
index ad78449781c48..eee79ed97c698 100644
--- a/superset-frontend/src/dashboard/types.ts
+++ b/superset-frontend/src/dashboard/types.ts
@@ -178,6 +178,10 @@ type ComponentTypesKeys = keyof typeof componentTypes;
export type ComponentType = (typeof componentTypes)[ComponentTypesKeys];
/** State of dashboardLayout item in redux */
+type MetaDodoExtended = {
+ sliceNameRU?: string; // DODO added 44120742
+ sliceNameOverrideRU?: string; // DODO added 44120742
+};
export type LayoutItem = {
children: string[];
parents: string[];
@@ -193,7 +197,7 @@ export type LayoutItem = {
text?: string;
uuid: string;
width: number;
- };
+ } & MetaDodoExtended;
};
type ActiveFilter = {
diff --git a/superset-frontend/src/dashboard/util/permissionUtils.test.ts b/superset-frontend/src/dashboard/util/permissionUtils.test.ts
index 32687509bdc68..1025b5c052df0 100644
--- a/superset-frontend/src/dashboard/util/permissionUtils.test.ts
+++ b/superset-frontend/src/dashboard/util/permissionUtils.test.ts
@@ -85,6 +85,7 @@ const undefinedUser: UndefinedUser = {};
const dashboard: Dashboard = {
id: 1,
dashboard_title: 'Test Dash',
+ dashboard_title_RU: 'Test Dash',
url: 'https://dashboard.example.com/1',
thumbnail_url: 'https://dashboard.example.com/1/thumbnail.png',
published: true,
diff --git a/superset-frontend/src/explore/components/SaveModal.tsx b/superset-frontend/src/explore/components/SaveModal.tsx
index 8c216056a123e..862ed8eb9ebb3 100644
--- a/superset-frontend/src/explore/components/SaveModal.tsx
+++ b/superset-frontend/src/explore/components/SaveModal.tsx
@@ -62,13 +62,16 @@ interface SaveModalProps extends RouteComponentProps {
dispatch: Dispatch;
}
+type DashboardDodoExtended = {
+ labelRU: string; // DODO added 44120742
+};
type SaveModalState = {
newSliceName?: string;
datasetName: string;
action: SaveActionType;
isLoading: boolean;
saveStatus?: string | null;
- dashboard?: { label: string; value: string | number };
+ dashboard?: { label: string; value: string | number } & DashboardDodoExtended;
};
export const StyledModal = styled(Modal)`
@@ -128,7 +131,11 @@ class SaveModal extends Component {
const result = (await this.loadDashboard(dashboardId)) as Dashboard;
if (canUserEditDashboard(result, this.props.user)) {
this.setState({
- dashboard: { label: result.dashboard_title, value: result.id },
+ dashboard: {
+ label: result.dashboard_title,
+ labelRU: result.dashboard_title_RU, // DODO added 44120742
+ value: result.id,
+ },
});
}
} catch (error) {
@@ -148,7 +155,12 @@ class SaveModal extends Component {
this.setState({ newSliceName: event.target.value });
}
- onDashboardChange(dashboard: { label: string; value: string | number }) {
+ onDashboardChange(
+ dashboard: {
+ label: string;
+ value: string | number;
+ } & DashboardDodoExtended, // DODO added 44120742
+ ) {
this.setState({ dashboard });
}
@@ -175,11 +187,14 @@ class SaveModal extends Component {
this.setState({ isLoading: true });
// Create or retrieve dashboard
+ type DashboardGetResponseDodoExtended = {
+ dashboard_title_RU: string; // DODO added 44120742
+ };
type DashboardGetResponse = {
id: number;
url: string;
dashboard_title: string;
- };
+ } & DashboardGetResponseDodoExtended;
try {
if (this.props.datasource?.type === DatasourceType.Query) {
@@ -244,6 +259,7 @@ class SaveModal extends Component {
dashboard
? {
title: dashboard.dashboard_title,
+ titleRU: dashboard.dashboard_title_RU, // DODO added 44120742
new: this.isNewDashboard(),
}
: null,
@@ -255,6 +271,7 @@ class SaveModal extends Component {
dashboard
? {
title: dashboard.dashboard_title,
+ titleRU: dashboard.dashboard_title_RU, // DODO added 44120742
new: this.isNewDashboard(),
}
: null,
@@ -296,13 +313,21 @@ class SaveModal extends Component {
loadDashboards = async (search: string, page: number, pageSize: number) => {
const queryParams = rison.encode({
- columns: ['id', 'dashboard_title'],
+ // columns: ['id', 'dashboard_title'],
+ columns: ['id', 'dashboard_title', 'dashboard_title_RU'], // DODO changed 44120742
filters: [
{
col: 'dashboard_title',
opr: 'ct',
value: search,
},
+ // DODO added start 44120742
+ {
+ col: 'dashboard_title_RU',
+ opr: 'ct',
+ value: search,
+ },
+ // DODO added stop 44120742
{
col: 'owners',
opr: 'rel_m_m',
@@ -320,9 +345,14 @@ class SaveModal extends Component {
const { result, count } = json;
return {
data: result.map(
- (dashboard: { id: number; dashboard_title: string }) => ({
+ (dashboard: {
+ id: number;
+ dashboard_title: string;
+ dashboard_title_RU: string; // DODO added 44120742
+ }) => ({
value: dashboard.id,
label: dashboard.dashboard_title,
+ labelRU: dashboard.dashboard_title_RU, // DODO added 44120742
}),
),
totalCount: count,
diff --git a/superset-frontend/src/hooks/apiResources/dashboards.ts b/superset-frontend/src/hooks/apiResources/dashboards.ts
index 61896ba1309dc..ecfe9224e9a26 100644
--- a/superset-frontend/src/hooks/apiResources/dashboards.ts
+++ b/superset-frontend/src/hooks/apiResources/dashboards.ts
@@ -36,14 +36,34 @@ export const useDashboard = (idOrSlug: string | number) =>
);
// gets the chart definitions for a dashboard
-export const useDashboardCharts = (idOrSlug: string | number) =>
- useApiV1Resource(`/api/v1/dashboard/${idOrSlug}/charts`);
+// export const useDashboardCharts = (idOrSlug: string | number) =>
+// useApiV1Resource(`/api/v1/dashboard/${idOrSlug}/charts`);
+// DODO changed 44120742
+export const useDashboardCharts = (
+ idOrSlug: string | number,
+ language?: string,
+) =>
+ useApiV1Resource(
+ !language
+ ? `/api/v1/dashboard/${idOrSlug}/charts`
+ : `/api/v1/dashboard/${idOrSlug}/charts?language=${language}`,
+ );
// gets the datasets for a dashboard
// important: this endpoint only returns the fields in the dataset
// that are necessary for rendering the given dashboard
-export const useDashboardDatasets = (idOrSlug: string | number) =>
- useApiV1Resource(`/api/v1/dashboard/${idOrSlug}/datasets`);
+// export const useDashboardDatasets = (idOrSlug: string | number) =>
+// useApiV1Resource(`/api/v1/dashboard/${idOrSlug}/datasets`);
+// DODO changed 44120742
+export const useDashboardDatasets = (
+ idOrSlug: string | number,
+ language?: string,
+) =>
+ useApiV1Resource(
+ !language
+ ? `/api/v1/dashboard/${idOrSlug}/datasets`
+ : `/api/v1/dashboard/${idOrSlug}/datasets?language=${language}`,
+ );
export const useEmbeddedDashboard = (idOrSlug: string | number) =>
useApiV1Resource(`/api/v1/dashboard/${idOrSlug}/embedded`);
diff --git a/superset-frontend/src/pages/DashboardList/index.tsx b/superset-frontend/src/pages/DashboardList/index.tsx
index 361168f4188e3..304fca54e3686 100644
--- a/superset-frontend/src/pages/DashboardList/index.tsx
+++ b/superset-frontend/src/pages/DashboardList/index.tsx
@@ -93,7 +93,10 @@ interface DashboardListProps {
};
}
-export interface Dashboard {
+interface DashboardDodoExtended {
+ dashboard_title_RU: string; // DODO added 44120742
+}
+export interface Dashboard extends DashboardDodoExtended {
changed_by_name: string;
changed_on_delta_humanized: string;
changed_by: string;
@@ -114,6 +117,7 @@ const Actions = styled.div`
const DASHBOARD_COLUMNS_TO_FETCH = [
'id',
'dashboard_title',
+ 'dashboard_title_RU', // DODO added 44120742
'published',
'url',
'slug',
@@ -229,6 +233,7 @@ function DashboardList(props: DashboardListProps) {
changed_by_name,
changed_by,
dashboard_title = '',
+ dashboard_title_RU = '', // DODO added 44120742
slug = '',
json_metadata = '',
changed_on_delta_humanized,
@@ -243,6 +248,7 @@ function DashboardList(props: DashboardListProps) {
changed_by_name,
changed_by,
dashboard_title,
+ dashboard_title_RU, // DODO added 44120742
slug,
json_metadata,
changed_on_delta_humanized,
@@ -335,9 +341,23 @@ function DashboardList(props: DashboardListProps) {
{dashboardTitle}
),
- Header: t('Name'),
+ // Header: t('Name'),
+ Header: t('Title'), // DODO changed 44120742
accessor: 'dashboard_title',
},
+ // DODO added start 44120742
+ {
+ Cell: ({
+ row: {
+ original: { url, dashboard_title_RU: dashboardTitleRU },
+ },
+ }: any) => (
+ {dashboardTitleRU ? `${dashboardTitleRU}` : '-'}
+ ),
+ Header: t('Title (Rus)'),
+ accessor: 'dashboard_title_RU',
+ },
+ // DODO added stop 44120742
{
Cell: ({
row: {
diff --git a/superset-frontend/src/preamble.ts b/superset-frontend/src/preamble.ts
index c43afc76f88e5..269568c3c1782 100644
--- a/superset-frontend/src/preamble.ts
+++ b/superset-frontend/src/preamble.ts
@@ -39,7 +39,7 @@ if (process.env.WEBPACK_MODE === 'development') {
}
// eslint-disable-next-line import/no-mutable-exports
-const bootstrapData = getBootstrapData();
+export const bootstrapData = getBootstrapData();
// Configure translation
if (typeof window !== 'undefined') {
diff --git a/superset-frontend/src/types/Dashboard.ts b/superset-frontend/src/types/Dashboard.ts
index faecc0bc4acf7..2bb8d49907daa 100644
--- a/superset-frontend/src/types/Dashboard.ts
+++ b/superset-frontend/src/types/Dashboard.ts
@@ -19,7 +19,10 @@
import Owner from './Owner';
import Role from './Role';
-export interface Dashboard {
+interface DashboardDodoExtended {
+ dashboard_title_RU: string; // DODO added 44120742
+}
+export interface Dashboard extends DashboardDodoExtended {
id: number;
slug?: string | null;
url: string;
diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py
index fb7409adba589..7fc97dccdbaba 100644
--- a/superset/connectors/sqla/models.py
+++ b/superset/connectors/sqla/models.py
@@ -834,6 +834,10 @@ class TableColumn(AuditMixinNullable, ImportExportMixin, CertificationMixin, Mod
expression = Column(utils.MediumText())
python_date_format = Column(String(255))
extra = Column(Text)
+ description_EN = Column(utils.MediumText(), nullable=True)
+ description_RU = Column(utils.MediumText(), nullable=True)
+ verbose_name_RU = Column(String(1024), nullable=True)
+ verbose_name_EN = Column(String(1024), nullable=True, default=None)
table: Mapped[SqlaTable] = relationship(
"SqlaTable",
@@ -1022,6 +1026,10 @@ def data(self) -> dict[str, Any]:
"type_generic",
"verbose_name",
"warning_markdown",
+ "description_EN",
+ "description_RU",
+ "verbose_name_RU",
+ "verbose_name_EN",
)
return {s: getattr(self, s) for s in attrs if hasattr(self, s)}
@@ -1044,6 +1052,10 @@ class SqlMetric(AuditMixinNullable, ImportExportMixin, CertificationMixin, Model
table_id = Column(Integer, ForeignKey("tables.id", ondelete="CASCADE"))
expression = Column(utils.MediumText(), nullable=False)
extra = Column(Text)
+ description_EN = Column(utils.MediumText(), nullable=True)
+ description_RU = Column(utils.MediumText(), nullable=True)
+ verbose_name_RU = Column(String(1024), nullable=True)
+ verbose_name_EN = Column(String(1024), nullable=True, default=None)
table: Mapped[SqlaTable] = relationship(
"SqlaTable",
@@ -1119,6 +1131,10 @@ def data(self) -> dict[str, Any]:
"warning_markdown",
"warning_text",
"verbose_name",
+ "description_EN",
+ "description_RU",
+ "verbose_name_RU",
+ "verbose_name_EN",
)
return {s: getattr(self, s) for s in attrs}
diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py
index 8b459fa945a0e..2b3069714e137 100644
--- a/superset/dashboards/api.py
+++ b/superset/dashboards/api.py
@@ -209,6 +209,7 @@ def ensure_screenshots_enabled(self) -> Optional[Response]:
"created_by.id",
"created_by.last_name",
"dashboard_title",
+ "dashboard_title_RU",
"owners.id",
"owners.first_name",
"owners.last_name",
diff --git a/superset/dashboards/filters.py b/superset/dashboards/filters.py
index 9a4c496b20b31..2a21658c9ed81 100644
--- a/superset/dashboards/filters.py
+++ b/superset/dashboards/filters.py
@@ -48,6 +48,8 @@ def apply(self, query: Query, value: Any) -> Query:
or_(
Dashboard.dashboard_title.ilike(ilike_value),
Dashboard.slug.ilike(ilike_value),
+ Dashboard.dashboard_title_RU.ilike(ilike_value),
+ Dashboard.id(ilike_value),
)
)
diff --git a/superset/dashboards/schemas.py b/superset/dashboards/schemas.py
index c3c655e7e89c3..730cc68413999 100644
--- a/superset/dashboards/schemas.py
+++ b/superset/dashboards/schemas.py
@@ -45,6 +45,7 @@
},
}
dashboard_title_description = "A title for the dashboard."
+dashboard_title_ru_description = "A title RU for the dashboard."
slug_description = "Unique identifying part for the web address of the dashboard."
owners_description = (
"Owner are users ids allowed to delete or change this dashboard. "
@@ -212,6 +213,9 @@ class DashboardGetResponseSchema(Schema):
dashboard_title = fields.String(
metadata={"description": dashboard_title_description}
)
+ dashboard_title_RU = fields.String(
+ metadata={"description": dashboard_title_ru_description}
+ )
thumbnail_url = fields.String()
published = fields.Boolean()
css = fields.String(metadata={"description": css_description})
@@ -383,6 +387,11 @@ class DashboardPutSchema(BaseDashboardSchema):
allow_none=True,
validate=Length(0, 500),
)
+ dashboard_title_RU = fields.String(
+ metadata={"description": dashboard_title_ru_description},
+ allow_none=True,
+ validate=Length(0, 500),
+ )
slug = fields.String(
metadata={"description": slug_description},
allow_none=True,
diff --git a/superset/datasets/api.py b/superset/datasets/api.py
index f8f6bdc0b9604..c7e34d5135188 100644
--- a/superset/datasets/api.py
+++ b/superset/datasets/api.py
@@ -161,6 +161,8 @@ class DatasetRestApi(BaseSupersetModelRestApi):
"columns.column_name",
"columns.created_on",
"columns.description",
+ "columns.description_RU",
+ "columns.description_EN",
"columns.expression",
"columns.filterable",
"columns.groupby",
@@ -172,17 +174,23 @@ class DatasetRestApi(BaseSupersetModelRestApi):
"columns.type",
"columns.uuid",
"columns.verbose_name",
+ "columns.verbose_name_RU",
+ "columns.verbose_name_EN",
"metrics.changed_on",
"metrics.created_on",
"metrics.d3format",
"metrics.currency",
"metrics.description",
+ "metrics.description_RU",
+ "metrics.description_EN",
"metrics.expression",
"metrics.extra",
"metrics.id",
"metrics.metric_name",
"metrics.metric_type",
"metrics.verbose_name",
+ "metrics.verbose_name_RU",
+ "metrics.verbose_name_EN",
"metrics.warning_text",
"datasource_type",
"url",
diff --git a/superset/datasets/schemas.py b/superset/datasets/schemas.py
index 5b899d8402f23..0aaa5ecc311d7 100644
--- a/superset/datasets/schemas.py
+++ b/superset/datasets/schemas.py
@@ -62,7 +62,11 @@ class DatasetColumnsPutSchema(Schema):
validate=Length(1, 255),
)
verbose_name = fields.String(allow_none=True, metadata={Length: (1, 1024)})
+ verbose_name_RU = fields.String(allow_none=True, metadata={Length: (1, 1024)})
+ verbose_name_EN = fields.String(allow_none=True, metadata={Length: (1, 1024)})
description = fields.String(allow_none=True)
+ description_RU = fields.String(allow_none=True)
+ description_EN = fields.String(allow_none=True)
expression = fields.String(allow_none=True)
extra = fields.String(allow_none=True)
filterable = fields.Boolean()
@@ -79,12 +83,16 @@ class DatasetMetricsPutSchema(Schema):
id = fields.Integer()
expression = fields.String(required=True)
description = fields.String(allow_none=True)
+ description_RU = fields.String(allow_none=True)
+ description_EN = fields.String(allow_none=True)
extra = fields.String(allow_none=True)
metric_name = fields.String(required=True, validate=Length(1, 255))
metric_type = fields.String(allow_none=True, validate=Length(1, 32))
d3format = fields.String(allow_none=True, validate=Length(1, 128))
currency = fields.String(allow_none=True, required=False, validate=Length(1, 128))
verbose_name = fields.String(allow_none=True, metadata={Length: (1, 1024)})
+ verbose_name_RU = fields.String(allow_none=True, metadata={Length: (1, 1024)})
+ verbose_name_EN = fields.String(allow_none=True, metadata={Length: (1, 1024)})
warning_text = fields.String(allow_none=True)
uuid = fields.UUID(allow_none=True)
diff --git a/superset/migrations/versions/2022-06-19_16-17_f3afaf1f11f0_add_unique_name_desc_rls.py b/superset/migrations/versions/2022-06-19_16-17_f3afaf1f11f0_add_unique_name_desc_rls.py
index 34f5522ebdd79..da786b9a489c2 100644
--- a/superset/migrations/versions/2022-06-19_16-17_f3afaf1f11f0_add_unique_name_desc_rls.py
+++ b/superset/migrations/versions/2022-06-19_16-17_f3afaf1f11f0_add_unique_name_desc_rls.py
@@ -24,7 +24,7 @@
# revision identifiers, used by Alembic.
revision = "f3afaf1f11f0"
-down_revision = "e09b4ae78457"
+down_revision = "687e66be3a9d"
import sqlalchemy as sa # noqa: E402
from alembic import op # noqa: E402
diff --git a/superset/migrations/versions/2023-09-26_18-54_168cfd974dd1_required_fields_for_multilingualism.py b/superset/migrations/versions/2023-09-26_18-54_168cfd974dd1_required_fields_for_multilingualism.py
new file mode 100644
index 0000000000000..2d4019f401804
--- /dev/null
+++ b/superset/migrations/versions/2023-09-26_18-54_168cfd974dd1_required_fields_for_multilingualism.py
@@ -0,0 +1,65 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+"""required fields for multilingualism
+
+Revision ID: 168cfd974dd1
+Revises: e09b4ae78457
+Create Date: 2023-09-26 18:54:13.683355
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '168cfd974dd1'
+down_revision = 'e09b4ae78457'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('dashboards', sa.Column('selected_lang', sa.Text(), nullable=True))
+ op.add_column('dashboards', sa.Column('dashboard_title_second_lang', sa.Text(), nullable=True))
+
+ op.add_column('slices', sa.Column('extra_lang', sa.Text(), nullable=True))
+ op.add_column('slices', sa.Column('slice_name_second_lang', sa.Text(), nullable=True))
+ op.add_column('slices', sa.Column('primary_lang', sa.Text(), nullable=True))
+
+ op.add_column('sql_metrics', sa.Column('verbose_name_2nd_lang', sa.Text(), nullable=True))
+ op.add_column('sql_metrics', sa.Column('description_2nd_lang', sa.Text(), nullable=True))
+
+ op.add_column('table_columns', sa.Column('verbose_name_2nd_lang', sa.Text(), nullable=True))
+ op.add_column('table_columns', sa.Column('description_2nd_lang', sa.Text(), nullable=True))
+
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('table_columns', 'description_2nd_lang')
+ op.drop_column('table_columns', 'verbose_name_2nd_lang')
+
+ op.drop_column('sql_metrics', 'description_2nd_lang')
+ op.drop_column('sql_metrics', 'verbose_name_2nd_lang')
+
+ op.drop_column('slices', 'primary_lang')
+ op.drop_column('slices', 'slice_name_second_lang')
+ op.drop_column('slices', 'extra_lang')
+
+ op.drop_column('dashboards', 'dashboard_title_second_lang')
+ op.drop_column('dashboards', 'selected_lang')
+ # ### end Alembic commands ###
diff --git a/superset/migrations/versions/2023-10-05_09-35_049632555b84_.py b/superset/migrations/versions/2023-10-05_09-35_049632555b84_.py
new file mode 100644
index 0000000000000..34f87c2d798f0
--- /dev/null
+++ b/superset/migrations/versions/2023-10-05_09-35_049632555b84_.py
@@ -0,0 +1,52 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+"""empty message
+
+Revision ID: 049632555b84
+Revises: 4b85906e5b91
+Create Date: 2023-10-05 09:35:35.005365
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '049632555b84'
+down_revision = '168cfd974dd1'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('dashboards', sa.Column('dashboard_title_RU', sa.Text(), nullable=True))
+ op.add_column('slices', sa.Column('slice_name_RU', sa.Text(), nullable=True))
+ op.add_column('sql_metrics', sa.Column('verbose_name_RU', sa.Text(), nullable=True))
+ op.add_column('sql_metrics', sa.Column('description_RU', sa.Text(), nullable=True))
+ op.add_column('table_columns', sa.Column('verbose_name_RU', sa.Text(), nullable=True))
+ op.add_column('table_columns', sa.Column('description_RU', sa.Text(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('table_columns', 'description_RU')
+ op.drop_column('table_columns', 'verbose_name_RU')
+ op.drop_column('sql_metrics', 'description_RU')
+ op.drop_column('sql_metrics', 'verbose_name_RU')
+ op.drop_column('slices', 'slice_name_RU')
+ op.drop_column('dashboards', 'dashboard_title_RU')
+ # ### end Alembic commands ###
diff --git a/superset/migrations/versions/2023-11-17_11-36_a18f4c261e8b_.py b/superset/migrations/versions/2023-11-17_11-36_a18f4c261e8b_.py
new file mode 100644
index 0000000000000..67f566e5d231d
--- /dev/null
+++ b/superset/migrations/versions/2023-11-17_11-36_a18f4c261e8b_.py
@@ -0,0 +1,50 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+"""empty message
+
+Revision ID: a18f4c261e8b
+Revises: 049632555b84
+Create Date: 2023-11-17 11:36:09.634360
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'a18f4c261e8b'
+down_revision = '049632555b84'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('sql_metrics', sa.Column('verbose_name_EN', sa.Text(), nullable=True))
+ op.add_column('sql_metrics', sa.Column('description_EN', sa.Text(), nullable=True))
+
+ op.add_column('table_columns', sa.Column('verbose_name_EN', sa.Text(), nullable=True))
+ op.add_column('table_columns', sa.Column('description_EN', sa.Text(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('table_columns', 'description_EN')
+ op.drop_column('table_columns', 'verbose_name_EN')
+
+ op.drop_column('sql_metrics', 'description_EN')
+ op.drop_column('sql_metrics', 'verbose_name_EN')
+ # ### end Alembic commands ###
diff --git a/superset/migrations/versions/2023-11-17_13-24_687e66be3a9d_.py b/superset/migrations/versions/2023-11-17_13-24_687e66be3a9d_.py
new file mode 100644
index 0000000000000..c2669ced41924
--- /dev/null
+++ b/superset/migrations/versions/2023-11-17_13-24_687e66be3a9d_.py
@@ -0,0 +1,38 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+"""empty message
+
+Revision ID: 687e66be3a9d
+Revises: a18f4c261e8b
+Create Date: 2023-11-17 13:24:09.629107
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '687e66be3a9d'
+down_revision = 'a18f4c261e8b'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ pass
+
+
+def downgrade():
+ pass
diff --git a/superset/models/dashboard.py b/superset/models/dashboard.py
index 28d8aacc7bed9..e80db25c2e21c 100644
--- a/superset/models/dashboard.py
+++ b/superset/models/dashboard.py
@@ -133,6 +133,7 @@ class Dashboard(AuditMixinNullable, ImportExportMixin, Model):
__tablename__ = "dashboards"
id = Column(Integer, primary_key=True)
dashboard_title = Column(String(500))
+ dashboard_title_RU = Column(String(500))
position_json = Column(utils.MediumText())
description = Column(Text)
css = Column(utils.MediumText())