diff --git a/docs/static/resources/openapi.json b/docs/static/resources/openapi.json
index 8077af91c1906..cf03bff32d104 100644
--- a/docs/static/resources/openapi.json
+++ b/docs/static/resources/openapi.json
@@ -345,7 +345,7 @@
"AnnotationLayerRestApi.get_list": {
"properties": {
"changed_by": {
- "$ref": "#/components/schemas/AnnotationLayerRestApi.get_list.User"
+ "$ref": "#/components/schemas/AnnotationLayerRestApi.get_list.User1"
},
"changed_on": {
"format": "date-time",
@@ -356,7 +356,7 @@
"readOnly": true
},
"created_by": {
- "$ref": "#/components/schemas/AnnotationLayerRestApi.get_list.User1"
+ "$ref": "#/components/schemas/AnnotationLayerRestApi.get_list.User"
},
"created_on": {
"format": "date-time",
@@ -502,13 +502,13 @@
"AnnotationRestApi.get_list": {
"properties": {
"changed_by": {
- "$ref": "#/components/schemas/AnnotationRestApi.get_list.User"
+ "$ref": "#/components/schemas/AnnotationRestApi.get_list.User1"
},
"changed_on_delta_humanized": {
"readOnly": true
},
"created_by": {
- "$ref": "#/components/schemas/AnnotationRestApi.get_list.User1"
+ "$ref": "#/components/schemas/AnnotationRestApi.get_list.User"
},
"end_dttm": {
"format": "date-time",
@@ -1768,7 +1768,7 @@
"type": "string"
},
"changed_by": {
- "$ref": "#/components/schemas/ChartDataRestApi.get_list.User1"
+ "$ref": "#/components/schemas/ChartDataRestApi.get_list.User3"
},
"changed_by_name": {
"readOnly": true
@@ -1783,7 +1783,7 @@
"readOnly": true
},
"created_by": {
- "$ref": "#/components/schemas/ChartDataRestApi.get_list.User3"
+ "$ref": "#/components/schemas/ChartDataRestApi.get_list.User1"
},
"created_on_delta_humanized": {
"readOnly": true
@@ -1830,10 +1830,10 @@
"type": "string"
},
"last_saved_by": {
- "$ref": "#/components/schemas/ChartDataRestApi.get_list.User"
+ "$ref": "#/components/schemas/ChartDataRestApi.get_list.User2"
},
"owners": {
- "$ref": "#/components/schemas/ChartDataRestApi.get_list.User2"
+ "$ref": "#/components/schemas/ChartDataRestApi.get_list.User"
},
"params": {
"nullable": true,
@@ -1904,11 +1904,16 @@
"last_name": {
"maxLength": 64,
"type": "string"
+ },
+ "username": {
+ "maxLength": 64,
+ "type": "string"
}
},
"required": [
"first_name",
- "last_name"
+ "last_name",
+ "username"
],
"type": "object"
},
@@ -1918,6 +1923,10 @@
"maxLength": 64,
"type": "string"
},
+ "id": {
+ "format": "int32",
+ "type": "integer"
+ },
"last_name": {
"maxLength": 64,
"type": "string"
@@ -1942,16 +1951,11 @@
"last_name": {
"maxLength": 64,
"type": "string"
- },
- "username": {
- "maxLength": 64,
- "type": "string"
}
},
"required": [
"first_name",
- "last_name",
- "username"
+ "last_name"
],
"type": "object"
},
@@ -1961,10 +1965,6 @@
"maxLength": 64,
"type": "string"
},
- "id": {
- "format": "int32",
- "type": "integer"
- },
"last_name": {
"maxLength": 64,
"type": "string"
@@ -2560,7 +2560,7 @@
"type": "string"
},
"changed_by": {
- "$ref": "#/components/schemas/ChartRestApi.get_list.User1"
+ "$ref": "#/components/schemas/ChartRestApi.get_list.User3"
},
"changed_by_name": {
"readOnly": true
@@ -2575,7 +2575,7 @@
"readOnly": true
},
"created_by": {
- "$ref": "#/components/schemas/ChartRestApi.get_list.User3"
+ "$ref": "#/components/schemas/ChartRestApi.get_list.User1"
},
"created_on_delta_humanized": {
"readOnly": true
@@ -2622,10 +2622,10 @@
"type": "string"
},
"last_saved_by": {
- "$ref": "#/components/schemas/ChartRestApi.get_list.User"
+ "$ref": "#/components/schemas/ChartRestApi.get_list.User2"
},
"owners": {
- "$ref": "#/components/schemas/ChartRestApi.get_list.User2"
+ "$ref": "#/components/schemas/ChartRestApi.get_list.User"
},
"params": {
"nullable": true,
@@ -2696,11 +2696,16 @@
"last_name": {
"maxLength": 64,
"type": "string"
+ },
+ "username": {
+ "maxLength": 64,
+ "type": "string"
}
},
"required": [
"first_name",
- "last_name"
+ "last_name",
+ "username"
],
"type": "object"
},
@@ -2710,6 +2715,10 @@
"maxLength": 64,
"type": "string"
},
+ "id": {
+ "format": "int32",
+ "type": "integer"
+ },
"last_name": {
"maxLength": 64,
"type": "string"
@@ -2734,16 +2743,11 @@
"last_name": {
"maxLength": 64,
"type": "string"
- },
- "username": {
- "maxLength": 64,
- "type": "string"
}
},
"required": [
"first_name",
- "last_name",
- "username"
+ "last_name"
],
"type": "object"
},
@@ -2753,10 +2757,6 @@
"maxLength": 64,
"type": "string"
},
- "id": {
- "format": "int32",
- "type": "integer"
- },
"last_name": {
"maxLength": 64,
"type": "string"
@@ -3027,13 +3027,13 @@
"CssTemplateRestApi.get_list": {
"properties": {
"changed_by": {
- "$ref": "#/components/schemas/CssTemplateRestApi.get_list.User"
+ "$ref": "#/components/schemas/CssTemplateRestApi.get_list.User1"
},
"changed_on_delta_humanized": {
"readOnly": true
},
"created_by": {
- "$ref": "#/components/schemas/CssTemplateRestApi.get_list.User1"
+ "$ref": "#/components/schemas/CssTemplateRestApi.get_list.User"
},
"created_on": {
"format": "date-time",
@@ -3400,7 +3400,7 @@
"type": "string"
},
"changed_by": {
- "$ref": "#/components/schemas/DashboardRestApi.get_list.User"
+ "$ref": "#/components/schemas/DashboardRestApi.get_list.User2"
},
"changed_by_name": {
"readOnly": true
@@ -3415,7 +3415,7 @@
"readOnly": true
},
"created_by": {
- "$ref": "#/components/schemas/DashboardRestApi.get_list.User2"
+ "$ref": "#/components/schemas/DashboardRestApi.get_list.User1"
},
"created_on_delta_humanized": {
"readOnly": true
@@ -3441,7 +3441,7 @@
"type": "string"
},
"owners": {
- "$ref": "#/components/schemas/DashboardRestApi.get_list.User1"
+ "$ref": "#/components/schemas/DashboardRestApi.get_list.User"
},
"position_json": {
"nullable": true,
@@ -3489,6 +3489,10 @@
},
"DashboardRestApi.get_list.User": {
"properties": {
+ "email": {
+ "maxLength": 64,
+ "type": "string"
+ },
"first_name": {
"maxLength": 64,
"type": "string"
@@ -3507,6 +3511,7 @@
}
},
"required": [
+ "email",
"first_name",
"last_name",
"username"
@@ -3515,10 +3520,6 @@
},
"DashboardRestApi.get_list.User1": {
"properties": {
- "email": {
- "maxLength": 64,
- "type": "string"
- },
"first_name": {
"maxLength": 64,
"type": "string"
@@ -3530,17 +3531,11 @@
"last_name": {
"maxLength": 64,
"type": "string"
- },
- "username": {
- "maxLength": 64,
- "type": "string"
}
},
"required": [
- "email",
"first_name",
- "last_name",
- "username"
+ "last_name"
],
"type": "object"
},
@@ -3557,11 +3552,16 @@
"last_name": {
"maxLength": 64,
"type": "string"
+ },
+ "username": {
+ "maxLength": 64,
+ "type": "string"
}
},
"required": [
"first_name",
- "last_name"
+ "last_name",
+ "username"
],
"type": "object"
},
@@ -4056,7 +4056,7 @@
"type": "boolean"
},
"allow_run_async": {
- "description": "Operate the database in asynchronous mode, meaning that the queries are executed on remote workers as opposed to on the web server itself. This assumes that you have a Celery worker setup as well as a results backend. Refer to the installation docs for more information.",
+ "description": "Operate the database in asynchronous mode, meaning that the queries are executed on remote workers as opposed to on the web server itself. This assumes that you have a Celery worker setup as well as a results backend. Refer to the installation docs for more information.",
"type": "boolean"
},
"cache_timeout": {
@@ -4169,7 +4169,7 @@
"type": "boolean"
},
"allow_run_async": {
- "description": "Operate the database in asynchronous mode, meaning that the queries are executed on remote workers as opposed to on the web server itself. This assumes that you have a Celery worker setup as well as a results backend. Refer to the installation docs for more information.",
+ "description": "Operate the database in asynchronous mode, meaning that the queries are executed on remote workers as opposed to on the web server itself. This assumes that you have a Celery worker setup as well as a results backend. Refer to the installation docs for more information.",
"type": "boolean"
},
"cache_timeout": {
@@ -4881,7 +4881,7 @@
"type": "integer"
},
"changed_by": {
- "$ref": "#/components/schemas/DatasetRestApi.get.User"
+ "$ref": "#/components/schemas/DatasetRestApi.get.User1"
},
"changed_on": {
"format": "date-time",
@@ -4959,7 +4959,7 @@
"type": "integer"
},
"owners": {
- "$ref": "#/components/schemas/DatasetRestApi.get.User1"
+ "$ref": "#/components/schemas/DatasetRestApi.get.User"
},
"schema": {
"maxLength": 255,
@@ -5156,14 +5156,23 @@
"maxLength": 64,
"type": "string"
},
+ "id": {
+ "format": "int32",
+ "type": "integer"
+ },
"last_name": {
"maxLength": 64,
"type": "string"
+ },
+ "username": {
+ "maxLength": 64,
+ "type": "string"
}
},
"required": [
"first_name",
- "last_name"
+ "last_name",
+ "username"
],
"type": "object"
},
@@ -5173,23 +5182,14 @@
"maxLength": 64,
"type": "string"
},
- "id": {
- "format": "int32",
- "type": "integer"
- },
"last_name": {
"maxLength": 64,
"type": "string"
- },
- "username": {
- "maxLength": 64,
- "type": "string"
}
},
"required": [
"first_name",
- "last_name",
- "username"
+ "last_name"
],
"type": "object"
},
@@ -5606,6 +5606,29 @@
},
"type": "object"
},
+ "EstimateQueryCostSchema": {
+ "properties": {
+ "database_id": {
+ "format": "int32",
+ "type": "integer"
+ },
+ "schema": {
+ "nullable": true,
+ "type": "string"
+ },
+ "sql": {
+ "type": "string"
+ },
+ "template_params": {
+ "type": "object"
+ }
+ },
+ "required": [
+ "database_id",
+ "sql"
+ ],
+ "type": "object"
+ },
"ExecutePayloadSchema": {
"properties": {
"client_id": {
@@ -6933,7 +6956,7 @@
"type": "boolean"
},
"changed_by": {
- "$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User"
+ "$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User2"
},
"changed_on": {
"format": "date-time",
@@ -6949,7 +6972,7 @@
"type": "integer"
},
"created_by": {
- "$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User2"
+ "$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User1"
},
"created_on": {
"format": "date-time",
@@ -6999,7 +7022,7 @@
"type": "string"
},
"owners": {
- "$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User1"
+ "$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User"
},
"recipients": {
"$ref": "#/components/schemas/ReportScheduleRestApi.get_list.ReportRecipients"
@@ -7043,6 +7066,10 @@
"maxLength": 64,
"type": "string"
},
+ "id": {
+ "format": "int32",
+ "type": "integer"
+ },
"last_name": {
"maxLength": 64,
"type": "string"
@@ -7060,10 +7087,6 @@
"maxLength": 64,
"type": "string"
},
- "id": {
- "format": "int32",
- "type": "integer"
- },
"last_name": {
"maxLength": 64,
"type": "string"
@@ -16686,6 +16709,99 @@
]
}
},
+ "/api/v1/datasource/{datasource_type}/{datasource_id}/column/{column_name}/values/": {
+ "get": {
+ "parameters": [
+ {
+ "description": "The type of datasource",
+ "in": "path",
+ "name": "datasource_type",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "description": "The id of the datasource",
+ "in": "path",
+ "name": "datasource_id",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "description": "The name of the column to get values for",
+ "in": "path",
+ "name": "column_name",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "properties": {
+ "result": {
+ "items": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "integer"
+ },
+ {
+ "type": "number"
+ },
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "object"
+ }
+ ]
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "A List of distinct values for the column"
+ },
+ "400": {
+ "$ref": "#/components/responses/400"
+ },
+ "401": {
+ "$ref": "#/components/responses/401"
+ },
+ "403": {
+ "$ref": "#/components/responses/403"
+ },
+ "404": {
+ "$ref": "#/components/responses/404"
+ },
+ "500": {
+ "$ref": "#/components/responses/500"
+ }
+ },
+ "security": [
+ {
+ "jwt": []
+ }
+ ],
+ "summary": "Get possible values for a datasource column",
+ "tags": [
+ "Datasources"
+ ]
+ }
+ },
"/api/v1/embedded_dashboard/{uuid}": {
"get": {
"description": "Get a report schedule log",
@@ -19738,6 +19854,62 @@
]
}
},
+ "/api/v1/sqllab/estimate/": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/EstimateQueryCostSchema"
+ }
+ }
+ },
+ "description": "SQL query and params",
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "properties": {
+ "result": {
+ "type": "object"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "Query estimation result"
+ },
+ "400": {
+ "$ref": "#/components/responses/400"
+ },
+ "401": {
+ "$ref": "#/components/responses/401"
+ },
+ "403": {
+ "$ref": "#/components/responses/403"
+ },
+ "404": {
+ "$ref": "#/components/responses/404"
+ },
+ "500": {
+ "$ref": "#/components/responses/500"
+ }
+ },
+ "security": [
+ {
+ "jwt": []
+ }
+ ],
+ "summary": "Estimates the SQL query execution cost",
+ "tags": [
+ "SQL Lab"
+ ]
+ }
+ },
"/api/v1/sqllab/execute/": {
"post": {
"description": "Starts the execution of a SQL query",
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/constants.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/constants.tsx
index b940375aa9eb7..ecdee5be1f28a 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/constants.tsx
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/components/ColumnConfigControl/constants.tsx
@@ -81,7 +81,7 @@ const columnWidth: ControlFormItemSpec<'InputNumber'> = {
"Default minimal column width in pixels, actual width may still be larger than this if other columns don't need much space",
),
width: 120,
- placeholder: 'auto',
+ placeholder: t('auto'),
debounceDelay: 400,
validators: [validateNumber],
};
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js
index 0417ea3e8b5af..d97557a77b506 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js
@@ -19,7 +19,7 @@
import PropTypes from 'prop-types';
import { extent as d3Extent, range as d3Range } from 'd3-array';
import { select as d3Select } from 'd3-selection';
-import { getSequentialSchemeRegistry } from '@superset-ui/core';
+import { getSequentialSchemeRegistry, t } from '@superset-ui/core';
import CalHeatMap from './vendor/cal-heatmap';
const propTypes = {
@@ -85,10 +85,12 @@ function Calendar(element, props) {
const metricsData = data.data;
+ const METRIC_TEXT = t('Metric');
+
Object.keys(metricsData).forEach(metric => {
const calContainer = div.append('div');
if (showMetricName) {
- calContainer.text(`Metric: ${verboseMap[metric] || metric}`);
+ calContainer.text(`${METRIC_TEXT}: ${verboseMap[metric] || metric}`);
}
const timestamps = metricsData[metric];
const extents = d3Extent(Object.keys(timestamps), key => timestamps[key]);
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js
index 3320693f5cc63..760bf0ce2b0c4 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js
@@ -9,7 +9,7 @@
/* eslint-disable */
import d3tip from 'd3-tip';
-import { getContrastingColor } from '@superset-ui/core';
+import { getContrastingColor, t } from '@superset-ui/core';
var d3 = typeof require === 'function' ? require('d3') : window.d3;
@@ -256,9 +256,9 @@ var CalHeatMap = function () {
// Formatting of the title displayed when hovering a legend cell
legendTitleFormat: {
- lower: 'less than {min} {name}',
- inner: 'between {down} and {up} {name}',
- upper: 'more than {max} {name}',
+ lower: t('less than {min} {name}'),
+ inner: t('between {down} and {up} {name}'),
+ upper: t('more than {max} {name}'),
},
// Animation duration, in ms
diff --git a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/Histogram.jsx b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/Histogram.jsx
index 2c07267748614..67d5f30f9022d 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/Histogram.jsx
+++ b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/Histogram.jsx
@@ -111,7 +111,7 @@ class CustomHistogram extends React.PureComponent {
renderTooltip={({ datum, color }) => (
- {datum.bin0} to {datum.bin1}
+ {datum.bin0} {t('to')} {datum.bin1}
{t('count')}
diff --git a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js
index f967c985fcfd4..dd70afad3b36c 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js
@@ -24,6 +24,7 @@ import {
NumberFormats,
CategoricalColorNamespace,
getSequentialSchemeRegistry,
+ t,
} from '@superset-ui/core';
import wrapSvgText from './utils/wrapSvgText';
@@ -381,7 +382,10 @@ function Sunburst(element, props) {
.append('text')
.attr('class', 'path-abs-percent')
.attr('y', yOffsets[offsetIndex])
- .text(`${absolutePercString} of total`);
+ // eslint-disable-next-line prefer-template
+ .text(absolutePercString + ' ' + t('of total'));
+
+ const OF_PARENT_TEXT = t('of parent');
if (conditionalPercString) {
offsetIndex += 1;
@@ -389,7 +393,7 @@ function Sunburst(element, props) {
.append('text')
.attr('class', 'path-cond-percent')
.attr('y', yOffsets[offsetIndex])
- .text(`${conditionalPercString} of parent`);
+ .text(`${conditionalPercString} ${OF_PARENT_TEXT}`);
}
offsetIndex += 1;
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx
index 3c320b05c4a04..68b9a17345ccf 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx
@@ -50,7 +50,7 @@ const radarMetricMaxValue: { name: string; config: ControlFormItemSpec } = {
'The maximum value of metrics. It is an optional configuration',
),
width: 120,
- placeholder: 'auto',
+ placeholder: t('auto'),
debounceDelay: 400,
validators: [validateNumber],
},
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
index 3e1bb68f49d0f..ae0dd2409c1fb 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx
@@ -52,7 +52,7 @@ function createAxisTitleControl(axis: 'x' | 'y'): ControlSetRow[] {
const isXAxis = axis === 'x';
const isVertical = (controls: ControlStateMapping) =>
Boolean(controls?.orientation.value === OrientationType.vertical);
- const isHorizental = (controls: ControlStateMapping) =>
+ const isHorizontal = (controls: ControlStateMapping) =>
Boolean(controls?.orientation.value === OrientationType.horizontal);
return [
[
@@ -65,7 +65,7 @@ function createAxisTitleControl(axis: 'x' | 'y'): ControlSetRow[] {
default: '',
description: t('Changing this control takes effect instantly'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isVertical(controls) : isHorizental(controls),
+ isXAxis ? isVertical(controls) : isHorizontal(controls),
},
},
],
@@ -82,7 +82,7 @@ function createAxisTitleControl(axis: 'x' | 'y'): ControlSetRow[] {
choices: formatSelectOptions(sections.TITLE_MARGIN_OPTIONS),
description: t('Changing this control takes effect instantly'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isVertical(controls) : isHorizental(controls),
+ isXAxis ? isVertical(controls) : isHorizontal(controls),
},
},
],
@@ -96,7 +96,7 @@ function createAxisTitleControl(axis: 'x' | 'y'): ControlSetRow[] {
default: '',
description: t('Changing this control takes effect instantly'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isHorizental(controls) : isVertical(controls),
+ isXAxis ? isHorizontal(controls) : isVertical(controls),
},
},
],
@@ -113,7 +113,7 @@ function createAxisTitleControl(axis: 'x' | 'y'): ControlSetRow[] {
choices: formatSelectOptions(sections.TITLE_MARGIN_OPTIONS),
description: t('Changing this control takes effect instantly'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isHorizental(controls) : isVertical(controls),
+ isXAxis ? isHorizontal(controls) : isVertical(controls),
},
},
],
@@ -130,7 +130,7 @@ function createAxisTitleControl(axis: 'x' | 'y'): ControlSetRow[] {
choices: sections.TITLE_POSITION_OPTIONS,
description: t('Changing this control takes effect instantly'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isHorizental(controls) : isVertical(controls),
+ isXAxis ? isHorizontal(controls) : isVertical(controls),
},
},
],
@@ -141,7 +141,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
const isXAxis = axis === 'x';
const isVertical = (controls: ControlStateMapping) =>
Boolean(controls?.orientation.value === OrientationType.vertical);
- const isHorizental = (controls: ControlStateMapping) =>
+ const isHorizontal = (controls: ControlStateMapping) =>
Boolean(controls?.orientation.value === OrientationType.horizontal);
return [
[
@@ -154,7 +154,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
'When using other than adaptive formatting, labels may overlap.',
)}`,
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isVertical(controls) : isHorizental(controls),
+ isXAxis ? isVertical(controls) : isHorizontal(controls),
},
},
],
@@ -176,7 +176,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
'Input field supports custom rotation. e.g. 30 for 30°',
),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isVertical(controls) : isHorizental(controls),
+ isXAxis ? isVertical(controls) : isHorizontal(controls),
},
},
],
@@ -187,7 +187,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
...sharedControls.y_axis_format,
label: t('Axis Format'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isHorizental(controls) : isVertical(controls),
+ isXAxis ? isHorizontal(controls) : isVertical(controls),
},
},
],
@@ -201,7 +201,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
default: logAxis,
description: t('Logarithmic axis'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isHorizental(controls) : isVertical(controls),
+ isXAxis ? isHorizontal(controls) : isVertical(controls),
},
},
],
@@ -215,7 +215,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
default: minorSplitLine,
description: t('Draw split lines for minor axis ticks'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isHorizental(controls) : isVertical(controls),
+ isXAxis ? isHorizontal(controls) : isVertical(controls),
},
},
],
@@ -229,7 +229,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
renderTrigger: true,
description: t('It’s not recommended to truncate axis in Bar chart.'),
visibility: ({ controls }: ControlPanelsContainerProps) =>
- isXAxis ? isHorizental(controls) : isVertical(controls),
+ isXAxis ? isHorizontal(controls) : isVertical(controls),
},
},
],
@@ -249,7 +249,7 @@ function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
),
visibility: ({ controls }: ControlPanelsContainerProps) =>
Boolean(controls?.truncateYAxis?.value) &&
- (isXAxis ? isHorizental(controls) : isVertical(controls)),
+ (isXAxis ? isHorizontal(controls) : isVertical(controls)),
},
},
],
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
index a5feed70f21bb..d9653686e11d6 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
@@ -29,6 +29,7 @@ import {
useTheme,
isAdhocColumn,
BinaryQueryObjectFilterClause,
+ t,
} from '@superset-ui/core';
import { PivotTable, sortAs, aggregatorTemplates } from './react-pivottable';
import {
@@ -55,7 +56,7 @@ const PivotTableWrapper = styled.div`
overflow: auto;
`;
-const METRIC_KEY = 'metric';
+const METRIC_KEY = t('metric');
const vals = ['value'];
const StyledPlusSquareOutlined = styled(PlusSquareOutlined)`
diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx
index ebfc4a2536640..576325a21ddec 100644
--- a/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx
+++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.jsx
@@ -487,7 +487,9 @@ export class TableRenderer extends React.Component {
true,
)}
>
- {`Total (${this.props.aggregatorName})`}
+ {t('Total (%(aggregatorName)s)', {
+ aggregatorName: t(this.props.aggregatorName),
+ })}
) : null;
@@ -550,7 +552,9 @@ export class TableRenderer extends React.Component {
)}
>
{colAttrs.length === 0
- ? `Total (${this.props.aggregatorName})`
+ ? t('Total (%(aggregatorName)s)', {
+ aggregatorName: t(this.props.aggregatorName),
+ })
: null}
@@ -764,10 +768,9 @@ export class TableRenderer extends React.Component {
true,
)}
>
- {
- // eslint-disable-next-line prefer-template
- t('Total') + ` (${this.props.aggregatorName})`
- }
+ {t('Total (%(aggregatorName)s)', {
+ aggregatorName: t(this.props.aggregatorName),
+ })}
);
diff --git a/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/SelectPageSize.tsx b/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/SelectPageSize.tsx
index 989b121a3352a..48d4b9a66874e 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/SelectPageSize.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/DataTable/components/SelectPageSize.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import React from 'react';
+import { t } from '@superset-ui/core';
import { formatSelectOptions } from '@superset-ui/chart-controls';
export type SizeOption = [number, string];
@@ -34,7 +35,7 @@ function DefaultSelectRenderer({
}: SelectPageSizeRendererProps) {
return (
- Show{' '}
+ {t('Show')}{' '}
);
}
diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
index 3c4c74af9e10b..465f7bdbff135 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
@@ -198,7 +198,7 @@ function SelectPageSize({
}
const getNoResultsMessage = (filter: string) =>
- t(filter ? 'No matching records found' : 'No records found');
+ filter ? t('No matching records found') : t('No records found');
export default function TableChart(
props: TableChartTransformedProps & {
diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.js b/superset-frontend/src/SqlLab/actions/sqlLab.js
index 559c9866d5a9b..d93a96efe9eb9 100644
--- a/superset-frontend/src/SqlLab/actions/sqlLab.js
+++ b/superset-frontend/src/SqlLab/actions/sqlLab.js
@@ -1414,7 +1414,7 @@ export function popQuery(queryId) {
dbId: queryData.database.id,
schema: queryData.schema,
sql: queryData.sql,
- name: `Copy of ${queryData.tab_name}`,
+ name: t('Copy of %s', queryData.tab_name),
autorun: false,
};
return dispatch(addQueryEditor(queryEditorProps));
@@ -1424,6 +1424,7 @@ export function popQuery(queryId) {
}
export function popDatasourceQuery(datasourceKey, sql) {
return function (dispatch) {
+ const QUERY_TEXT = t('Query');
const datasetId = datasourceKey.split('__')[0];
return SupersetClient.get({
endpoint: `/api/v1/dataset/${datasetId}?q=(keys:!(none))`,
@@ -1431,7 +1432,7 @@ export function popDatasourceQuery(datasourceKey, sql) {
.then(({ json }) =>
dispatch(
addQueryEditor({
- name: `Query ${json.result.name}`,
+ name: `${QUERY_TEXT} ${json.result.name}`,
dbId: json.result.database.id,
schema: json.result.schema,
autorun: sql !== undefined,
diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx
index 4a8c581a82b1a..0ae092ef5efe3 100644
--- a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx
@@ -18,7 +18,7 @@
*/
import React from 'react';
import Label from 'src/components/Label';
-import { STATE_TYPE_MAP } from 'src/SqlLab/constants';
+import { STATE_TYPE_MAP, STATE_TYPE_MAP_LOCALIZED } from 'src/SqlLab/constants';
import { styled, Query } from '@superset-ui/core';
interface QueryStateLabelProps {
@@ -31,6 +31,8 @@ const StyledLabel = styled(Label)`
export default function QueryStateLabel({ query }: QueryStateLabelProps) {
return (
- {query.state}
+
+ {STATE_TYPE_MAP_LOCALIZED[query.state]}
+
);
}
diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
index 81a4e47a11368..62912d66a274d 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
@@ -360,8 +360,8 @@ const ResultSet = ({
message={t('%(rows)d rows returned', { rows })}
onClose={() => setAlertIsOpen(false)}
description={t(
- 'The number of rows displayed is limited to %s by the dropdown.',
- rows,
+ 'The number of rows displayed is limited to %(rows)d by the dropdown.',
+ { rows },
)}
/>
diff --git a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
index d8b2d5a0cfba8..0b389dfb39838 100644
--- a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
@@ -35,6 +35,7 @@ import {
STATUS_OPTIONS,
STATE_TYPE_MAP,
LOCALSTORAGE_MAX_QUERY_AGE_MS,
+ STATUS_OPTIONS_LOCALIZED,
} from '../../constants';
const TAB_HEIGHT = 140;
@@ -145,7 +146,7 @@ const SouthPane = ({
};
const renderOfflineStatus = () => (
);
diff --git a/superset-frontend/src/SqlLab/constants.ts b/superset-frontend/src/SqlLab/constants.ts
index 29b0f6cf6be0b..108ce895618a0 100644
--- a/superset-frontend/src/SqlLab/constants.ts
+++ b/superset-frontend/src/SqlLab/constants.ts
@@ -16,6 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { t } from '@superset-ui/core';
+
export const STATE_TYPE_MAP = {
offline: 'danger',
failed: 'danger',
@@ -26,6 +28,16 @@ export const STATE_TYPE_MAP = {
success: 'success',
};
+export const STATE_TYPE_MAP_LOCALIZED = {
+ offline: t('offline'),
+ failed: t('failed'),
+ pending: t('pending'),
+ fetching: t('fetching'),
+ running: t('running'),
+ stopped: t('stopped'),
+ success: t('success'),
+};
+
export const STATUS_OPTIONS = {
success: 'success',
failed: 'failed',
@@ -34,6 +46,14 @@ export const STATUS_OPTIONS = {
pending: 'pending',
};
+export const STATUS_OPTIONS_LOCALIZED = {
+ success: t('success'),
+ failed: t('failed'),
+ running: t('running'),
+ offline: t('offline'),
+ pending: t('pending'),
+};
+
export const TIME_OPTIONS = [
'now',
'1 hour ago',
diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx b/superset-frontend/src/components/DatabaseSelector/index.tsx
index 3b10e70f20901..e14bad09a89b3 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -248,7 +248,7 @@ export default function DatabaseSelector({
setSchemaOptions(options);
setLoadingSchemas(false);
if (options.length === 1) changeSchema(options[0]);
- if (refresh > 0) addSuccessToast('List refreshed');
+ if (refresh > 0) addSuccessToast(t('List refreshed'));
})
.catch(err => {
setLoadingSchemas(false);
@@ -321,6 +321,7 @@ export default function DatabaseSelector({
labelInValue
loading={loadingSchemas}
name="select-schema"
+ notFoundContent={t('No compatible schema found')}
placeholder={t('Select schema or type schema name')}
onChange={item => changeSchema(item as SchemaValue)}
options={schemaOptions}
diff --git a/superset-frontend/src/components/Datasource/ChangeDatasourceModal.tsx b/superset-frontend/src/components/Datasource/ChangeDatasourceModal.tsx
index 9875a3c1f7c30..5ec5926808da8 100644
--- a/superset-frontend/src/components/Datasource/ChangeDatasourceModal.tsx
+++ b/superset-frontend/src/components/Datasource/ChangeDatasourceModal.tsx
@@ -190,7 +190,7 @@ const ChangeDatasourceModal: FunctionComponent
= ({
);
});
onHide();
- addSuccessToast('Successfully changed dataset!');
+ addSuccessToast(t('Successfully changed dataset!'));
};
const handlerCancelConfirm = () => {
diff --git a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
index 4a484f12569b6..6f0f8a84862b1 100644
--- a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
@@ -491,7 +491,7 @@ ColumnCollectionTable.defaultProps = {
allowAddItem: false,
allowEditDataType: false,
itemGenerator: () => ({
- column_name: '',
+ column_name: t(''),
filterable: true,
groupby: true,
}),
@@ -976,8 +976,8 @@ class DatasourceEditor extends React.PureComponent {
tableColumns={['name', 'config']}
onChange={this.onDatasourcePropChange.bind(this, 'spatials')}
itemGenerator={() => ({
- name: '',
- type: '',
+ name: t(''),
+ type: t(''),
config: null,
})}
collection={spatials}
@@ -1256,7 +1256,7 @@ class DatasourceEditor extends React.PureComponent {
allowAddItem
onChange={this.onDatasourcePropChange.bind(this, 'metrics')}
itemGenerator={() => ({
- metric_name: '',
+ metric_name: t(''),
verbose_name: '',
expression: '',
})}
@@ -1418,10 +1418,10 @@ class DatasourceEditor extends React.PureComponent {
allowAddItem
allowEditDataType
itemGenerator={() => ({
- column_name: '',
+ column_name: t(''),
filterable: true,
groupby: true,
- expression: '',
+ expression: t(''),
__expanded: true,
})}
/>
diff --git a/superset-frontend/src/components/Datasource/DatasourceModal.tsx b/superset-frontend/src/components/Datasource/DatasourceModal.tsx
index 726cfaf615605..b0a0bb9c42f5f 100644
--- a/superset-frontend/src/components/Datasource/DatasourceModal.tsx
+++ b/superset-frontend/src/components/Datasource/DatasourceModal.tsx
@@ -177,6 +177,8 @@ const DatasourceModal: FunctionComponent = ({
content: renderSaveDialog(),
onOk: onConfirmSave,
icon: null,
+ okText: t('OK'),
+ cancelText: t('Cancel'),
});
};
diff --git a/superset-frontend/src/components/ListView/Filters/DateRange.tsx b/superset-frontend/src/components/ListView/Filters/DateRange.tsx
index 4dfaf11f79fdf..121131248f2d9 100644
--- a/superset-frontend/src/components/ListView/Filters/DateRange.tsx
+++ b/superset-frontend/src/components/ListView/Filters/DateRange.tsx
@@ -23,7 +23,7 @@ import React, {
useImperativeHandle,
} from 'react';
import moment, { Moment } from 'moment';
-import { styled } from '@superset-ui/core';
+import { styled, t } from '@superset-ui/core';
import { RangePicker } from 'src/components/DatePicker';
import { FormLabel } from 'src/components/Form';
import { BaseFilter, FilterHandler } from './Base';
@@ -64,6 +64,7 @@ function DateRangeFilter(
{Header}
{
diff --git a/superset-frontend/src/constants.ts b/superset-frontend/src/constants.ts
index dcef70da7e8a5..66f2f70aa795a 100644
--- a/superset-frontend/src/constants.ts
+++ b/superset-frontend/src/constants.ts
@@ -16,6 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { t } from '@superset-ui/core';
+
import { BootstrapData, CommonBootstrapData } from './types/bootstrapTypes';
export const DATETIME_WITH_TIME_ZONE = 'YYYY-MM-DD HH:mm:ssZ';
@@ -142,7 +144,7 @@ export const SLOW_DEBOUNCE = 500;
/**
* Display null as `N/A`
*/
-export const NULL_DISPLAY = 'N/A';
+export const NULL_DISPLAY = t('N/A');
export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = {
flash_messages: [],
diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js
index 9f45b986fcdba..8db1a500eee32 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -652,7 +652,10 @@ export function maxUndoHistoryToast() {
return dispatch(
addWarningToast(
- `You have used all ${historyLength} undo slots and will not be able to fully undo subsequent actions. You may save your current state to reset the history.`,
+ t(
+ 'You have used all %(historyLength)s undo slots and will not be able to fully undo subsequent actions. You may save your current state to reset the history.',
+ { historyLength },
+ ),
),
);
};
diff --git a/superset-frontend/src/dashboard/components/SaveModal.tsx b/superset-frontend/src/dashboard/components/SaveModal.tsx
index 3cbaa40ba3532..dbdcb7b55200e 100644
--- a/superset-frontend/src/dashboard/components/SaveModal.tsx
+++ b/superset-frontend/src/dashboard/components/SaveModal.tsx
@@ -81,7 +81,7 @@ class SaveModal extends React.PureComponent {
super(props);
this.state = {
saveType: props.saveType,
- newDashName: `${props.dashboardTitle} [copy]`,
+ newDashName: props.dashboardTitle + t('[copy]'),
duplicateSlices: false,
};
diff --git a/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx b/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx
index cf9bdc1a58ab2..5498b72169da4 100644
--- a/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx
+++ b/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx
@@ -18,7 +18,7 @@
*/
import React from 'react';
import cx from 'classnames';
-import { css, styled } from '@superset-ui/core';
+import { css, styled, t } from '@superset-ui/core';
import backgroundStyleOptions from 'src/dashboard/util/backgroundStyleOptions';
import PopoverDropdown, {
@@ -73,11 +73,12 @@ const BackgroundStyleOption = styled.div`
`;
function renderButton(option: OptionProps) {
+ const BACKGROUND_TEXT = t('background');
return (
- {`${option.label} background`}
+ {`${option.label} ${BACKGROUND_TEXT}`}
);
}
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx
index ed69c6734f1f6..fa0b4fbea09b2 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx
@@ -90,6 +90,7 @@ const DatasetSelect = ({ onChange, value }: DatasetSelectProps) => {
value={value}
options={loadDatasetOptions}
onChange={onChange}
+ notFoundContent={t('No compatible datasets found')}
/>
);
};
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index fcfde1f33a067..0b6a33f06932d 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -944,7 +944,9 @@ const FiltersConfigForm = (
{t('Time range')}}
- initialValue={filterToEdit?.time_range || 'No filter'}
+ initialValue={
+ filterToEdit?.time_range || t('No filter')
+ }
required={!hasAdhoc}
rules={[
{
diff --git a/superset-frontend/src/dashboard/util/newComponentFactory.js b/superset-frontend/src/dashboard/util/newComponentFactory.js
index 8fddfda83c385..bec990b4e6600 100644
--- a/superset-frontend/src/dashboard/util/newComponentFactory.js
+++ b/superset-frontend/src/dashboard/util/newComponentFactory.js
@@ -46,7 +46,7 @@ const typeToDefaultMetaData = {
},
[DIVIDER_TYPE]: null,
[HEADER_TYPE]: {
- text: 'New header',
+ text: t('New header'),
headerSize: MEDIUM_HEADER,
background: BACKGROUND_TRANSPARENT,
},
diff --git a/superset-frontend/src/explore/constants.ts b/superset-frontend/src/explore/constants.ts
index f45dbf2043594..cd038dac20ca3 100644
--- a/superset-frontend/src/explore/constants.ts
+++ b/superset-frontend/src/explore/constants.ts
@@ -56,33 +56,39 @@ export interface OperatorType {
export const OPERATOR_ENUM_TO_OPERATOR_TYPE: {
[key in Operators]: OperatorType;
} = {
- [Operators.EQUALS]: { display: 'Equal to (=)', operation: '==' },
- [Operators.NOT_EQUALS]: { display: 'Not equal to (≠)', operation: '!=' },
- [Operators.LESS_THAN]: { display: 'Less than (<)', operation: '<' },
+ [Operators.EQUALS]: { display: t('Equal to (=)'), operation: '==' },
+ [Operators.NOT_EQUALS]: { display: t('Not equal to (≠)'), operation: '!=' },
+ [Operators.LESS_THAN]: { display: t('Less than (<)'), operation: '<' },
[Operators.LESS_THAN_OR_EQUAL]: {
- display: 'Less or equal (<=)',
+ display: t('Less or equal (<=)'),
operation: '<=',
},
- [Operators.GREATER_THAN]: { display: 'Greater than (>)', operation: '>' },
+ [Operators.GREATER_THAN]: { display: t('Greater than (>)'), operation: '>' },
[Operators.GREATER_THAN_OR_EQUAL]: {
- display: 'Greater or equal (>=)',
+ display: t('Greater or equal (>=)'),
operation: '>=',
},
- [Operators.IN]: { display: 'In', operation: 'IN' },
- [Operators.NOT_IN]: { display: 'Not in', operation: 'NOT IN' },
- [Operators.LIKE]: { display: 'Like', operation: 'LIKE' },
- [Operators.ILIKE]: { display: 'Like (case insensitive)', operation: 'ILIKE' },
- [Operators.REGEX]: { display: 'Regex', operation: 'REGEX' },
- [Operators.IS_NOT_NULL]: { display: 'Is not null', operation: 'IS NOT NULL' },
- [Operators.IS_NULL]: { display: 'Is null', operation: 'IS NULL' },
+ [Operators.IN]: { display: t('In'), operation: 'IN' },
+ [Operators.NOT_IN]: { display: t('Not in'), operation: 'NOT IN' },
+ [Operators.LIKE]: { display: t('Like'), operation: 'LIKE' },
+ [Operators.ILIKE]: {
+ display: t('Like (case insensitive)'),
+ operation: 'ILIKE',
+ },
+ [Operators.REGEX]: { display: t('Regex'), operation: 'REGEX' },
+ [Operators.IS_NOT_NULL]: {
+ display: t('Is not null'),
+ operation: 'IS NOT NULL',
+ },
+ [Operators.IS_NULL]: { display: t('Is null'), operation: 'IS NULL' },
[Operators.LATEST_PARTITION]: {
- display: 'use latest_partition template',
+ display: t('use latest_partition template'),
operation: 'LATEST PARTITION',
},
- [Operators.IS_TRUE]: { display: 'Is true', operation: '==' },
- [Operators.IS_FALSE]: { display: 'Is false', operation: '==' },
+ [Operators.IS_TRUE]: { display: t('Is true'), operation: '==' },
+ [Operators.IS_FALSE]: { display: t('Is false'), operation: '==' },
[Operators.TEMPORAL_RANGE]: {
- display: 'TEMPORAL_RANGE',
+ display: t('TEMPORAL_RANGE'),
operation: 'TEMPORAL_RANGE',
},
};
diff --git a/superset-frontend/src/profile/components/RecentActivity.tsx b/superset-frontend/src/profile/components/RecentActivity.tsx
index 39d7b7d11c045..975d8cb3ddf3d 100644
--- a/superset-frontend/src/profile/components/RecentActivity.tsx
+++ b/superset-frontend/src/profile/components/RecentActivity.tsx
@@ -18,6 +18,7 @@
*/
import React from 'react';
import moment from 'moment';
+import { t } from '@superset-ui/core';
import rison from 'rison';
import TableLoader from '../../components/TableLoader';
@@ -48,6 +49,7 @@ export default function RecentActivity({ user }: RecentActivityProps) {
mutator={mutator}
sortable
dataEndpoint={`/api/v1/log/recent_activity/${user?.userId}/?q=${params}`}
+ noDataText={t('No Data')}
/>
);
diff --git a/superset-frontend/src/utils/getChartRequiredFieldsMissingMessage.ts b/superset-frontend/src/utils/getChartRequiredFieldsMissingMessage.ts
index ac11e8503dc2f..865452ade8251 100644
--- a/superset-frontend/src/utils/getChartRequiredFieldsMissingMessage.ts
+++ b/superset-frontend/src/utils/getChartRequiredFieldsMissingMessage.ts
@@ -19,8 +19,11 @@
import { t } from '@superset-ui/core';
+const CREATE_CHART_TEXT = t('Create chart');
+const UPDATE_CHART_TEXT = t('Update chart');
+
export const getChartRequiredFieldsMissingMessage = (isCreating: boolean) =>
t(
'Select values in highlighted field(s) in the control panel. Then run the query by clicking on the %s button.',
- isCreating ? '"Create chart"' : '"Update chart"',
+ `"${isCreating ? CREATE_CHART_TEXT : UPDATE_CHART_TEXT}"`,
);
diff --git a/superset-frontend/src/views/CRUD/alert/AlertList.tsx b/superset-frontend/src/views/CRUD/alert/AlertList.tsx
index 826b5519f4a1a..1c34167652460 100644
--- a/superset-frontend/src/views/CRUD/alert/AlertList.tsx
+++ b/superset-frontend/src/views/CRUD/alert/AlertList.tsx
@@ -455,7 +455,7 @@ function AlertList({
id: 'owners',
input: 'select',
operator: FilterOperator.relationManyMany,
- unfilteredLabel: 'All',
+ unfilteredLabel: t('All'),
fetchSelects: createFetchRelated(
'report',
'owners',
diff --git a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx
index 79a2d945fe858..d7f7a4c7364c3 100644
--- a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx
+++ b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx
@@ -571,7 +571,7 @@ const AlertReportModal: FunctionComponent = ({
const shouldEnableForceScreenshot = contentType === 'chart' && !isReport;
const data: any = {
...currentAlert,
- type: isReport ? 'Report' : 'Alert',
+ type: isReport ? t('Report') : t('Alert'),
force_screenshot: shouldEnableForceScreenshot || forceScreenshot,
validator_type: conditionNotNull ? 'not null' : 'operator',
validator_config_json: conditionNotNull
diff --git a/superset-frontend/src/views/CRUD/alert/ExecutionLog.tsx b/superset-frontend/src/views/CRUD/alert/ExecutionLog.tsx
index 85e248c470bcf..61b801c1c091a 100644
--- a/superset-frontend/src/views/CRUD/alert/ExecutionLog.tsx
+++ b/superset-frontend/src/views/CRUD/alert/ExecutionLog.tsx
@@ -159,13 +159,23 @@ function ExecutionLog({ addDangerToast, isReportEnabled }: ExecutionLogProps) {
[isReportEnabled],
);
const path = `/${isReportEnabled ? 'report' : 'alert'}/list/`;
+ const ALERT_TEXT = t('Alert');
+ const REPORT_TEXT = t('Report');
+
return (
<>
- {alertResource?.type} {alertResource?.name}
+ {alertResource
+ ? alertResource.type === 'Alert'
+ ? `${ALERT_TEXT}:`
+ : alertResource.type === 'Report'
+ ? `${REPORT_TEXT}:`
+ : null
+ : null}{' '}
+ {alertResource?.name}
{t('Back to all')}
diff --git a/superset-frontend/src/views/CRUD/annotation/AnnotationModal.tsx b/superset-frontend/src/views/CRUD/annotation/AnnotationModal.tsx
index 6d438c8d594ea..81225fedaa5e5 100644
--- a/superset-frontend/src/views/CRUD/annotation/AnnotationModal.tsx
+++ b/superset-frontend/src/views/CRUD/annotation/AnnotationModal.tsx
@@ -304,6 +304,7 @@ const AnnotationModal: FunctionComponent = ({
*
{t('Copy and paste the entire service account .json file here')}
diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/DatabaseConnectionForm/ValidatedInputField.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/DatabaseConnectionForm/ValidatedInputField.tsx
index 93ba1f2d1ffe9..a00b3ba9354e9 100644
--- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/DatabaseConnectionForm/ValidatedInputField.tsx
+++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/DatabaseConnectionForm/ValidatedInputField.tsx
@@ -26,14 +26,14 @@ const FIELD_TEXT_MAP = {
helpText: t(
'Copy the account name of that database you are trying to connect to.',
),
- placeholder: 'e.g. world_population',
+ placeholder: t('e.g. world_population'),
},
warehouse: {
- placeholder: 'e.g. compute_wh',
+ placeholder: t('e.g. compute_wh'),
className: 'form-group-w-50',
},
role: {
- placeholder: 'e.g. AccountAdmin',
+ placeholder: t('e.g. AccountAdmin'),
className: 'form-group-w-50',
},
};
diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/SSHTunnelForm.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/SSHTunnelForm.tsx
index a9b79a554fb4b..49dbc4e0e059f 100644
--- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/SSHTunnelForm.tsx
+++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/SSHTunnelForm.tsx
@@ -193,11 +193,11 @@ const SSHTunnelForm = ({
data-test="ssh-tunnel-password-input"
iconRender={visible =>
visible ? (
-
+
) : (
-
+
)
diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx
index 62346d16b0cf5..a721f41cbf560 100644
--- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx
+++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx
@@ -1216,7 +1216,7 @@ const DatabaseModal: FunctionComponent = ({
required
validationMethods={{ onBlur: () => {} }}
errorMessage={validationErrors?.confirm_overwrite}
- label={t(`TYPE "OVERWRITE" TO CONFIRM`)}
+ label={t('Type "%s" to confirm', t('OVERWRITE'))}
onChange={confirmOverwrite}
css={formScrollableStyles}
/>
@@ -1615,7 +1615,7 @@ const DatabaseModal: FunctionComponent = ({
antDAlertStyles(theme)}
- message="Additional fields may be required"
+ message={t('Additional fields may be required')}
showIcon
description={
<>
diff --git a/superset-frontend/src/views/CRUD/data/query/QueryList.tsx b/superset-frontend/src/views/CRUD/data/query/QueryList.tsx
index dbe8e259dacce..ff7a268f20b97 100644
--- a/superset-frontend/src/views/CRUD/data/query/QueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/query/QueryList.tsx
@@ -351,7 +351,7 @@ function QueryList({ addDangerToast }: QueryListProps) {
id: 'database',
input: 'select',
operator: FilterOperator.relationOneMany,
- unfilteredLabel: 'All',
+ unfilteredLabel: t('All'),
fetchSelects: createFetchRelated(
'query',
'database',
diff --git a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
index ca2f26e61af03..291063aa88b1a 100644
--- a/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/savedquery/SavedQueryList.tsx
@@ -427,7 +427,7 @@ function SavedQueryList({
id: 'database',
input: 'select',
operator: FilterOperator.relationOneMany,
- unfilteredLabel: 'All',
+ unfilteredLabel: t('All'),
fetchSelects: createFetchRelated(
'saved_query',
'database',
diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts
index 18d87e600dee0..80a6c4793bbed 100644
--- a/superset-frontend/src/views/CRUD/hooks.ts
+++ b/superset-frontend/src/views/CRUD/hooks.ts
@@ -420,6 +420,10 @@ export function useImportResource(
const formData = new FormData();
formData.append('formData', bundle);
+ const RE_EXPORT_TEXT = t(
+ 'Please re-export your file and try importing again',
+ );
+
/* The import bundle never contains database passwords; if required
* they should be provided by the user during import.
*/
@@ -468,7 +472,7 @@ export function useImportResource(
resourceLabel,
[
...error.errors.map(payload => payload.message),
- t('Please re-export your file and try importing again.'),
+ RE_EXPORT_TEXT,
].join('.\n'),
),
);
diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
index 702833c394331..9427c537dd564 100644
--- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
@@ -30,6 +30,13 @@ const welcomeTableLabels: Record = {
[WelcomeTable.SavedQueries]: t('saved queries'),
};
+const welcomeTableEmpty: Record = {
+ [WelcomeTable.Charts]: t('No charts yet'),
+ [WelcomeTable.Dashboards]: t('No dashboards yet'),
+ [WelcomeTable.Recents]: t('No recents yet'),
+ [WelcomeTable.SavedQueries]: t('No saved queries yet'),
+};
+
export interface EmptyStateProps {
tableName: WelcomeTable;
tab?: string;
@@ -75,11 +82,7 @@ export default function EmptyState({
[WelcomeTable.Recents]: 'union.svg',
[WelcomeTable.SavedQueries]: 'empty-queries.svg',
};
- const mine = (
-
- {t('No %(tableName)s yet', { tableName: welcomeTableLabels[tableName] })}
-
- );
+ const mine = {welcomeTableEmpty[tableName]};
const recent = (
{(() => {
diff --git a/superset/charts/post_processing.py b/superset/charts/post_processing.py
index ffd8becddea62..acfe95f391f6c 100644
--- a/superset/charts/post_processing.py
+++ b/superset/charts/post_processing.py
@@ -30,6 +30,7 @@
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
import pandas as pd
+from flask_babel import gettext as __
from superset.common.chart_data import ChartDataResultFormat
from superset.utils.core import (
@@ -68,7 +69,7 @@ def pivot_df( # pylint: disable=too-many-locals, too-many-arguments, too-many-s
show_columns_total: bool = False,
apply_metrics_on_rows: bool = False,
) -> pd.DataFrame:
- metric_name = f"Total ({aggfunc})"
+ metric_name = __("Total (%(aggfunc)s)", aggfunc=aggfunc)
if transpose_pivot:
rows, columns = columns, rows
@@ -156,7 +157,7 @@ def pivot_df( # pylint: disable=too-many-locals, too-many-arguments, too-many-s
slice_ = df.columns.get_loc(subgroup)
subtotal = pivot_v2_aggfunc_map[aggfunc](df.iloc[:, slice_], axis=1)
depth = df.columns.nlevels - len(subgroup) - 1
- total = metric_name if level == 0 else "Subtotal"
+ total = metric_name if level == 0 else __("Subtotal")
subtotal_name = tuple([*subgroup, total, *([""] * depth)])
# insert column after subgroup
df.insert(int(slice_.stop), subtotal_name, subtotal)
@@ -173,7 +174,7 @@ def pivot_df( # pylint: disable=too-many-locals, too-many-arguments, too-many-s
df.iloc[slice_, :].apply(pd.to_numeric), axis=0
)
depth = df.index.nlevels - len(subgroup) - 1
- total = metric_name if level == 0 else "Subtotal"
+ total = metric_name if level == 0 else __("Subtotal")
subtotal.name = tuple([*subgroup, total, *([""] * depth)])
# insert row after subgroup
df = pd.concat(
diff --git a/superset/connectors/base/models.py b/superset/connectors/base/models.py
index a03f088c07b7a..a5bea92ffa11e 100644
--- a/superset/connectors/base/models.py
+++ b/superset/connectors/base/models.py
@@ -22,6 +22,7 @@
from typing import Any, Dict, Hashable, List, Optional, Set, Type, TYPE_CHECKING, Union
from flask_appbuilder.security.sqla.models import User
+from flask_babel import gettext as __
from sqlalchemy import and_, Boolean, Column, Integer, String, Text
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import foreign, Query, relationship, RelationshipProperty, Session
@@ -248,10 +249,10 @@ def data(self) -> Dict[str, Any]:
for column_name in self.column_names:
column_name = str(column_name or "")
order_by_choices.append(
- (json.dumps([column_name, True]), column_name + " [asc]")
+ (json.dumps([column_name, True]), f"{column_name} " + __("[asc]"))
)
order_by_choices.append(
- (json.dumps([column_name, False]), column_name + " [desc]")
+ (json.dumps([column_name, False]), f"{column_name} " + __("[desc]"))
)
verbose_map = {"__timestamp": "Time"}
diff --git a/superset/databases/schemas.py b/superset/databases/schemas.py
index 7f9ea04d7a939..10e77584fa41b 100644
--- a/superset/databases/schemas.py
+++ b/superset/databases/schemas.py
@@ -52,7 +52,7 @@
)
expose_in_sqllab_description = "Expose this database to SQLLab"
allow_run_async_description = (
- "Operate the database in asynchronous mode, meaning "
+ "Operate the database in asynchronous mode, meaning "
"that the queries are executed on remote workers as opposed "
"to on the web server itself. "
"This assumes that you have a Celery worker setup as well "
diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py
index d90e7c43a50bf..90a888c8749f5 100644
--- a/superset/initialization/__init__.py
+++ b/superset/initialization/__init__.py
@@ -341,7 +341,7 @@ def init_views(self) -> None:
)
appbuilder.add_link(
"SQL Editor",
- label=_("SQL Lab"),
+ label=__("SQL Lab"),
href="/superset/sqllab/",
category_icon="fa-flask",
icon="fa-flask",
@@ -349,7 +349,8 @@ def init_views(self) -> None:
category_label=__("SQL"),
)
appbuilder.add_link(
- __("Saved Queries"),
+ "Saved Queries",
+ label=__("Saved Queries"),
href="/savedqueryview/list/",
icon="fa-save",
category="SQL Lab",
@@ -357,7 +358,7 @@ def init_views(self) -> None:
)
appbuilder.add_link(
"Query Search",
- label=_("Query History"),
+ label=__("Query History"),
href="/superset/sqllab/history/",
icon="fa-search",
category_icon="fa-flask",
@@ -396,7 +397,7 @@ def init_views(self) -> None:
appbuilder.add_view(
AnnotationLayerView,
"Annotation Layers",
- label=_("Annotation Layers"),
+ label=__("Annotation Layers"),
href="/annotationlayer/list/",
icon="fa-comment",
category_icon="",
diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py
index 2e5af7402fb4d..23c13629b95ed 100644
--- a/superset/models/sql_lab.py
+++ b/superset/models/sql_lab.py
@@ -26,6 +26,7 @@
from flask import current_app, Markup
from flask_appbuilder import Model
from flask_appbuilder.models.decorators import renders
+from flask_babel import gettext as __
from humanize import naturaltime
from sqlalchemy import (
Boolean,
@@ -224,10 +225,10 @@ def data(self) -> Dict[str, Any]:
for col in self.columns:
column_name = str(col.get("column_name") or "")
order_by_choices.append(
- (json.dumps([column_name, True]), column_name + " [asc]")
+ (json.dumps([column_name, True]), f"{column_name} " + __("[asc]"))
)
order_by_choices.append(
- (json.dumps([column_name, False]), column_name + " [desc]")
+ (json.dumps([column_name, False]), f"{column_name} " + __("[desc]"))
)
return {
diff --git a/superset/sql_lab.py b/superset/sql_lab.py
index c8b3bca2b1ebb..149feb163926c 100644
--- a/superset/sql_lab.py
+++ b/superset/sql_lab.py
@@ -485,7 +485,11 @@ def execute_sql_statements( # pylint: disable=too-many-arguments, too-many-loca
or (query.ctas_method == CtasMethod.TABLE and i == len(statements) - 1)
)
# Run statement
- msg = f"Running statement {i+1} out of {statement_count}"
+ msg = __(
+ "Running statement %(statement_num)s out of %(statement_count)s",
+ statement_num=i + 1,
+ statement_count=statement_count,
+ )
logger.info("Query %s: %s", str(query_id), msg)
query.set_extra_json_key("progress", msg)
session.commit()
@@ -504,7 +508,11 @@ def execute_sql_statements( # pylint: disable=too-many-arguments, too-many-loca
except Exception as ex: # pylint: disable=broad-except
msg = str(ex)
prefix_message = (
- f"[Statement {i+1} out of {statement_count}]"
+ __(
+ "Statement %(statement_num)s out of %(statement_count)s",
+ statement_num=i + 1,
+ statement_count=statement_count,
+ )
if statement_count > 1
else ""
)
diff --git a/superset/sqllab/api.py b/superset/sqllab/api.py
index aa8a0f7c4af0c..0d6196f668da3 100644
--- a/superset/sqllab/api.py
+++ b/superset/sqllab/api.py
@@ -80,6 +80,7 @@ class SqlLabRestApi(BaseSupersetApi):
}
openapi_spec_tag = "SQL Lab"
openapi_spec_component_schemas = (
+ EstimateQueryCostSchema,
ExecutePayloadSchema,
QueryExecutionResponseSchema,
)
@@ -87,13 +88,13 @@ class SqlLabRestApi(BaseSupersetApi):
@expose("/estimate/", methods=["POST"])
@protect()
@statsd_metrics
+ @requires_json
@event_logger.log_this_with_context(
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
f".estimate_query_cost",
log_to_statsd=False,
)
- @requires_json
- def estimate_query_cost(self, **kwargs: Any) -> Response:
+ def estimate_query_cost(self) -> Response:
"""Estimates the SQL query execution cost
---
post:
diff --git a/superset/sqllab/exceptions.py b/superset/sqllab/exceptions.py
index cac462585ad49..c0096d5db6b47 100644
--- a/superset/sqllab/exceptions.py
+++ b/superset/sqllab/exceptions.py
@@ -19,11 +19,11 @@
import os
from typing import Optional, TYPE_CHECKING
+from flask_babel import lazy_gettext as _
+
from superset.errors import SupersetError, SupersetErrorType
from superset.exceptions import SupersetException
-MSG_FORMAT = "Failed to execute {}"
-
if TYPE_CHECKING:
from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext
@@ -61,7 +61,10 @@ def __init__( # pylint: disable=too-many-arguments
super().__init__(self._generate_message(), exception, error_type)
def _generate_message(self) -> str:
- msg = MSG_FORMAT.format(self.sql_json_execution_context.get_query_details())
+ msg = _(
+ "Failed to execute %(query)s",
+ query=self.sql_json_execution_context.get_query_details(),
+ )
if self.failed_reason_msg:
msg = msg + self.failed_reason_msg
if self.suggestion_help_msg is not None:
diff --git a/superset/utils/date_parser.py b/superset/utils/date_parser.py
index 00e6c7a2b442f..7e79c72f1eb78 100644
--- a/superset/utils/date_parser.py
+++ b/superset/utils/date_parser.py
@@ -178,7 +178,7 @@ def get_since_until( # pylint: disable=too-many-arguments,too-many-locals,too-m
_relative_start = relative_start if relative_start else "today"
_relative_end = relative_end if relative_end else "today"
- if time_range == NO_TIME_RANGE:
+ if time_range == NO_TIME_RANGE or time_range == _(NO_TIME_RANGE):
return None, None
if time_range and time_range.startswith("Last") and separator not in time_range:
diff --git a/superset/utils/encrypt.py b/superset/utils/encrypt.py
index bd78b10f7fcab..0c230c6cd9248 100644
--- a/superset/utils/encrypt.py
+++ b/superset/utils/encrypt.py
@@ -19,6 +19,7 @@
from typing import Any, Dict, List, Optional
from flask import Flask
+from flask_babel import lazy_gettext as _
from sqlalchemy import text, TypeDecorator
from sqlalchemy.engine import Connection, Dialect, Row
from sqlalchemy_utils import EncryptedType
@@ -109,7 +110,13 @@ def _read_bytes(col_name: str, value: Any) -> Optional[bytes]:
return bytes(value.encode("utf8"))
# Just bail if we haven't seen this type before...
- raise ValueError(f"DB column {col_name} has unknown type: {type(value)}")
+ raise ValueError(
+ _(
+ "DB column %(col_name)s has unknown type: %(value_type)s",
+ col_name=col_name,
+ value_type=type(value),
+ )
+ )
@staticmethod
def _select_columns_from_table(
diff --git a/superset/views/api.py b/superset/views/api.py
index bde25236460da..fc7c5b76d26c1 100644
--- a/superset/views/api.py
+++ b/superset/views/api.py
@@ -23,6 +23,7 @@
from flask_appbuilder import expose
from flask_appbuilder.api import rison
from flask_appbuilder.security.decorators import has_access_api
+from flask_babel import lazy_gettext as _
from superset import db, event_logger
from superset.charts.commands.exceptions import (
@@ -105,7 +106,7 @@ def time_range(self, **kwargs: Any) -> FlaskResponse:
}
return self.json_response({"result": result})
except (ValueError, TimeRangeParseFailError, TimeRangeAmbiguousError) as error:
- error_msg = {"message": f"Unexpected time range: {error}"}
+ error_msg = {"message": _("Unexpected time range: %s" % error)}
return self.json_response(error_msg, 400)
def get_query_context_factory(self) -> QueryContextFactory:
diff --git a/superset/views/core.py b/superset/views/core.py
index 8d632dcde21bf..153194af7fbd6 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -2048,6 +2048,7 @@ def extra_table_metadata( # pylint: disable=no-self-use
@expose("/estimate_query_cost//", methods=["POST"])
@expose("/estimate_query_cost///", methods=["POST"])
@event_logger.log_this
+ @deprecated()
def estimate_query_cost( # pylint: disable=no-self-use
self, database_id: int, schema: Optional[str] = None
) -> FlaskResponse:
diff --git a/superset/views/dashboard/mixin.py b/superset/views/dashboard/mixin.py
index 43e4df1c0af3d..d8838b26c9d30 100644
--- a/superset/views/dashboard/mixin.py
+++ b/superset/views/dashboard/mixin.py
@@ -66,7 +66,7 @@ class DashboardMixin: # pylint: disable=too-few-public-methods
"roles": _(
"Roles is a list which defines access to the dashboard. "
"Granting a role access to a dashboard will bypass dataset level checks."
- "If no roles defined then the dashboard is available to all roles."
+ "If no roles are defined then the dashboard is available to all roles."
),
"published": _(
"Determines whether or not this dashboard is "
diff --git a/superset/views/database/views.py b/superset/views/database/views.py
index 62f7d45d3ab62..5f94fe6fc8737 100644
--- a/superset/views/database/views.py
+++ b/superset/views/database/views.py
@@ -25,7 +25,7 @@
from flask_appbuilder import expose, SimpleFormView
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_appbuilder.security.decorators import has_access
-from flask_babel import gettext as __, lazy_gettext as _
+from flask_babel import lazy_gettext as _
from werkzeug.wrappers import Response
from wtforms.fields import StringField
from wtforms.validators import ValidationError
@@ -176,7 +176,7 @@ def form_post(self, form: CsvToDatabaseForm) -> Response:
delimiter_input = form.delimiter.data
if not schema_allows_file_upload(database, csv_table.schema):
- message = __(
+ message = _(
'Database "%(database_name)s" schema "%(schema_name)s" '
"is not allowed for csv uploads. Please contact your Superset Admin.",
database_name=database.database_name,
@@ -265,7 +265,7 @@ def form_post(self, form: CsvToDatabaseForm) -> Response:
db.session.commit()
except Exception as ex: # pylint: disable=broad-except
db.session.rollback()
- message = __(
+ message = _(
'Unable to upload CSV file "%(filename)s" to table '
'"%(table_name)s" in database "%(db_name)s". '
"Error message: %(error_msg)s",
@@ -280,7 +280,7 @@ def form_post(self, form: CsvToDatabaseForm) -> Response:
return redirect("/csvtodatabaseview/form")
# Go back to welcome page / splash screen
- message = __(
+ message = _(
'CSV file "%(csv_filename)s" uploaded to table "%(table_name)s" in '
'database "%(db_name)s"',
csv_filename=form.csv_file.data.filename,
@@ -315,7 +315,7 @@ def form_post(self, form: ExcelToDatabaseForm) -> Response:
excel_table = Table(table=form.name.data, schema=form.schema.data)
if not schema_allows_file_upload(database, excel_table.schema):
- message = __(
+ message = _(
'Database "%(database_name)s" schema "%(schema_name)s" '
"is not allowed for excel uploads. Please contact your Superset Admin.",
database_name=database.database_name,
@@ -402,7 +402,7 @@ def form_post(self, form: ExcelToDatabaseForm) -> Response:
db.session.commit()
except Exception as ex: # pylint: disable=broad-except
db.session.rollback()
- message = __(
+ message = _(
'Unable to upload Excel file "%(filename)s" to table '
'"%(table_name)s" in database "%(db_name)s". '
"Error message: %(error_msg)s",
@@ -417,7 +417,7 @@ def form_post(self, form: ExcelToDatabaseForm) -> Response:
return redirect("/exceltodatabaseview/form")
# Go back to welcome page / splash screen
- message = __(
+ message = _(
'Excel file "%(excel_filename)s" uploaded to table "%(table_name)s" in '
'database "%(db_name)s"',
excel_filename=form.excel_file.data.filename,
@@ -462,7 +462,7 @@ def form_post( # pylint: disable=too-many-locals
]
if len(file_type) > 1:
- message = __(
+ message = _(
"Multiple file extensions are not allowed for columnar uploads."
" Please make sure all files are of the same extension.",
)
@@ -475,7 +475,7 @@ def form_post( # pylint: disable=too-many-locals
}
if not schema_allows_file_upload(database, columnar_table.schema):
- message = __(
+ message = _(
'Database "%(database_name)s" schema "%(schema_name)s" '
"is not allowed for columnar uploads. "
"Please contact your Superset Admin.",
@@ -543,7 +543,7 @@ def form_post( # pylint: disable=too-many-locals
db.session.commit()
except Exception as ex: # pylint: disable=broad-except
db.session.rollback()
- message = __(
+ message = _(
'Unable to upload Columnar file "%(filename)s" to table '
'"%(table_name)s" in database "%(db_name)s". '
"Error message: %(error_msg)s",
@@ -558,7 +558,7 @@ def form_post( # pylint: disable=too-many-locals
return redirect("/columnartodatabaseview/form")
# Go back to welcome page / splash screen
- message = __(
+ message = _(
'Columnar file "%(columnar_filename)s" uploaded to table "%(table_name)s" '
'in database "%(db_name)s"',
columnar_filename=[file.filename for file in form.columnar_file.data],
diff --git a/superset/views/sql_lab/views.py b/superset/views/sql_lab/views.py
index 509ff4211aacc..99b57fe195bb5 100644
--- a/superset/views/sql_lab/views.py
+++ b/superset/views/sql_lab/views.py
@@ -145,7 +145,7 @@ def post(self) -> FlaskResponse: # pylint: disable=no-self-use
user_id=get_user_id(),
# This is for backward compatibility
label=query_editor.get("name")
- or query_editor.get("title", "Untitled Query"),
+ or query_editor.get("title", _("Untitled Query")),
active=True,
database_id=query_editor["dbId"],
schema=query_editor.get("schema"),
diff --git a/superset/viz.py b/superset/viz.py
index c4582925da120..1f4c795325b4b 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -3224,7 +3224,7 @@ def get_data(self, df: pd.DataFrame) -> VizData:
groups = get_column_names(self.form_data.get("groupby"))
time_op = self.form_data.get("time_series_option", "not_time")
if not groups:
- raise ValueError("Please choose at least one groupby")
+ raise ValueError(_("Please choose at least one groupby"))
if time_op == "not_time":
levels = self.levels_for("agg_sum", groups, df)
elif time_op in ["agg_sum", "agg_mean"]:
diff --git a/tests/unit_tests/charts/test_post_processing.py b/tests/unit_tests/charts/test_post_processing.py
index f63ee5d66af9c..be28aba922037 100644
--- a/tests/unit_tests/charts/test_post_processing.py
+++ b/tests/unit_tests/charts/test_post_processing.py
@@ -18,6 +18,7 @@
import json
import pandas as pd
+from flask_babel import lazy_gettext as _
from numpy import True_
from pytest import raises
from sqlalchemy.orm.session import Session
@@ -57,10 +58,10 @@ def test_pivot_df_no_cols_no_rows_single_metric():
)
assert (
pivoted.to_markdown()
- == """
+ == f"""
| | ('SUM(num)',) |
|:-----------------|----------------:|
-| ('Total (Sum)',) | 8.06797e+07 |
+| ('{_("Total")} (Sum)',) | 8.06797e+07 |
""".strip()
)
@@ -79,10 +80,10 @@ def test_pivot_df_no_cols_no_rows_single_metric():
)
assert (
pivoted.to_markdown()
- == """
+ == f"""
| | ('SUM(num)',) |
|:-----------------|----------------:|
-| ('Total (Sum)',) | 8.06797e+07 |
+| ('{_("Total")} (Sum)',) | 8.06797e+07 |
""".strip()
)
@@ -102,8 +103,8 @@ def test_pivot_df_no_cols_no_rows_single_metric():
)
assert (
pivoted.to_markdown()
- == """
-| | ('Total (Sum)',) |
+ == f"""
+| | ('{_("Total")} (Sum)',) |
|:--------------|-------------------:|
| ('SUM(num)',) | 8.06797e+07 |
""".strip()
@@ -124,10 +125,10 @@ def test_pivot_df_no_cols_no_rows_single_metric():
)
assert (
pivoted.to_markdown()
- == """
+ == f"""
| | ('SUM(num)',) | ('Total (Sum)',) |
|:-----------------|----------------:|-------------------:|
-| ('Total (Sum)',) | 8.06797e+07 | 8.06797e+07 |
+| ('{_("Total")} (Sum)',) | 8.06797e+07 | 8.06797e+07 |
""".strip()
)
@@ -162,10 +163,10 @@ def test_pivot_df_no_cols_no_rows_two_metrics():
)
assert (
pivoted.to_markdown()
- == """
+ == f"""
| | ('SUM(num)',) | ('MAX(num)',) |
|:-----------------|----------------:|----------------:|
-| ('Total (Sum)',) | 8.06797e+07 | 37296 |
+| ('{_("Total")} (Sum)',) | 8.06797e+07 | 37296 |
""".strip()
)
@@ -207,8 +208,8 @@ def test_pivot_df_no_cols_no_rows_two_metrics():
)
assert (
pivoted.to_markdown()
- == """
-| | ('Total (Sum)',) |
+ == f"""
+| | ('{_("Total")} (Sum)',) |
|:--------------|-------------------:|
| ('SUM(num)',) | 8.06797e+07 |
| ('MAX(num)',) | 37296 |
@@ -231,10 +232,10 @@ def test_pivot_df_no_cols_no_rows_two_metrics():
)
assert (
pivoted.to_markdown()
- == """
-| | ('SUM(num)',) | ('MAX(num)',) | ('Total (Sum)',) |
+ == f"""
+| | ('SUM(num)',) | ('MAX(num)',) | ('{_("Total")} (Sum)',) |
|:-----------------|----------------:|----------------:|-------------------:|
-| ('Total (Sum)',) | 8.06797e+07 | 37296 | 8.0717e+07 |
+| ('{_("Total")} (Sum)',) | 8.06797e+07 | 37296 | 8.0717e+07 |
""".strip()
)
@@ -297,10 +298,10 @@ def test_pivot_df_single_row_two_metrics():
)
assert (
pivoted.to_markdown()
- == """
+ == f"""
| | ('SUM(num)', 'boy') | ('SUM(num)', 'girl') | ('MAX(num)', 'boy') | ('MAX(num)', 'girl') |
|:-----------------|----------------------:|-----------------------:|----------------------:|-----------------------:|
-| ('Total (Sum)',) | 47123 | 118065 | 1280 | 2588 |
+| ('{_("Total")} (Sum)',) | 47123 | 118065 | 1280 | 2588 |
""".strip()
)
@@ -342,12 +343,12 @@ def test_pivot_df_single_row_two_metrics():
)
assert (
pivoted.to_markdown()
- == """
-| | ('SUM(num)',) | ('MAX(num)',) | ('Total (Sum)',) |
+ == f"""
+| | ('SUM(num)',) | ('MAX(num)',) | ('{_("Total")} (Sum)',) |
|:-----------------|----------------:|----------------:|-------------------:|
| ('boy',) | 47123 | 1280 | 48403 |
| ('girl',) | 118065 | 2588 | 120653 |
-| ('Total (Sum)',) | 165188 | 3868 | 169056 |
+| ('{_("Total")} (Sum)',) | 165188 | 3868 | 169056 |
""".strip()
)
@@ -366,8 +367,8 @@ def test_pivot_df_single_row_two_metrics():
)
assert (
pivoted.to_markdown()
- == """
-| | ('Total (Sum)',) |
+ == f"""
+| | ('{_("Total")} (Sum)',) |
|:-------------------------|-------------------:|
| ('SUM(num)', 'boy') | 47123 |
| ('SUM(num)', 'girl') | 118065 |
@@ -375,7 +376,7 @@ def test_pivot_df_single_row_two_metrics():
| ('MAX(num)', 'boy') | 1280 |
| ('MAX(num)', 'girl') | 2588 |
| ('MAX(num)', 'Subtotal') | 3868 |
-| ('Total (Sum)', '') | 169056 |
+| ('{_("Total")} (Sum)', '') | 169056 |
""".strip()
)
@@ -394,8 +395,8 @@ def test_pivot_df_single_row_two_metrics():
)
assert (
pivoted.to_markdown()
- == """
-| | ('Total (Sum)',) |
+ == f"""
+| | ('{_("Total")} (Sum)',) |
|:---------------------|-------------------:|
| ('boy', 'SUM(num)') | 47123 |
| ('boy', 'MAX(num)') | 1280 |
@@ -403,7 +404,7 @@ def test_pivot_df_single_row_two_metrics():
| ('girl', 'SUM(num)') | 118065 |
| ('girl', 'MAX(num)') | 2588 |
| ('girl', 'Subtotal') | 120653 |
-| ('Total (Sum)', '') | 169056 |
+| ('{_("Total")} (Sum)', '') | 169056 |
""".strip()
)