mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
fix(i18n): wrap untranslated frontend strings and add i18n lint rule (#37776)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -288,11 +288,11 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => {
|
||||
{schema && <Breadcrumb.Item>{schema}</Breadcrumb.Item>}
|
||||
<Breadcrumb.Item> </Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div style={{ display: 'none' }}>
|
||||
<div style={{ display: 'none' }} aria-hidden="true">
|
||||
<CopyToClipboard
|
||||
copyNode={
|
||||
<button type="button" ref={copyStatementActionRef}>
|
||||
invisible button
|
||||
{t('Copy')}
|
||||
</button>
|
||||
}
|
||||
text={tableData.selectStar}
|
||||
@@ -305,7 +305,7 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => {
|
||||
title={t('CREATE VIEW statement')}
|
||||
triggerNode={
|
||||
<button type="button" ref={showViewStatementActionRef}>
|
||||
invisible button
|
||||
{t('Show SQL')}
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -375,7 +375,7 @@ export default class CRUDCollection extends PureComponent<
|
||||
`}
|
||||
>
|
||||
<Icons.DeleteOutlined
|
||||
aria-label="Delete item"
|
||||
aria-label={t('Delete item')}
|
||||
className="pointer"
|
||||
data-test="crud-delete-icon"
|
||||
role="button"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { Icons } from '@superset-ui/core/components';
|
||||
import { t } from '@apache-superset/core';
|
||||
import { useTheme } from '@apache-superset/core/ui';
|
||||
|
||||
interface IssueCodeProps {
|
||||
@@ -33,7 +34,7 @@ export function IssueCode({ code, message }: IssueCodeProps) {
|
||||
href={`https://superset.apache.org/docs/using-superset/issue-codes#issue-${code}`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
aria-label="Superset docs link"
|
||||
aria-label={t('Superset docs link')}
|
||||
>
|
||||
<Icons.Full iconSize="m" iconColor={theme.colorPrimary} />
|
||||
</a>
|
||||
|
||||
@@ -146,24 +146,23 @@ export function OAuth2RedirectMessage({
|
||||
|
||||
const body = (
|
||||
<p>
|
||||
This database uses OAuth2 for authentication. Please click the link above
|
||||
to grant Apache Superset permission to access the data. Your personal
|
||||
access token will be stored encrypted and used only for queries run by
|
||||
you.
|
||||
{t(
|
||||
'This database uses OAuth2 for authentication. Please click the link above to grant Apache Superset permission to access the data. Your personal access token will be stored encrypted and used only for queries run by you.',
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
const subtitle = (
|
||||
<>
|
||||
You need to{' '}
|
||||
{t('You need to')}{' '}
|
||||
<a
|
||||
href={extra.url}
|
||||
onClick={handleOAuthClick}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
provide authorization
|
||||
{t('provide authorization')}
|
||||
</a>{' '}
|
||||
in order to run this operation.
|
||||
{t('in order to run this operation.')}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { styled, css, SupersetTheme, useTheme } from '@apache-superset/core/ui';
|
||||
import { t } from '@apache-superset/core';
|
||||
import cx from 'classnames';
|
||||
import { Interweave } from 'interweave';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
@@ -152,7 +153,7 @@ export default function Toast({ toast, onCloseToast }: ToastPresenterProps) {
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClosePress}
|
||||
aria-label="Close"
|
||||
aria-label={t('Close')}
|
||||
data-test="close-button"
|
||||
/>
|
||||
</ToastContainer>
|
||||
|
||||
@@ -212,7 +212,7 @@ export const DashboardEmbedControls = ({ dashboardId, onHide }: Props) => {
|
||||
<Input
|
||||
id="allowed-domains"
|
||||
value={allowedDomains}
|
||||
placeholder="superset.example.com"
|
||||
placeholder={t('superset.example.com')}
|
||||
onChange={event => setAllowedDomains(event.target.value)}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
@@ -583,7 +583,7 @@ const SliceHeaderControls = (
|
||||
<Button
|
||||
id={`slice_${slice.slice_id}-controls`}
|
||||
buttonStyle="link"
|
||||
aria-label="More Options"
|
||||
aria-label={t('More Options')}
|
||||
aria-haspopup="true"
|
||||
css={theme => css`
|
||||
padding: ${theme.sizeUnit * 2}px;
|
||||
|
||||
@@ -114,7 +114,7 @@ export default function URLShortLinkButton({
|
||||
}
|
||||
/>
|
||||
|
||||
<Typography.Link href={emailLink} aria-label="Email link">
|
||||
<Typography.Link href={emailLink} aria-label={t('Email link')}>
|
||||
<Icons.MailOutlined iconSize="m" iconColor={theme.colorPrimary} />
|
||||
</Typography.Link>
|
||||
</div>
|
||||
|
||||
@@ -142,7 +142,7 @@ test('Popover opens with "Vertical" selected', async () => {
|
||||
|
||||
const verticalItem = screen.getByText('Vertical (Left)');
|
||||
expect(
|
||||
within(verticalItem.closest('li')!).getByLabelText('check'),
|
||||
within(verticalItem.closest('li')!).getByLabelText('Selected'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ test('Popover opens with "Horizontal" selected', async () => {
|
||||
|
||||
const horizontalItem = screen.getByText('Horizontal (Top)');
|
||||
expect(
|
||||
within(horizontalItem.closest('li')!).getByLabelText('check'),
|
||||
within(horizontalItem.closest('li')!).getByLabelText('Selected'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -182,7 +182,7 @@ test('On selection change, send request and update checked value', async () => {
|
||||
|
||||
const verticalItem = await screen.findByText('Vertical (Left)');
|
||||
expect(
|
||||
within(verticalItem.closest('li')!).getByLabelText('check'),
|
||||
within(verticalItem.closest('li')!).getByLabelText('Selected'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
userEvent.click(screen.getByText('Horizontal (Top)'));
|
||||
@@ -192,7 +192,7 @@ test('On selection change, send request and update checked value', async () => {
|
||||
|
||||
const horizontalItem = await screen.findByText('Horizontal (Top)');
|
||||
expect(
|
||||
within(horizontalItem.closest('li')!).getByLabelText('check'),
|
||||
within(horizontalItem.closest('li')!).getByLabelText('Selected'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
await waitFor(() =>
|
||||
@@ -211,10 +211,10 @@ test('On selection change, send request and update checked value', async () => {
|
||||
userEvent.hover(screen.getByText('Orientation of filter bar'));
|
||||
const updatedHorizontalItem = screen.getByText('Horizontal (Top)');
|
||||
expect(
|
||||
within(updatedHorizontalItem.closest('li')!).getByLabelText('check'),
|
||||
within(updatedHorizontalItem.closest('li')!).getByLabelText('Selected'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(verticalItem.closest('li')!).queryByLabelText('check'),
|
||||
within(verticalItem.closest('li')!).queryByLabelText('Selected'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -241,10 +241,10 @@ test('On failed request, restore previous selection', async () => {
|
||||
|
||||
// Verify initial state
|
||||
expect(
|
||||
within(verticalItem.closest('li')!).getByLabelText('check'),
|
||||
within(verticalItem.closest('li')!).getByLabelText('Selected'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(horizontalItem.closest('li')!).queryByLabelText('check'),
|
||||
within(horizontalItem.closest('li')!).queryByLabelText('Selected'),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// Click horizontal option
|
||||
@@ -266,10 +266,10 @@ test('On failed request, restore previous selection', async () => {
|
||||
const verticalItemAfter = screen.getByText('Vertical (Left)');
|
||||
const horizontalItemAfter = screen.getByText('Horizontal (Top)');
|
||||
expect(
|
||||
within(verticalItemAfter.closest('li')!).getByLabelText('check'),
|
||||
within(verticalItemAfter.closest('li')!).getByLabelText('Selected'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(horizontalItemAfter.closest('li')!).queryByLabelText('check'),
|
||||
within(horizontalItemAfter.closest('li')!).queryByLabelText('Selected'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -206,7 +206,7 @@ const FilterBarSettings = () => {
|
||||
<Icons.CheckOutlined
|
||||
iconColor={theme.colorPrimary}
|
||||
iconSize="m"
|
||||
aria-label="check"
|
||||
aria-label={t('Selected')}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
@@ -224,7 +224,7 @@ const FilterBarSettings = () => {
|
||||
css={css`
|
||||
vertical-align: middle;
|
||||
`}
|
||||
aria-label="check"
|
||||
aria-label={t('Selected')}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
|
||||
@@ -163,7 +163,7 @@ const ConfigModalSidebar: FC<ConfigModalSidebarProps> = ({
|
||||
onRemove={onRemove}
|
||||
restoreItem={restoreItem}
|
||||
dataTestId="filter-title-container"
|
||||
deleteAltText="RemoveFilter"
|
||||
deleteAltText={t('Remove filter')}
|
||||
dragType={FILTER_TYPE}
|
||||
isCurrentSection={isFilterId(currentItemId)}
|
||||
onCrossListDrop={handleFilterCrossListDrop}
|
||||
@@ -185,7 +185,7 @@ const ConfigModalSidebar: FC<ConfigModalSidebarProps> = ({
|
||||
onRemove={onRemove}
|
||||
restoreItem={restoreItem}
|
||||
dataTestId="customization-title-container"
|
||||
deleteAltText="RemoveCustomization"
|
||||
deleteAltText={t('Remove customization')}
|
||||
dragType={CUSTOMIZATION_TYPE}
|
||||
isCurrentSection={isChartCustomizationId(currentItemId)}
|
||||
onCrossListDrop={handleCustomizationCrossListDrop}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { t } from '@apache-superset/core';
|
||||
import { styled } from '@apache-superset/core/ui';
|
||||
import { useRef, FC } from 'react';
|
||||
import {
|
||||
@@ -155,7 +156,7 @@ export const DraggableFilter: FC<FilterTabTitleProps> = ({
|
||||
<Container ref={ref} isDragging={isDragging}>
|
||||
<DragIcon
|
||||
isDragging={isDragging}
|
||||
alt="Move icon"
|
||||
alt={t('Move')}
|
||||
className="dragIcon"
|
||||
viewBox="4 4 16 16"
|
||||
/>
|
||||
|
||||
@@ -88,7 +88,7 @@ test('drag and drop', async () => {
|
||||
test('remove filter', async () => {
|
||||
defaultRender();
|
||||
// First trash icon
|
||||
const removeFilterIcon = document.querySelector("[alt='RemoveFilter']")!;
|
||||
const removeFilterIcon = document.querySelector("[alt='Remove filter']")!;
|
||||
userEvent.click(removeFilterIcon);
|
||||
expect(defaultProps.onRemove).toHaveBeenCalledWith('NATIVE_FILTER-1');
|
||||
});
|
||||
|
||||
@@ -161,7 +161,7 @@ const FilterTitleContainer = forwardRef<HTMLDivElement, Props>(
|
||||
event.stopPropagation();
|
||||
onRemove(id);
|
||||
}}
|
||||
alt="RemoveFilter"
|
||||
alt={t('Remove filter')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -105,7 +105,7 @@ const EmbedCodeContent: FC<EmbedCodeContentProps> = ({
|
||||
shouldShowText={false}
|
||||
text={html}
|
||||
copyNode={
|
||||
<span role="button" aria-label="Copy to clipboard">
|
||||
<span role="button" aria-label={t('Copy to clipboard')}>
|
||||
<Icons.CopyOutlined />
|
||||
</span>
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ function PropertiesModal({
|
||||
bottomSpacing={false}
|
||||
>
|
||||
<Input
|
||||
aria-label="Cache timeout"
|
||||
aria-label={t('Cache timeout')}
|
||||
value={cacheTimeout}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
||||
setCacheTimeout(event.target.value ?? '')
|
||||
|
||||
@@ -616,7 +616,7 @@ class SaveModal extends Component<SaveModalProps, SaveModalState> {
|
||||
<Input
|
||||
name="new_slice_name"
|
||||
type="text"
|
||||
placeholder="Name"
|
||||
placeholder={t('Name')}
|
||||
value={this.state.newSliceName}
|
||||
onChange={this.onSliceNameChange}
|
||||
data-test="new-chart-name"
|
||||
@@ -631,7 +631,7 @@ class SaveModal extends Component<SaveModalProps, SaveModalState> {
|
||||
<Input
|
||||
name="dataset_name"
|
||||
type="text"
|
||||
placeholder="Dataset Name"
|
||||
placeholder={t('Dataset Name')}
|
||||
value={this.state.datasetName}
|
||||
onChange={this.handleDatasetNameChange}
|
||||
data-test="new-dataset-name"
|
||||
|
||||
@@ -1051,7 +1051,7 @@ class AnnotationLayer extends PureComponent<
|
||||
<>
|
||||
{this.props.error && (
|
||||
<span style={{ color: this.props.theme.colorError }}>
|
||||
ERROR: {this.props.error}
|
||||
{t('ERROR')}: {this.props.error}
|
||||
</span>
|
||||
)}
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
|
||||
@@ -206,7 +206,7 @@ class AnnotationLayerControl extends PureComponent<Props, PopoverState> {
|
||||
);
|
||||
}
|
||||
if (!anno.show) {
|
||||
return <span style={{ color: theme.colorError }}> Hidden </span>;
|
||||
return <span style={{ color: theme.colorError }}> {t('Hidden')} </span>;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ test('Should have remove button', async () => {
|
||||
test('Should have SortableDragger icon', async () => {
|
||||
const props = createProps();
|
||||
render(<CollectionControl {...props} />);
|
||||
expect(await screen.findByLabelText('drag')).toBeVisible();
|
||||
expect(await screen.findByLabelText('Drag to reorder')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Should call Control component', async () => {
|
||||
|
||||
@@ -72,7 +72,7 @@ const SortableList = SortableContainer(List);
|
||||
const SortableDragger = SortableHandle(() => (
|
||||
<Icons.MenuOutlined
|
||||
role="img"
|
||||
aria-label="drag"
|
||||
aria-label={t('Drag to reorder')}
|
||||
className="text-primary"
|
||||
style={{ cursor: 'ns-resize' }}
|
||||
/>
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
useState,
|
||||
MouseEvent as ReactMouseEvent,
|
||||
} from 'react';
|
||||
|
||||
import { t } from '@apache-superset/core';
|
||||
import { throttle } from 'lodash';
|
||||
import {
|
||||
POPOVER_INITIAL_HEIGHT,
|
||||
@@ -135,7 +135,7 @@ export default function useResizeButton(
|
||||
return [
|
||||
<Icons.ArrowsAltOutlined
|
||||
role="button"
|
||||
aria-label="Resize"
|
||||
aria-label={t('Resize')}
|
||||
tabIndex={0}
|
||||
onMouseDown={onDragDown}
|
||||
className="edit-popover-resize"
|
||||
|
||||
@@ -470,7 +470,7 @@ export default class AdhocFilterEditPopover extends Component<
|
||||
</Button>
|
||||
<Icons.ArrowsAltOutlined
|
||||
role="button"
|
||||
aria-label="Resize"
|
||||
aria-label={t('Resize')}
|
||||
tabIndex={0}
|
||||
onMouseDown={this.onDragDown}
|
||||
className="edit-popover-resize"
|
||||
|
||||
@@ -591,7 +591,7 @@ export default class AdhocMetricEditPopover extends PureComponent<
|
||||
</Button>
|
||||
<Icons.ArrowsAltOutlined
|
||||
role="button"
|
||||
aria-label="Resize"
|
||||
aria-label={t('Resize')}
|
||||
tabIndex={0}
|
||||
onMouseDown={this.onDragDown}
|
||||
className="edit-popover-resize"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { t } from '@apache-superset/core';
|
||||
import { css, SupersetTheme } from '@apache-superset/core/ui';
|
||||
import { Flex, Icons } from '@superset-ui/core/components';
|
||||
import { getChartKey } from 'src/explore/exploreUtils';
|
||||
@@ -55,7 +56,7 @@ export const FastVizSwitcher = memo(
|
||||
vizTiles.unshift({
|
||||
name: currentSelection,
|
||||
icon: CUSTOM_CHART_ICONS[currentSelection] || (
|
||||
<Icons.MonitorOutlined {...antdIconProps} aria-label="monitor" />
|
||||
<Icons.MonitorOutlined {...antdIconProps} aria-label={t('Chart')} />
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ describe('VizTypeControl', () => {
|
||||
expect(screen.getByLabelText('pie-chart')).toBeVisible();
|
||||
expect(screen.getByLabelText('bar-chart')).toBeVisible();
|
||||
expect(screen.getByLabelText('area-chart')).toBeVisible();
|
||||
expect(screen.queryByLabelText('monitor')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Chart')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('check-square')).not.toBeInTheDocument();
|
||||
// Multi Chart should NOT appear when other charts are selected
|
||||
expect(screen.queryByLabelText('multiple')).not.toBeInTheDocument();
|
||||
@@ -189,7 +189,7 @@ describe('VizTypeControl', () => {
|
||||
};
|
||||
await waitForRenderWrapper(props);
|
||||
|
||||
expect(screen.getByLabelText('monitor')).toBeVisible();
|
||||
expect(screen.getByLabelText('Chart')).toBeVisible();
|
||||
expect(
|
||||
within(screen.getByTestId('fast-viz-switcher')).getByText('Line Chart'),
|
||||
).toBeVisible();
|
||||
|
||||
@@ -239,7 +239,9 @@ export const ZoomConfigControl: FC<ZoomConfigsControlProps> = ({
|
||||
min={0}
|
||||
max={3}
|
||||
/>
|
||||
<Tag>Current Zoom: {value?.configs.zoom}</Tag>
|
||||
<Tag>
|
||||
{t('Current Zoom')}: {value?.configs.zoom}
|
||||
</Tag>
|
||||
</Form>
|
||||
<ZoomConfigsChart
|
||||
name="zoomlevels"
|
||||
|
||||
@@ -97,7 +97,7 @@ export const httpPath = ({
|
||||
validationMethods={{ onBlur: getValidation }}
|
||||
errorMessage={validationErrors?.http_path}
|
||||
placeholder={t('e.g. sql/protocolv1/o/12345')}
|
||||
label="HTTP Path"
|
||||
label={t('HTTP Path')}
|
||||
onChange={changeMethods.onExtraInputChange}
|
||||
helpText={t('Copy the name of the HTTP Path of your cluster.')}
|
||||
/>
|
||||
@@ -187,7 +187,7 @@ export const httpPathField = ({
|
||||
validationMethods={{ onBlur: getValidation }}
|
||||
errorMessage={validationErrors?.http_path}
|
||||
placeholder={t('e.g. sql/protocolv1/o/12345')}
|
||||
label="HTTP Path"
|
||||
label={t('HTTP Path')}
|
||||
onChange={changeMethods.onParametersChange}
|
||||
helpText={t('Copy the name of the HTTP Path of your cluster.')}
|
||||
/>
|
||||
@@ -335,7 +335,7 @@ export const forceSSLField = ({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<span css={toggleStyle}>SSL</span>
|
||||
<span css={toggleStyle}>{t('SSL')}</span>
|
||||
<InfoTooltip
|
||||
tooltip={t('SSL Mode "require" will be used.')}
|
||||
placement="right"
|
||||
@@ -359,7 +359,7 @@ export const projectIdfield = ({
|
||||
value={db?.parameters?.project_id}
|
||||
validationMethods={{ onBlur: getValidation }}
|
||||
errorMessage={validationErrors?.project_id}
|
||||
placeholder="your-project-1234-a1"
|
||||
placeholder={t('your-project-1234-a1')}
|
||||
label={t('Project Id')}
|
||||
onChange={changeMethods.onParametersChange}
|
||||
helpText={t('Enter the unique project id for your database.')}
|
||||
|
||||
@@ -18,15 +18,16 @@
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { t } from '@apache-superset/core';
|
||||
import { Input, Collapse, FormItem } from '@superset-ui/core/components';
|
||||
import { CustomParametersChangeType, FieldPropTypes } from '../../types';
|
||||
|
||||
const LABELS = {
|
||||
CLIENT_ID: 'Client ID',
|
||||
SECRET: 'Client Secret',
|
||||
AUTH_URI: 'Authorization Request URI',
|
||||
TOKEN_URI: 'Token Request URI',
|
||||
SCOPE: 'Scope',
|
||||
CLIENT_ID: t('Client ID'),
|
||||
SECRET: t('Client Secret'),
|
||||
AUTH_URI: t('Authorization Request URI'),
|
||||
TOKEN_URI: t('Token Request URI'),
|
||||
SCOPE: t('Scope'),
|
||||
};
|
||||
|
||||
interface OAuth2ClientInfo {
|
||||
@@ -81,7 +82,7 @@ export const OAuth2ClientField = ({
|
||||
items={[
|
||||
{
|
||||
key: 'oauth2-client-information',
|
||||
label: 'OAuth2 client information',
|
||||
label: t('OAuth2 client information'),
|
||||
children: (
|
||||
<>
|
||||
<FormItem label={LABELS.CLIENT_ID}>
|
||||
|
||||
@@ -22,19 +22,19 @@ import { DatabaseParameters, FieldPropTypes } from '../../types';
|
||||
|
||||
const FIELD_TEXT_MAP = {
|
||||
account: {
|
||||
label: 'Account',
|
||||
label: t('Account'),
|
||||
helpText: t(
|
||||
'Copy the identifier of the account you are trying to connect to.',
|
||||
),
|
||||
placeholder: t('e.g. xy12345.us-east-2.aws'),
|
||||
},
|
||||
warehouse: {
|
||||
label: 'Warehouse',
|
||||
label: t('Warehouse'),
|
||||
placeholder: t('e.g. compute_wh'),
|
||||
className: 'form-group-w-50',
|
||||
},
|
||||
role: {
|
||||
label: 'Role',
|
||||
label: t('Role'),
|
||||
placeholder: t('e.g. AccountAdmin'),
|
||||
className: 'form-group-w-50',
|
||||
},
|
||||
|
||||
@@ -537,7 +537,7 @@ const ExtraOptions = ({
|
||||
type="text"
|
||||
name="schemas_allowed_for_file_upload"
|
||||
value={schemasText}
|
||||
placeholder="schema1,schema2"
|
||||
placeholder={t('schema1,schema2')}
|
||||
onChange={e => setSchemasText(e.target.value)}
|
||||
onBlur={() =>
|
||||
onExtraInputChange({
|
||||
|
||||
@@ -159,11 +159,11 @@ const SSHTunnelForm = ({
|
||||
data-test="ssh-tunnel-password-input"
|
||||
iconRender={visible =>
|
||||
visible ? (
|
||||
<Tooltip title="Hide password.">
|
||||
<Tooltip title={t('Hide password.')}>
|
||||
<Icons.EyeInvisibleOutlined />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title="Show password.">
|
||||
<Tooltip title={t('Show password.')}>
|
||||
<Icons.EyeOutlined />
|
||||
</Tooltip>
|
||||
)
|
||||
@@ -207,11 +207,11 @@ const SSHTunnelForm = ({
|
||||
data-test="ssh-tunnel-private_key_password-input"
|
||||
iconRender={visible =>
|
||||
visible ? (
|
||||
<Tooltip title="Hide password.">
|
||||
<Tooltip title={t('Hide password.')}>
|
||||
<Icons.EyeInvisibleOutlined />
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title="Show password.">
|
||||
<Tooltip title={t('Show password.')}>
|
||||
<Icons.EyeOutlined />
|
||||
</Tooltip>
|
||||
)
|
||||
|
||||
@@ -114,13 +114,14 @@ const TABS_KEYS = {
|
||||
|
||||
const engineSpecificAlertMapping = {
|
||||
[Engines.GSheet]: {
|
||||
message: 'Why do I need to create a database?',
|
||||
description:
|
||||
message: t('Why do I need to create a database?'),
|
||||
description: t(
|
||||
'To begin using your Google Sheets, you need to create a database first. ' +
|
||||
'Databases are used as a way to identify ' +
|
||||
'your data so that it can be queried and visualized. This ' +
|
||||
'database will hold all of your individual Google Sheets ' +
|
||||
'you choose to connect here.',
|
||||
'Databases are used as a way to identify ' +
|
||||
'your data so that it can be queried and visualized. This ' +
|
||||
'database will hold all of your individual Google Sheets ' +
|
||||
'you choose to connect here.',
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ const ColumnsPreview: FC<ColumnsPreviewProps> = ({
|
||||
|
||||
return (
|
||||
<StyledDivContainer>
|
||||
<Typography.Text type="secondary">Columns:</Typography.Text>
|
||||
<Typography.Text type="secondary">{t('Columns')}:</Typography.Text>
|
||||
{columns.length === 0 ? (
|
||||
<p className="help-block">{t('Upload file to preview columns')}</p>
|
||||
) : (
|
||||
|
||||
@@ -731,7 +731,10 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
name="table_name"
|
||||
required
|
||||
rules={[
|
||||
{ required: true, message: 'Table name is required' },
|
||||
{
|
||||
required: true,
|
||||
message: t('Table name is required'),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
@@ -1024,7 +1027,7 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Header row is required',
|
||||
message: t('Header row is required'),
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -1057,7 +1060,7 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Skip rows is required',
|
||||
message: t('Skip rows is required'),
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { t } from '@apache-superset/core';
|
||||
|
||||
export default function RightPanel() {
|
||||
return <div>Right Panel</div>;
|
||||
return <div>{t('Right Panel')}</div>;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ function UserInfoModal({
|
||||
>
|
||||
<Input.Password
|
||||
name="password"
|
||||
placeholder="Enter the user's password"
|
||||
placeholder={t("Enter the user's password")}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
|
||||
@@ -236,7 +236,7 @@ function UserListModal({
|
||||
>
|
||||
<Input.Password
|
||||
name="password"
|
||||
placeholder="Enter the user's password"
|
||||
placeholder={t("Enter the user's password")}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
|
||||
@@ -86,8 +86,10 @@ export default function Login() {
|
||||
>
|
||||
<Result
|
||||
status="success"
|
||||
title="Registration successful"
|
||||
subTitle="Your account is activated. You can log in with your credentials."
|
||||
title={t('Registration successful')}
|
||||
subTitle={t(
|
||||
'Your account is activated. You can log in with your credentials.',
|
||||
)}
|
||||
extra={[
|
||||
<Button type="default" href="/login/" data-test="login-button">
|
||||
{t('Login')}
|
||||
@@ -220,7 +222,7 @@ export default function Login() {
|
||||
/>
|
||||
</Form.Item>
|
||||
{authRecaptchaPublicKey && (
|
||||
<Form.Item label="Captcha">
|
||||
<Form.Item label={t('Captcha')}>
|
||||
<ReactCAPTCHA
|
||||
sitekey={authRecaptchaPublicKey}
|
||||
onChange={value => {
|
||||
|
||||
@@ -105,7 +105,7 @@ export function UserInfo({ user }: { user: UserWithPermissionsAndRoles }) {
|
||||
setUserDetails(transformedUser);
|
||||
})
|
||||
.catch(error => {
|
||||
addDangerToast('Failed to fetch user info:', error);
|
||||
addDangerToast(`${t('Failed to fetch user info')}:`, error);
|
||||
});
|
||||
}, [userDetails]);
|
||||
|
||||
@@ -157,11 +157,11 @@ export function UserInfo({ user }: { user: UserWithPermissionsAndRoles }) {
|
||||
|
||||
return (
|
||||
<StyledLayout>
|
||||
<StyledHeader>Your user information</StyledHeader>
|
||||
<StyledHeader>{t('Your user information')}</StyledHeader>
|
||||
<DescriptionsContainer>
|
||||
<Collapse defaultActiveKey={['userInfo', 'personalInfo']} ghost>
|
||||
<Collapse.Panel
|
||||
header={<DescriptionTitle>User info</DescriptionTitle>}
|
||||
header={<DescriptionTitle>{t('User info')}</DescriptionTitle>}
|
||||
key="userInfo"
|
||||
>
|
||||
<Descriptions
|
||||
@@ -170,22 +170,22 @@ export function UserInfo({ user }: { user: UserWithPermissionsAndRoles }) {
|
||||
column={1}
|
||||
labelStyle={{ width: '120px' }}
|
||||
>
|
||||
<Descriptions.Item label="User Name">
|
||||
<Descriptions.Item label={t('User Name')}>
|
||||
{user.username}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Is Active?">
|
||||
{user.isActive ? 'Yes' : 'No'}
|
||||
<Descriptions.Item label={t('Is Active?')}>
|
||||
{user.isActive ? t('Yes') : t('No')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Role">
|
||||
{user.roles ? Object.keys(user.roles).join(', ') : 'None'}
|
||||
<Descriptions.Item label={t('Role')}>
|
||||
{user.roles ? Object.keys(user.roles).join(', ') : t('None')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Login count">
|
||||
<Descriptions.Item label={t('Login count')}>
|
||||
{user.loginCount}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel
|
||||
header={<DescriptionTitle>Personal info</DescriptionTitle>}
|
||||
header={<DescriptionTitle>{t('Personal info')}</DescriptionTitle>}
|
||||
key="personalInfo"
|
||||
>
|
||||
<Descriptions
|
||||
@@ -194,13 +194,15 @@ export function UserInfo({ user }: { user: UserWithPermissionsAndRoles }) {
|
||||
column={1}
|
||||
labelStyle={{ width: '120px' }}
|
||||
>
|
||||
<Descriptions.Item label="First Name">
|
||||
<Descriptions.Item label={t('First Name')}>
|
||||
{userDetails.firstName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Last Name">
|
||||
<Descriptions.Item label={t('Last Name')}>
|
||||
{userDetails.lastName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Email">{user.email}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Email')}>
|
||||
{user.email}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
|
||||
Reference in New Issue
Block a user