Task: Add i18n support for public dashboards (#79659)

* feat(public-dashboards): add i18n support

* fix(public-dashboard): correct name convention for i18n

* fix(public-dashboard): correct i18n key convention + extraction

* feat(public-dashboard): mark up i18n for remaining cfg modal

* fix(public-dashboard): de-dynamicize ack comps for i18n + markup i18n for missing parts

* feat(public-dashboard): mark up i18n for DeletePubDashModal

* feat(public-dashboard): mark up i18n for ShareModal public dashboard

* chore(i18n): run yarn i18n:extract

* update naming cconvention

* add mark up phrases

* update json file

* fix import

* fix title

* fix url

* Copy button translation

* Update user admin page

* escape char

* interpolation

* fix escape

* prettier space

* update naming

* update setting keys

* standardize key naming convention

* fix radiobutton

* Fix naming convention as recommended by frontend team

* Prettier and fix naming

* fix variables that cannot be translated

* prettier check

---------

Co-authored-by: hainenber <dotronghai96@gmail.com>
This commit is contained in:
Lucy Chen 2024-01-08 13:30:01 -05:00 committed by GitHub
parent df8624c8cd
commit 49af992661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1081 additions and 159 deletions

View File

@ -5,6 +5,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { config, featureEnabled } from '@grafana/runtime';
import { useStyles2, TabsBar, Tab } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv';
import { isPublicDashboardsEnabled } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
@ -27,7 +28,7 @@ const selectors = e2eSelectors.pages.UserListPage;
const PublicDashboardsTab = ({ view, setView }: { view: TabView | null; setView: (v: TabView | null) => void }) => (
<Tab
label="Public dashboard users"
label={t('users-access-list.tabs.public-dashboard-users-tab-title', 'Public dashboard users')}
active={view === TabView.PUBLIC_DASHBOARDS}
onChangeTab={() => setView(TabView.PUBLIC_DASHBOARDS)}
data-testid={selectors.tabs.publicDashboardsUsers}

View File

@ -4,6 +4,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data/src';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Button, LoadingPlaceholder, Modal, ModalsController, useStyles2 } from '@grafana/ui/src';
import { Trans, t } from 'app/core/internationalization';
import {
generatePublicDashboardConfigUrl,
generatePublicDashboardUrl,
@ -18,10 +19,17 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis
const { data: dashboards, isLoading } = useGetActiveUserDashboardsQuery(email);
return (
<Modal className={styles.modal} isOpen title="Public dashboards" onDismiss={onDismiss}>
<Modal
className={styles.modal}
isOpen
title={t('public-dashboard-users-access-list.modal.dashboard-modal-title', 'Public dashboards')}
onDismiss={onDismiss}
>
{isLoading ? (
<div className={styles.loading}>
<LoadingPlaceholder text="Loading..." />
<LoadingPlaceholder
text={t('public-dashboard-users-access-list.dashboard-modal.loading-text', 'Loading...')}
/>
</div>
) : (
dashboards?.map((dash) => (
@ -35,7 +43,9 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis
href={generatePublicDashboardUrl(dash.publicDashboardAccessToken)}
onClick={onDismiss}
>
Public dashboard URL
<Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.public-dashboard-link">
Public dashboard URL
</Trans>
</a>
<span className={styles.urlsDivider}></span>
<a
@ -43,7 +53,9 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis
href={generatePublicDashboardConfigUrl(dash.dashboardUid, dash.slug)}
onClick={onDismiss}
>
Public dashboard settings
<Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.public-dashboard-setting">
Public dashboard settings
</Trans>
</a>
</div>
<hr className={styles.divider} />
@ -54,20 +66,26 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis
);
};
export const DashboardsListModalButton = ({ email }: { email: string }) => (
<ModalsController>
{({ showModal, hideModal }) => (
<Button
variant="secondary"
size="sm"
icon="question-circle"
title="Open dashboards list"
aria-label="Open dashboards list"
onClick={() => showModal(DashboardsListModal, { email, onDismiss: hideModal })}
/>
)}
</ModalsController>
);
export const DashboardsListModalButton = ({ email }: { email: string }) => {
const translatedDashboardListModalButtonText = t(
'public-dashboard-users-access-list.dashboard-modal.open-dashboard-list-text',
'Open dashboards list'
);
return (
<ModalsController>
{({ showModal, hideModal }) => (
<Button
variant="secondary"
size="sm"
icon="question-circle"
title={translatedDashboardListModalButtonText}
aria-label={translatedDashboardListModalButtonText}
onClick={() => showModal(DashboardsListModal, { email, onDismiss: hideModal })}
/>
)}
</ModalsController>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
modal: css`

View File

@ -3,6 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data/src';
import { Button, Modal, ModalsController, useStyles2 } from '@grafana/ui/src';
import { Trans, t } from 'app/core/internationalization';
import { SessionUser } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
import { useRevokeAllAccessMutation } from '../../dashboard/api/publicDashboardApi';
@ -17,37 +18,59 @@ const DeleteUserModal = ({ user, hideModal }: { user: SessionUser; hideModal: ()
};
return (
<Modal className={styles.modal} isOpen title="Revoke access" onDismiss={hideModal}>
<p className={styles.description}>Are you sure you want to revoke access for {user.email}?</p>
<Modal
className={styles.modal}
isOpen
title={t('public-dashboard-users-access-list.delete-user-modal.revoke-access-title', 'Revoke access')}
onDismiss={hideModal}
>
<p className={styles.description}>
This action will immediately revoke {user.email}&apos;s access to all public dashboards.
<Trans i18nKey="public-dashboard-users-access-list.delete-user-modal.revoke-user-access-modal-desc-line1">
Are you sure you want to revoke access for {{ email: user.email }}?
</Trans>
</p>
<p className={styles.description}>
<Trans
i18nKey="public-dashboard-users-access-list.delete-user-modal.revoke-user-access-modal-desc-line2"
shouldUnescape
>
This action will immediately revoke {{ email: user.email }}&apos;s access to all public dashboards.
</Trans>
</p>
<Modal.ButtonRow>
<Button type="button" variant="secondary" onClick={hideModal} fill="outline">
Cancel
<Trans i18nKey="public-dashboard-users-access-list.delete-user-modal.delete-user-cancel-button">Cancel</Trans>
</Button>
<Button type="button" variant="destructive" onClick={onRevokeAccessClick}>
Revoke access
<Trans i18nKey="public-dashboard-users-access-list.delete-user-modal.delete-user-revoke-access-button">
Revoke access
</Trans>
</Button>
</Modal.ButtonRow>
</Modal>
);
};
export const DeleteUserModalButton = ({ user }: { user: SessionUser }) => (
<ModalsController>
{({ showModal, hideModal }) => (
<Button
size="sm"
variant="destructive"
onClick={() => showModal(DeleteUserModal, { user, hideModal })}
icon="times"
aria-label="Delete user"
title="Delete user"
/>
)}
</ModalsController>
);
export const DeleteUserModalButton = ({ user }: { user: SessionUser }) => {
const translatedDeleteUserText = t(
'public-dashboard-users-access-list.delete-user-modal.delete-user-button-text',
'Delete user'
);
return (
<ModalsController>
{({ showModal, hideModal }) => (
<Button
size="sm"
variant="destructive"
onClick={() => showModal(DeleteUserModal, { user, hideModal })}
icon="times"
aria-label={translatedDeleteUserText}
title={translatedDeleteUserText}
/>
)}
</ModalsController>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
modal: css`

View File

@ -3,6 +3,7 @@ import React from 'react';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { HorizontalGroup, Icon, Tag, Tooltip } from '@grafana/ui/src';
import { Page } from 'app/core/components/Page/Page';
import { Trans, t } from 'app/core/internationalization';
import { useGetActiveUsersQuery } from '../../dashboard/api/publicDashboardApi';
@ -19,16 +20,32 @@ export const UserListPublicDashboardPage = () => {
<table className="filter-table form-inline" data-testid={selectors.container}>
<thead>
<tr>
<th>Email</th>
<th>
<span>Activated </span>
<Tooltip placement="top" content={'Earliest time user has been an active user to a dashboard'}>
<Trans i18nKey="public-dashboard-users-access-list.table-header.email-label">Email</Trans>
</th>
<th>
<span>
<Trans i18nKey="public-dashboard-users-access-list.table-header.activated-label">Activated</Trans>
</span>
<Tooltip
placement="top"
content={t(
'public-dashboard-users-access-list.table-header.activated-tooltip',
'Earliest time user has been an active user to a dashboard'
)}
>
<Icon name="question-circle" />
</Tooltip>
</th>
<th>Last active</th>
<th>Origin</th>
<th>Role</th>
<th>
<Trans i18nKey="public-dashboard-users-access-list.table-header.last-active-label">Last active</Trans>
</th>
<th>
<Trans i18nKey="public-dashboard-users-access-list.table-header.origin-label">Origin</Trans>
</th>
<th>
<Trans i18nKey="public-dashboard-users-access-list.table-header.role-label">Role</Trans>
</th>
<th></th>
</tr>
</thead>

View File

@ -59,7 +59,7 @@ function getTabs(panel?: PanelModel, activeTab?: string) {
if (isPublicDashboardsEnabled()) {
tabs.push({
label: 'Public dashboard',
label: t('share-modal.tab-title.public-dashboard-title', 'Public dashboard'),
value: shareDashboardType.publicDashboard,
component: SharePublicDashboard,
});

View File

@ -17,6 +17,7 @@ import {
useStyles2,
} from '@grafana/ui/src';
import { Layout } from '@grafana/ui/src/components/Layout/Layout';
import { Trans, t } from 'app/core/internationalization';
import {
useDeletePublicDashboardMutation,
useUpdatePublicDashboardMutation,
@ -125,7 +126,10 @@ export function ConfigPublicDashboardBase({
{hasEmailSharingEnabled && <EmailSharingConfiguration />}
<Field label="Dashboard URL" className={styles.fieldSpace}>
<Field
label={t('public-dashboard.config.dashboard-url-field-label', 'Dashboard URL')}
className={styles.fieldSpace}
>
<Input
value={generatePublicDashboardUrl(publicDashboard!.accessToken!)}
readOnly
@ -139,7 +143,7 @@ export function ConfigPublicDashboardBase({
getText={() => generatePublicDashboardUrl(publicDashboard!.accessToken!)}
onClipboardCopy={onCopyURL}
>
Copy
<Trans i18nKey="public-dashboard.config.copy-button">Copy</Trans>
</ClipboardButton>
}
/>
@ -163,14 +167,14 @@ export function ConfigPublicDashboardBase({
margin-bottom: 0;
`}
>
Pause sharing dashboard
<Trans i18nKey="public-dashboard.config.pause-sharing-dashboard-label">Pause sharing dashboard</Trans>
</Label>
</Layout>
</Field>
<Field className={styles.fieldSpace}>
<SettingsBar
title="Settings"
title={t('public-dashboard.config.settings-title', 'Settings')}
headerElement={({ className }) => (
<SettingsSummary
className={className}
@ -193,8 +197,7 @@ export function ConfigPublicDashboardBase({
>
<HorizontalGroup justify="flex-end">
<Button
aria-label="Revoke public URL"
title="Revoke public URL"
title={t('public-dashboard.config.revoke-public-URL-button-title', 'Revoke public URL')}
onClick={onRevoke}
type="button"
disabled={disableInputs}
@ -202,7 +205,7 @@ export function ConfigPublicDashboardBase({
variant="destructive"
fill="outline"
>
Revoke public URL
<Trans i18nKey="public-dashboard.config.revoke-public-URL-button">Revoke public URL</Trans>
</Button>
</HorizontalGroup>
</Layout>

View File

@ -5,6 +5,7 @@ import { TimeRange } from '@grafana/data/src';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { FieldSet, Label, Switch, TimeRangeInput, VerticalGroup } from '@grafana/ui/src';
import { Layout } from '@grafana/ui/src/components/Layout/Layout';
import { Trans, t } from 'app/core/internationalization';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
import { ConfigPublicDashboardForm } from './ConfigPublicDashboard';
@ -27,8 +28,15 @@ export const Configuration = ({
<FieldSet disabled={disabled}>
<VerticalGroup spacing="md">
<Layout orientation={1} spacing="xs" justify="space-between">
<Label description="The public dashboard uses the default time range settings of the dashboard">
Default time range
<Label
description={t(
'public-dashboard.settings-configuration.default-time-range-label-desc',
'The public dashboard uses the default time range settings of the dashboard'
)}
>
<Trans i18nKey="public-dashboard.settings-configuration.default-time-range-label">
Default time range
</Trans>
</Label>
<TimeRangeInput value={timeRange} disabled onChange={() => {}} />
</Layout>
@ -43,7 +51,16 @@ export const Configuration = ({
});
}}
/>
<Label description="Allow viewers to change time range">Time range picker enabled</Label>
<Label
description={t(
'public-dashboard.settings-configuration.time-range-picker-label-desc',
'Allow viewers to change time range'
)}
>
<Trans i18nKey="public-dashboard.settings-configuration.time-range-picker-label">
Time range picker enabled
</Trans>
</Label>
</Layout>
<Layout orientation={0} spacing="sm">
<Switch
@ -56,7 +73,14 @@ export const Configuration = ({
}}
data-testid={selectors.EnableAnnotationsSwitch}
/>
<Label description="Show annotations on public dashboard">Show annotations</Label>
<Label
description={t(
'public-dashboard.settings-configuration.show-annotations-label-desc',
'Show annotations on public dashboard'
)}
>
<Trans i18nKey="public-dashboard.settings-configuration.show-annotations-label">Show annotations</Trans>
</Label>
</Layout>
</VerticalGroup>
</FieldSet>

View File

@ -16,6 +16,7 @@ import {
Spinner,
useStyles2,
} from '@grafana/ui/src';
import { Trans, t } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv';
import {
useAddRecipientMutation,
@ -34,11 +35,6 @@ interface EmailSharingConfigurationForm {
email: string;
}
const options: Array<SelectableValue<PublicDashboardShareType>> = [
{ label: 'Anyone with a link', value: PublicDashboardShareType.PUBLIC },
{ label: 'Only specified people', value: PublicDashboardShareType.EMAIL },
];
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard.EmailSharingConfiguration;
const EmailList = ({
@ -78,27 +74,25 @@ const EmailList = ({
type="button"
variant="destructive"
fill="text"
aria-label="Revoke"
title="Revoke"
title={t('public-dashboard.email-sharing.revoke-button-title', 'Revoke')}
size="sm"
disabled={isLoading}
onClick={() => onDeleteEmail(recipient.uid, recipient.recipient)}
data-testid={`${selectors.DeleteEmail}-${idx}`}
>
Revoke
<Trans i18nKey="public-dashboard.email-sharing.revoke-button">Revoke</Trans>
</Button>
<Button
type="button"
variant="primary"
fill="text"
aria-label="Resend"
title="Resend"
title={t('public-dashboard.email-sharing.resend-button-title', 'Resend')}
size="sm"
disabled={isLoading}
onClick={() => onReshare(recipient.uid)}
data-testid={`${selectors.ReshareLink}-${idx}`}
>
Resend
<Trans i18nKey="public-dashboard.email-sharing.resend-button">Resend</Trans>
</Button>
</ButtonGroup>
</td>
@ -158,12 +152,25 @@ export const EmailSharingConfiguration = () => {
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FieldSet disabled={!hasWritePermissions} data-testid={selectors.Container} className={styles.container}>
<Field label="Can view dashboard" className={styles.field}>
<Field
label={t('public-dashboard.config.can-view-dashboard-radio-button-label', 'Can view dashboard')}
className={styles.field}
>
<InputControl
name="shareType"
control={control}
render={({ field }) => {
const { ref, ...rest } = field;
const options: Array<SelectableValue<PublicDashboardShareType>> = [
{
label: t('public-dashboard.config.public-share-type-option-label', 'Anyone with a link'),
value: PublicDashboardShareType.PUBLIC,
},
{
label: t('public-dashboard.config.email-share-type-option-label', 'Only specified people'),
value: PublicDashboardShareType.EMAIL,
},
];
return (
<RadioButtonGroup
{...rest}
@ -184,8 +191,8 @@ export const EmailSharingConfiguration = () => {
{watch('shareType') === PublicDashboardShareType.EMAIL && (
<>
<Field
label="Invite"
description="Invite people by email"
label={t('public-dashboard.email-sharing.invite-field-label', 'Invite')}
description={t('public-dashboard.email-sharing.invite-field-desc', 'Invite people by email')}
error={errors.email?.message}
invalid={!!errors.email?.message || undefined}
className={styles.field}
@ -196,8 +203,11 @@ export const EmailSharingConfiguration = () => {
placeholder="email"
autoCapitalize="none"
{...register('email', {
required: 'Email is required',
pattern: { value: validEmailRegex, message: 'Invalid email' },
required: t('public-dashboard.email-sharing.input-required-email-text', 'Email is required'),
pattern: {
value: validEmailRegex,
message: t('public-dashboard.email-sharing.input-invalid-email-text', 'Invalid email'),
},
})}
data-testid={selectors.EmailSharingInput}
/>
@ -207,7 +217,8 @@ export const EmailSharingConfiguration = () => {
disabled={isAddEmailLoading}
data-testid={selectors.EmailSharingInviteButton}
>
Invite {isAddEmailLoading && <Spinner />}
<Trans i18nKey="public-dashboard.email-sharing.invite-button">Invite</Trans>
{isAddEmailLoading && <Spinner />}
</Button>
</div>
</Field>

View File

@ -3,6 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { IconButton, ReactUtils, useStyles2 } from '@grafana/ui';
import { t } from 'app/core/internationalization';
export interface Props {
onRowToggle: () => void;
@ -22,7 +23,11 @@ export function SettingsBarHeader({ headerElement, isContentVisible = false, onR
<div className={styles.header}>
<IconButton
name={isContentVisible ? 'angle-down' : 'angle-right'}
tooltip={isContentVisible ? 'Collapse settings' : 'Expand settings'}
tooltip={
isContentVisible
? t('public-dashboard.settings-bar-header.collapse-settings-tooltip', 'Collapse settings')
: t('public-dashboard.settings-bar-header.expand-settings-tooltip', 'Expand settings')
}
className={styles.collapseIcon}
onClick={onRowToggle}
aria-expanded={isContentVisible}

View File

@ -3,6 +3,7 @@ import React from 'react';
import { GrafanaTheme2, TimeRange } from '@grafana/data';
import { Spinner, TimeRangeLabel, useStyles2 } from '@grafana/ui';
import { Trans, t } from 'app/core/internationalization';
export interface Props {
timeRange: TimeRange;
@ -21,6 +22,23 @@ export function SettingsSummary({
}: Props) {
const styles = useStyles2(getStyles);
const translatedTimeRangePickerEnabledStatus = t(
'public-dashboard.settings-summary.time-range-picker-enabled-text',
'Time range picker = enabled'
);
const translatedTimeRangePickerDisabledStatus = t(
'public-dashboard.settings-summary.time-range-picker-disabled-text',
'Time range picker = disabled'
);
const translatedAnnotationShownStatus = t(
'public-dashboard.settings-summary.annotations-show-text',
'Annotations = show'
);
const translatedAnnotationHiddenStatus = t(
'public-dashboard.settings-summary.annotations-hide-text',
'Annotations = hide'
);
return isDataLoading ? (
<div className={cx(styles.summaryWrapper, className)}>
<Spinner className={styles.summary} inline={true} size="sm" />
@ -28,11 +46,15 @@ export function SettingsSummary({
) : (
<div className={cx(styles.summaryWrapper, className)}>
<span className={styles.summary}>
{'Time range = '}
<Trans i18nKey="public-dashboard.settings-summary.time-range-text">Time range = </Trans>
<TimeRangeLabel className={styles.timeRange} value={timeRange} />
</span>
<span className={styles.summary}>{`Time range picker = ${timeSelectionEnabled ? 'enabled' : 'disabled'}`}</span>
<span className={styles.summary}>{`Annotations = ${annotationsEnabled ? 'show' : 'hide'}`}</span>
<span className={styles.summary}>
{timeSelectionEnabled ? translatedTimeRangePickerEnabledStatus : translatedTimeRangePickerDisabledStatus}
</span>
<span className={styles.summary}>
{annotationsEnabled ? translatedAnnotationShownStatus : translatedAnnotationHiddenStatus}
</span>
</div>
);
}

View File

@ -5,6 +5,7 @@ import { UseFormRegister } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data/src';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Checkbox, FieldSet, HorizontalGroup, LinkButton, useStyles2, VerticalGroup } from '@grafana/ui/src';
import { t, Trans } from 'app/core/internationalization';
import { SharePublicDashboardAcknowledgmentInputs } from './CreatePublicDashboard';
@ -19,37 +20,6 @@ type Acknowledge = {
};
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
const ACKNOWLEDGES: Acknowledge[] = [
{
type: 'publicAcknowledgment',
description: 'Your entire dashboard will be public*',
testId: selectors.WillBePublicCheckbox,
info: {
href: 'https://grafana.com/docs/grafana/latest/dashboards/dashboard-public/',
tooltip: 'Learn more about public dashboards',
},
},
{
type: 'dataSourcesAcknowledgment',
description: 'Publishing currently only works with a subset of data sources*',
testId: selectors.LimitedDSCheckbox,
info: {
href: 'https://grafana.com/docs/grafana/latest/datasources/',
tooltip: 'Learn more about public datasources',
},
},
{
type: 'usageAcknowledgment',
description:
'Making a dashboard public will cause queries to run each time it is viewed, which may increase costs*',
testId: selectors.CostIncreaseCheckbox,
info: {
href: 'https://grafana.com/docs/grafana/latest/enterprise/query-caching/',
tooltip: 'Learn more about query caching',
},
},
];
export const AcknowledgeCheckboxes = ({
disabled,
register,
@ -58,10 +28,61 @@ export const AcknowledgeCheckboxes = ({
register: UseFormRegister<SharePublicDashboardAcknowledgmentInputs>;
}) => {
const styles = useStyles2(getStyles);
const ACKNOWLEDGES: Acknowledge[] = [
{
type: 'publicAcknowledgment',
description: t(
'public-dashboard.acknowledgment-checkboxes.public-ack-desc',
'Your entire dashboard will be public*'
),
testId: selectors.WillBePublicCheckbox,
info: {
href: 'https://grafana.com/docs/grafana/latest/dashboards/dashboard-public/',
tooltip: t(
'public-dashboard.acknowledgment-checkboxes.public-ack-tooltip',
'Learn more about public dashboards'
),
},
},
{
type: 'dataSourcesAcknowledgment',
description: t(
'public-dashboard.acknowledgment-checkboxes.data-src-ack-desc',
'Publishing currently only works with a subset of data sources*'
),
testId: selectors.LimitedDSCheckbox,
info: {
href: 'https://grafana.com/docs/grafana/latest/datasources/',
tooltip: t(
'public-dashboard.acknowledgment-checkboxes.data-src-ack-tooltip',
'Learn more about public datasources'
),
},
},
{
type: 'usageAcknowledgment',
description: t(
'public-dashboard.acknowledgment-checkboxes.usage-ack-desc',
'Making a dashboard public will cause queries to run each time it is viewed, which may increase costs*'
),
testId: selectors.CostIncreaseCheckbox,
info: {
href: 'https://grafana.com/docs/grafana/latest/enterprise/query-caching/',
tooltip: t(
'public-dashboard.acknowledgment-checkboxes.usage-ack-desc-tooltip',
'Learn more about query caching'
),
},
},
];
return (
<>
<p className={styles.title}>Before you make the dashboard public, acknowledge the following:</p>
<p className={styles.title}>
<Trans i18nKey="public-dashboard.acknowledgment-checkboxes.ack-title">
Before you make the dashboard public, acknowledge the following:
</Trans>
</p>
<FieldSet disabled={disabled}>
<VerticalGroup spacing="md">
{ACKNOWLEDGES.map((acknowledge) => (

View File

@ -5,6 +5,7 @@ import { FormState, UseFormRegister } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data/src';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Button, Form, Spinner, useStyles2 } from '@grafana/ui/src';
import { Trans } from 'app/core/internationalization';
import { useCreatePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi';
import { DashboardModel } from 'app/features/dashboard/state';
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
@ -54,8 +55,14 @@ export const CreatePublicDashboardBase = ({
return (
<div className={styles.container}>
<div>
<p className={styles.title}>Welcome to public dashboards!</p>
<p className={styles.description}>Currently, we dont support template variables or frontend data sources</p>
<p className={styles.title}>
<Trans i18nKey="public-dashboard.create-page.welcome-title">Welcome to public dashboards!</Trans>
</p>
<p className={styles.description}>
<Trans i18nKey="public-dashboard.create-page.unsupported-features-desc">
Currently, we dont support template variables or frontend data sources
</Trans>
</p>
</div>
{!hasWritePermissions && <NoUpsertPermissionsAlert mode="create" />}
@ -80,7 +87,8 @@ export const CreatePublicDashboardBase = ({
</div>
<div className={styles.buttonContainer}>
<Button type="submit" disabled={disableInputs || !isValid} data-testid={selectors.CreateButton}>
Generate public URL {isLoading && <Spinner className={styles.loadingSpinner} />}
<Trans i18nKey="public-dashboard.create-page.generate-public-url-button">Generate public URL</Trans>
{isLoading && <Spinner className={styles.loadingSpinner} />}
</Button>
</div>
</>

View File

@ -2,16 +2,23 @@ import React from 'react';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Alert } from '@grafana/ui/src';
import { Trans, t } from 'app/core/internationalization';
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
export const NoUpsertPermissionsAlert = ({ mode }: { mode: 'create' | 'edit' }) => (
<Alert
severity="info"
title={`You dont have permission to ${mode} a public dashboard`}
title={t(
'public-dashboard.modal-alerts.no-upsert-perm-alert-title',
'You dont have permission to {{ mode }} a public dashboard',
{ mode }
)}
data-testid={selectors.NoUpsertPermissionsWarningAlert}
bottomSpacing={0}
>
Contact your admin to get permission to {mode} public dashboards
<Trans i18nKey="public-dashboard.modal-alerts.no-upsert-perm-alert-desc">
Contact your admin to get permission to {{ mode }} public dashboards
</Trans>
</Alert>
);

View File

@ -1,10 +1,14 @@
import React from 'react';
import { Alert } from '@grafana/ui/src';
import { t } from 'app/core/internationalization';
export const SaveDashboardChangesAlert = () => (
<Alert
title="Please save your dashboard changes before updating the public configuration"
title={t(
'public-dashboard.modal-alerts.save-dashboard-changes-alert-title',
'Please save your dashboard changes before updating the public configuration'
)}
severity="warning"
bottomSpacing={0}
/>

View File

@ -5,6 +5,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data/src';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Alert, useStyles2 } from '@grafana/ui/src';
import { Trans, t } from 'app/core/internationalization';
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
@ -14,19 +15,23 @@ export const UnsupportedDataSourcesAlert = ({ unsupportedDataSources }: { unsupp
return (
<Alert
severity="warning"
title="Unsupported data sources"
title={t('public-dashboard.modal-alerts.unsupported-data-source-alert-title', 'Unsupported data sources')}
data-testid={selectors.UnsupportedDataSourcesWarningAlert}
bottomSpacing={0}
>
<p className={styles.unsupportedDataSourceDescription}>
There are data sources in this dashboard that are unsupported for public dashboards. Panels that use these data
sources may not function properly: {unsupportedDataSources}.
<Trans i18nKey="public-dashboard.modal-alerts.unsupported-data-source-alert-desc">
There are data sources in this dashboard that are unsupported for public dashboards. Panels that use these
data sources may not function properly: {{ unsupportedDataSources }}.
</Trans>
</p>
<a
href="https://grafana.com/docs/grafana/next/dashboards/dashboard-public/"
className={cx('text-link', styles.unsupportedDataSourceDescription)}
>
Read more about supported data sources
<Trans i18nKey="public-dashboard.modal-alerts.unsupport-data-source-alert-readmore-link">
Read more about supported data sources
</Trans>
</a>
</Alert>
);

View File

@ -2,16 +2,22 @@ import React from 'react';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Alert } from '@grafana/ui/src';
import { Trans, t } from 'app/core/internationalization';
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
export const UnsupportedTemplateVariablesAlert = () => (
<Alert
severity="warning"
title="Template variables are not supported"
title={t(
'public-dashboard.modal-alerts.unsupported-template-variable-alert-title',
'Template variables are not supported'
)}
data-testid={selectors.TemplateVariablesWarningAlert}
bottomSpacing={0}
>
This public dashboard may not work since it uses template variables
<Trans i18nKey="public-dashboard.modal-alerts.unsupported-template-variable-alert-desc">
This public dashboard may not work since it uses template variables
</Trans>
</Alert>
);

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Button, ModalsController, ButtonProps } from '@grafana/ui/src';
import { t } from 'app/core/internationalization';
import { useDeletePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi';
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
@ -39,24 +40,30 @@ export const DeletePublicDashboardButton = ({
return (
<ModalsController>
{({ showModal, hideModal }) => (
<Button
aria-label="Revoke public URL"
title="Revoke public URL"
onClick={() =>
showModal(DeletePublicDashboardModal, {
dashboardTitle: publicDashboard.title,
onConfirm: () => onDeletePublicDashboardClick(publicDashboard, hideModal),
onDismiss: () => {
onDismiss ? onDismiss() : hideModal();
},
})
}
{...rest}
>
{isLoading && loader ? loader : children}
</Button>
)}
{({ showModal, hideModal }) => {
const translatedRevocationButtonText = t(
'public-dashboard-list.button.revoke-button-text',
'Revoke public URL'
);
return (
<Button
aria-label={translatedRevocationButtonText}
title={translatedRevocationButtonText}
onClick={() =>
showModal(DeletePublicDashboardModal, {
dashboardTitle: publicDashboard.title,
onConfirm: () => onDeletePublicDashboardClick(publicDashboard, hideModal),
onDismiss: () => {
onDismiss ? onDismiss() : hideModal();
},
})
}
{...rest}
>
{isLoading && loader ? loader : children}
</Button>
);
}}
</ModalsController>
);
};

View File

@ -3,6 +3,7 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data/src';
import { ConfirmModal, useStyles2 } from '@grafana/ui/src';
import { t } from 'app/core/internationalization';
const Body = ({ title }: { title?: string }) => {
const styles = useStyles2(getStyles);
@ -10,8 +11,14 @@ const Body = ({ title }: { title?: string }) => {
return (
<p className={styles.description}>
{title
? 'Are you sure you want to revoke this URL? The dashboard will no longer be public.'
: 'Orphaned public dashboard will no longer be public.'}
? t(
'public-dashboard.delete-modal.revoke-nonorphaned-body-text',
'Are you sure you want to revoke this URL? The dashboard will no longer be public.'
)
: t(
'public-dashboard.delete-modal.revoke-orphaned-body-text',
'Orphaned public dashboard will no longer be public.'
)}
</p>
);
};
@ -24,17 +31,20 @@ export const DeletePublicDashboardModal = ({
dashboardTitle?: string;
onConfirm: () => void;
onDismiss: () => void;
}) => (
<ConfirmModal
isOpen
body={<Body title={dashboardTitle} />}
onConfirm={onConfirm}
onDismiss={onDismiss}
title="Revoke public URL"
icon="trash-alt"
confirmText="Revoke public URL"
/>
);
}) => {
const translatedRevocationModalText = t('public-dashboard.delete-modal.revoke-title', 'Revoke public URL');
return (
<ConfirmModal
isOpen
body={<Body title={dashboardTitle} />}
onConfirm={onConfirm}
onDismiss={onDismiss}
title={translatedRevocationModalText}
icon="trash-alt"
confirmText={translatedRevocationModalText}
/>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
title: css`

View File

@ -18,6 +18,7 @@ import {
HorizontalGroup,
} from '@grafana/ui/src';
import { Page } from 'app/core/components/Page/Page';
import { Trans, t } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv';
import {
useListPublicDashboardsQuery,
@ -57,6 +58,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
};
const CardActions = useMemo(() => (isMobile ? Card.Actions : Card.SecondaryActions), [isMobile]);
const translatedPauseSharingText = t('public-dashboard-list.toggle.pause-sharing-toggle-text', 'Pause sharing');
return (
<Card className={styles.card} href={!isOrphaned ? `/d/${pd.dashboardUid}` : undefined}>
@ -64,9 +66,17 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
{!isOrphaned ? (
<span>{pd.title}</span>
) : (
<Tooltip content="The linked dashboard has already been deleted" placement="top">
<Tooltip
content={t(
'public-dashboard-list.dashboard-title.orphaned-tooltip',
'The linked dashboard has already been deleted'
)}
placement="top"
>
<div className={styles.orphanedTitle}>
<span>Orphaned public dashboard</span>
<Trans i18nKey="public-dashboard-list.dashboard-title.orphaned-title">
<span>Orphaned public dashboard</span>
</Trans>
<Icon name="info-circle" />
</div>
</Tooltip>
@ -76,7 +86,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
<div className={styles.pauseSwitch}>
<Switch
value={!pd.isEnabled}
label="Pause sharing"
label={translatedPauseSharingText}
disabled={isUpdateLoading}
onChange={(e) => {
reportInteraction('grafana_dashboards_public_enable_clicked', {
@ -86,7 +96,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
}}
data-testid={selectors.ListItem.pauseSwitch}
/>
<span>Pause sharing</span>
<span>{translatedPauseSharingText}</span>
</div>
<LinkButton
disabled={isOrphaned}
@ -97,7 +107,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
color={theme.colors.warning.text}
href={generatePublicDashboardUrl(pd.accessToken)}
key="public-dashboard-url"
tooltip="View public dashboard"
tooltip={t('public-dashboard-list.button.view-button-tooltip', 'View public dashboard')}
data-testid={selectors.ListItem.linkButton}
/>
<LinkButton
@ -108,7 +118,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
color={theme.colors.warning.text}
href={generatePublicDashboardConfigUrl(pd.dashboardUid, pd.slug)}
key="public-dashboard-config-url"
tooltip="Configure public dashboard"
tooltip={t('public-dashboard-list.button.config-button-tooltip', 'Configure public dashboard')}
data-testid={selectors.ListItem.configButton}
/>
{hasWritePermissions && (
@ -117,7 +127,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
icon="trash-alt"
variant="secondary"
publicDashboard={pd}
tooltip="Revoke public dashboard url"
tooltip={t('public-dashboard-list.button.revoke-button-tooltip', 'Revoke public dashboard URL')}
loader={<Spinner />}
data-testid={selectors.ListItem.trashcanButton}
/>

View File

@ -1072,6 +1072,120 @@
"passwords-must-match": "Passwörter müssen übereinstimmen"
}
},
"public-dashboard": {
"acknowledgment-checkboxes": {
"ack-title": "",
"data-src-ack-desc": "",
"data-src-ack-tooltip": "",
"public-ack-desc": "",
"public-ack-tooltip": "",
"usage-ack-desc": "",
"usage-ack-desc-tooltip": ""
},
"config": {
"can-view-dashboard-radio-button-label": "",
"copy-button": "",
"dashboard-url-field-label": "",
"email-share-type-option-label": "",
"pause-sharing-dashboard-label": "",
"public-share-type-option-label": "",
"revoke-public-URL-button": "",
"revoke-public-URL-button-title": "",
"settings-title": ""
},
"create-page": {
"generate-public-url-button": "",
"unsupported-features-desc": "",
"welcome-title": ""
},
"delete-modal": {
"revoke-nonorphaned-body-text": "",
"revoke-orphaned-body-text": "",
"revoke-title": ""
},
"email-sharing": {
"input-invalid-email-text": "",
"input-required-email-text": "",
"invite-button": "",
"invite-field-desc": "",
"invite-field-label": "",
"resend-button": "",
"resend-button-title": "",
"revoke-button": "",
"revoke-button-title": ""
},
"modal-alerts": {
"no-upsert-perm-alert-desc": "",
"no-upsert-perm-alert-title": "",
"save-dashboard-changes-alert-title": "",
"unsupport-data-source-alert-readmore-link": "",
"unsupported-data-source-alert-desc": "",
"unsupported-data-source-alert-title": "",
"unsupported-template-variable-alert-desc": "",
"unsupported-template-variable-alert-title": ""
},
"settings-bar-header": {
"collapse-settings-tooltip": "",
"expand-settings-tooltip": ""
},
"settings-configuration": {
"default-time-range-label": "",
"default-time-range-label-desc": "",
"show-annotations-label": "",
"show-annotations-label-desc": "",
"time-range-picker-label": "",
"time-range-picker-label-desc": ""
},
"settings-summary": {
"annotations-hide-text": "",
"annotations-show-text": "",
"time-range-picker-disabled-text": "",
"time-range-picker-enabled-text": "",
"time-range-text": ""
}
},
"public-dashboard-list": {
"button": {
"config-button-tooltip": "",
"revoke-button-text": "",
"revoke-button-tooltip": "",
"view-button-tooltip": ""
},
"dashboard-title": {
"orphaned-title": "",
"orphaned-tooltip": ""
},
"toggle": {
"pause-sharing-toggle-text": ""
}
},
"public-dashboard-users-access-list": {
"dashboard-modal": {
"loading-text": "",
"open-dashboard-list-text": "",
"public-dashboard-link": "",
"public-dashboard-setting": ""
},
"delete-user-modal": {
"delete-user-button-text": "",
"delete-user-cancel-button": "",
"delete-user-revoke-access-button": "",
"revoke-access-title": "",
"revoke-user-access-modal-desc-line1": "",
"revoke-user-access-modal-desc-line2": ""
},
"modal": {
"dashboard-modal-title": ""
},
"table-header": {
"activated-label": "",
"activated-tooltip": "",
"email-label": "",
"last-active-label": "",
"origin-label": "",
"role-label": ""
}
},
"query-operation": {
"header": {
"collapse-row": "Abfragezeile einklappen",
@ -1201,6 +1315,7 @@
"link": "Link",
"panel-embed": "Einbetten",
"public-dashboard": "Öffentliches Dashboard",
"public-dashboard-title": "",
"snapshot": "Schnappschuss"
},
"theme-picker": {
@ -1349,6 +1464,11 @@
"user-sessions": {
"loading": "Sitzungen werden geladen …"
},
"users-access-list": {
"tabs": {
"public-dashboard-users-tab-title": ""
}
},
"variable": {
"adhoc": {
"placeholder": "Wert auswählen"

View File

@ -1072,6 +1072,120 @@
"passwords-must-match": "Passwords must match"
}
},
"public-dashboard": {
"acknowledgment-checkboxes": {
"ack-title": "Before you make the dashboard public, acknowledge the following:",
"data-src-ack-desc": "Publishing currently only works with a subset of data sources*",
"data-src-ack-tooltip": "Learn more about public datasources",
"public-ack-desc": "Your entire dashboard will be public*",
"public-ack-tooltip": "Learn more about public dashboards",
"usage-ack-desc": "Making a dashboard public will cause queries to run each time it is viewed, which may increase costs*",
"usage-ack-desc-tooltip": "Learn more about query caching"
},
"config": {
"can-view-dashboard-radio-button-label": "Can view dashboard",
"copy-button": "Copy",
"dashboard-url-field-label": "Dashboard URL",
"email-share-type-option-label": "Only specified people",
"pause-sharing-dashboard-label": "Pause sharing dashboard",
"public-share-type-option-label": "Anyone with a link",
"revoke-public-URL-button": "Revoke public URL",
"revoke-public-URL-button-title": "Revoke public URL",
"settings-title": "Settings"
},
"create-page": {
"generate-public-url-button": "Generate public URL",
"unsupported-features-desc": "Currently, we dont support template variables or frontend data sources",
"welcome-title": "Welcome to public dashboards!"
},
"delete-modal": {
"revoke-nonorphaned-body-text": "Are you sure you want to revoke this URL? The dashboard will no longer be public.",
"revoke-orphaned-body-text": "Orphaned public dashboard will no longer be public.",
"revoke-title": "Revoke public URL"
},
"email-sharing": {
"input-invalid-email-text": "Invalid email",
"input-required-email-text": "Email is required",
"invite-button": "Invite",
"invite-field-desc": "Invite people by email",
"invite-field-label": "Invite",
"resend-button": "Resend",
"resend-button-title": "Resend",
"revoke-button": "Revoke",
"revoke-button-title": "Revoke"
},
"modal-alerts": {
"no-upsert-perm-alert-desc": "Contact your admin to get permission to {{mode}} public dashboards",
"no-upsert-perm-alert-title": "You dont have permission to {{ mode }} a public dashboard",
"save-dashboard-changes-alert-title": "Please save your dashboard changes before updating the public configuration",
"unsupport-data-source-alert-readmore-link": "Read more about supported data sources",
"unsupported-data-source-alert-desc": "There are data sources in this dashboard that are unsupported for public dashboards. Panels that use these data sources may not function properly: {{unsupportedDataSources}}.",
"unsupported-data-source-alert-title": "Unsupported data sources",
"unsupported-template-variable-alert-desc": "This public dashboard may not work since it uses template variables",
"unsupported-template-variable-alert-title": "Template variables are not supported"
},
"settings-bar-header": {
"collapse-settings-tooltip": "Collapse settings",
"expand-settings-tooltip": "Expand settings"
},
"settings-configuration": {
"default-time-range-label": "Default time range",
"default-time-range-label-desc": "The public dashboard uses the default time range settings of the dashboard",
"show-annotations-label": "Show annotations",
"show-annotations-label-desc": "Show annotations on public dashboard",
"time-range-picker-label": "Time range picker enabled",
"time-range-picker-label-desc": "Allow viewers to change time range"
},
"settings-summary": {
"annotations-hide-text": "Annotations = hide",
"annotations-show-text": "Annotations = show",
"time-range-picker-disabled-text": "Time range picker = disabled",
"time-range-picker-enabled-text": "Time range picker = enabled",
"time-range-text": "Time range = "
}
},
"public-dashboard-list": {
"button": {
"config-button-tooltip": "Configure public dashboard",
"revoke-button-text": "Revoke public URL",
"revoke-button-tooltip": "Revoke public dashboard URL",
"view-button-tooltip": "View public dashboard"
},
"dashboard-title": {
"orphaned-title": "<0>Orphaned public dashboard</0>",
"orphaned-tooltip": "The linked dashboard has already been deleted"
},
"toggle": {
"pause-sharing-toggle-text": "Pause sharing"
}
},
"public-dashboard-users-access-list": {
"dashboard-modal": {
"loading-text": "Loading...",
"open-dashboard-list-text": "Open dashboards list",
"public-dashboard-link": "Public dashboard URL",
"public-dashboard-setting": "Public dashboard settings"
},
"delete-user-modal": {
"delete-user-button-text": "Delete user",
"delete-user-cancel-button": "Cancel",
"delete-user-revoke-access-button": "Revoke access",
"revoke-access-title": "Revoke access",
"revoke-user-access-modal-desc-line1": "Are you sure you want to revoke access for {{email}}?",
"revoke-user-access-modal-desc-line2": "This action will immediately revoke {{email}}&apos;s access to all public dashboards."
},
"modal": {
"dashboard-modal-title": "Public dashboards"
},
"table-header": {
"activated-label": "Activated",
"activated-tooltip": "Earliest time user has been an active user to a dashboard",
"email-label": "Email",
"last-active-label": "Last active",
"origin-label": "Origin",
"role-label": "Role"
}
},
"query-operation": {
"header": {
"collapse-row": "Collapse query row",
@ -1201,6 +1315,7 @@
"link": "Link",
"panel-embed": "Embed",
"public-dashboard": "Public Dashboard",
"public-dashboard-title": "Public dashboard",
"snapshot": "Snapshot"
},
"theme-picker": {
@ -1349,6 +1464,11 @@
"user-sessions": {
"loading": "Loading sessions..."
},
"users-access-list": {
"tabs": {
"public-dashboard-users-tab-title": "Public dashboard users"
}
},
"variable": {
"adhoc": {
"placeholder": "Select value"

View File

@ -1078,6 +1078,120 @@
"passwords-must-match": "Las contraseñas deben coincidir"
}
},
"public-dashboard": {
"acknowledgment-checkboxes": {
"ack-title": "",
"data-src-ack-desc": "",
"data-src-ack-tooltip": "",
"public-ack-desc": "",
"public-ack-tooltip": "",
"usage-ack-desc": "",
"usage-ack-desc-tooltip": ""
},
"config": {
"can-view-dashboard-radio-button-label": "",
"copy-button": "",
"dashboard-url-field-label": "",
"email-share-type-option-label": "",
"pause-sharing-dashboard-label": "",
"public-share-type-option-label": "",
"revoke-public-URL-button": "",
"revoke-public-URL-button-title": "",
"settings-title": ""
},
"create-page": {
"generate-public-url-button": "",
"unsupported-features-desc": "",
"welcome-title": ""
},
"delete-modal": {
"revoke-nonorphaned-body-text": "",
"revoke-orphaned-body-text": "",
"revoke-title": ""
},
"email-sharing": {
"input-invalid-email-text": "",
"input-required-email-text": "",
"invite-button": "",
"invite-field-desc": "",
"invite-field-label": "",
"resend-button": "",
"resend-button-title": "",
"revoke-button": "",
"revoke-button-title": ""
},
"modal-alerts": {
"no-upsert-perm-alert-desc": "",
"no-upsert-perm-alert-title": "",
"save-dashboard-changes-alert-title": "",
"unsupport-data-source-alert-readmore-link": "",
"unsupported-data-source-alert-desc": "",
"unsupported-data-source-alert-title": "",
"unsupported-template-variable-alert-desc": "",
"unsupported-template-variable-alert-title": ""
},
"settings-bar-header": {
"collapse-settings-tooltip": "",
"expand-settings-tooltip": ""
},
"settings-configuration": {
"default-time-range-label": "",
"default-time-range-label-desc": "",
"show-annotations-label": "",
"show-annotations-label-desc": "",
"time-range-picker-label": "",
"time-range-picker-label-desc": ""
},
"settings-summary": {
"annotations-hide-text": "",
"annotations-show-text": "",
"time-range-picker-disabled-text": "",
"time-range-picker-enabled-text": "",
"time-range-text": ""
}
},
"public-dashboard-list": {
"button": {
"config-button-tooltip": "",
"revoke-button-text": "",
"revoke-button-tooltip": "",
"view-button-tooltip": ""
},
"dashboard-title": {
"orphaned-title": "",
"orphaned-tooltip": ""
},
"toggle": {
"pause-sharing-toggle-text": ""
}
},
"public-dashboard-users-access-list": {
"dashboard-modal": {
"loading-text": "",
"open-dashboard-list-text": "",
"public-dashboard-link": "",
"public-dashboard-setting": ""
},
"delete-user-modal": {
"delete-user-button-text": "",
"delete-user-cancel-button": "",
"delete-user-revoke-access-button": "",
"revoke-access-title": "",
"revoke-user-access-modal-desc-line1": "",
"revoke-user-access-modal-desc-line2": ""
},
"modal": {
"dashboard-modal-title": ""
},
"table-header": {
"activated-label": "",
"activated-tooltip": "",
"email-label": "",
"last-active-label": "",
"origin-label": "",
"role-label": ""
}
},
"query-operation": {
"header": {
"collapse-row": "Contraer la fila de la consulta",
@ -1207,6 +1321,7 @@
"link": "Enlace",
"panel-embed": "Incrustar",
"public-dashboard": "Tablero público",
"public-dashboard-title": "",
"snapshot": "Instantánea"
},
"theme-picker": {
@ -1355,6 +1470,11 @@
"user-sessions": {
"loading": "Cargando sesiones..."
},
"users-access-list": {
"tabs": {
"public-dashboard-users-tab-title": ""
}
},
"variable": {
"adhoc": {
"placeholder": "Seleccionar valor"

View File

@ -1078,6 +1078,120 @@
"passwords-must-match": "Les mots de passe doivent être identiques"
}
},
"public-dashboard": {
"acknowledgment-checkboxes": {
"ack-title": "",
"data-src-ack-desc": "",
"data-src-ack-tooltip": "",
"public-ack-desc": "",
"public-ack-tooltip": "",
"usage-ack-desc": "",
"usage-ack-desc-tooltip": ""
},
"config": {
"can-view-dashboard-radio-button-label": "",
"copy-button": "",
"dashboard-url-field-label": "",
"email-share-type-option-label": "",
"pause-sharing-dashboard-label": "",
"public-share-type-option-label": "",
"revoke-public-URL-button": "",
"revoke-public-URL-button-title": "",
"settings-title": ""
},
"create-page": {
"generate-public-url-button": "",
"unsupported-features-desc": "",
"welcome-title": ""
},
"delete-modal": {
"revoke-nonorphaned-body-text": "",
"revoke-orphaned-body-text": "",
"revoke-title": ""
},
"email-sharing": {
"input-invalid-email-text": "",
"input-required-email-text": "",
"invite-button": "",
"invite-field-desc": "",
"invite-field-label": "",
"resend-button": "",
"resend-button-title": "",
"revoke-button": "",
"revoke-button-title": ""
},
"modal-alerts": {
"no-upsert-perm-alert-desc": "",
"no-upsert-perm-alert-title": "",
"save-dashboard-changes-alert-title": "",
"unsupport-data-source-alert-readmore-link": "",
"unsupported-data-source-alert-desc": "",
"unsupported-data-source-alert-title": "",
"unsupported-template-variable-alert-desc": "",
"unsupported-template-variable-alert-title": ""
},
"settings-bar-header": {
"collapse-settings-tooltip": "",
"expand-settings-tooltip": ""
},
"settings-configuration": {
"default-time-range-label": "",
"default-time-range-label-desc": "",
"show-annotations-label": "",
"show-annotations-label-desc": "",
"time-range-picker-label": "",
"time-range-picker-label-desc": ""
},
"settings-summary": {
"annotations-hide-text": "",
"annotations-show-text": "",
"time-range-picker-disabled-text": "",
"time-range-picker-enabled-text": "",
"time-range-text": ""
}
},
"public-dashboard-list": {
"button": {
"config-button-tooltip": "",
"revoke-button-text": "",
"revoke-button-tooltip": "",
"view-button-tooltip": ""
},
"dashboard-title": {
"orphaned-title": "",
"orphaned-tooltip": ""
},
"toggle": {
"pause-sharing-toggle-text": ""
}
},
"public-dashboard-users-access-list": {
"dashboard-modal": {
"loading-text": "",
"open-dashboard-list-text": "",
"public-dashboard-link": "",
"public-dashboard-setting": ""
},
"delete-user-modal": {
"delete-user-button-text": "",
"delete-user-cancel-button": "",
"delete-user-revoke-access-button": "",
"revoke-access-title": "",
"revoke-user-access-modal-desc-line1": "",
"revoke-user-access-modal-desc-line2": ""
},
"modal": {
"dashboard-modal-title": ""
},
"table-header": {
"activated-label": "",
"activated-tooltip": "",
"email-label": "",
"last-active-label": "",
"origin-label": "",
"role-label": ""
}
},
"query-operation": {
"header": {
"collapse-row": "Réduire la ligne de requête",
@ -1207,6 +1321,7 @@
"link": "Lien",
"panel-embed": "Intégrer",
"public-dashboard": "Tableau de bord public",
"public-dashboard-title": "",
"snapshot": "Instantané"
},
"theme-picker": {
@ -1355,6 +1470,11 @@
"user-sessions": {
"loading": "Chargement des sessions..."
},
"users-access-list": {
"tabs": {
"public-dashboard-users-tab-title": ""
}
},
"variable": {
"adhoc": {
"placeholder": "Sélectionner une valeur"

View File

@ -1072,6 +1072,120 @@
"passwords-must-match": "Päşşŵőřđş mūşŧ mäŧčĥ"
}
},
"public-dashboard": {
"acknowledgment-checkboxes": {
"ack-title": "ßęƒőřę yőū mäĸę ŧĥę đäşĥþőäřđ pūþľįč, äčĸʼnőŵľęđģę ŧĥę ƒőľľőŵįʼnģ:",
"data-src-ack-desc": "Pūþľįşĥįʼnģ čūřřęʼnŧľy őʼnľy ŵőřĸş ŵįŧĥ ä şūþşęŧ őƒ đäŧä şőūřčęş*",
"data-src-ack-tooltip": "Ŀęäřʼn mőřę äþőūŧ pūþľįč đäŧäşőūřčęş",
"public-ack-desc": "Ÿőūř ęʼnŧįřę đäşĥþőäřđ ŵįľľ þę pūþľįč*",
"public-ack-tooltip": "Ŀęäřʼn mőřę äþőūŧ pūþľįč đäşĥþőäřđş",
"usage-ack-desc": "Mäĸįʼnģ ä đäşĥþőäřđ pūþľįč ŵįľľ čäūşę qūęřįęş ŧő řūʼn ęäčĥ ŧįmę įŧ įş vįęŵęđ, ŵĥįčĥ mäy įʼnčřęäşę čőşŧş*",
"usage-ack-desc-tooltip": "Ŀęäřʼn mőřę äþőūŧ qūęřy čäčĥįʼnģ"
},
"config": {
"can-view-dashboard-radio-button-label": "Cäʼn vįęŵ đäşĥþőäřđ",
"copy-button": "Cőpy",
"dashboard-url-field-label": "Đäşĥþőäřđ ŮŖĿ",
"email-share-type-option-label": "Øʼnľy şpęčįƒįęđ pęőpľę",
"pause-sharing-dashboard-label": "Päūşę şĥäřįʼnģ đäşĥþőäřđ",
"public-share-type-option-label": "Åʼnyőʼnę ŵįŧĥ ä ľįʼnĸ",
"revoke-public-URL-button": "Ŗęvőĸę pūþľįč ŮŖĿ",
"revoke-public-URL-button-title": "Ŗęvőĸę pūþľįč ŮŖĿ",
"settings-title": "Ŝęŧŧįʼnģş"
},
"create-page": {
"generate-public-url-button": "Ğęʼnęřäŧę pūþľįč ŮŖĿ",
"unsupported-features-desc": "Cūřřęʼnŧľy, ŵę đőʼn’ŧ şūppőřŧ ŧęmpľäŧę väřįäþľęş őř ƒřőʼnŧęʼnđ đäŧä şőūřčęş",
"welcome-title": "Ŵęľčőmę ŧő pūþľįč đäşĥþőäřđş!"
},
"delete-modal": {
"revoke-nonorphaned-body-text": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő řęvőĸę ŧĥįş ŮŖĿ? Ŧĥę đäşĥþőäřđ ŵįľľ ʼnő ľőʼnģęř þę pūþľįč.",
"revoke-orphaned-body-text": "Øřpĥäʼnęđ pūþľįč đäşĥþőäřđ ŵįľľ ʼnő ľőʼnģęř þę pūþľįč.",
"revoke-title": "Ŗęvőĸę pūþľįč ŮŖĿ"
},
"email-sharing": {
"input-invalid-email-text": "Ĩʼnväľįđ ęmäįľ",
"input-required-email-text": "Ēmäįľ įş řęqūįřęđ",
"invite-button": "Ĩʼnvįŧę",
"invite-field-desc": "Ĩʼnvįŧę pęőpľę þy ęmäįľ",
"invite-field-label": "Ĩʼnvįŧę",
"resend-button": "Ŗęşęʼnđ",
"resend-button-title": "Ŗęşęʼnđ",
"revoke-button": "Ŗęvőĸę",
"revoke-button-title": "Ŗęvőĸę"
},
"modal-alerts": {
"no-upsert-perm-alert-desc": "Cőʼnŧäčŧ yőūř äđmįʼn ŧő ģęŧ pęřmįşşįőʼn ŧő {{mode}} pūþľįč đäşĥþőäřđş",
"no-upsert-perm-alert-title": "Ÿőū đőʼn’ŧ ĥävę pęřmįşşįőʼn ŧő {{ mode }} ä pūþľįč đäşĥþőäřđ",
"save-dashboard-changes-alert-title": "Pľęäşę şävę yőūř đäşĥþőäřđ čĥäʼnģęş þęƒőřę ūpđäŧįʼnģ ŧĥę pūþľįč čőʼnƒįģūřäŧįőʼn",
"unsupport-data-source-alert-readmore-link": "Ŗęäđ mőřę äþőūŧ şūppőřŧęđ đäŧä şőūřčęş",
"unsupported-data-source-alert-desc": "Ŧĥęřę äřę đäŧä şőūřčęş įʼn ŧĥįş đäşĥþőäřđ ŧĥäŧ äřę ūʼnşūppőřŧęđ ƒőř pūþľįč đäşĥþőäřđş. Päʼnęľş ŧĥäŧ ūşę ŧĥęşę đäŧä şőūřčęş mäy ʼnőŧ ƒūʼnčŧįőʼn přőpęřľy: {{unsupportedDataSources}}.",
"unsupported-data-source-alert-title": "Ůʼnşūppőřŧęđ đäŧä şőūřčęş",
"unsupported-template-variable-alert-desc": "Ŧĥįş pūþľįč đäşĥþőäřđ mäy ʼnőŧ ŵőřĸ şįʼnčę įŧ ūşęş ŧęmpľäŧę väřįäþľęş",
"unsupported-template-variable-alert-title": "Ŧęmpľäŧę väřįäþľęş äřę ʼnőŧ şūppőřŧęđ"
},
"settings-bar-header": {
"collapse-settings-tooltip": "Cőľľäpşę şęŧŧįʼnģş",
"expand-settings-tooltip": "Ēχpäʼnđ şęŧŧįʼnģş"
},
"settings-configuration": {
"default-time-range-label": "Đęƒäūľŧ ŧįmę řäʼnģę",
"default-time-range-label-desc": "Ŧĥę pūþľįč đäşĥþőäřđ ūşęş ŧĥę đęƒäūľŧ ŧįmę řäʼnģę şęŧŧįʼnģş őƒ ŧĥę đäşĥþőäřđ",
"show-annotations-label": "Ŝĥőŵ äʼnʼnőŧäŧįőʼnş",
"show-annotations-label-desc": "Ŝĥőŵ äʼnʼnőŧäŧįőʼnş őʼn pūþľįč đäşĥþőäřđ",
"time-range-picker-label": "Ŧįmę řäʼnģę pįčĸęř ęʼnäþľęđ",
"time-range-picker-label-desc": "Åľľőŵ vįęŵęřş ŧő čĥäʼnģę ŧįmę řäʼnģę"
},
"settings-summary": {
"annotations-hide-text": "Åʼnʼnőŧäŧįőʼnş = ĥįđę",
"annotations-show-text": "Åʼnʼnőŧäŧįőʼnş = şĥőŵ",
"time-range-picker-disabled-text": "Ŧįmę řäʼnģę pįčĸęř = đįşäþľęđ",
"time-range-picker-enabled-text": "Ŧįmę řäʼnģę pįčĸęř = ęʼnäþľęđ",
"time-range-text": "Ŧįmę řäʼnģę = "
}
},
"public-dashboard-list": {
"button": {
"config-button-tooltip": "Cőʼnƒįģūřę pūþľįč đäşĥþőäřđ",
"revoke-button-text": "Ŗęvőĸę pūþľįč ŮŖĿ",
"revoke-button-tooltip": "Ŗęvőĸę pūþľįč đäşĥþőäřđ ŮŖĿ",
"view-button-tooltip": "Vįęŵ pūþľįč đäşĥþőäřđ"
},
"dashboard-title": {
"orphaned-title": "<0>Øřpĥäʼnęđ pūþľįč đäşĥþőäřđ</0>",
"orphaned-tooltip": "Ŧĥę ľįʼnĸęđ đäşĥþőäřđ ĥäş äľřęäđy þęęʼn đęľęŧęđ"
},
"toggle": {
"pause-sharing-toggle-text": "Päūşę şĥäřįʼnģ"
}
},
"public-dashboard-users-access-list": {
"dashboard-modal": {
"loading-text": "Ŀőäđįʼnģ...",
"open-dashboard-list-text": "Øpęʼn đäşĥþőäřđş ľįşŧ",
"public-dashboard-link": "Pūþľįč đäşĥþőäřđ ŮŖĿ",
"public-dashboard-setting": "Pūþľįč đäşĥþőäřđ şęŧŧįʼnģş"
},
"delete-user-modal": {
"delete-user-button-text": "Đęľęŧę ūşęř",
"delete-user-cancel-button": "Cäʼnčęľ",
"delete-user-revoke-access-button": "Ŗęvőĸę äččęşş",
"revoke-access-title": "Ŗęvőĸę äččęşş",
"revoke-user-access-modal-desc-line1": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő řęvőĸę äččęşş ƒőř {{email}}?",
"revoke-user-access-modal-desc-line2": "Ŧĥįş äčŧįőʼn ŵįľľ įmmęđįäŧęľy řęvőĸę {{email}}&äpőş;ş äččęşş ŧő äľľ pūþľįč đäşĥþőäřđş."
},
"modal": {
"dashboard-modal-title": "Pūþľįč đäşĥþőäřđş"
},
"table-header": {
"activated-label": "Åčŧįväŧęđ",
"activated-tooltip": "Ēäřľįęşŧ ŧįmę ūşęř ĥäş þęęʼn äʼn äčŧįvę ūşęř ŧő ä đäşĥþőäřđ",
"email-label": "Ēmäįľ",
"last-active-label": "Ŀäşŧ äčŧįvę",
"origin-label": "Øřįģįʼn",
"role-label": "Ŗőľę"
}
},
"query-operation": {
"header": {
"collapse-row": "Cőľľäpşę qūęřy řőŵ",
@ -1201,6 +1315,7 @@
"link": "Ŀįʼnĸ",
"panel-embed": "Ēmþęđ",
"public-dashboard": "Pūþľįč Đäşĥþőäřđ",
"public-dashboard-title": "Pūþľįč đäşĥþőäřđ",
"snapshot": "Ŝʼnäpşĥőŧ"
},
"theme-picker": {
@ -1349,6 +1464,11 @@
"user-sessions": {
"loading": "Ŀőäđįʼnģ şęşşįőʼnş..."
},
"users-access-list": {
"tabs": {
"public-dashboard-users-tab-title": "Pūþľįč đäşĥþőäřđ ūşęřş"
}
},
"variable": {
"adhoc": {
"placeholder": "Ŝęľęčŧ väľūę"

View File

@ -1066,6 +1066,120 @@
"passwords-must-match": "密码必须一致"
}
},
"public-dashboard": {
"acknowledgment-checkboxes": {
"ack-title": "",
"data-src-ack-desc": "",
"data-src-ack-tooltip": "",
"public-ack-desc": "",
"public-ack-tooltip": "",
"usage-ack-desc": "",
"usage-ack-desc-tooltip": ""
},
"config": {
"can-view-dashboard-radio-button-label": "",
"copy-button": "",
"dashboard-url-field-label": "",
"email-share-type-option-label": "",
"pause-sharing-dashboard-label": "",
"public-share-type-option-label": "",
"revoke-public-URL-button": "",
"revoke-public-URL-button-title": "",
"settings-title": ""
},
"create-page": {
"generate-public-url-button": "",
"unsupported-features-desc": "",
"welcome-title": ""
},
"delete-modal": {
"revoke-nonorphaned-body-text": "",
"revoke-orphaned-body-text": "",
"revoke-title": ""
},
"email-sharing": {
"input-invalid-email-text": "",
"input-required-email-text": "",
"invite-button": "",
"invite-field-desc": "",
"invite-field-label": "",
"resend-button": "",
"resend-button-title": "",
"revoke-button": "",
"revoke-button-title": ""
},
"modal-alerts": {
"no-upsert-perm-alert-desc": "",
"no-upsert-perm-alert-title": "",
"save-dashboard-changes-alert-title": "",
"unsupport-data-source-alert-readmore-link": "",
"unsupported-data-source-alert-desc": "",
"unsupported-data-source-alert-title": "",
"unsupported-template-variable-alert-desc": "",
"unsupported-template-variable-alert-title": ""
},
"settings-bar-header": {
"collapse-settings-tooltip": "",
"expand-settings-tooltip": ""
},
"settings-configuration": {
"default-time-range-label": "",
"default-time-range-label-desc": "",
"show-annotations-label": "",
"show-annotations-label-desc": "",
"time-range-picker-label": "",
"time-range-picker-label-desc": ""
},
"settings-summary": {
"annotations-hide-text": "",
"annotations-show-text": "",
"time-range-picker-disabled-text": "",
"time-range-picker-enabled-text": "",
"time-range-text": ""
}
},
"public-dashboard-list": {
"button": {
"config-button-tooltip": "",
"revoke-button-text": "",
"revoke-button-tooltip": "",
"view-button-tooltip": ""
},
"dashboard-title": {
"orphaned-title": "",
"orphaned-tooltip": ""
},
"toggle": {
"pause-sharing-toggle-text": ""
}
},
"public-dashboard-users-access-list": {
"dashboard-modal": {
"loading-text": "",
"open-dashboard-list-text": "",
"public-dashboard-link": "",
"public-dashboard-setting": ""
},
"delete-user-modal": {
"delete-user-button-text": "",
"delete-user-cancel-button": "",
"delete-user-revoke-access-button": "",
"revoke-access-title": "",
"revoke-user-access-modal-desc-line1": "",
"revoke-user-access-modal-desc-line2": ""
},
"modal": {
"dashboard-modal-title": ""
},
"table-header": {
"activated-label": "",
"activated-tooltip": "",
"email-label": "",
"last-active-label": "",
"origin-label": "",
"role-label": ""
}
},
"query-operation": {
"header": {
"collapse-row": "折叠查询行",
@ -1195,6 +1309,7 @@
"link": "链接",
"panel-embed": "嵌入",
"public-dashboard": "公共仪表板",
"public-dashboard-title": "",
"snapshot": "快照"
},
"theme-picker": {
@ -1343,6 +1458,11 @@
"user-sessions": {
"loading": "正在加载会话..."
},
"users-access-list": {
"tabs": {
"public-dashboard-users-tab-title": ""
}
},
"variable": {
"adhoc": {
"placeholder": "选择值"