[Code] Add localization for Code

This commit is contained in:
Fuyao Zhao 2019-07-30 18:16:26 +08:00 committed by GitHub
parent 7bf104460f
commit 27dd02128f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 1085 additions and 214 deletions

View file

@ -17,4 +17,4 @@
* under the License.
*/
module.exports = require('../node_modules/moment/min/moment.min.js');
module.exports = require('../node_modules/moment/min/moment-with-locales.min.js');

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import GitUrlParse from 'git-url-parse';
// return true if the git url is valid, otherwise throw Error with
@ -18,14 +19,22 @@ export function validateGitUrl(
if (hostWhitelist && hostWhitelist.length > 0) {
const hostSet = new Set(hostWhitelist);
if (!hostSet.has(repo.source)) {
throw new Error('Git url host is not whitelisted.');
throw new Error(
i18n.translate('xpack.code.gitUrlUtil.urlNotWhitelistedMessage', {
defaultMessage: 'Git url host is not whitelisted.',
})
);
}
}
if (protocolWhitelist && protocolWhitelist.length > 0) {
const protocolSet = new Set(protocolWhitelist);
if (!protocolSet.has(repo.protocol)) {
throw new Error('Git url protocol is not whitelisted.');
throw new Error(
i18n.translate('xpack.code.gitUrlUtil.protocolNotWhitelistedMessage', {
defaultMessage: 'Git url protocol is not whitelisted.',
})
);
}
}
return true;

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
export enum RepoFileStatus {
LANG_SERVER_IS_INITIALIZING = 'Language server is initializing.',
LANG_SERVER_INITIALIZED = 'Language server initialized.',
@ -27,6 +29,63 @@ export enum LangServerType {
DEDICATED = 'Current file is covered by dedicated language server',
}
export const RepoFileStatusText = {
[RepoFileStatus.LANG_SERVER_IS_INITIALIZING]: i18n.translate(
'xpack.code.repoFileStatus.langugageServerIsInitializitingMessage',
{
defaultMessage: 'Language server is initializing.',
}
),
[RepoFileStatus.LANG_SERVER_INITIALIZED]: i18n.translate(
'xpack.code.repoFileStatus.languageServerInitializedMessage',
{
defaultMessage: 'Language server initialized.',
}
),
[RepoFileStatus.INDEXING]: i18n.translate('xpack.code.repoFileStatus.IndexingInProgressMessage', {
defaultMessage: 'Indexing in progress.',
}),
[RepoFileStatus.FILE_NOT_SUPPORTED]: i18n.translate(
'xpack.code.repoFileStatus.fileNotSupportedMessage',
{
defaultMessage: 'Current file is not of a supported language.',
}
),
[RepoFileStatus.REVISION_NOT_INDEXED]: i18n.translate(
'xpack.code.repoFileStatus.revisionNotIndexedMessage',
{
defaultMessage: 'Current revision is not indexed.',
}
),
[RepoFileStatus.LANG_SERVER_NOT_INSTALLED]: i18n.translate(
'xpack.code.repoFileStatus.langServerNotInstalledMessage',
{
defaultMessage: 'Install additional language server to support current file.',
}
),
[RepoFileStatus.FILE_IS_TOO_BIG]: i18n.translate(
'xpack.code.repoFileStatus.fileIsTooBigMessage',
{
defaultMessage: 'Current file is too big.',
}
),
[LangServerType.NONE]: i18n.translate('xpack.code.repoFileStatus.langserverType.noneMessage', {
defaultMessage: 'Current file is not supported by any language server.',
}),
[LangServerType.GENERIC]: i18n.translate(
'xpack.code.repoFileStatus.langserverType.genericMessage',
{
defaultMessage: 'Current file is only covered by generic language server.',
}
),
[LangServerType.DEDICATED]: i18n.translate(
'xpack.code.repoFileStatus.langserverType.dedicatedMessage',
{
defaultMessage: 'Current file is covered by dedicated language server.',
}
),
};
export enum CTA {
SWITCH_TO_HEAD,
GOTO_LANG_MANAGE_PAGE,

View file

@ -7,17 +7,26 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Provider } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { I18nProvider } from '@kbn/i18n/react';
import moment from 'moment';
import 'ui/autoload/all';
import 'ui/autoload/styles';
import chrome from 'ui/chrome';
// @ts-ignore
import { uiModules } from 'ui/modules';
import { APP_TITLE } from '../common/constants';
import { App } from './components/app';
import { HelpMenu } from './components/help_menu';
import { store } from './stores';
if (chrome.getInjected('codeUiEnabled')) {
// TODO the entire Kibana uses moment, we might need to move it to a more common place
moment.locale(i18n.getLocale());
const app = uiModules.get('apps/code');
app.config(($locationProvider: any) => {
@ -34,9 +43,11 @@ if (chrome.getInjected('codeUiEnabled')) {
// render react to DOM
render(
<Provider store={store}>
<App />
</Provider>,
<I18nProvider>
<Provider store={store}>
<App />
</Provider>
</I18nProvider>,
domNode
);
@ -55,7 +66,12 @@ if (chrome.getInjected('codeUiEnabled')) {
]);
chrome.helpExtension.set(domNode => {
render(<HelpMenu />, domNode);
render(
<I18nProvider>
<HelpMenu />
</I18nProvider>,
domNode
);
return () => {
unmountComponentAtNode(domNode);
};

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { ReactNode } from 'react';
import { SearchScope } from '../../model';
@ -15,17 +16,33 @@ export enum PathTypes {
}
export const SearchScopeText = {
[SearchScope.DEFAULT]: 'Search Everything',
[SearchScope.REPOSITORY]: 'Search Repositories',
[SearchScope.SYMBOL]: 'Search Symbols',
[SearchScope.FILE]: 'Search Files',
[SearchScope.DEFAULT]: i18n.translate('xpack.code.searchScope.defaultDropDownOptionLabel', {
defaultMessage: 'Search Everything',
}),
[SearchScope.REPOSITORY]: i18n.translate('xpack.code.searchScope.repositoryDropDownOptionLabel', {
defaultMessage: 'Search Repositories',
}),
[SearchScope.SYMBOL]: i18n.translate('xpack.code.searchScope.symbolDropDownOptionLabel', {
defaultMessage: 'Search Symbols',
}),
[SearchScope.FILE]: i18n.translate('xpack.code.searchScope.fileDropDownOptionLabel', {
defaultMessage: 'Search Files',
}),
};
export const SearchScopePlaceholderText = {
[SearchScope.DEFAULT]: 'Type to find anything',
[SearchScope.REPOSITORY]: 'Type to find repositories',
[SearchScope.SYMBOL]: 'Type to find symbols',
[SearchScope.FILE]: 'Type to find files',
[SearchScope.DEFAULT]: i18n.translate('xpack.code.searchScope.defaultPlaceholder', {
defaultMessage: 'Type to find anything',
}),
[SearchScope.REPOSITORY]: i18n.translate('xpack.code.searchScope.repositoryPlaceholder', {
defaultMessage: 'Type to find repositories',
}),
[SearchScope.SYMBOL]: i18n.translate('xpack.code.searchScope.symbolPlaceholder', {
defaultMessage: 'Type to find symbols',
}),
[SearchScope.FILE]: i18n.translate('xpack.code.searchScope.filePlaceholder', {
defaultMessage: 'Type to find files',
}),
};
export interface MainRouteParams {

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { parse as parseQuery } from 'querystring';
import React from 'react';
import { connect } from 'react-redux';
@ -19,9 +20,8 @@ import { LanguageSeverTab } from './language_server_tab';
import { ProjectTab } from './project_tab';
enum AdminTabs {
projects = 'Repos',
roles = 'Roles',
languageServers = 'LanguageServers',
projects = '0',
languageServers = '1',
}
interface Props extends RouteComponentProps {
@ -56,12 +56,14 @@ class AdminPage extends React.PureComponent<Props, State> {
public tabs = [
{
id: AdminTabs.projects,
name: AdminTabs.projects,
name: i18n.translate('xpack.code.adminPage.repoTabLabel', { defaultMessage: 'Repositories' }),
disabled: false,
},
{
id: AdminTabs.languageServers,
name: 'Language servers',
name: i18n.translate('xpack.code.adminPage.langserverTabLabel', {
defaultMessage: 'Language servers',
}),
disabled: false,
},
];

View file

@ -8,6 +8,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import { EuiButton, EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { capabilities } from 'ui/capabilities';
import { ImportProject } from './import_project';
@ -19,15 +20,34 @@ export const EmptyProject = () => {
<EuiSpacer size="xl" />
<div className="codeTab__projects--emptyHeader">
<EuiText>
<h1>You don't have any repos yet</h1>
<h1>
<FormattedMessage
id="xpack.code.adminPage.repoTab.emptyRepo.noRepositoryText"
defaultMessage="You don't have any repos yet"
/>
</h1>
</EuiText>
<EuiText color="subdued">
{isAdmin && (
<p>
<FormattedMessage
id="xpack.code.adminPage.repoTab.emptyRepo.importFirstRepositoryText"
defaultMessage="Let's import your first one"
/>
</p>
)}
</EuiText>
<EuiText color="subdued">{isAdmin && <p>Let's import your first one</p>}</EuiText>
</div>
{isAdmin && <ImportProject />}
<EuiSpacer />
<EuiFlexGroup justifyContent="center">
<Link to="/setup-guide">
<EuiButton>View the Setup Guide</EuiButton>
<EuiButton>
<FormattedMessage
id="xpack.code.adminPage.repoTab.emptyRepo.viewSetupGuideButtonLabel"
defaultMessage="View the Setup Guide"
/>
</EuiButton>
</Link>
</EuiFlexGroup>
</div>

View file

@ -13,6 +13,9 @@ import {
EuiGlobalToastList,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { ChangeEvent } from 'react';
import { connect } from 'react-redux';
import { closeToast, importRepo } from '../../actions';
@ -71,11 +74,15 @@ class CodeImportProject extends React.PureComponent<
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow
label="Repository URL"
label={i18n.translate('xpack.code.adminPage.repoTab.repositoryUrlTitle', {
defaultMessage: 'Repository URL',
})}
helpText="e.g. https://github.com/Microsoft/TypeScript-Node-Starter"
fullWidth
isInvalid={this.state.isInvalid}
error="The URL shouldn't be empty."
error={i18n.translate('xpack.code.adminPage.repoTab.repositoryUrlEmptyText', {
defaultMessage: "The URL shouldn't be empty.",
})}
>
<EuiFieldText
value={this.state.value}
@ -97,7 +104,10 @@ class CodeImportProject extends React.PureComponent<
onClick={this.submitImportProject}
data-test-subj="importRepositoryButton"
>
Import
<FormattedMessage
id="xpack.code.adminPage.repoTab.importButtonLabel"
defaultMessage="Import"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -20,6 +20,7 @@ import {
EuiTabbedContent,
EuiText,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { connect } from 'react-redux';
import { InstallationType } from '../../../common/installation';
@ -51,24 +52,40 @@ const LanguageServerLi = (props: {
let button = null;
let state = null;
if (status === LanguageServerStatus.RUNNING) {
state = <EuiText size="xs">Running ...</EuiText>;
state = (
<EuiText size="xs">
<FormattedMessage
id="xpack.code.adminPage.langserverTab.runningText"
defaultMessage="Running ..."
/>
</EuiText>
);
} else if (status === LanguageServerStatus.NOT_INSTALLED) {
state = (
<EuiText size="xs" color={'subdued'}>
Not Installed
<FormattedMessage
id="xpack.code.adminPage.langserverTab.notInstalledText"
defaultMessage="Not Installed"
/>
</EuiText>
);
} else if (status === LanguageServerStatus.READY) {
state = (
<EuiText size="xs" color={'subdued'}>
Installed
<FormattedMessage
id="xpack.code.adminPage.langserverTab.installedText"
defaultMessage="Installed"
/>
</EuiText>
);
}
if (props.languageServer.installationType === InstallationType.Plugin) {
button = (
<EuiButton size="s" color="secondary" onClick={onInstallClick}>
Setup
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setupButtonLabel"
defaultMessage="Setup"
/>
</EuiButton>
);
}
@ -139,12 +156,13 @@ class AdminLanguageSever extends React.PureComponent<Props, State> {
<EuiSpacer />
<EuiText>
<h3>
{this.props.languageServers.length}
{this.props.languageServers.length > 1 ? (
<span> Language servers</span>
) : (
<span> Language server</span>
)}
<span>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.languageServersDescription"
defaultMessage="{serverCount} {serverCount, plural, one {Language server} other {Language servers}}"
values={{ serverCount: this.props.languageServers.length }}
/>
</span>
</h3>
</EuiText>
<EuiSpacer />
@ -186,16 +204,48 @@ const LanguageServerInstruction = (props: {
<div>
<EuiSpacer />
<EuiText grow={false}>
<h3>Install</h3>
<h3>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.installTitle"
defaultMessage="Install"
/>
</h3>
<ol>
<li>Stop your kibana Code node.</li>
<li>Use the following command to install the {props.name} language server.</li>
<li>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.stopKibanaDescription"
defaultMessage="Stop your kibana Code node."
/>
</li>
<li>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.useFollowingCommandToInstallDescription"
defaultMessage="Use the following command to install the {name} language server."
values={{ name: props.name }}
/>
</li>
</ol>
<EuiCodeBlock language="shell">{installCode}</EuiCodeBlock>
<h3>Uninstall</h3>
<h3>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.uninstallTitle"
defaultMessage="Uninstall"
/>
</h3>
<ol>
<li>Stop your kibana Code node.</li>
<li>Use the following command to remove the {props.name} language server.</li>
<li>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.stopKibanaDescription"
defaultMessage="Stop your kibana Code node."
/>
</li>
<li>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.useFollowingCommandToRemoveDescription"
defaultMessage="Use the following command to remove the {name} language server."
values={{ name: props.name }}
/>
</li>
</ol>
<EuiCodeBlock language="shell">
bin/kibana-plugin remove {props.pluginName}
@ -213,14 +263,22 @@ const LanguageServerInstruction = (props: {
<EuiOverlayMask>
<EuiModal onClose={props.close} maxWidth={false}>
<EuiModalHeader>
<EuiModalHeaderTitle>Installation Instructions</EuiModalHeaderTitle>
<EuiModalHeaderTitle>
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.installationInstructionTitle"
defaultMessage="Installation Instructions"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[1]} size={'m'} />
</EuiModalBody>
<EuiModalFooter>
<EuiButton onClick={props.close} fill>
Close
<FormattedMessage
id="xpack.code.adminPage.langserverTab.setup.closeButtonLabel"
defaultMessage="Close"
/>
</EuiButton>
</EuiModalFooter>
</EuiModal>

View file

@ -18,10 +18,12 @@ import {
EuiOverlayMask,
EUI_MODAL_CONFIRM_BUTTON,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import moment from 'moment';
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { Repository, WorkerReservedProgress } from '../../../model';
import { deleteRepo, indexRepo, initRepoCommand } from '../../actions';
import { RepoState, RepoStatus } from '../../actions/status';
@ -88,33 +90,70 @@ class CodeProjectItem extends React.PureComponent<
let disableRepoLink = false;
let hasError = false;
if (!status) {
footer = <div className="codeFooter">INIT...</div>;
footer = (
<div className="codeFooter">
<FormattedMessage id="xpack.code.repoItem.initText" defaultMessage="INIT..." />
</div>
);
} else if (status.state === RepoState.READY) {
footer = (
<div className="codeFooter" data-test-subj="repositoryIndexDone">
LAST UPDATED: {moment(status.timestamp).fromNow()}
<FormattedMessage
id="xpack.code.repoItem.lastUpdatedText"
defaultMessage="LAST UPDATED"
/>
:{' '}
{moment(status.timestamp)
.locale(i18n.getLocale())
.fromNow()}
</div>
);
} else if (status.state === RepoState.DELETING) {
footer = <div className="codeFooter">DELETING...</div>;
footer = (
<div className="codeFooter">
<FormattedMessage id="xpack.code.repoItem.deletingText" defaultMessage="DELETING..." />
</div>
);
} else if (status.state === RepoState.INDEXING) {
footer = (
<div className="codeFooter" data-test-subj="repositoryIndexOngoing">
INDEXING...
<FormattedMessage id="xpack.code.repoItem.indexingText" defaultMessage="INDEXING..." />
</div>
);
} else if (status.state === RepoState.CLONING) {
footer = <div className="codeFooter">CLONING...</div>;
footer = (
<div className="codeFooter">
<FormattedMessage id="xpack.code.repoItem.cloningText" defaultMessage="CLONING..." />
</div>
);
} else if (status.state === RepoState.DELETE_ERROR) {
footer = <div className="codeFooter codeFooter--error">ERROR DELETE REPO</div>;
footer = (
<div className="codeFooter codeFooter--error">
<FormattedMessage
id="xpack.code.repoItem.deleteErrorText"
defaultMessage="ERROR DELETE REPO"
/>
</div>
);
hasError = true;
} else if (status.state === RepoState.INDEX_ERROR) {
footer = <div className="codeFooter codeFooter--error">ERROR INDEX REPO</div>;
footer = (
<div className="codeFooter codeFooter--error">
<FormattedMessage
id="xpack.code.repoItem.indexErrorText"
defaultMessage="ERROR INDEX REPO"
/>
</div>
);
hasError = true;
} else if (status.state === RepoState.CLONE_ERROR) {
footer = (
<div className="codeFooter codeFooter--error">
ERROR CLONING REPO&nbsp;
<FormattedMessage
id="xpack.code.repoItem.cloneErrorText"
defaultMessage="ERROR CLONING REPO"
/>
&nbsp;
<EuiToolTip position="top" content={status.errorMessage}>
<EuiIcon type="iInCircle" />
</EuiToolTip>
@ -161,7 +200,10 @@ class CodeProjectItem extends React.PureComponent<
>
<EuiIcon type="gear" />
<EuiText size="xs" color="subdued">
Settings
<FormattedMessage
id="xpack.code.repoItem.settingsButtonLabel"
defaultMessage="Settings"
/>
</EuiText>
</div>
</EuiFlexItem>
@ -177,7 +219,10 @@ class CodeProjectItem extends React.PureComponent<
>
<EuiIcon type="indexSettings" />
<EuiText size="xs" color="subdued">
Reindex
<FormattedMessage
id="xpack.code.repoItem.reindexButtonLabel"
defaultMessage="Reindex"
/>
</EuiText>
</div>
</EuiFlexItem>
@ -193,7 +238,10 @@ class CodeProjectItem extends React.PureComponent<
>
<EuiIcon type="trash" color="danger" />
<EuiText size="xs" color="subdued">
Delete
<FormattedMessage
id="xpack.code.repoItem.deleteButtonLabel"
defaultMessage="Delete"
/>
</EuiText>
</div>
</EuiFlexItem>
@ -244,11 +292,17 @@ class CodeProjectItem extends React.PureComponent<
return (
<EuiOverlayMask>
<EuiConfirmModal
title="Reindex this repository?"
title={i18n.translate('xpack.code.repoItem.reindexConfirmTitle', {
defaultMessage: 'Reindex this repository?',
})}
onCancel={this.closeReindexModal}
onConfirm={this.confirmReindex}
cancelButtonText="No, don't do it"
confirmButtonText="Yes, do it"
cancelButtonText={i18n.translate('xpack.code.repoItem.cancelButtonText', {
defaultMessage: "No, don't do it",
})}
confirmButtonText={i18n.translate('xpack.code.repoItem.confirmButtonText', {
defaultMessage: 'Yes, do it',
})}
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
/>
</EuiOverlayMask>
@ -259,11 +313,17 @@ class CodeProjectItem extends React.PureComponent<
return (
<EuiOverlayMask>
<EuiConfirmModal
title="Delete this repository?"
title={i18n.translate('xpack.code.repoItem.deleteConfirmTitle', {
defaultMessage: 'Delete this repository?',
})}
onCancel={this.closeDeleteModal}
onConfirm={this.confirmDelete}
cancelButtonText="No, don't do it"
confirmButtonText="Yes, do it"
cancelButtonText={i18n.translate('xpack.code.repoItem.cancelButtonText', {
defaultMessage: "No, don't do it",
})}
confirmButtonText={i18n.translate('xpack.code.repoItem.confirmButtonText', {
defaultMessage: 'Yes, do it',
})}
buttonColor="danger"
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
/>

View file

@ -24,6 +24,8 @@ import {
EuiText,
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import moment from 'moment';
import React, { ChangeEvent } from 'react';
import { connect } from 'react-redux';
@ -62,10 +64,36 @@ const sortFunctionsFactory = (status: { [key: string]: RepoStatus }) => {
};
const sortOptions = [
{ value: SortOptionsValue.AlphabeticalAsc, inputDisplay: 'A to Z' },
{ value: SortOptionsValue.AlphabeticalDesc, inputDisplay: 'Z to A' },
{ value: SortOptionsValue.UpdatedAsc, inputDisplay: 'Last Updated ASC' },
{ value: SortOptionsValue.UpdatedDesc, inputDisplay: 'Last Updated DESC' },
{
value: SortOptionsValue.AlphabeticalAsc,
inputDisplay: i18n.translate('xpack.code.adminPage.repoTab.sort.aToZDropDownOptionLabel', {
defaultMessage: 'A to Z',
}),
},
{
value: SortOptionsValue.AlphabeticalDesc,
inputDisplay: i18n.translate('xpack.code.adminPage.repoTab.sort.zToADropDownOptionLabel', {
defaultMessage: 'Z to A',
}),
},
{
value: SortOptionsValue.UpdatedAsc,
inputDisplay: i18n.translate(
'xpack.code.adminPage.repoTab.sort.updatedAscDropDownOptionLabel',
{
defaultMessage: 'Last Updated ASC',
}
),
},
{
value: SortOptionsValue.UpdatedDesc,
inputDisplay: i18n.translate(
'xpack.code.adminPage.repoTab.sort.updatedDescDropDownOptionLabel',
{
defaultMessage: 'Last Updated DESC',
}
),
},
// { value: SortOptionsValue.recently_added, inputDisplay: 'Recently Added' },
];
@ -148,14 +176,29 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
<EuiOverlayMask>
<EuiModal onClose={this.closeModal}>
<EuiModalHeader>
<EuiModalHeaderTitle>Import a new repo</EuiModalHeaderTitle>
<EuiModalHeaderTitle>
<FormattedMessage
id="xpack.code.adminPage.repoTab.importRepoTitle"
defaultMessage="Import a new repo"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiTitle size="xs">
<h3>Repository URL</h3>
<h3>
<FormattedMessage
id="xpack.code.adminPage.repoTab.repositoryUrlFormLabel"
defaultMessage="Repository URL"
/>
</h3>
</EuiTitle>
<EuiForm>
<EuiFormRow isInvalid={this.state.isInvalid} error="The URL shouldn't be empty.">
<EuiFormRow
isInvalid={this.state.isInvalid}
error={i18n.translate('xpack.code.adminPage.repoTab.repositoryUrlEmptyText', {
defaultMessage: "The URL shouldn't be empty.",
})}
>
<EuiFieldText
value={this.state.repoURL}
onChange={this.onChange}
@ -172,9 +215,17 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
</EuiForm>
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty onClick={this.closeModal}>Cancel</EuiButtonEmpty>
<EuiButtonEmpty onClick={this.closeModal}>
<FormattedMessage
id="xpack.code.adminPage.repoTab.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
<EuiButton fill onClick={this.submitImportProject} disabled={this.props.importLoading}>
Import project
<FormattedMessage
id="xpack.code.adminPage.repoTab.importButtonLabel"
defaultMessage="Import"
/>
</EuiButton>
</EuiModalFooter>
</EuiModal>
@ -226,7 +277,11 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
<EuiSpacer />
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow label="Sort By">
<EuiFormRow
label={i18n.translate('xpack.code.adminPage.repoTab.sort.sortByFormLabel', {
defaultMessage: 'Sort By',
})}
>
<EuiSuperSelect
options={sortOptions}
valueOfSelected={this.state.sortOption}
@ -244,7 +299,10 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
onClick={this.openModal}
data-test-subj="newProjectButton"
>
Import a new repo
<FormattedMessage
id="xpack.code.adminPage.repoTab.importRepoButtonLabel"
defaultMessage="Import a new repo"
/>
</EuiButton>
)}
</EuiFlexItem>
@ -252,8 +310,11 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
<EuiSpacer />
<EuiText>
<h3>
{projectsCount}
{projectsCount === 1 ? <span> Repo</span> : <span> Repos</span>}
<FormattedMessage
id="xpack.code.adminPage.repoTab.repoDescription"
defaultMessage="{projectsCount} {projectsCount, plural, one {Repo} other {Repos}}"
values={{ projectsCount }}
/>
</h3>
</EuiText>
<EuiSpacer />

View file

@ -15,6 +15,8 @@ import {
EuiTitle,
EuiLink,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
@ -23,80 +25,149 @@ import { RootState } from '../../reducers';
const steps = [
{
title: 'Check if multiple Kibana instances are used as a cluster',
title: i18n.translate('xpack.code.adminPage.setupGuide.checkMultiInstanceTitle', {
defaultMessage: 'Check if multiple Kibana instances are used as a clusterURL',
}),
children: (
<EuiText>
<p>If you are using single Kibana instance, you can skip this step.</p>
<p>
If you are using multiple Kibana instances, you need to assign one Kibana instance as
`Code node`. To do this, add the following line of code into your kibana.yml file of every
Kibana instance and restart the instances:
<FormattedMessage
id="xpack.code.adminPage.setupGuide.checkMultiInstanceDescription1"
defaultMessage="If you are using single Kibana instance, you can skip this step."
/>
</p>
<p>
<FormattedMessage
id="xpack.code.adminPage.setupGuide.checkMultiInstanceDescription2"
defaultMessage="If you are using multiple Kibana instances, you need to assign one Kibana instance as `Code node`.
To do this, add the following line of code into your kibana.yml file of every
Kibana instance and restart the instances:"
/>
</p>
<pre>
<code>xpack.code.codeNodeUrl: 'http://$YourCodeNodeAddress'</code>
</pre>
<p>
Where `$YourCodeNoteAddress` is the URL of your assigned Code node accessible by other
Kibana instances.
<FormattedMessage
id="xpack.code.adminPage.setupGuide.checkMultiInstanceDescription3"
defaultMessage="Where `$YourCodeNoteAddress` is the URL of your assigned Code node accessible by other Kibana instances."
/>
</p>
</EuiText>
),
},
{
title: 'Install extra language support optionally',
title: i18n.translate('xpack.code.adminPage.setupGuide.installExtraLangSupportTitle', {
defaultMessage: 'Install extra language support optionally',
}),
children: (
<EuiText>
<p>
Look{' '}
<EuiLink href={documentationLinks.codeInstallLangServer} target="_blank">
here
</EuiLink>{' '}
to learn more about supported languages and language server installation.
<FormattedMessage
id="xpack.code.adminPage.setupGuide.installExtraLangSupportDescription1"
defaultMessage="Look {link} to learn more about supported languages and language server installation."
values={{
link: (
<EuiLink href={documentationLinks.codeInstallLangServer} target="_blank">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.installExtraLangSupportHereLinkText"
defaultMessage="here"
/>
</EuiLink>
),
}}
/>
</p>
<p>
If you need Java language support, you can manage language server installation{' '}
<Link to="/admin?tab=LanguageServers">here</Link>
<FormattedMessage
id="xpack.code.adminPage.setupGuide.installExtraLangSupportDescription2"
defaultMessage="If you need Java language support, you can manage language server installation {link}."
values={{
link: (
<Link to="/admin?tab=LanguageServers">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.installExtraLangSupportHereLinkText"
defaultMessage="here"
/>
</Link>
),
}}
/>
</p>
</EuiText>
),
},
{
title: 'Add a repository to Code',
title: i18n.translate('xpack.code.adminPage.setupGuide.addRepositoryTitle', {
defaultMessage: 'Add a repository to Code',
}),
children: (
<EuiText>
<p>
Import{' '}
<EuiLink href={documentationLinks.codeGettingStarted} target="_blank">
{' '}
a sample repo
</EuiLink>{' '}
or{' '}
<EuiLink href={documentationLinks.codeRepoManagement} target="_blank">
your own repo
</EuiLink>
. It is as easy as copy and paste git clone URLs to Code.
<FormattedMessage
id="xpack.code.adminPage.setupGuide.addRepositoryDescription"
defaultMessage="Import {sampleRepoLink} or {ownRepoLink}. It is as easy as copy and paste git clone URLs to Code."
values={{
sampleRepoLink: (
<EuiLink href={documentationLinks.codeGettingStarted} target="_blank">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.addRepositorySampleRepoLinkText"
defaultMessage="a sample repo"
/>
</EuiLink>
),
ownRepoLink: (
<EuiLink href={documentationLinks.codeRepoManagement} target="_blank">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.addRepositoryOwnRepoLinkText"
defaultMessage="your own repo"
/>
</EuiLink>
),
}}
/>
</p>
</EuiText>
),
},
{
title: 'Verify the repo is successfully imported',
title: i18n.translate('xpack.code.adminPage.setupGuide.verifyImportTitle', {
defaultMessage: 'Verify the repo is successfully imported',
}),
children: (
<EuiText>
<p>
You can verify your repo is successfully imported by{' '}
<EuiLink href={documentationLinks.codeSearch} target="_blank">
searching
</EuiLink>{' '}
and{' '}
<EuiLink href={documentationLinks.codeOtherFeatures} target="_blank">
navigating
</EuiLink>{' '}
the repo. If language support is available to the repo, make sure{' '}
<EuiLink href={documentationLinks.semanticNavigation} target="_blank">
semantic navigation
</EuiLink>{' '}
is available as well.
<FormattedMessage
id="xpack.code.adminPage.setupGuide.verifyImportDescription"
defaultMessage="You can verify your repo is successfully imported by {searchingLink} and {navigatingLink} the repo. If language support is available to the repo, make sure {semanticNavigationLink} is available as well."
values={{
searchingLink: (
<EuiLink href={documentationLinks.codeSearch} target="_blank">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.verifyImportSearchingLinkText"
defaultMessage="searching"
/>
</EuiLink>
),
navigatingLink: (
<EuiLink href={documentationLinks.codeOtherFeatures} target="_blank">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.verifyImportNavigatingLinkText"
defaultMessage="navigating"
/>
</EuiLink>
),
semanticNavigationLink: (
<EuiLink href={documentationLinks.semanticNavigation} target="_blank">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.verifyImportSemanticNavigatingLinkText"
defaultMessage="semantic navigation"
/>
</EuiLink>
),
}}
/>
</p>
</EuiText>
),
@ -106,11 +177,16 @@ const steps = [
const toastMessage = (
<div>
<p>
Weve made some changes to roles and permissions in Kibana. Read more about how these changes
affect your Code implementation below.{' '}
<FormattedMessage
id="xpack.code.adminPage.setupGuide.permissionChangesDescription"
defaultMessage="Weve made some changes to roles and permissions in Kibana. Read more about how these changes affect your Code implementation below."
/>
</p>
<EuiButton size="s" href={documentationLinks.kibanaRoleManagement}>
Learn more
<FormattedMessage
id="xpack.code.adminPage.setupGuide.learnMoreButtonLabel"
defaultMessage="Learn more"
/>
</EuiButton>
</div>
);
@ -133,7 +209,9 @@ class SetupGuidePage extends React.PureComponent<{ setupOk?: boolean }, { hideTo
<EuiGlobalToastList
toasts={[
{
title: 'Permission Changes',
title: i18n.translate('xpack.code.adminPage.setupGuide.permissionChangesTitle', {
defaultMessage: 'Permission Changes',
}),
color: 'primary',
iconType: 'iInCircle',
text: toastMessage,
@ -158,15 +236,25 @@ class SetupGuidePage extends React.PureComponent<{ setupOk?: boolean }, { hideTo
{this.props.setupOk === true && (
<React.Fragment>
<EuiSpacer size="s" />
<EuiButton iconType="sortLeft">
<Link to="/admin">Back To project dashboard</Link>
</EuiButton>
<Link to="/admin">
<EuiButton iconType="sortLeft">
<FormattedMessage
id="xpack.code.adminPage.setupGuide.backToDashboardButtonLabel"
defaultMessage="Back To repository dashboard"
/>
</EuiButton>
</Link>
<EuiSpacer size="s" />
</React.Fragment>
)}
<EuiPanel>
<EuiTitle>
<h3>Getting started in Elastic Code</h3>
<h3>
<FormattedMessage
id="xpack.code.adminPage.setupGuide.getStartedTitle"
defaultMessage="Getting started in Elastic Code"
/>
</h3>
</EuiTitle>
<EuiSpacer />
<EuiSteps steps={steps} />

View file

@ -7,6 +7,7 @@
import React from 'react';
import chrome from 'ui/chrome';
import { EuiButton, EuiHorizontalRule, EuiText, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { documentationLinks } from '../../lib/documentation_links';
export class HelpMenu extends React.PureComponent {
@ -16,15 +17,26 @@ export class HelpMenu extends React.PureComponent {
<EuiHorizontalRule margin="none" />
<EuiSpacer />
<EuiText size="s">
<p>For Code specific information</p>
<p>
<FormattedMessage
id="xpack.code.helpMenu.helpDescription"
defaultMessage="For Code specific information"
/>
</p>
</EuiText>
<EuiSpacer />
<EuiButton fill iconType="popout" href={chrome.addBasePath('/app/code#/setup-guide')}>
Setup Guide
<FormattedMessage
id="xpack.code.helpMenu.setupGuideButtonLabel"
defaultMessage="Setup Guide"
/>
</EuiButton>
<EuiSpacer />
<EuiButton fill iconType="popout" href={documentationLinks.code} target="_blank">
Code documentation
<FormattedMessage
id="xpack.code.helpMenu.codeDocumentationButtonLabel"
defaultMessage="Code documentation"
/>
</EuiButton>
</React.Fragment>
);

View file

@ -5,6 +5,8 @@
*/
import { EuiButton, EuiFlexGroup } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { HoverState } from './hover_widget';
@ -25,7 +27,10 @@ export class HoverButtons extends React.PureComponent<HoverButtonProps> {
onClick={this.props.gotoDefinition}
data-test-subj="codeGoToDefinitionButton"
>
Goto Definition
<FormattedMessage
id="xpack.code.monaco.hover.gotoDefinitionButtonLabel"
defaultMessage="Goto Definition"
/>
</EuiButton>
<EuiButton
size="s"
@ -33,7 +38,10 @@ export class HoverButtons extends React.PureComponent<HoverButtonProps> {
onClick={this.props.findReferences}
data-test-subj="codeFindReferenceButton"
>
Find Reference
<FormattedMessage
id="xpack.code.monaco.hover.findReferenceButtonLabel"
defaultMessage="Find Reference"
/>
</EuiButton>
</EuiFlexGroup>
</React.Fragment>

View file

@ -5,6 +5,7 @@
*/
import { EuiText, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { MarkedString } from 'vscode-languageserver-types';
import ReactMarkdown from 'react-markdown';
@ -98,9 +99,19 @@ export class HoverWidget extends React.PureComponent<HoverWidgetProps> {
{/*
// @ts-ignore */}
<EuiText textAlign="center">
<h4>Language Server is initializing</h4>
<h4>
<FormattedMessage
id="xpack.code.monaco.hover.languageServerInitializingTitle"
defaultMessage="Language Server is initializing…"
/>
</h4>
<EuiText size="xs" color="subdued">
<p>Depending on the size of your repo, this could take a few minutes.</p>
<p>
<FormattedMessage
id="xpack.code.monaco.hover.languageServerInitializingDescription"
defaultMessage="Depending on the size of your repo, this could take a few minutes."
/>
</p>
</EuiText>
</EuiText>
</div>

View file

@ -5,8 +5,11 @@
*/
import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiSpacer, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import theme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { CloneProgress } from '../../../model';
interface Props {
@ -17,20 +20,38 @@ interface Props {
export const CloneStatus = (props: Props) => {
const { progress: progressRate, cloneProgress, repoName } = props;
let progress = `Receiving objects: ${progressRate.toFixed(2)}%`;
let progress = i18n.translate(
'xpack.code.mainPage.content.cloneStatus.progress.receivingRateOnlyText',
{
defaultMessage: 'Receiving objects: {progressRate}%',
values: { progressRate: progressRate.toFixed(2) },
}
);
if (progressRate < 0) {
progress = 'Clone Failed';
progress = i18n.translate('xpack.code.mainPage.content.cloneStatus.progress.cloneFailedText', {
defaultMessage: 'Clone Failed',
});
} else if (cloneProgress) {
const { receivedObjects, totalObjects, indexedObjects } = cloneProgress;
if (receivedObjects === totalObjects) {
progress = `Indexing objects: ${((indexedObjects * 100) / totalObjects).toFixed(
2
)}% (${indexedObjects}/${totalObjects})`;
progress = i18n.translate('xpack.code.mainPage.content.cloneStatus.progress.indexingText', {
defaultMessage: 'Indexing objects: {progressRate}% {indexedObjects}/{totalObjects}',
values: {
progressRate: ((indexedObjects * 100) / totalObjects).toFixed(2),
indexedObjects,
totalObjects,
},
});
} else {
progress = `Receiving objects: ${((receivedObjects * 100) / totalObjects).toFixed(
2
)}% (${receivedObjects}/${totalObjects})`;
progress = i18n.translate('xpack.code.mainPage.content.cloneStatus.progress.receivingText', {
defaultMessage: 'Receiving objects: {progressRate}% {receivedObjects}/{totalObjects}',
values: {
progressRate: ((receivedObjects * 100) / totalObjects).toFixed(2),
receivedObjects,
totalObjects,
},
});
}
}
return (
@ -39,13 +60,20 @@ export const CloneStatus = (props: Props) => {
<EuiSpacer size="xxl" />
<EuiFlexItem grow={false}>
<EuiText style={{ fontSize: theme.euiSizeXXL, color: '#1A1A1A' }}>
{repoName} is cloning
{repoName}{' '}
<FormattedMessage
id="xpack.code.mainPage.content.cloneStatus.isCloningText"
defaultMessage="is cloning"
/>
</EuiText>
</EuiFlexItem>
<EuiSpacer size="s" />
<EuiFlexItem grow={false}>
<EuiText style={{ fontSize: theme.euiSizeM, color: '#69707D' }}>
Your project will be available when this process is complete
<FormattedMessage
id="xpack.code.mainPage.content.cloneStatus.yourProjectWillBeAvailableText"
defaultMessage="Your project will be available when this process is complete"
/>
</EuiText>
</EuiFlexItem>
<EuiSpacer size="xl" />

View file

@ -13,10 +13,13 @@ import {
EuiText,
EuiTextColor,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import _ from 'lodash';
import moment from 'moment';
import React from 'react';
import { connect } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { CommitInfo } from '../../../model/commit';
import { CommitLink } from '../diff_page/commit_link';
import { RootState } from '../../reducers';
@ -64,7 +67,14 @@ const CommitGroup = (props: { commits: CommitInfo[]; date: string; repoUri: stri
<EuiFlexItem>
<EuiText>
<h4>
<EuiTextColor color="subdued">Commits on {props.date}</EuiTextColor>
<EuiTextColor color="subdued">
<FormattedMessage
id="xpack.code.mainPage.history.commitsOnTitle"
defaultMessage="Commits on {date}"
values={{ date: props.date }}
/>{' '}
{}
</EuiTextColor>
</h4>
</EuiText>
</EuiFlexItem>
@ -94,12 +104,17 @@ export const PageButtons = (props: {
isDisabled={props.disabled}
size="s"
>
More
<FormattedMessage id="xpack.code.mainPage.history.moreButtonLabel" defaultMessage="More" />
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
);
const commitDateFormatMap: { [key: string]: string } = {
en: 'MMMM Do, YYYY',
'zh-cn': 'YYYY年MoDo',
};
export const CommitHistoryComponent = (props: {
commits: CommitInfo[];
repoUri: string;
@ -111,10 +126,13 @@ export const CommitHistoryComponent = (props: {
}) => {
const commits = _.groupBy(props.commits, commit => moment(commit.updated).format('YYYYMMDD'));
const commitDates = Object.keys(commits).sort((a, b) => b.localeCompare(a)); // sort desc
const locale = i18n.getLocale();
const commitDateFormat =
locale in commitDateFormatMap ? commitDateFormatMap[locale] : commitDateFormatMap.en;
const commitList = commitDates.map(cd => (
<CommitGroup
commits={commits[cd]}
date={moment(cd).format('MMMM Do, YYYY')}
date={moment(cd).format(commitDateFormat)}
key={cd}
repoUri={props.repoUri}
/>

View file

@ -5,6 +5,9 @@
*/
import { EuiButton, EuiButtonGroup, EuiFlexGroup, EuiTitle, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import 'github-markdown-css/github-markdown.css';
import React from 'react';
import ReactMarkdown from 'react-markdown';
@ -69,13 +72,6 @@ enum ButtonOption {
Folder = 'Directory',
}
enum ButtonLabel {
Code = 'Code',
Content = 'Content',
Download = 'Download',
Raw = 'Raw',
}
class CodeContent extends React.PureComponent<Props> {
public findNode = (pathSegments: string[], node: FileTree): FileTree | undefined => {
if (!node) {
@ -161,20 +157,40 @@ class CodeContent extends React.PureComponent<Props> {
const buttonOptions = [
{
id: ButtonOption.Code,
label: isText && !isMarkdown ? ButtonLabel.Code : ButtonLabel.Content,
label:
isText && !isMarkdown
? i18n.translate('xpack.code.mainPage.content.buttons.codeButtonLabel', {
defaultMessage: 'Code',
})
: i18n.translate('xpack.code.mainPage.content.buttons.contentButtonLabel', {
defaultMessage: 'content',
}),
},
{
id: ButtonOption.Blame,
label: ButtonOption.Blame,
label: i18n.translate('xpack.code.mainPage.content.buttons.blameButtonLabel', {
defaultMessage: 'Blame',
}),
isDisabled: isUnsupported || isImage || isOversize,
},
{
id: ButtonOption.History,
label: ButtonOption.History,
label: i18n.translate('xpack.code.mainPage.content.buttons.historyButtonLabel', {
defaultMessage: 'History',
}),
},
];
const rawButtonOptions = [
{ id: 'Raw', label: isText ? ButtonLabel.Raw : ButtonLabel.Download },
{
id: 'Raw',
label: isText
? i18n.translate('xpack.code.mainPage.content.buttons.rawButtonLabel', {
defaultMessage: 'Raw',
})
: i18n.translate('xpack.code.mainPage.content.buttons.downloadButtonLabel', {
defaultMessage: 'Download',
}),
},
];
return (
@ -210,11 +226,15 @@ class CodeContent extends React.PureComponent<Props> {
options={[
{
id: ButtonOption.Folder,
label: ButtonOption.Folder,
label: i18n.translate('xpack.code.mainPage.content.buttons.folderButtonLabel', {
defaultMessage: 'Directory',
}),
},
{
id: ButtonOption.History,
label: ButtonOption.History,
label: i18n.translate('xpack.code.mainPage.content.buttons.historyButtonLabel', {
defaultMessage: 'History',
}),
},
]}
type="single"
@ -296,7 +316,12 @@ class CodeContent extends React.PureComponent<Props> {
header={
<React.Fragment>
<EuiTitle size="s" className="codeMargin__title">
<h3>Recent Commits</h3>
<h3>
<FormattedMessage
id="xpack.code.mainPage.directory.recentCommitsTitle"
defaultMessage="Recent Commits"
/>
</h3>
</EuiTitle>
<EuiButton
size="s"
@ -304,7 +329,10 @@ class CodeContent extends React.PureComponent<Props> {
revision
)}/${path || ''}`}
>
View All
<FormattedMessage
id="xpack.code.mainPage.directory.viewAllCommitsButtonLabel"
defaultMessage="View All"
/>
</EuiButton>
</React.Fragment>
}
@ -383,7 +411,12 @@ class CodeContent extends React.PureComponent<Props> {
repoUri={repoUri}
header={
<EuiTitle className="codeMargin__title">
<h3>Commit History</h3>
<h3>
<FormattedMessage
id="xpack.code.mainPage.history.commitHistoryTitle"
defaultMessage="Commit History"
/>
</h3>
</EuiTitle>
}
showPagination={true}

View file

@ -14,6 +14,8 @@ import {
EuiLoadingSpinner,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { Link, RouteComponentProps, withRouter } from 'react-router-dom';
import { FileTree, FileTreeItemType } from '../../../model';
@ -84,9 +86,23 @@ export const Directory = withRouter((props: Props) => {
const { resource, org, repo, revision } = props.match.params;
const getUrl = (pathType: PathTypes) => (path: string) =>
`/${resource}/${org}/${repo}/${pathType}/${encodeRevisionString(revision)}/${path}`;
const fileList = <DirectoryNodes nodes={files} title="Files" getUrl={getUrl(PathTypes.blob)} />;
const fileList = (
<DirectoryNodes
nodes={files}
title={i18n.translate('xpack.code.mainPage.content.directory.filesTitle', {
defaultMessage: 'Files',
})}
getUrl={getUrl(PathTypes.blob)}
/>
);
const folderList = (
<DirectoryNodes nodes={folders} title="Directories" getUrl={getUrl(PathTypes.tree)} />
<DirectoryNodes
nodes={folders}
title={i18n.translate('xpack.code.mainPage.content.directory.directoriesTitle', {
defaultMessage: 'Directories',
})}
getUrl={getUrl(PathTypes.tree)}
/>
);
const children = props.loading ? (
<div>

View file

@ -9,6 +9,7 @@ import { parse as parseQuery } from 'querystring';
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { QueryString } from 'ui/utils/query_string';
import { i18n } from '@kbn/i18n';
import { MainRouteParams } from '../../common/types';
import { FileTree } from '../file_tree/file_tree';
import { Shortcut } from '../shortcuts';
@ -56,28 +57,44 @@ class CodeSideTabs extends React.PureComponent<Props> {
public get tabs() {
const { languageServerInitializing, loadingFileTree, loadingStructureTree } = this.props;
const fileTabContent = loadingFileTree ? (
this.renderLoadingSpinner('Loading file tree')
this.renderLoadingSpinner(
i18n.translate('xpack.code.mainPage.sideTab.loadingFileTreeText', {
defaultMessage: 'Loading file tree',
})
)
) : (
<div className="codeFileTree__container">{<FileTree />}</div>
);
let structureTabContent: React.ReactNode;
if (languageServerInitializing) {
structureTabContent = this.renderLoadingSpinner('Language server is initializing');
structureTabContent = this.renderLoadingSpinner(
i18n.translate('xpack.code.mainPage.sideTab.languageServerInitializingText', {
defaultMessage: 'Language server is initializing',
})
);
} else if (loadingStructureTree) {
structureTabContent = this.renderLoadingSpinner('Loading structure tree');
structureTabContent = this.renderLoadingSpinner(
i18n.translate('xpack.code.mainPage.sideTab.loadingStructureTreeText', {
defaultMessage: 'Loading structure tree',
})
);
} else {
structureTabContent = <SymbolTree />;
}
return [
{
id: Tabs.file,
name: 'Files',
name: i18n.translate('xpack.code.mainPage.sideTab.fileTabLabel', {
defaultMessage: 'Files',
}),
content: fileTabContent,
'data-test-subj': `codeFileTreeTab${this.sideTab === Tabs.file ? 'Active' : ''}`,
},
{
id: Tabs.structure,
name: 'Structure',
name: i18n.translate('xpack.code.mainPage.sideTab.structureTabLabel', {
defaultMessage: 'Structure',
}),
content: structureTabContent,
disabled:
!(this.props.currentTree && this.props.currentTree.type === FileTreeItemType.File) ||

View file

@ -623,7 +623,19 @@ exports[`render correctly with empty query string 1`] = `
<span
className="euiTextColor euiTextColor--secondary"
>
Search Filters
<FormattedMessage
defaultMessage=" {filterCount, plural, one {Search Filter} other {Search Filters}} "
id="xpack.code.searchBar.searchFilterTitle"
values={
Object {
"filterCount": 0,
}
}
>
<span>
Search Filters
</span>
</FormattedMessage>
</span>
</EuiTextColor>
</span>
@ -1332,7 +1344,19 @@ exports[`render correctly with input query string changed 1`] = `
<span
className="euiTextColor euiTextColor--secondary"
>
Search Filters
<FormattedMessage
defaultMessage=" {filterCount, plural, one {Search Filter} other {Search Filters}} "
id="xpack.code.searchBar.searchFilterTitle"
values={
Object {
"filterCount": 0,
}
}
>
<span>
Search Filters
</span>
</FormattedMessage>
</span>
</EuiTextColor>
</span>

View file

@ -20,6 +20,8 @@ import {
EuiNotificationBadge,
} from '@elastic/eui';
import { EuiIcon } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { unique } from 'lodash';
import React, { Component } from 'react';
import { SearchOptions as ISearchOptions, Repository } from '../../../../model';
@ -114,6 +116,14 @@ export class SearchOptions extends Component<Props, State> {
);
});
const repoCandidates = this.state.query
? this.props.repoSearchResults.map(repo => ({
label: repo.name,
}))
: this.props.defaultRepoOptions.map(repo => ({
label: repo.name,
}));
optionsFlyout = (
<EuiFlyout
onClose={this.closeOptionsFlyout}
@ -128,30 +138,38 @@ export class SearchOptions extends Component<Props, State> {
{repoScope.length}
</EuiNotificationBadge>
<EuiTextColor color="secondary" className="code-flyout-title">
{' '}
Search Filters{' '}
<FormattedMessage
id="xpack.code.searchBar.searchFilterTitle"
defaultMessage=" {filterCount, plural, one {Search Filter} other {Search Filters}} "
values={{ filterCount: repoScope.length }}
/>
</EuiTextColor>
</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiTitle size="xs">
<h3>Repo Scope</h3>
<h3>
<FormattedMessage
id="xpack.code.searchBar.searchScopeTitle"
defaultMessage="Search Scope"
/>
</h3>
</EuiTitle>
<EuiText size="xs">Add indexed repos to your search scope</EuiText>
<EuiText size="xs">
<FormattedMessage
id="xpack.code.searchBar.addToScopeDescription"
defaultMessage="Add indexed repos to your search scope"
/>
</EuiText>
<EuiSpacer size="m" />
<EuiComboBox
placeholder="Search to add repos"
noSuggestions={repoCandidates.length === 0}
placeholder={i18n.translate('xpack.code.searchBar.addRepoPlaceholder', {
defaultMessage: 'Search to add repos',
})}
async={true}
options={
this.state.query
? this.props.repoSearchResults.map(repo => ({
label: repo.name,
}))
: this.props.defaultRepoOptions.map(repo => ({
label: repo.name,
}))
}
options={repoCandidates}
selectedOptions={[]}
isLoading={this.props.searchLoading}
onChange={this.onRepoChange}
@ -163,7 +181,10 @@ export class SearchOptions extends Component<Props, State> {
<EuiSpacer size="s" />
<EuiFlexGroup justifyContent="flexEnd" gutterSize="none">
<EuiButton onClick={this.applyAndClose} fill={true} iconSide="right">
Apply and Close
<FormattedMessage
id="xpack.code.searchBar.applyAndCloseButtonLabel"
defaultMessage="Apply and Close"
/>
</EuiButton>
</EuiFlexGroup>
</EuiFlyoutBody>
@ -178,7 +199,13 @@ export class SearchOptions extends Component<Props, State> {
<EuiNotificationBadge size="m" className="code-notification-badge">
{repoScope.length}
</EuiNotificationBadge>
<EuiTextColor color="secondary"> Search Filters </EuiTextColor>
<EuiTextColor color="secondary">
<FormattedMessage
id="xpack.code.searchBar.searchFilterTitle"
defaultMessage=" {filterCount, plural, one {Search Filter} other {Search Filters}} "
values={{ filterCount: repoScope.length }}
/>
</EuiTextColor>
</EuiButtonEmpty>
</div>
{optionsFlyout}

View file

@ -195,8 +195,19 @@ exports[`render full suggestions component 1`] = `
<div
className="codeSearch-suggestion__group-result"
>
1
Result
<FormattedMessage
defaultMessage="{total} {total, plural, one {Result} other {Results}}"
id="xpack.code.searchBar.resultCountText"
values={
Object {
"total": 1,
}
}
>
<span>
1 Result
</span>
</FormattedMessage>
</div>
</div>
</EuiFlexGroup>
@ -347,8 +358,19 @@ exports[`render full suggestions component 1`] = `
<div
className="codeSearch-suggestion__group-result"
>
1
Result
<FormattedMessage
defaultMessage="{total} {total, plural, one {Result} other {Results}}"
id="xpack.code.searchBar.resultCountText"
values={
Object {
"total": 1,
}
}
>
<span>
1 Result
</span>
</FormattedMessage>
</div>
</div>
</EuiFlexGroup>
@ -463,9 +485,19 @@ exports[`render full suggestions component 1`] = `
<div
className="codeSearch-suggestion__group-result"
>
2
Result
s
<FormattedMessage
defaultMessage="{total} {total, plural, one {Result} other {Results}}"
id="xpack.code.searchBar.resultCountText"
values={
Object {
"total": 2,
}
}
>
<span>
2 Results
</span>
</FormattedMessage>
</div>
</div>
</EuiFlexGroup>
@ -568,7 +600,16 @@ exports[`render full suggestions component 1`] = `
href="/search?q=string"
onClick={[Function]}
>
View More
<FormattedMessage
defaultMessage="View More"
id="xpack.code.searchBar.viewMoreLinkText"
values={Object {}}
>
<span>
View More
</span>
</FormattedMessage>
</a>
</Link>
</div>
@ -584,7 +625,15 @@ exports[`render full suggestions component 1`] = `
<div
className="codeSearch__full-text-button"
>
Press ⮐ Return for Full Text Search
<FormattedMessage
defaultMessage="Press ⮐ Return for Full Text Search"
id="xpack.code.searchBar.viewFullSearchLinkText"
values={Object {}}
>
<span>
Press ⮐ Return for Full Text Search
</span>
</FormattedMessage>
</div>
</a>
</Link>

View file

@ -5,6 +5,9 @@
*/
import { EuiFlexGroup, EuiText, EuiToken, IconType } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { isEmpty } from 'lodash';
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
@ -53,7 +56,10 @@ export class SuggestionsComponent extends Component<Props> {
{this.renderSuggestionGroups()}
<Link to={this.viewMoreUrl()}>
<div className="codeSearch__full-text-button">
Press Return for Full Text Search
<FormattedMessage
id="xpack.code.searchBar.viewFullSearchLinkText"
defaultMessage="Press ⮐ Return for Full Text Search"
/>
</div>
</Link>
</div>
@ -109,15 +115,24 @@ export class SuggestionsComponent extends Component<Props> {
</EuiText>
</EuiFlexGroup>
<div className="codeSearch-suggestion__group-result">
{total} Result
{total === 1 ? '' : 's'}
<FormattedMessage
id="xpack.code.searchBar.resultCountText"
defaultMessage="{total} {total, plural, one {Result} other {Results}}"
values={{ total }}
/>
</div>
</EuiFlexGroup>
);
const viewMore = (
<div className="codeSearch-suggestion__link">
<Link to={this.viewMoreUrl()}>View More</Link>
<Link to={this.viewMoreUrl()}>
{' '}
<FormattedMessage
id="xpack.code.searchBar.viewMoreLinkText"
defaultMessage="View More"
/>
</Link>
</div>
);
@ -153,11 +168,17 @@ export class SuggestionsComponent extends Component<Props> {
private getGroupTitle(type: AutocompleteSuggestionType): string {
switch (type) {
case AutocompleteSuggestionType.FILE:
return 'Files';
return i18n.translate('xpack.code.searchBar.fileGroupTitle', {
defaultMessage: 'Files',
});
case AutocompleteSuggestionType.REPOSITORY:
return 'Repos';
return i18n.translate('xpack.code.searchBar.repositorylGroupTitle', {
defaultMessage: 'Repos',
});
case AutocompleteSuggestionType.SYMBOL:
return 'Symbols';
return i18n.translate('xpack.code.searchBar.symbolGroupTitle', {
defaultMessage: 'Symbols',
});
}
}

View file

@ -5,6 +5,7 @@
*/
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { IPosition } from 'monaco-editor';
import React from 'react';
import { Link } from 'react-router-dom';
@ -63,7 +64,10 @@ export class CodeResult extends React.PureComponent<Props> {
<EuiBadge color="default">{hits}</EuiBadge>
</EuiFlexItem>
<EuiText size="s">
&nbsp;hits from&nbsp;
<FormattedMessage
id="xpack.code.searchPage.hitsCountText"
defaultMessage=" hits from "
/>
<Link to={fileLinkUrl} data-test-subj="codeSearchResultFileItem">
{filePath}
</Link>

View file

@ -5,6 +5,7 @@
*/
import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
export const EmptyPlaceholder = (props: any) => {
@ -25,11 +26,17 @@ export const EmptyPlaceholder = (props: any) => {
</EuiText>
<EuiSpacer size="xl" />
<EuiText textAlign="center" style={{ fontSize: '28px', color: '#1A1A1A' }}>
Hmmm... we looked for that, but couldnt find anything.
<FormattedMessage
id="xpack.code.searchPage.emptyTitle"
defaultMessage="Hmmm... we looked for that, but couldnt find anything."
/>
</EuiText>
<EuiSpacer size="xl" />
<EuiText textAlign="center" color="subdued">
You can search for something else or modify your search settings.
<FormattedMessage
id="xpack.code.searchPage.emptyText"
defaultMessage="You can search for something else or modify your search settings."
/>
</EuiText>
<EuiSpacer size="l" />
<EuiText textAlign="center">
@ -41,7 +48,10 @@ export const EmptyPlaceholder = (props: any) => {
}
}}
>
Modify your search settings
<FormattedMessage
id="xpack.code.searchPage.modifySearchSettingButtonLabel"
defaultMessage="Modify your search settings"
/>
</EuiButton>
</EuiText>
</div>

View file

@ -5,6 +5,7 @@
*/
import { EuiTab, EuiTabs } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import querystring from 'querystring';
import React from 'react';
import url from 'url';
@ -44,14 +45,20 @@ export class ScopeTabs extends React.PureComponent<Props> {
isSelected={this.props.scope !== SearchScope.REPOSITORY}
onClick={this.onTabClicked(SearchScope.DEFAULT)}
>
Code
<FormattedMessage
id="xpack.code.searchPage.scopeTabs.codeTabLabel"
defaultMessage="Code"
/>
</EuiTab>
<EuiTab
className="codeUtility__width--half"
isSelected={this.props.scope === SearchScope.REPOSITORY}
onClick={this.onTabClicked(SearchScope.REPOSITORY)}
>
Repository
<FormattedMessage
id="xpack.code.searchPage.scopeTabs.repositoryTabLabel"
defaultMessage="Repository"
/>
</EuiTab>
</EuiTabs>
</div>

View file

@ -5,6 +5,7 @@
*/
import { EuiFlexItem, EuiLoadingSpinner, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import querystring from 'querystring';
import React from 'react';
import { connect } from 'react-redux';
@ -166,7 +167,11 @@ class SearchPage extends React.PureComponent<Props, State> {
const statsComp = (
<EuiTitle size="m">
<h1>
Showing {total > 0 ? from : 0} - {to} of {total} results.
<FormattedMessage
id="xpack.code.searchPage.showingResultsTitle"
defaultMessage="Showing {from} - {to} of {total} results."
values={{ from, to, total }}
/>
</h1>
</EuiTitle>
);
@ -186,7 +191,11 @@ class SearchPage extends React.PureComponent<Props, State> {
const statsComp = (
<EuiTitle size="m">
<h1>
Showing {total > 0 ? from : 0} - {to} of {total} results.
<FormattedMessage
id="xpack.code.searchPage.showingResultsTitle"
defaultMessage="Showing {from} - {to} of {total} results."
values={{ from, to, total }}
/>
</h1>
</EuiTitle>
);

View file

@ -13,6 +13,7 @@ import {
EuiTitle,
EuiToken,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { RepositoryUtils } from '../../../common/repository_utils';
@ -116,7 +117,12 @@ export class SideBar extends React.PureComponent<Props> {
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="xxs">
<h3>Repositories</h3>
<h3>
<FormattedMessage
id="xpack.code.searchPage.sideBar.repositoriesTitle"
defaultMessage="Repositories"
/>
</h3>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
@ -136,7 +142,12 @@ export class SideBar extends React.PureComponent<Props> {
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="xxs">
<h3>Languages</h3>
<h3>
<FormattedMessage
id="xpack.code.searchPage.sideBar.languagesTitle"
defaultMessage="Languages"
/>
</h3>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -17,6 +17,7 @@ import {
LangServerType,
REPO_FILE_STATUS_SEVERITY,
RepoFileStatus,
RepoFileStatusText as StatusText,
Severity,
StatusReport,
} from '../../../common/repo_file_status';
@ -80,7 +81,7 @@ export class StatusIndicatorComponent extends React.Component<Props, State> {
</p>
);
} else {
children.push(<p key={`${error}_key`}>{error}</p>);
children.push(<p key={`${error}_key`}>{StatusText[error]}</p>);
}
}
};

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { I18nProvider } from '@kbn/i18n/react';
import { editor as Editor, languages, Range as EditorRange } from 'monaco-editor';
// @ts-ignore
import { createCancelablePromise } from 'monaco-editor/esm/vs/base/common/async';
@ -135,11 +136,20 @@ export class ContentHoverWidget extends ContentWidget {
}
this.showAt(new monaco.Position(renderRange.startLineNumber, startColumn), this.shouldFocus);
const element = React.createElement(HoverWidget, props, null);
const element = (
<I18nProvider>
<HoverWidget {...props} />
</I18nProvider>
);
// @ts-ignore
ReactDOM.render(element, fragment);
const buttonFragment = document.createDocumentFragment();
const buttons = React.createElement(HoverButtons, props, null);
const buttons = (
<I18nProvider>
<HoverButtons {...props} />
</I18nProvider>
);
// @ts-ignore
ReactDOM.render(buttons, buttonFragment);
this.updateContents(fragment, buttonFragment);

View file

@ -6,6 +6,7 @@
import produce from 'immer';
import { Action, handleActions } from 'redux-actions';
import { i18n } from '@kbn/i18n';
import { Repository, RepoConfigs, RepositoryConfig } from '../../model';
import {
@ -89,22 +90,35 @@ export const repositoryManagement = handleActions<
draft.importLoading = true;
}),
[String(importRepoSuccess)]: (state, action: Action<Repository>) =>
// TODO is it possible and how to deal with action.payload === undefined?
produce<RepositoryManagementState>(state, draft => {
draft.importLoading = false;
draft.showToast = true;
draft.toastType = ToastType.success;
draft.toastMessage = `${action.payload!.name} has been successfully submitted!`;
draft.toastMessage = i18n.translate(
'xpack.code.repositoryManagement.repoSubmittedMessage',
{
defaultMessage: '{name} has been successfully submitted!',
values: { name: action.payload!.name },
}
);
draft.repositories = [...state.repositories, action.payload!];
}),
[String(importRepoFailed)]: (state, action: Action<any>) =>
produce<RepositoryManagementState>(state, draft => {
if (action.payload) {
if (action.payload.res.status === 304) {
draft.toastMessage = 'This Repository has already been imported!';
draft.toastMessage = i18n.translate(
'xpack.code.repositoryManagement.repoImportedMessage',
{
defaultMessage: 'This Repository has already been imported!',
}
);
draft.showToast = true;
draft.toastType = ToastType.warning;
draft.importLoading = false;
} else {
// TODO add localication for those messages
draft.toastMessage = action.payload.body.message;
draft.showToast = true;
draft.toastType = ToastType.danger;

View file

@ -4279,7 +4279,148 @@
"xpack.canvas.functions.timefilterControl.args.columnHelpText": "附加筛选的列字段",
"xpack.canvas.functions.timefilterControl.args.compactHelpText": "将时间筛选显示为触发弹出框的按钮",
"xpack.canvas.sampleDataLinkLabel": "Canvas",
"xpack.code.adminPage.langserverTab.installedText": "已安装",
"xpack.code.adminPage.langserverTab.languageServersDescription": "{serverCount} {serverCount, plural, one {服务器} other {服务器}}",
"xpack.code.adminPage.langserverTab.notInstalledText": "未安装",
"xpack.code.adminPage.langserverTab.runningText": "运行中",
"xpack.code.adminPage.langserverTab.setup.closeButtonLabel": "关闭",
"xpack.code.adminPage.langserverTab.setup.installationInstructionTitle": "安装指南",
"xpack.code.adminPage.langserverTab.setup.installTitle": "安装",
"xpack.code.adminPage.langserverTab.setup.stopKibanaDescription": "停止您的 Kibana Code 服务器",
"xpack.code.adminPage.langserverTab.setup.uninstallTitle": "卸载",
"xpack.code.adminPage.langserverTab.setup.useFollowingCommandToInstallDescription": "使用以下命令来安装 {name} 语言服务器",
"xpack.code.adminPage.langserverTab.setup.useFollowingCommandToRemoveDescription": "使用以下命令来移除 {name} 语言服务器",
"xpack.code.adminPage.langserverTab.setupButtonLabel": "安装",
"xpack.code.adminPage.langserverTabLabel": "语言服务器",
"xpack.code.adminPage.repoTab.cancelButtonLabel": "导入",
"xpack.code.adminPage.repoTab.emptyRepo.importFirstRepositoryText": "请导入第一个代码仓库",
"xpack.code.adminPage.repoTab.emptyRepo.noRepositoryText": "您还没有导入任何代码仓库",
"xpack.code.adminPage.repoTab.emptyRepo.viewSetupGuideButtonLabel": "查看配置指南",
"xpack.code.adminPage.repoTab.importButtonLabel": "导入",
"xpack.code.adminPage.repoTab.importRepoButtonLabel": "导入新仓库",
"xpack.code.adminPage.repoTab.importRepoTitle": "导入新仓库",
"xpack.code.adminPage.repoTab.repoDescription": "{projectsCount} 仓库",
"xpack.code.adminPage.repoTab.repositoryUrlEmptyText": "URL 不能为空",
"xpack.code.adminPage.repoTab.repositoryUrlFormLabel": "代码仓库 URL",
"xpack.code.adminPage.repoTab.repositoryUrlTitle": "代码仓库 URL",
"xpack.code.adminPage.repoTab.sort.aToZDropDownOptionLabel": "A 到 Z",
"xpack.code.adminPage.repoTab.sort.sortByFormLabel": "排序依据",
"xpack.code.adminPage.repoTab.sort.updatedAscDropDownOptionLabel": "最近更新升序",
"xpack.code.adminPage.repoTab.sort.updatedDescDropDownOptionLabel": "最近更新降序",
"xpack.code.adminPage.repoTab.sort.zToADropDownOptionLabel": "Z 到 A",
"xpack.code.adminPage.repoTabLabel": "代码仓库",
"xpack.code.adminPage.setupGuide.checkMultiInstanceTitle": "检查是否是多个 Kibana 实例组成的集群",
"xpack.code.adminPage.setupGuide.checkMultiInstanceDescription1": "如果您使用的是单 Kibana 实例,可以跳过这一步。",
"xpack.code.adminPage.setupGuide.checkMultiInstanceDescription2": "如果您使用多个 Kibana 实例,您需要制定其中一个实例为 `Code 节点`。您可以将以下配置添加到您每个 Kibana 实例的 kibana.yml 文件中,然后重启所有实例:",
"xpack.code.adminPage.setupGuide.checkMultiInstanceDescription3": "其中 `$YourCodeNoteAddress` 是您 Code 节点的 URL。",
"xpack.code.adminPage.setupGuide.installExtraLangSupportTitle": "安装额外的语言支持(可选)",
"xpack.code.adminPage.setupGuide.installExtraLangSupportDescription1": "查看{link}了解我们支持的语言和语言服务器的安装。",
"xpack.code.adminPage.setupGuide.installExtraLangSupportDescription2": "如果您需要 Java 支持,您可以在{link}管理语言服务器。",
"xpack.code.adminPage.setupGuide.installExtraLangSupportHereLinkText": "此处",
"xpack.code.adminPage.setupGuide.addRepositoryTitle": "添加代码仓库到 Code",
"xpack.code.adminPage.setupGuide.addRepositoryDescription": "导入{sampleRepoLink}或者{ownRepoLink}。您可以很容易地复制粘贴 Git 的克隆 URL 到Code。",
"xpack.code.adminPage.setupGuide.addRepositorySampleRepoLinkText": "示例仓库",
"xpack.code.adminPage.setupGuide.addRepositoryOwnRepoLinkText": "您自己的仓库",
"xpack.code.adminPage.setupGuide.verifyImportTitle": "验证代码仓库是否被成功导入",
"xpack.code.adminPage.setupGuide.verifyImportDescription": "您可以尝试{searchingLink}{navigatingLink},以及{semanticNavigationLink}来验证仓库是否被成功导入并索引。",
"xpack.code.adminPage.setupGuide.verifyImportSearchingLinkText": "搜索",
"xpack.code.adminPage.setupGuide.verifyImportNavigatingLinkText": "导航",
"xpack.code.adminPage.setupGuide.verifyImportSemanticNavigatingLinkText": "语义导航",
"xpack.code.adminPage.setupGuide.backToDashboardButtonLabel": "返回到仪表板",
"xpack.code.adminPage.setupGuide.getStartedTitle": "开始使用 Elastic Code",
"xpack.code.adminPage.setupGuide.learnMoreButtonLabel": "了解更多",
"xpack.code.adminPage.setupGuide.permissionChangesDescription": "Kibana 的角色和权限模型有新的改动,请了解这些改动对您 Code 部署的影响",
"xpack.code.adminPage.setupGuide.permissionChangesTitle": "权限模型变动",
"xpack.code.featureRegistry.codeFeatureName": "Code",
"xpack.code.gitUrlUtil.urlNotWhitelistedMessage": "Git URL 主机地址没有在白名单中。",
"xpack.code.gitUrlUtil.protocolNotWhitelistedMessage": "Git URL 的协议没有在白名单中。",
"xpack.code.helpMenu.codeDocumentationButtonLabel": "Code 文档",
"xpack.code.helpMenu.helpDescription": "Code 相关信息",
"xpack.code.helpMenu.setupGuideButtonLabel": "配置指南",
"xpack.code.mainPage.content.cloneStatus.isCloningText": "正在克隆",
"xpack.code.mainPage.content.cloneStatus.yourProjectWillBeAvailableText": "当此过程完成后您的项目即可访问",
"xpack.code.mainPage.content.cloneStatus.progress.receivingText": "正在接收对象 {progressRate}% {receivedObjects}/{totalObjects}",
"xpack.code.mainPage.content.cloneStatus.progress.receivingRateOnlyText": "正在接收对象 {progressRate}%",
"xpack.code.mainPage.content.cloneStatus.progress.indexingText": "正在索引对象 {progressRate}% {indexedObjects}/{totalObjects}",
"xpack.code.mainPage.content.cloneStatus.progress.cloneFailedText": "克隆失败",
"xpack.code.mainPage.content.buttons.blameButtonLabel": "注解",
"xpack.code.mainPage.content.buttons.codeButtonLabel": "代码",
"xpack.code.mainPage.content.buttons.contentButtonLabel": "内容",
"xpack.code.mainPage.content.buttons.downloadButtonLabel": "下载",
"xpack.code.mainPage.content.buttons.folderButtonLabel": "目录",
"xpack.code.mainPage.content.buttons.historyButtonLabel": "历史",
"xpack.code.mainPage.content.buttons.rawButtonLabel": "原件",
"xpack.code.mainPage.content.directory.directoriesTitle": "目录",
"xpack.code.mainPage.content.directory.filesTitle": "文件",
"xpack.code.mainPage.directory.recentCommitsTitle": "近期提交",
"xpack.code.mainPage.directory.viewAllCommitsButtonLabel": "查看全部",
"xpack.code.mainPage.history.commitHistoryTitle": "提交记录",
"xpack.code.mainPage.history.commitsOnTitle": "{date} 的提交",
"xpack.code.mainPage.history.moreButtonLabel": "更多",
"xpack.code.mainPage.sideTab.fileTabLabel": "文件",
"xpack.code.mainPage.sideTab.languageServerInitializingText": "语言服务器初始化中",
"xpack.code.mainPage.sideTab.loadingFileTreeText": "读取目录树",
"xpack.code.mainPage.sideTab.loadingStructureTreeText": "读取结构树",
"xpack.code.mainPage.sideTab.structureTabLabel": "结构",
"xpack.code.monaco.hover.findReferenceButtonLabel": "查看引用",
"xpack.code.monaco.hover.gotoDefinitionButtonLabel": "跳转到定义",
"xpack.code.monaco.hover.languageServerInitializingDescription": "取决于代码仓库大小,初始化需要花费若干分钟",
"xpack.code.monaco.hover.languageServerInitializingTitle": "语言服务器正在初始化…",
"xpack.code.repoItem.cancelButtonText": "否,取消",
"xpack.code.repoItem.cloneErrorText": "克隆错误",
"xpack.code.repoItem.cloningText": "克隆中...",
"xpack.code.repoItem.confirmButtonText": "是,继续",
"xpack.code.repoItem.deleteButtonLabel": "删除",
"xpack.code.repoItem.deleteConfirmTitle": "确认删除仓库?",
"xpack.code.repoItem.deleteErrorText": "删除错误",
"xpack.code.repoItem.deletingText": "删除中...",
"xpack.code.repoItem.indexErrorText": "索引错误",
"xpack.code.repoItem.indexingText": "索引中...",
"xpack.code.repoItem.initText": "初始化中...",
"xpack.code.repoItem.lastUpdatedText": "最后更新",
"xpack.code.repoItem.reindexButtonLabel": "重新索引",
"xpack.code.repoItem.reindexConfirmTitle": "确认重新索引仓库?",
"xpack.code.repoItem.settingsButtonLabel": "设置",
"xpack.code.repositoryManagement.repoImportedMessage": "此代码仓库已经被成功导入!",
"xpack.code.repositoryManagement.repoSubmittedMessage": "{name} 已经被成功提交!",
"xpack.code.repoFileStatus.langugageServerIsInitializitingMessage": "语言服务器正在初始化。",
"xpack.code.repoFileStatus.languageServerInitializedMessage": "语言服务器已经初始化。",
"xpack.code.repoFileStatus.IndexingInProgressMessage": "索引正在进行中。",
"xpack.code.repoFileStatus.fileNotSupportedMessage": "当前文件的语言没有支持。",
"xpack.code.repoFileStatus.revisionNotIndexedMessage": "当前版本还没有被索引。",
"xpack.code.repoFileStatus.langServerNotInstalledMessage": "请安装其他语言服务器以支持当前文件。",
"xpack.code.repoFileStatus.fileIsTooBigMessage": "当前文件过大。",
"xpack.code.repoFileStatus.langserverType.noneMessage": "当前文件不被任何语言服务器支持。",
"xpack.code.repoFileStatus.langserverType.genericMessage": "当前文件由通用语言服务器支持。",
"xpack.code.repoFileStatus.langserverType.dedicatedMessage": "当前文件由专用语言服务器支持。",
"xpack.code.searchBar.addRepoPlaceholder": "查找并添加仓库",
"xpack.code.searchBar.addToScopeDescription": "添加已索引代码仓库到搜索范围",
"xpack.code.searchBar.applyAndCloseButtonLabel": "应用并关闭",
"xpack.code.searchBar.fileGroupTitle": "文件",
"xpack.code.searchBar.repositorylGroupTitle": "仓库",
"xpack.code.searchBar.resultCountText": "{total} {total, plural, one {结果} other {结果}}",
"xpack.code.searchBar.searchFilterTitle": " {filterCount, plural, one {搜索筛选} other {搜索筛选}}",
"xpack.code.searchBar.searchScopeTitle": "搜索范围",
"xpack.code.searchBar.symbolGroupTitle": "符号",
"xpack.code.searchBar.viewFullSearchLinkText": "按 ⮐ 以返回全文搜索结果",
"xpack.code.searchBar.viewMoreLinkText": "查看更多",
"xpack.code.searchPage.emptyText": "您可以输入其他查询或者修改搜索设置。",
"xpack.code.searchPage.emptyTitle": "对不起,我们并没有找到任何结果。",
"xpack.code.searchPage.hitsCountText": " 个匹配在 ",
"xpack.code.searchPage.modifySearchSettingButtonLabel": "修改搜索设置",
"xpack.code.searchPage.scopeTabs.codeTabLabel": "代码",
"xpack.code.searchPage.scopeTabs.repositoryTabLabel": "仓库",
"xpack.code.searchPage.showingResultsTitle": "以下是从 {from} 到 {to} 个结果,总共找到 {total} 个结果",
"xpack.code.searchPage.sideBar.languagesTitle": "语言",
"xpack.code.searchPage.sideBar.repositoriesTitle": "代码仓库",
"xpack.code.searchScope.defaultDropDownOptionLabel": "搜索全部",
"xpack.code.searchScope.defaultPlaceholder": "请输入任何查询",
"xpack.code.searchScope.fileDropDownOptionLabel": "搜索文件",
"xpack.code.searchScope.filePlaceholder": "请输入文件查询",
"xpack.code.searchScope.repositoryDropDownOptionLabel": "搜索仓库",
"xpack.code.searchScope.repositoryPlaceholder": "请输入仓库查询",
"xpack.code.searchScope.symbolDropDownOptionLabel": "搜索符号",
"xpack.code.searchScope.symbolPlaceholder": "请输入符号查询",
"xpack.crossClusterReplication.addAutoFollowPatternButtonLabel": "创建自动跟随模式",
"xpack.crossClusterReplication.addBreadcrumbTitle": "添加",
"xpack.crossClusterReplication.addFollowerButtonLabel": "创建 Follower 索引",