mirror of
https://github.com/grafana/grafana.git
synced 2025-09-19 21:24:56 +08:00
Internationalisation: Mark up @grafana/sql
package (#105842)
* scaffolding for package * crowdin scaffolding * markup * add translations * fix locale location * fix tsconfig? * undo bundler change * object tranlsations, expose loadResources and call in mssql * prettier * remove useTranslate * extract translations * last couple of fixes * remove deleted files
This commit is contained in:
2
Makefile
2
Makefile
@ -140,6 +140,8 @@ endif
|
||||
i18n-extract: i18n-extract-enterprise
|
||||
@echo "Extracting i18n strings for OSS"
|
||||
yarn run i18next --config public/locales/i18next-parser.config.cjs
|
||||
@echo "Extracting i18n strings for packages"
|
||||
yarn run packages:i18n-extract
|
||||
@echo "Extracting i18n strings for plugins"
|
||||
yarn run plugin:i18n-extract
|
||||
|
||||
|
@ -21,4 +21,10 @@ files: [
|
||||
"type": "i18next_json",
|
||||
"dest": "plugins/mssql/en-US/%original_file_name%"
|
||||
},
|
||||
{
|
||||
"source": "packages/grafana-sql/src/locales/en-US/grafana-sql.json",
|
||||
"translation": "packages/grafana-sql/src/locales/%locale%/%original_file_name%",
|
||||
"type": "i18next_json",
|
||||
"dest": "packages/grafana-sql/en-US/%original_file_name%"
|
||||
},
|
||||
]
|
||||
|
@ -304,6 +304,7 @@ module.exports = [
|
||||
files: [
|
||||
'public/app/!(plugins)/**/*.{ts,tsx,js,jsx}',
|
||||
'packages/grafana-ui/**/*.{ts,tsx,js,jsx}',
|
||||
'packages/grafana-sql/**/*.{ts,tsx,js,jsx}',
|
||||
...pluginsToTranslate.map((plugin) => `${plugin}/**/*.{ts,tsx,js,jsx}`),
|
||||
],
|
||||
ignores: [
|
||||
|
@ -38,6 +38,7 @@
|
||||
"lint:fix": "yarn lint:ts --fix",
|
||||
"packages:build": "nx run-many -t build --projects='tag:scope:package'",
|
||||
"packages:clean": "rimraf ./npm-artifacts && nx run-many -t clean --projects='tag:scope:package' --maxParallel=100",
|
||||
"packages:i18n-extract": "nx run-many -t i18n-extract --projects='tag:scope:package'",
|
||||
"packages:prepare": "lerna version --no-push --no-git-tag-version --force-publish --exact",
|
||||
"packages:pack": "mkdir -p ./npm-artifacts && lerna exec --no-private -- yarn pack --out \"../../npm-artifacts/%s-%v.tgz\"",
|
||||
"packages:typecheck": "nx run-many -t typecheck --projects='tag:scope:package'",
|
||||
|
@ -11,12 +11,14 @@
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"i18n-extract": "i18next --config src/locales/i18next-parser.config.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.13.5",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/e2e-selectors": "12.1.0-pre",
|
||||
"@grafana/i18n": "12.1.0-pre",
|
||||
"@grafana/plugin-ui": "0.10.6",
|
||||
"@grafana/runtime": "12.1.0-pre",
|
||||
"@grafana/ui": "12.1.0-pre",
|
||||
@ -47,6 +49,7 @@
|
||||
"@types/react-virtualized-auto-sizer": "1.0.4",
|
||||
"@types/systemjs": "6.15.1",
|
||||
"@types/uuid": "10.0.0",
|
||||
"i18next-parser": "9.3.0",
|
||||
"jest": "^29.6.4",
|
||||
"ts-jest": "29.2.5",
|
||||
"ts-node": "10.9.2",
|
||||
|
@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
||||
import { useRef, useEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { Button, Icon, Modal, useStyles2 } from '@grafana/ui';
|
||||
|
||||
type ConfirmModalProps = {
|
||||
@ -27,26 +28,32 @@ export function ConfirmModal({ isOpen, onCancel, onDiscard, onCopy }: ConfirmMod
|
||||
title={
|
||||
<div className={styles.modalHeaderTitle}>
|
||||
<Icon name="exclamation-triangle" size="lg" />
|
||||
<span className={styles.titleText}>Warning</span>
|
||||
<span className={styles.titleText}>
|
||||
<Trans i18nKey="components.confirm-modal.warning">Warning</Trans>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
onDismiss={onCancel}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
<p>
|
||||
Builder mode does not display changes made in code. The query builder will display the last changes you made in
|
||||
builder mode.
|
||||
<Trans i18nKey="components.confirm-modal.builder-mode">
|
||||
Builder mode does not display changes made in code. The query builder will display the last changes you made
|
||||
in builder mode.
|
||||
</Trans>
|
||||
</p>
|
||||
<p>
|
||||
<Trans i18nKey="components.confirm-modal.clipboard">Do you want to copy your code to the clipboard?</Trans>
|
||||
</p>
|
||||
<p>Do you want to copy your code to the clipboard?</p>
|
||||
<Modal.ButtonRow>
|
||||
<Button type="button" variant="secondary" onClick={onCancel} fill="outline">
|
||||
Cancel
|
||||
<Trans i18nKey="components.confirm-modal.cancel">Cancel</Trans>
|
||||
</Button>
|
||||
<Button variant="destructive" type="button" onClick={onDiscard} ref={buttonRef}>
|
||||
Discard code and switch
|
||||
<Trans i18nKey="components.confirm-modal.discard-code-and-switch">Discard code and switch</Trans>
|
||||
</Button>
|
||||
<Button variant="primary" onClick={onCopy}>
|
||||
Copy code and switch
|
||||
<Trans i18nKey="components.confirm-modal.copy-code-and-switch">Copy code and switch</Trans>
|
||||
</Button>
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
|
@ -2,6 +2,7 @@ import { useEffect } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
import { DB, ResourceSelectorProps, SQLDialect, toOption } from '../types';
|
||||
@ -75,7 +76,7 @@ export const DatasetSelector = ({
|
||||
|
||||
return (
|
||||
<Select
|
||||
aria-label="Dataset selector"
|
||||
aria-label={t('components.dataset-selector.aria-label-dataset-selector', 'Dataset selector')}
|
||||
inputId={inputId}
|
||||
value={dataset}
|
||||
options={state.value}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Trans } from '@grafana/i18n';
|
||||
|
||||
type Props = {
|
||||
fallBackComponent?: React.ReactNode;
|
||||
};
|
||||
@ -16,7 +18,11 @@ export class ErrorBoundary extends React.Component<React.PropsWithChildren<Props
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
const FallBack = this.props.fallBackComponent || <div>Error</div>;
|
||||
const FallBack = this.props.fallBackComponent || (
|
||||
<div>
|
||||
<Trans i18nKey="components.error-boundary.fall-back.error">Error</Trans>
|
||||
</div>
|
||||
);
|
||||
return FallBack;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
||||
import { lazy, Suspense } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { LoadingPlaceholder, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import type { SqlQueryEditorProps } from './QueryEditor';
|
||||
@ -11,7 +12,14 @@ export function SqlQueryEditorLazy(props: SqlQueryEditorProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<LoadingPlaceholder text={'Loading editor'} className={styles.container} />}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<LoadingPlaceholder
|
||||
text={t('components.sql-query-editor-lazy.text-loading-editor', 'Loading editor')}
|
||||
className={styles.container}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<QueryEditor {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
@ -3,6 +3,7 @@ import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { EditorField, EditorHeader, EditorMode, EditorRow, FlexItem, InlineSelect } from '@grafana/plugin-ui';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Button, InlineSwitch, RadioButtonGroup, Tooltip, Space } from '@grafana/ui';
|
||||
@ -126,9 +127,9 @@ export function QueryHeader({
|
||||
<>
|
||||
<EditorHeader>
|
||||
<InlineSelect
|
||||
label="Format"
|
||||
label={t('components.query-header.label-format', 'Format')}
|
||||
value={query.format}
|
||||
placeholder="Select format"
|
||||
placeholder={t('components.query-header.placeholder-select-format', 'Select format')}
|
||||
menuShouldPortal
|
||||
onChange={onFormatChange}
|
||||
options={QUERY_FORMAT_OPTIONS}
|
||||
@ -138,7 +139,7 @@ export function QueryHeader({
|
||||
<>
|
||||
<InlineSwitch
|
||||
id={`sql-filter-${htmlId}`}
|
||||
label="Filter"
|
||||
label={t('components.query-header.label-filter', 'Filter')}
|
||||
data-testid={selectors.components.SQLQueryEditor.headerFilterSwitch}
|
||||
transparent={true}
|
||||
showLabel={true}
|
||||
@ -159,7 +160,7 @@ export function QueryHeader({
|
||||
|
||||
<InlineSwitch
|
||||
id={`sql-group-${htmlId}`}
|
||||
label="Group"
|
||||
label={t('components.query-header.label-group', 'Group')}
|
||||
data-testid={selectors.components.SQLQueryEditor.headerGroupSwitch}
|
||||
transparent={true}
|
||||
showLabel={true}
|
||||
@ -180,7 +181,7 @@ export function QueryHeader({
|
||||
|
||||
<InlineSwitch
|
||||
id={`sql-order-${htmlId}`}
|
||||
label="Order"
|
||||
label={t('components.query-header.label-order', 'Order')}
|
||||
data-testid={selectors.components.SQLQueryEditor.headerOrderSwitch}
|
||||
transparent={true}
|
||||
showLabel={true}
|
||||
@ -201,7 +202,7 @@ export function QueryHeader({
|
||||
|
||||
<InlineSwitch
|
||||
id={`sql-preview-${htmlId}`}
|
||||
label="Preview"
|
||||
label={t('components.query-header.label-preview', 'Preview')}
|
||||
data-testid={selectors.components.SQLQueryEditor.headerPreviewSwitch}
|
||||
transparent={true}
|
||||
showLabel={true}
|
||||
@ -226,21 +227,21 @@ export function QueryHeader({
|
||||
|
||||
{isQueryRunnable ? (
|
||||
<Button icon="play" variant="primary" size="sm" onClick={() => onRunQuery()}>
|
||||
Run query
|
||||
<Trans i18nKey="components.query-header.run-query">Run query</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<Tooltip
|
||||
theme="error"
|
||||
content={
|
||||
<>
|
||||
<Trans i18nKey="components.query-header.content-invalid-query">
|
||||
Your query is invalid. Check below for details. <br />
|
||||
However, you can still run this query.
|
||||
</>
|
||||
</Trans>
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<Button icon="exclamation-triangle" variant="secondary" size="sm" onClick={() => onRunQuery()}>
|
||||
Run query
|
||||
<Trans i18nKey="components.query-header.run-query">Run query</Trans>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
@ -295,7 +296,7 @@ export function QueryHeader({
|
||||
<Space v={0.5} />
|
||||
<EditorRow>
|
||||
{datasetDropdownIsAvailable() && (
|
||||
<EditorField label="Dataset" width={25}>
|
||||
<EditorField label={t('components.query-header.label-dataset', 'Dataset')} width={25}>
|
||||
<DatasetSelector
|
||||
db={db}
|
||||
inputId={`sql-dataset-${htmlId}`}
|
||||
@ -306,7 +307,7 @@ export function QueryHeader({
|
||||
/>
|
||||
</EditorField>
|
||||
)}
|
||||
<EditorField label="Table" width={25}>
|
||||
<EditorField label={t('components.query-header.label-table', 'Table')} width={25}>
|
||||
<TableSelector
|
||||
db={db}
|
||||
inputId={`sql-tableselect-${htmlId}`}
|
||||
|
@ -2,6 +2,7 @@ import { useAsync } from 'react-use';
|
||||
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
import { DB, ResourceSelectorProps } from '../types';
|
||||
@ -29,7 +30,7 @@ export const TableSelector = ({ db, dataset, table, className, onChange, inputId
|
||||
<Select
|
||||
className={className}
|
||||
disabled={state.loading}
|
||||
aria-label="Table selector"
|
||||
aria-label={t('components.table-selector.aria-label-table-selector', 'Table selector')}
|
||||
inputId={inputId}
|
||||
data-testid={selectors.components.SQLQueryEditor.headerTableSelector}
|
||||
value={table}
|
||||
@ -37,7 +38,11 @@ export const TableSelector = ({ db, dataset, table, className, onChange, inputId
|
||||
onChange={onChange}
|
||||
isLoading={state.loading}
|
||||
menuShouldPortal={true}
|
||||
placeholder={state.loading ? 'Loading tables' : 'Select table'}
|
||||
placeholder={
|
||||
state.loading
|
||||
? t('components.table-selector.placeholder-loading', 'Loading tables')
|
||||
: t('components.table-selector.placeholder-select-table', 'Select table')
|
||||
}
|
||||
allowCustomValue={true}
|
||||
/>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { ConfigSubSection } from '@grafana/plugin-ui';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Field, Icon, InlineLabel, Label, Stack, Switch, Tooltip } from '@grafana/ui';
|
||||
@ -83,19 +84,23 @@ export const ConnectionLimits = <T extends SQLConnectionLimits>(props: Props<T>)
|
||||
const labelWidth = 40;
|
||||
|
||||
return (
|
||||
<ConfigSubSection title="Connection limits">
|
||||
<ConfigSubSection title={t('components.connection-limits.title-connection-limits', 'Connection limits')}>
|
||||
<Field
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>Max open</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.connection-limits.max-open">Max open</Trans>
|
||||
</span>
|
||||
<Tooltip
|
||||
content={
|
||||
<span>
|
||||
<Trans i18nKey="components.connection-limits.content-max-open">
|
||||
The maximum number of open connections to the database. If <i>Max idle connections</i> is greater
|
||||
than 0 and the <i>Max open connections</i> is less than <i>Max idle connections</i>, then
|
||||
<i>Max idle connections</i> will be reduced to match the <i>Max open connections</i> limit. If set
|
||||
to 0, there is no limit on the number of open connections.
|
||||
</Trans>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
@ -119,13 +124,20 @@ export const ConnectionLimits = <T extends SQLConnectionLimits>(props: Props<T>)
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>Auto max idle</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.connection-limits.auto-max-idle">Auto max idle</Trans>
|
||||
</span>
|
||||
<Tooltip
|
||||
content={
|
||||
<span>
|
||||
<Trans
|
||||
i18nKey="components.connection-limits.content-auto-max-idle"
|
||||
values={{ defaultMaxIdle: config.sqlConnectionLimits.maxIdleConns }}
|
||||
>
|
||||
If enabled, automatically set the number of <i>Maximum idle connections</i> to the same value as
|
||||
<i> Max open connections</i>. If the number of maximum open connections is not set it will be set to
|
||||
the default ({config.sqlConnectionLimits.maxIdleConns}).
|
||||
<i> Max open connections</i>. If the number of maximum open connections is not set it will be set
|
||||
to the default ({'{{defaultMaxIdle}}'}).
|
||||
</Trans>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
@ -142,14 +154,18 @@ export const ConnectionLimits = <T extends SQLConnectionLimits>(props: Props<T>)
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>Max idle</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.connection-limits.max-idle">Max idle</Trans>
|
||||
</span>
|
||||
<Tooltip
|
||||
content={
|
||||
<span>
|
||||
<Trans i18nKey="components.connection-limits.content-max-idle">
|
||||
The maximum number of connections in the idle connection pool.If <i>Max open connections</i> is
|
||||
greater than 0 but less than the <i>Max idle connections</i>, then the <i>Max idle connections</i>{' '}
|
||||
will be reduced to match the <i>Max open connections</i> limit. If set to 0, no idle connections are
|
||||
retained.
|
||||
will be reduced to match the <i>Max open connections</i> limit. If set to 0, no idle connections
|
||||
are retained.
|
||||
</Trans>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
@ -177,12 +193,16 @@ export const ConnectionLimits = <T extends SQLConnectionLimits>(props: Props<T>)
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>Max lifetime</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.connection-limits.max-lifetime">Max lifetime</Trans>
|
||||
</span>
|
||||
<Tooltip
|
||||
content={
|
||||
<span>
|
||||
<Trans i18nKey="components.connection-limits.content-max-lifetime">
|
||||
The maximum amount of time in seconds a connection may be reused. If set to 0, connections are
|
||||
reused forever.
|
||||
</Trans>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
onUpdateDatasourceSecureJsonDataOption,
|
||||
updateDatasourcePluginResetOption,
|
||||
} from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { Field, Icon, Label, SecretTextArea, Tooltip, Stack } from '@grafana/ui';
|
||||
|
||||
export interface Props<T extends DataSourceJsonData, S> {
|
||||
@ -25,11 +26,17 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>TLS/SSL Client Certificate</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.tlssecrets-config.tlsssl-client-certificate">
|
||||
TLS/SSL Client Certificate
|
||||
</Trans>
|
||||
</span>
|
||||
<Tooltip
|
||||
content={
|
||||
<span>
|
||||
<Trans i18nKey="components.tlssecrets-config.content-tlsssl-client-certificate">
|
||||
To authenticate with an TLS/SSL client certificate, provide the client certificate here.
|
||||
</Trans>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
@ -40,6 +47,7 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
||||
}
|
||||
>
|
||||
<SecretTextArea
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="-----BEGIN CERTIFICATE-----"
|
||||
cols={45}
|
||||
rows={7}
|
||||
@ -56,10 +64,16 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>TLS/SSL Root Certificate</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.tlssecrets-config.tlsssl-root-certificate">TLS/SSL Root Certificate</Trans>
|
||||
</span>
|
||||
<Tooltip
|
||||
content={
|
||||
<span>If the selected TLS/SSL mode requires a server root certificate, provide it here.</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.tlssecrets-config.content-tlsssl-root-certificate">
|
||||
If the selected TLS/SSL mode requires a server root certificate, provide it here
|
||||
</Trans>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Icon name="info-circle" size="sm" />
|
||||
@ -69,6 +83,7 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
||||
}
|
||||
>
|
||||
<SecretTextArea
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="-----BEGIN CERTIFICATE-----"
|
||||
cols={45}
|
||||
rows={7}
|
||||
@ -85,9 +100,17 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
||||
label={
|
||||
<Label>
|
||||
<Stack gap={0.5}>
|
||||
<span>TLS/SSL Client Key</span>
|
||||
<span>
|
||||
<Trans i18nKey="components.tlssecrets-config.tlsssl-client-key">TLS/SSL Client Key</Trans>
|
||||
</span>
|
||||
<Tooltip
|
||||
content={<span>To authenticate with a client TLS/SSL certificate, provide the key here.</span>}
|
||||
content={
|
||||
<span>
|
||||
<Trans i18nKey="components.tlssecrets-config.content-tlsssl-client-key">
|
||||
To authenticate with a client TLS/SSL certificate, provide the key here.
|
||||
</Trans>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Icon name="info-circle" size="sm" />
|
||||
</Tooltip>
|
||||
@ -96,6 +119,7 @@ export const TLSSecretsConfig = <T extends DataSourceJsonData, S extends {} = {}
|
||||
}
|
||||
>
|
||||
<SecretTextArea
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="-----BEGIN RSA PRIVATE KEY-----"
|
||||
cols={45}
|
||||
rows={7}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { HorizontalGroup, Icon, IconButton, Tooltip, useTheme2 } from '@grafana/ui';
|
||||
|
||||
@ -80,7 +81,7 @@ export function QueryToolbox({ showTools, onFormatCode, onExpand, isExpanded, ..
|
||||
}}
|
||||
name="brackets-curly"
|
||||
size="xs"
|
||||
tooltip="Format query"
|
||||
tooltip={t('components.query-toolbox.tooltip-format-query', 'Format query')}
|
||||
/>
|
||||
)}
|
||||
{onExpand && (
|
||||
@ -95,10 +96,19 @@ export function QueryToolbox({ showTools, onFormatCode, onExpand, isExpanded, ..
|
||||
}}
|
||||
name={isExpanded ? 'angle-up' : 'angle-down'}
|
||||
size="xs"
|
||||
tooltip={isExpanded ? 'Collapse editor' : 'Expand editor'}
|
||||
tooltip={
|
||||
isExpanded
|
||||
? t('components.query-toolbox.tooltip-collapse', 'Collapse editor')
|
||||
: t('components.query-toolbox.tooltip-expand', 'Expand editor')
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Tooltip content="Hit CTRL/CMD+Return to run query">
|
||||
<Tooltip
|
||||
content={t(
|
||||
'components.query-toolbox.content-hit-ctrlcmdreturn-to-run-query',
|
||||
'Hit CTRL/CMD+Return to run query'
|
||||
)}
|
||||
>
|
||||
<Icon className={styles.hint} name="keyboard" />
|
||||
</Tooltip>
|
||||
</HorizontalGroup>
|
||||
|
@ -3,6 +3,7 @@ import { useState, useMemo, useEffect } from 'react';
|
||||
import { useAsyncFn, useDebounce } from 'react-use';
|
||||
|
||||
import { formattedValueToString, getValueFormat, TimeRange } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { Icon, Spinner, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { DB, SQLQuery, ValidationResults } from '../../types';
|
||||
@ -78,7 +79,8 @@ export function QueryValidator({ db, query, onValidate, range }: QueryValidatorP
|
||||
<>
|
||||
{state.loading && (
|
||||
<div className={styles.info}>
|
||||
<Spinner inline={true} size="xs" /> Validating query...
|
||||
<Spinner inline={true} size="xs" />{' '}
|
||||
<Trans i18nKey="components.query-validator.validating-query">Validating query...</Trans>
|
||||
</div>
|
||||
)}
|
||||
{!state.loading && state.value && (
|
||||
@ -86,9 +88,12 @@ export function QueryValidator({ db, query, onValidate, range }: QueryValidatorP
|
||||
<>
|
||||
{state.value.isValid && state.value.statistics && (
|
||||
<div className={styles.valid}>
|
||||
<Icon name="check" /> This query will process{' '}
|
||||
<strong>{formattedValueToString(valueFormatter(state.value.statistics.TotalBytesProcessed))}</strong>{' '}
|
||||
when run.
|
||||
<Trans
|
||||
i18nKey="components.query-validator.query-will-process"
|
||||
values={{ bytes: formattedValueToString(valueFormatter(state.value.statistics.TotalBytesProcessed)) }}
|
||||
>
|
||||
<Icon name="check" /> This query will process <strong>{'{{bytes}}'}</strong> when run.
|
||||
</Trans>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
@ -4,6 +4,7 @@ import { useMeasure } from 'react-use';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Modal, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
|
||||
@ -81,7 +82,9 @@ export function RawEditor({ db, query, onChange, onRunQuery, onValidate, queryTo
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Trans i18nKey="components.raw-editor.render-placeholder.editing-in-expanded-code-editor">
|
||||
Editing in expanded code editor
|
||||
</Trans>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -91,7 +94,7 @@ export function RawEditor({ db, query, onChange, onRunQuery, onValidate, queryTo
|
||||
{isExpanded ? renderPlaceholder() : renderEditor()}
|
||||
{isExpanded && (
|
||||
<Modal
|
||||
title={`Query ${query.refId}`}
|
||||
title={t('components.raw-editor.title-query-num', 'Query {{queryNum}}', { queryNum: query.refId })}
|
||||
closeOnBackdropClick={false}
|
||||
closeOnEscape={false}
|
||||
className={styles.modal}
|
||||
|
@ -17,6 +17,7 @@ import { isString } from 'lodash';
|
||||
|
||||
import { dateTime, toOption } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { Button, DateTimePicker, Input, Select } from '@grafana/ui';
|
||||
|
||||
const buttonLabels = {
|
||||
@ -68,7 +69,7 @@ export const widgets: Widgets = {
|
||||
return (
|
||||
<Select
|
||||
id={props.id}
|
||||
aria-label="Macros value selector"
|
||||
aria-label={t('components.widgets.aria-label-macros-value-selector', 'Macros value selector')}
|
||||
menuShouldPortal
|
||||
options={macros.map(toOption)}
|
||||
value={props?.value}
|
||||
@ -124,7 +125,7 @@ export const settings: Settings = {
|
||||
return (
|
||||
<Select
|
||||
id={conjProps?.id}
|
||||
aria-label="Conjunction"
|
||||
aria-label={t('components.settings.aria-label-conjunction', 'Conjunction')}
|
||||
data-testid={selectors.components.SQLQueryEditor.filterConjunction}
|
||||
menuShouldPortal
|
||||
options={conjProps?.conjunctionOptions ? Object.keys(conjProps?.conjunctionOptions).map(toOption) : undefined}
|
||||
@ -140,7 +141,7 @@ export const settings: Settings = {
|
||||
<Select
|
||||
id={fieldProps?.id}
|
||||
width={25}
|
||||
aria-label="Field"
|
||||
aria-label={t('components.settings.aria-label-field', 'Field')}
|
||||
data-testid={selectors.components.SQLQueryEditor.filterField}
|
||||
menuShouldPortal
|
||||
options={fieldProps?.items.map((f) => {
|
||||
@ -164,7 +165,9 @@ export const settings: Settings = {
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
title={`${buttonProps?.label} filter`}
|
||||
title={t('components.settings.title-button-filter', '{{ buttonLabel }} filter', {
|
||||
buttonLabel: buttonProps?.label,
|
||||
})}
|
||||
onClick={buttonProps?.onClick}
|
||||
variant="secondary"
|
||||
size="md"
|
||||
@ -177,7 +180,7 @@ export const settings: Settings = {
|
||||
return (
|
||||
<Select
|
||||
options={operatorProps?.items.map((op) => ({ label: op.label, value: op.key }))}
|
||||
aria-label="Operator"
|
||||
aria-label={t('components.settings.aria-label-operator', 'Operator')}
|
||||
data-testid={selectors.components.SQLQueryEditor.filterOperator}
|
||||
menuShouldPortal
|
||||
value={operatorProps?.selectedKey}
|
||||
@ -301,7 +304,7 @@ function getCustomOperators(config: BasicConfig) {
|
||||
sqlFormatOp: customSqlNotInFormatter,
|
||||
},
|
||||
[Op.MACROS]: {
|
||||
label: 'Macros',
|
||||
label: t('components.get-custom-operators.custom-operators.label.macros', 'Macros'),
|
||||
sqlFormatOp: (field: string, _operator: string, value: string | string[] | ImmutableList<string>) => {
|
||||
if (value === TIME_FILTER) {
|
||||
return `$__timeFilter(${field})`;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { AccessoryButton, EditorList, InputGroup } from '@grafana/plugin-ui';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
@ -46,12 +47,20 @@ function makeRenderColumn({ options }: { options?: Array<SelectableValue<string>
|
||||
<InputGroup>
|
||||
<Select
|
||||
value={item.property?.name ? toOption(item.property.name) : null}
|
||||
aria-label="Group by"
|
||||
aria-label={t('components.make-render-column.render-column.aria-label-group-by', 'Group by')}
|
||||
options={options}
|
||||
menuShouldPortal
|
||||
onChange={({ value }) => value && onChangeItem(setGroupByField(value))}
|
||||
/>
|
||||
<AccessoryButton title="Remove group by column" icon="times" variant="secondary" onClick={onDeleteItem} />
|
||||
<AccessoryButton
|
||||
title={t(
|
||||
'components.make-render-column.render-column.title-remove-group-by-column',
|
||||
'Remove group by column'
|
||||
)}
|
||||
icon="times"
|
||||
variant="secondary"
|
||||
onClick={onDeleteItem}
|
||||
/>
|
||||
</InputGroup>
|
||||
);
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ import { useCallback } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { EditorField, InputGroup } from '@grafana/plugin-ui';
|
||||
import { Input, RadioButtonGroup, Select, Space } from '@grafana/ui';
|
||||
|
||||
@ -59,10 +60,10 @@ export function OrderByRow({ sql, onSqlChange, columns, showOffset }: OrderByRow
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditorField label="Order by" width={25}>
|
||||
<EditorField label={t('components.order-by-row.label-order-by', 'Order by')} width={25}>
|
||||
<InputGroup>
|
||||
<Select
|
||||
aria-label="Order by"
|
||||
aria-label={t('components.order-by-row.aria-label-order-by', 'Order by')}
|
||||
options={columns}
|
||||
value={sql.orderBy?.property.name ? toOption(sql.orderBy.property.name) : null}
|
||||
isClearable
|
||||
@ -80,11 +81,11 @@ export function OrderByRow({ sql, onSqlChange, columns, showOffset }: OrderByRow
|
||||
/>
|
||||
</InputGroup>
|
||||
</EditorField>
|
||||
<EditorField label="Limit" optional width={25}>
|
||||
<EditorField label={t('components.order-by-row.label-limit', 'Limit')} optional width={25}>
|
||||
<Input type="number" min={0} id={uniqueId('limit-')} value={sql.limit || ''} onChange={onLimitChange} />
|
||||
</EditorField>
|
||||
{showOffset && (
|
||||
<EditorField label="Offset" optional width={25}>
|
||||
<EditorField label={t('components.order-by-row.label-offset', 'Offset')} optional width={25}>
|
||||
<Input type="number" id={uniqueId('offset-')} value={sql.offset || ''} onChange={onOffsetChange} />
|
||||
</EditorField>
|
||||
)}
|
||||
|
@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { CodeEditor, Field, IconButton, useStyles2 } from '@grafana/ui';
|
||||
|
||||
@ -26,8 +27,14 @@ export function Preview({ rawSql, datasourceType }: PreviewProps) {
|
||||
|
||||
const labelElement = (
|
||||
<div className={styles.labelWrapper}>
|
||||
<span className={styles.label}>Preview</span>
|
||||
<IconButton tooltip="Copy to clipboard" onClick={() => copyPreview(rawSql)} name="copy" />
|
||||
<span className={styles.label}>
|
||||
<Trans i18nKey="components.preview.label-element.preview">Preview</Trans>
|
||||
</span>
|
||||
<IconButton
|
||||
tooltip={t('components.preview.label-element.tooltip-copy-to-clipboard', 'Copy to clipboard')}
|
||||
onClick={() => copyPreview(rawSql)}
|
||||
name="copy"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { useId } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { EditorField } from '@grafana/plugin-ui';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
@ -15,7 +16,7 @@ export function SelectColumn({ columns, onParameterChange, value }: Props) {
|
||||
const selectInputId = useId();
|
||||
|
||||
return (
|
||||
<EditorField label="Column" width={25}>
|
||||
<EditorField label={t('components.select-column.label-column', 'Column')} width={25}>
|
||||
<Select
|
||||
value={value}
|
||||
data-testid={selectors.components.SQLQueryEditor.selectColumn}
|
||||
|
@ -3,6 +3,7 @@ import { useCallback } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { Button, InlineLabel, Input, Stack, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { QueryEditorExpressionType } from '../../expressions';
|
||||
@ -85,11 +86,18 @@ export function SelectCustomFunctionParameters({
|
||||
<Input
|
||||
onChange={(e) => onParameterChange(index)(e.currentTarget.value)}
|
||||
value={param.name}
|
||||
aria-label={`Parameter ${index} for column ${columnIndex}`}
|
||||
aria-label={t(
|
||||
'components.select-custom-function-parameters.aria-label-parameter',
|
||||
'Parameter {{index}} for column {{columnIndex}}',
|
||||
{ index, columnIndex }
|
||||
)}
|
||||
data-testid={selectors.components.SQLQueryEditor.selectInputParameter}
|
||||
addonAfter={
|
||||
<Button
|
||||
title="Remove parameter"
|
||||
title={t(
|
||||
'components.select-custom-function-parameters.render-parameters.params.title-remove-parameter',
|
||||
'Remove parameter'
|
||||
)}
|
||||
type="button"
|
||||
icon="times"
|
||||
variant="secondary"
|
||||
@ -119,7 +127,7 @@ export function SelectCustomFunctionParameters({
|
||||
variant="secondary"
|
||||
size="md"
|
||||
icon="plus"
|
||||
title="Add parameter"
|
||||
title={t('components.select-custom-function-parameters.title-add-parameter', 'Add parameter')}
|
||||
/>
|
||||
<InlineLabel className={styles.label}>)</InlineLabel>
|
||||
</>
|
||||
|
@ -4,6 +4,7 @@ import { useCallback } from 'react';
|
||||
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { EditorField } from '@grafana/plugin-ui';
|
||||
import { Button, Select, Stack, useStyles2 } from '@grafana/ui';
|
||||
|
||||
@ -29,8 +30,8 @@ export function SelectRow({ query, onQueryChange, db, columns }: SelectRowProps)
|
||||
// Add necessary alias options for time series format
|
||||
// when that format has been selected
|
||||
if (query.format === QueryFormat.Timeseries) {
|
||||
timeSeriesAliasOpts.push({ label: 'time', value: 'time' });
|
||||
timeSeriesAliasOpts.push({ label: 'value', value: 'value' });
|
||||
timeSeriesAliasOpts.push({ label: t('components.select-row.label.time', 'time'), value: 'time' });
|
||||
timeSeriesAliasOpts.push({ label: t('components.select-row.label.value', 'value'), value: 'value' });
|
||||
}
|
||||
|
||||
const onAggregationChange = useCallback(
|
||||
@ -92,8 +93,8 @@ export function SelectRow({ query, onQueryChange, db, columns }: SelectRowProps)
|
||||
|
||||
const aggregateOptions = () => {
|
||||
const options: Array<SelectableValue<string>> = [
|
||||
{ label: 'Aggregations', options: [] },
|
||||
{ label: 'Macros', options: [] },
|
||||
{ label: t('components.select-row.aggregate-options.options.label.aggregations', 'Aggregations'), options: [] },
|
||||
{ label: t('components.select-row.aggregate-options.options.label.macros', 'Macros'), options: [] },
|
||||
];
|
||||
for (const func of db.functions()) {
|
||||
// Create groups for macros
|
||||
@ -111,7 +112,11 @@ export function SelectRow({ query, onQueryChange, db, columns }: SelectRowProps)
|
||||
{query.sql?.columns?.map((item, index) => (
|
||||
<div key={index}>
|
||||
<Stack gap={2} alignItems="end">
|
||||
<EditorField label="Data operations" optional width={25}>
|
||||
<EditorField
|
||||
label={t('components.select-row.label-data-operations', 'Data operations')}
|
||||
optional
|
||||
width={25}
|
||||
>
|
||||
<Select
|
||||
value={item.name ? toOption(item.name) : null}
|
||||
inputId={`select-aggregation-${index}-${uniqueId()}`}
|
||||
@ -132,7 +137,7 @@ export function SelectRow({ query, onQueryChange, db, columns }: SelectRowProps)
|
||||
db={db}
|
||||
/>
|
||||
|
||||
<EditorField label="Alias" optional width={15}>
|
||||
<EditorField label={t('components.select-row.label-alias', 'Alias')} optional width={15}>
|
||||
<Select
|
||||
value={item.alias ? toOption(item.alias) : null}
|
||||
inputId={`select-alias-${index}-${uniqueId()}`}
|
||||
@ -145,7 +150,7 @@ export function SelectRow({ query, onQueryChange, db, columns }: SelectRowProps)
|
||||
/>
|
||||
</EditorField>
|
||||
<Button
|
||||
title="Remove column"
|
||||
title={t('components.select-row.title-remove-column', 'Remove column')}
|
||||
type="button"
|
||||
icon="trash-alt"
|
||||
variant="secondary"
|
||||
@ -159,7 +164,7 @@ export function SelectRow({ query, onQueryChange, db, columns }: SelectRowProps)
|
||||
type="button"
|
||||
onClick={addColumn}
|
||||
variant="secondary"
|
||||
title="Add column"
|
||||
title={t('components.select-row.title-add-column', 'Add column')}
|
||||
size="md"
|
||||
icon="plus"
|
||||
className={styles.addButton}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { t } from '@grafana/i18n';
|
||||
import { EditorRows, EditorRow, EditorField } from '@grafana/plugin-ui';
|
||||
|
||||
import { DB, QueryEditorProps, QueryRowFilter } from '../../types';
|
||||
@ -31,14 +32,17 @@ export const VisualEditor = ({ query, db, queryRowFilter, onChange, onValidate,
|
||||
</EditorRow>
|
||||
{queryRowFilter.filter && (
|
||||
<EditorRow>
|
||||
<EditorField label="Filter by column value" optional>
|
||||
<EditorField
|
||||
label={t('components.visual-editor.label-filter-by-column-value', 'Filter by column value')}
|
||||
optional
|
||||
>
|
||||
<SQLWhereRow fields={state.value || []} query={query} onQueryChange={onChange} db={db} />
|
||||
</EditorField>
|
||||
</EditorRow>
|
||||
)}
|
||||
{queryRowFilter.group && (
|
||||
<EditorRow>
|
||||
<EditorField label="Group by column">
|
||||
<EditorField label={t('components.visual-editor.label-group-by-column', 'Group by column')}>
|
||||
<SQLGroupByRow fields={state.value || []} query={query} onQueryChange={onChange} db={db} />
|
||||
</EditorField>
|
||||
</EditorRow>
|
||||
|
@ -23,3 +23,4 @@ export { createSelectClause, haveColumns } from './utils/sql.utils';
|
||||
export { applyQueryDefaults } from './defaults';
|
||||
export { makeVariable } from './utils/testHelpers';
|
||||
export { QueryEditorExpressionType } from './expressions';
|
||||
export { loadResources } from './loadResources';
|
||||
|
11
packages/grafana-sql/src/loadResources.ts
Normal file
11
packages/grafana-sql/src/loadResources.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { LANGUAGES, ResourceLoader, Resources } from '@grafana/i18n';
|
||||
|
||||
const resources = LANGUAGES.reduce<Record<string, () => Promise<{ default: Resources }>>>((acc, lang) => {
|
||||
acc[lang.code] = async () => await import(`./locales/${lang.code}/grafana-sql.json`);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const loadResources: ResourceLoader = async (resolvedLanguage: string) => {
|
||||
const translation = await resources[resolvedLanguage]();
|
||||
return translation.default;
|
||||
};
|
148
packages/grafana-sql/src/locales/en-US/grafana-sql.json
Normal file
148
packages/grafana-sql/src/locales/en-US/grafana-sql.json
Normal file
@ -0,0 +1,148 @@
|
||||
{
|
||||
"components": {
|
||||
"confirm-modal": {
|
||||
"builder-mode": "Builder mode does not display changes made in code. The query builder will display the last changes you made in builder mode.",
|
||||
"cancel": "Cancel",
|
||||
"clipboard": "Do you want to copy your code to the clipboard?",
|
||||
"copy-code-and-switch": "Copy code and switch",
|
||||
"discard-code-and-switch": "Discard code and switch",
|
||||
"warning": "Warning"
|
||||
},
|
||||
"connection-limits": {
|
||||
"auto-max-idle": "Auto max idle",
|
||||
"content-auto-max-idle": "If enabled, automatically set the number of <1>Maximum idle connections</1> to the same value as<3> Max open connections</3>. If the number of maximum open connections is not set it will be set to the default ({{defaultMaxIdle}}).",
|
||||
"content-max-idle": "The maximum number of connections in the idle connection pool.If <1>Max open connections</1> is greater than 0 but less than the <3>Max idle connections</3>, then the <5>Max idle connections</5> will be reduced to match the <8>Max open connections</8> limit. If set to 0, no idle connections are retained.",
|
||||
"content-max-lifetime": "The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever.",
|
||||
"content-max-open": "The maximum number of open connections to the database. If <1>Max idle connections</1> is greater than 0 and the <3>Max open connections</3> is less than <5>Max idle connections</5>, then<7>Max idle connections</7> will be reduced to match the <9>Max open connections</9> limit. If set to 0, there is no limit on the number of open connections.",
|
||||
"max-idle": "Max idle",
|
||||
"max-lifetime": "Max lifetime",
|
||||
"max-open": "Max open",
|
||||
"title-connection-limits": "Connection limits"
|
||||
},
|
||||
"dataset-selector": {
|
||||
"aria-label-dataset-selector": "Dataset selector"
|
||||
},
|
||||
"error-boundary": {
|
||||
"fall-back": {
|
||||
"error": "Error"
|
||||
}
|
||||
},
|
||||
"get-custom-operators": {
|
||||
"custom-operators": {
|
||||
"label": {
|
||||
"macros": "Macros"
|
||||
}
|
||||
}
|
||||
},
|
||||
"make-render-column": {
|
||||
"render-column": {
|
||||
"aria-label-group-by": "Group by",
|
||||
"title-remove-group-by-column": "Remove group by column"
|
||||
}
|
||||
},
|
||||
"order-by-row": {
|
||||
"aria-label-order-by": "Order by",
|
||||
"label-limit": "Limit",
|
||||
"label-offset": "Offset",
|
||||
"label-order-by": "Order by"
|
||||
},
|
||||
"preview": {
|
||||
"label-element": {
|
||||
"preview": "Preview",
|
||||
"tooltip-copy-to-clipboard": "Copy to clipboard"
|
||||
}
|
||||
},
|
||||
"query-header": {
|
||||
"content-invalid-query": "Your query is invalid. Check below for details. <1></1>However, you can still run this query.",
|
||||
"label-dataset": "Dataset",
|
||||
"label-filter": "Filter",
|
||||
"label-format": "Format",
|
||||
"label-group": "Group",
|
||||
"label-order": "Order",
|
||||
"label-preview": "Preview",
|
||||
"label-table": "Table",
|
||||
"placeholder-select-format": "Select format",
|
||||
"run-query": "Run query"
|
||||
},
|
||||
"query-toolbox": {
|
||||
"content-hit-ctrlcmdreturn-to-run-query": "Hit CTRL/CMD+Return to run query",
|
||||
"tooltip-collapse": "Collapse editor",
|
||||
"tooltip-expand": "Expand editor",
|
||||
"tooltip-format-query": "Format query"
|
||||
},
|
||||
"query-validator": {
|
||||
"query-will-process": "<0></0> This query will process <2>{{bytes}}</2> when run.",
|
||||
"validating-query": "Validating query..."
|
||||
},
|
||||
"raw-editor": {
|
||||
"render-placeholder": {
|
||||
"editing-in-expanded-code-editor": "Editing in expanded code editor"
|
||||
},
|
||||
"title-query-num": "Query {{queryNum}}"
|
||||
},
|
||||
"select-column": {
|
||||
"label-column": "Column"
|
||||
},
|
||||
"select-custom-function-parameters": {
|
||||
"aria-label-parameter": "Parameter {{index}} for column {{columnIndex}}",
|
||||
"render-parameters": {
|
||||
"params": {
|
||||
"title-remove-parameter": "Remove parameter"
|
||||
}
|
||||
},
|
||||
"title-add-parameter": "Add parameter"
|
||||
},
|
||||
"select-row": {
|
||||
"aggregate-options": {
|
||||
"options": {
|
||||
"label": {
|
||||
"aggregations": "Aggregations",
|
||||
"macros": "Macros"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"time": "time",
|
||||
"value": "value"
|
||||
},
|
||||
"label-alias": "Alias",
|
||||
"label-data-operations": "Data operations",
|
||||
"title-add-column": "Add column",
|
||||
"title-remove-column": "Remove column"
|
||||
},
|
||||
"settings": {
|
||||
"aria-label-conjunction": "Conjunction",
|
||||
"aria-label-field": "Field",
|
||||
"aria-label-operator": "Operator",
|
||||
"title-button-filter": "{{ buttonLabel }} filter"
|
||||
},
|
||||
"sql-query-editor-lazy": {
|
||||
"text-loading-editor": "Loading editor"
|
||||
},
|
||||
"table-selector": {
|
||||
"aria-label-table-selector": "Table selector",
|
||||
"placeholder-loading": "Loading tables",
|
||||
"placeholder-select-table": "Select table"
|
||||
},
|
||||
"tlssecrets-config": {
|
||||
"content-tlsssl-client-certificate": "To authenticate with an TLS/SSL client certificate, provide the client certificate here.",
|
||||
"content-tlsssl-client-key": "To authenticate with a client TLS/SSL certificate, provide the key here.",
|
||||
"content-tlsssl-root-certificate": "If the selected TLS/SSL mode requires a server root certificate, provide it here",
|
||||
"tlsssl-client-certificate": "TLS/SSL Client Certificate",
|
||||
"tlsssl-client-key": "TLS/SSL Client Key",
|
||||
"tlsssl-root-certificate": "TLS/SSL Root Certificate"
|
||||
},
|
||||
"visual-editor": {
|
||||
"label-filter-by-column-value": "Filter by column value",
|
||||
"label-group-by-column": "Group by column"
|
||||
},
|
||||
"widgets": {
|
||||
"aria-label-macros-value-selector": "Macros value selector"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"get-columns-width-indices": {
|
||||
"label-selected-columns": "Selected columns"
|
||||
}
|
||||
}
|
||||
}
|
12
packages/grafana-sql/src/locales/i18next-parser.config.cjs
Normal file
12
packages/grafana-sql/src/locales/i18next-parser.config.cjs
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
locales: ['en-US'], // Only en-US is updated - Crowdin will PR with other languages
|
||||
sort: true,
|
||||
createOldCatalogs: false,
|
||||
failOnWarnings: true,
|
||||
verbose: false,
|
||||
resetDefaultValueLocale: 'en-US', // Updates extracted values when they change in code
|
||||
|
||||
defaultNamespace: 'grafana-sql',
|
||||
input: ['../**/*.{tsx,ts}'],
|
||||
output: './src/locales/$LOCALE/$NAMESPACE.json',
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
|
||||
import { SQLQuery } from '../types';
|
||||
|
||||
@ -20,7 +21,7 @@ export function getColumnsWithIndices(query: SQLQuery, fields: SelectableValue[]
|
||||
return [
|
||||
{
|
||||
value: '',
|
||||
label: 'Selected columns',
|
||||
label: t('utils.get-columns-width-indices.label-selected-columns', 'Selected columns'),
|
||||
options,
|
||||
expanded: true,
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DataSourcePlugin } from '@grafana/data';
|
||||
import { initPluginTranslations } from '@grafana/i18n';
|
||||
import { SQLQuery, SqlQueryEditorLazy } from '@grafana/sql';
|
||||
import { SQLQuery, SqlQueryEditorLazy, loadResources as loadSQLResources } from '@grafana/sql';
|
||||
|
||||
import { CheatSheet } from './CheatSheet';
|
||||
import { ConfigurationEditor } from './configuration/ConfigurationEditor';
|
||||
@ -8,7 +8,7 @@ import { MssqlDatasource } from './datasource';
|
||||
import pluginJson from './plugin.json';
|
||||
import { MssqlOptions } from './types';
|
||||
|
||||
initPluginTranslations(pluginJson.id);
|
||||
initPluginTranslations(pluginJson.id, [loadSQLResources]);
|
||||
|
||||
export const plugin = new DataSourcePlugin<MssqlDatasource, SQLQuery, MssqlOptions>(MssqlDatasource)
|
||||
.setQueryEditor(SqlQueryEditorLazy)
|
||||
|
@ -3572,6 +3572,7 @@ __metadata:
|
||||
"@emotion/css": "npm:11.13.5"
|
||||
"@grafana/data": "npm:12.1.0-pre"
|
||||
"@grafana/e2e-selectors": "npm:12.1.0-pre"
|
||||
"@grafana/i18n": "npm:12.1.0-pre"
|
||||
"@grafana/plugin-ui": "npm:0.10.6"
|
||||
"@grafana/runtime": "npm:12.1.0-pre"
|
||||
"@grafana/tsconfig": "npm:^2.0.0"
|
||||
@ -3589,6 +3590,7 @@ __metadata:
|
||||
"@types/react-virtualized-auto-sizer": "npm:1.0.4"
|
||||
"@types/systemjs": "npm:6.15.1"
|
||||
"@types/uuid": "npm:10.0.0"
|
||||
i18next-parser: "npm:9.3.0"
|
||||
immutable: "npm:5.0.3"
|
||||
jest: "npm:^29.6.4"
|
||||
lodash: "npm:4.17.21"
|
||||
|
Reference in New Issue
Block a user