diff --git a/dashboard/src/components/NavigationItems.vue b/dashboard/src/components/NavigationItems.vue
index d47e0df..d2e2c12 100644
--- a/dashboard/src/components/NavigationItems.vue
+++ b/dashboard/src/components/NavigationItems.vue
@@ -76,7 +76,7 @@ export default {
disabled: enforce2FA,
},
{
- name: '工作台',
+ name: this.$t('Benches'),
icon: () => h(Package),
route: '/benches',
isActive: routeName.startsWith('Bench'),
@@ -84,7 +84,7 @@ export default {
disabled: !onboardingComplete || enforce2FA,
},
{
- name: '站点分组',
+ name: this.$t('Release Group'),
icon: () => h(Boxes),
route: '/groups',
isActive:
diff --git a/dashboard/src/objects/bench.ts b/dashboard/src/objects/bench.ts
index 0cd3003..840b8a9 100644
--- a/dashboard/src/objects/bench.ts
+++ b/dashboard/src/objects/bench.ts
@@ -26,6 +26,7 @@ import type {
} from './common/types';
import { getLogsTab } from './tabs/site/logs';
import { getPatchesTab } from './common/patches';
+import { t } from '../utils/i18n';
export default {
pagetype: 'Bench',
@@ -45,11 +46,11 @@ function getDetail() {
const team = getTeam();
return [
{
- label: '选项',
+ label: t('Options'),
condition: () => team.pg?.is_desk_user ?? false,
options: [
{
- label: '在 Desk 中查看',
+ label: t('View in Desk'),
icon: icon('external-link'),
condition: () => team.pg?.is_desk_user,
onClick() {
@@ -60,7 +61,7 @@ function getDetail() {
}
},
{
- label: '模拟团队',
+ label: t('Impersonate Team'),
icon: defineAsyncComponent(
() => import('~icons/lucide/venetian-mask')
),
@@ -106,7 +107,7 @@ function getRoutes() {
function getList() {
return {
route: '/benches',
- title: '工作台',
+ title: t('Benches'),
fields: [
'group.title as group_title',
'cluster.name as cluster_name',
@@ -117,32 +118,32 @@ function getList() {
searchField: 'name',
columns: [
{
- label: '工作台',
+ label: t('Benches'),
fieldname: 'name',
class: 'font-medium',
suffix: getBenchTitleSuffix
},
{
- label: '状态',
+ label: t('Status'),
fieldname: 'status',
type: 'Badge',
width: '100px'
},
{
- label: '站点',
+ label: t('Sites'),
fieldname: 'site_count',
type: 'Number',
width: '100px',
align: 'right'
},
{
- label: '区域',
+ label: t('Region'),
fieldname: 'cluster',
width: 0.75,
format: (value, row) => String(row.cluster_title || value || ''),
prefix: getClusterImagePrefix
},
- { label: '站点分组', fieldname: 'group_title', width: '350px' }
+ { label: t('Release Group'), fieldname: 'group_title', width: '350px' }
],
filterControls
} satisfies List as List;
@@ -167,9 +168,9 @@ function getInPlaceUpdatesSuffix(row: Row) {
String(row.inplace_update_docker_image).split('-').at(-1)
);
- let title = '工作台已就地更新';
+ let title = t('Bench has been updated in place');
if (!Number.isNaN(count) && count > 1) {
- title += ` ${count} 次`;
+ title += ` ${count} ${t('times')}`;
}
return h(
@@ -186,7 +187,7 @@ function getAppPatchSuffix(row: Row) {
return h(
'div',
{
- title: '此工作台中的应用可能已打补丁',
+ title: t('Apps in this bench may have been patched'),
class: 'rounded-full bg-gray-100 p-1'
},
h(icon('hash', 'w-3 h-3'))
@@ -207,21 +208,21 @@ function filterControls() {
return [
{
type: 'select',
- label: '状态',
+ label: t('Status'),
fieldname: 'status',
options: [
{ label: '', value: '' },
- { label: '激活', value: 'Active' },
- { label: '待定', value: 'Pending' },
- { label: '安装中', value: 'Installing' },
- { label: '更新中', value: 'Updating' },
- { label: '损坏', value: 'Broken' },
- { label: '已归档', value: 'Archived' }
+ { label: t('Active'), value: 'Active' },
+ { label: t('Pending'), value: 'Pending' },
+ { label: t('Installing'), value: 'Installing' },
+ { label: t('Updating'), value: 'Updating' },
+ { label: t('Broken'), value: 'Broken' },
+ { label: t('Archived'), value: 'Archived' }
]
},
{
type: 'link',
- label: '站点分组',
+ label: t('Release Group'),
fieldname: 'group',
options: {
pagetype: 'Release Group'
@@ -229,7 +230,7 @@ function filterControls() {
},
{
type: 'select',
- label: '区域',
+ label: t('Region'),
fieldname: 'cluster',
options: clusterOptions
}
@@ -238,7 +239,7 @@ function filterControls() {
export function getSitesTab() {
return {
- label: '站点',
+ label: t('Sites'),
icon: icon(LucideAppWindow),
route: 'sites',
type: 'list',
@@ -266,7 +267,7 @@ export function getSitesTab() {
route: sitesTabRoute,
primaryAction: r => {
return {
- label: '新建站点',
+ label: t('New Site'),
slots: {
prefix: icon('plus', 'w-4 h-4')
},
@@ -278,7 +279,7 @@ export function getSitesTab() {
},
rowActions: ({ row }) => [
{
- label: '在桌面查看',
+ label: t('View in Desk'),
condition: () => getTeam()?.pg?.is_desk_user,
onClick() {
window.open(
@@ -295,7 +296,7 @@ export function getSitesTab() {
export function getProcessesTab() {
const url = 'jcloud.api.bench.get_processes';
return {
- label: '进程',
+ label: t('Processes'),
icon: icon('cpu'),
route: 'processes',
type: 'list',
@@ -331,18 +332,18 @@ export function getProcessesColumns() {
type Status = keyof typeof processStatusColorMap;
return [
{
- label: '名称',
+ label: t('Name'),
width: 2,
fieldname: 'name'
},
{
- label: '组',
+ label: t('Group'),
width: 1.5,
fieldname: 'group',
format: v => String(v ?? '')
},
{
- label: '状态',
+ label: t('Status'),
type: 'Badge',
width: 0.7,
fieldname: 'status',
@@ -363,7 +364,7 @@ width: 1.5,
}
},
{
- label: '运行时间',
+ label: t('Uptime'),
fieldname: 'uptime_string'
}
] satisfies ColumnField[] as ColumnField[];
diff --git a/dashboard/src/objects/group.js b/dashboard/src/objects/group.js
index e5582a1..055cb74 100644
--- a/dashboard/src/objects/group.js
+++ b/dashboard/src/objects/group.js
@@ -15,6 +15,7 @@ import { date, duration } from '../utils/format';
import { getJobsTab } from './common/jobs';
import { getPatchesTab } from './common/patches';
import { tagTab } from './common/tags';
+import { t } from '../utils/i18n';
export default {
pagetype: 'Release Group',
@@ -41,14 +42,14 @@ export default {
},
list: {
route: '/groups',
- title: '站点分组',
+ title: t('Release Group'),
fields: [{ apps: ['app'] }],
searchField: 'title',
filterControls() {
return [
{
type: 'link',
- label: '版本',
+ label: t('Version'),
fieldname: 'version',
options: {
pagetype: 'Jingrow Version'
@@ -56,7 +57,7 @@ export default {
},
{
type: 'link',
- label: '标签',
+ label: t('Tag'),
fieldname: 'tags.tag',
options: {
pagetype: 'Jcloud Tag',
@@ -68,24 +69,24 @@ export default {
];
},
columns: [
- { label: '标题', fieldname: 'title', class: 'font-medium' },
+ { label: t('Title'), fieldname: 'title', class: 'font-medium' },
{
- label: '状态',
+ label: t('Status'),
fieldname: 'active_benches',
type: 'Badge',
width: 0.5,
format: (value, row) => {
- if (!value) return '等待部署';
- else return '激活';
+ if (!value) return t('Waiting for Deployment');
+ else return t('Active');
}
},
{
- label: '版本',
+ label: t('Version'),
fieldname: 'version',
width: 0.5
},
{
- label: '应用',
+ label: t('App'),
fieldname: 'app',
format: (value, row) => {
return (row.apps || []).map(d => d.app).join(', ');
@@ -93,7 +94,7 @@ export default {
width: '25rem'
},
{
- label: '站点',
+ label: t('Sites'),
fieldname: 'site_count',
class: 'text-gray-600',
width: 0.25
@@ -101,7 +102,7 @@ export default {
],
primaryAction() {
return {
- label: '新建站点分组',
+ label: t('New Release Group'),
variant: 'solid',
slots: {
prefix: icon('plus')
@@ -142,7 +143,7 @@ export default {
route: '/groups/:name',
tabs: [
{
- label: '站点',
+ label: t('Sites'),
icon: icon(LucideAppWindow),
route: 'sites',
type: 'Component',
@@ -154,7 +155,7 @@ export default {
}
},
{
- label: '应用',
+ label: t('App'),
icon: icon('grid'),
route: 'apps',
type: 'list',
@@ -169,12 +170,12 @@ export default {
pageLength: 99999,
columns: [
{
- label: '应用',
+ label: t('App'),
fieldname: 'title',
width: 1
},
{
- label: '仓库',
+ label: t('Repository'),
width: 1,
format(value, row) {
return `${row.repository_owner}/${row.repository}`;
@@ -184,7 +185,7 @@ export default {
}
},
{
- label: '分支',
+ label: t('Branch'),
fieldname: 'branch',
type: 'Badge',
width: 0.5,
@@ -193,7 +194,7 @@ export default {
}
},
{
- label: '版本',
+ label: t('Version'),
type: 'Badge',
fieldname: 'tag',
width: 0.5,
@@ -202,7 +203,7 @@ export default {
}
},
{
- label: '状态',
+ label: t('Status'),
type: 'Badge',
suffix(row) {
if (!row.last_github_poll_failed) return;
@@ -210,7 +211,7 @@ export default {
return h(
Tooltip,
{
- text: "这是什么?",
+ text: t("What is this?"),
placement: 'top',
class: 'rounded-full bg-gray-100 p-1'
},
@@ -231,12 +232,12 @@ export default {
row;
return last_github_poll_failed
- ? '需要操作'
+ ? t('Action Required')
: !deployed
- ? '未部署'
+ ? t('Not Deployed')
: update_available
- ? '有可用更新'
- : '最新版本';
+ ? t('Update Available')
+ : t('Latest Version');
},
width: 0.5
}
@@ -249,7 +250,7 @@ export default {
let team = getTeam();
return [
{
- label: '在桌面查看',
+ label: t('View in Desk'),
condition: () => team.pg?.is_desk_user,
onClick() {
window.open(
@@ -259,17 +260,17 @@ export default {
}
},
{
- label: '获取最新更新',
+ label: t('Fetch Latest Updates'),
onClick() {
toast.promise(
releaseGroup.fetchLatestAppUpdates.submit({
app: row.name
}),
{
- loading: `正在为 ${row.title} 获取最新更新...`,
+ loading: t('Fetching latest updates for {app}...', { app: row.title }),
success: () => {
apps.reload();
- return `已为 ${row.title} 获取最新更新`;
+ return t('Latest updates fetched for {app}', { app: row.title });
},
error: e => getToastErrorMessage(e)
}
@@ -277,7 +278,7 @@ export default {
}
},
{
- label: '更改分支',
+ label: t('Change Branch'),
onClick() {
renderDialog(
h(ChangeAppBranchDialog, {
@@ -291,24 +292,24 @@ export default {
}
},
{
- label: '移除应用',
+ label: t('Remove App'),
condition: () => row.name !== 'jingrow',
onClick() {
if (releaseGroup.removeApp.loading) return;
confirmDialog({
- title: '移除应用',
- message: `确定要移除应用 ${row.title} 吗?`,
+ title: t('Remove App'),
+ message: t('Are you sure you want to remove app {app}?', { app: row.title }),
onSuccess: ({ hide }) => {
toast.promise(
releaseGroup.removeApp.submit({
app: row.name
}),
{
- loading: '正在移除应用...',
+ loading: t('Removing app...'),
success: () => {
hide();
apps.reload();
- return '应用已移除';
+ return t('App removed');
},
error: e => getToastErrorMessage(e)
}
@@ -318,7 +319,7 @@ export default {
}
},
{
- label: '访问仓库',
+ label: t('Visit Repository'),
onClick() {
window.open(
`${row.repository_url}/tree/${row.branch}`,
@@ -327,7 +328,7 @@ export default {
}
},
{
- label: '应用补丁',
+ label: t('Apply Patch'),
onClick: () => {
renderDialog(
h(PatchAppDialog, {
@@ -344,7 +345,7 @@ export default {
documentResource: releaseGroup
}) {
return {
- label: '添加应用',
+ label: t('Add App'),
slots: {
prefix: icon('plus')
},
@@ -358,8 +359,8 @@ export default {
},
onNewApp(app, isUpdate) {
const loading = isUpdate
- ? '替换应用中...'
- : '添加应用中...';
+ ? t('Replacing app...')
+ : t('Adding app...');
toast.promise(
releaseGroup.addApp.submit({
@@ -373,10 +374,10 @@ export default {
releaseGroup.reload();
if (isUpdate) {
- return `应用 ${app.title} 已更新`;
+ return t('App {app} updated', { app: app.title });
}
- return `应用 ${app.title} 已添加`;
+ return t('App {app} added', { app: app.title });
},
error: e => getToastErrorMessage(e)
}
@@ -390,7 +391,7 @@ export default {
}
},
{
- label: '部署',
+ label: t('Deploy'),
route: 'deploys',
icon: icon('package'),
childrenRoutes: ['Deploy Candidate'],
@@ -412,7 +413,7 @@ export default {
return [
{
type: 'select',
- label: '状态',
+ label: t('Status'),
fieldname: 'status',
options: [
'',
@@ -431,7 +432,7 @@ export default {
if (releaseGroup.pg.are_builds_suspended) {
return {
title:
- '构建已暂停:更新将在构建恢复后计划运行。',
+ t('Builds suspended: Updates will be scheduled after builds resume.'),
type: 'warning'
};
} else {
@@ -440,15 +441,15 @@ export default {
},
columns: [
{
- label: '部署',
+ label: t('Deploy'),
fieldname: 'creation',
format(value) {
- return `部署于 ${date(value, 'llll')}`;
+ return t('Deployed at {date}', { date: date(value, 'llll') });
},
width: '20rem'
},
{
- label: '状态',
+ label: t('Status'),
fieldname: 'status',
type: 'Badge',
width: 0.5,
@@ -460,7 +461,7 @@ export default {
return h(
Tooltip,
{
- text: '需要关注!',
+ text: t('Attention required!'),
placement: 'top',
class: 'rounded-full bg-gray-100 p-1'
},
@@ -469,35 +470,35 @@ export default {
}
},
{
- label: '应用',
+ label: t('App'),
format(value, row) {
return (row.apps || []).map(d => d.app).join(', ');
},
width: '20rem'
},
{
- label: '持续时间',
+ label: t('Duration'),
fieldname: 'build_duration',
format: duration,
class: 'text-gray-600',
width: 1
},
{
- label: '部署者',
+ label: t('Deployer'),
fieldname: 'owner',
width: 1
}
],
primaryAction({ listResource: deploys, documentResource: group }) {
return {
- label: '部署',
+ label: t('Deploy'),
slots: {
prefix: icon(LucideRocket)
},
onClick() {
if (group.pg.deploy_information.deploy_in_progress) {
return toast.error(
- '部署正在进行中。请等待其完成。'
+ t('Deployment in progress. Please wait for it to complete.')
);
} else if (group.pg.deploy_information.update_available) {
let UpdateReleaseGroupDialog = defineAsyncComponent(() =>
@@ -517,16 +518,16 @@ export default {
);
} else {
confirmDialog({
- title: '无需应用更新即可部署?',
+ title: t('Deploy without app updates?'),
message:
- '未检测到应用更新。部署时将应用依赖项和环境变量的更改。',
+ t('No app updates detected. Deployment will apply dependency and environment variable changes.'),
onSuccess: ({ hide }) => {
toast.promise(group.redeploy.submit(), {
- loading: '正在部署...',
+ loading: t('Deploying...'),
success: () => {
hide();
deploys.reload();
- return '更改已部署';
+ return t('Changes deployed');
},
error: e => getToastErrorMessage(e)
});
@@ -540,7 +541,7 @@ export default {
},
getJobsTab('Release Group'),
{
- label: '配置',
+ label: t('Config'),
icon: icon('settings'),
route: 'bench-config',
type: 'list',
@@ -557,7 +558,7 @@ export default {
pageLength: 999,
columns: [
{
- label: '配置名称',
+ label: t('Config Name'),
fieldname: 'key',
format(value, row) {
if (row.title) {
@@ -567,12 +568,12 @@ export default {
}
},
{
- label: '配置值',
+ label: t('Config Value'),
fieldname: 'value',
class: 'font-mono'
},
{
- label: '类型',
+ label: t('Type'),
fieldname: 'type',
type: 'Badge',
width: '100px'
@@ -583,7 +584,7 @@ export default {
documentResource: releaseGroup
}) {
return {
- label: '添加配置',
+ label: t('Add Config'),
slots: {
prefix: icon('plus')
},
@@ -604,7 +605,7 @@ export default {
},
secondaryAction({ listResource: configs }) {
return {
- label: '预览',
+ label: t('Preview'),
slots: {
prefix: icon('eye')
},
@@ -627,7 +628,7 @@ export default {
}) {
return [
{
- label: '编辑',
+ label: t('Edit'),
onClick() {
let ConfigEditorDialog = defineAsyncComponent(() =>
import('../components/ConfigEditorDialog.vue')
@@ -644,11 +645,11 @@ export default {
}
},
{
- label: '删除',
+ label: t('Delete'),
onClick() {
confirmDialog({
- title: '删除配置',
- message: `确定要删除配置 ${row.key} 吗?`,
+ title: t('Delete Config'),
+ message: t('Are you sure you want to delete config {key}?', { key: row.key }),
onSuccess({ hide }) {
if (releaseGroup.deleteConfig.loading) return;
toast.promise(
@@ -662,8 +663,8 @@ export default {
}
),
{
- loading: '正在删除配置...',
- success: () => `配置 ${row.key} 已删除`,
+ loading: t('Deleting config...'),
+ success: () => t('Config {key} deleted', { key: row.key }),
error: e => getToastErrorMessage(e)
}
);
@@ -676,7 +677,7 @@ export default {
}
},
{
- label: '操作',
+ label: t('Actions'),
icon: icon('sliders'),
route: 'actions',
type: 'Component',
@@ -688,7 +689,7 @@ export default {
}
},
{
- label: '区域',
+ label: t('Region'),
icon: icon('globe'),
route: 'regions',
type: 'list',
@@ -699,11 +700,11 @@ export default {
},
columns: [
{
- label: '区域',
+ label: t('Region'),
fieldname: 'title'
},
{
- label: '国家',
+ label: t('Country'),
fieldname: 'image',
format(value, row) {
return '';
@@ -722,7 +723,7 @@ export default {
documentResource: releaseGroup
}) {
return {
- label: '添加区域',
+ label: t('Add Region'),
slots: {
prefix: icon('plus')
},
@@ -745,7 +746,7 @@ export default {
},
getPatchesTab(false),
{
- label: '依赖项',
+ label: t('Dependencies'),
icon: icon('box'),
route: 'bench-dependencies',
type: 'list',
@@ -759,14 +760,14 @@ export default {
},
columns: [
{
- label: '依赖项',
+ label: t('Dependencies'),
fieldname: 'dependency',
format(value, row) {
return row.title;
}
},
{
- label: '版本',
+ label: t('Version'),
fieldname: 'version',
suffix(row) {
if (!row.is_custom) {
@@ -776,7 +777,7 @@ export default {
return h(
Tooltip,
{
- text: '自定义版本',
+ text: t('Custom Version'),
placement: 'top',
class: 'rounded-full bg-gray-100 p-1'
},
@@ -792,7 +793,7 @@ export default {
}) {
return [
{
- label: '编辑',
+ label: t('Edit'),
onClick() {
let DependencyEditorDialog = defineAsyncComponent(() =>
import('../components/group/DependencyEditorDialog.vue')
@@ -813,7 +814,7 @@ export default {
}
},
{
- label: '环境',
+ label: t('Environment'),
icon: icon('tool'),
route: 'bench-environment-variable',
type: 'list',
@@ -829,11 +830,11 @@ export default {
fields: ['name'],
columns: [
{
- label: '环境变量名称',
+ label: t('Environment Variable Name'),
fieldname: 'key'
},
{
- label: '环境变量值',
+ label: t('Environment Variable Value'),
fieldname: 'value'
}
],
@@ -842,7 +843,7 @@ export default {
documentResource: releaseGroup
}) {
return {
- label: '添加环境变量',
+ label: t('Add Environment Variable'),
slots: {
prefix: icon('plus')
},
@@ -868,7 +869,7 @@ export default {
}) {
return [
{
- label: '编辑',
+ label: t('Edit'),
onClick() {
let ConfigEditorDialog = defineAsyncComponent(() =>
import('../components/EnvironmentVariableEditorDialog.vue')
@@ -885,11 +886,11 @@ export default {
}
},
{
- label: '删除',
+ label: t('Delete'),
onClick() {
confirmDialog({
- title: '删除环境变量',
- message: `确定要删除环境变量 ${row.key} 吗?`,
+ title: t('Delete Environment Variable'),
+ message: t('Are you sure you want to delete environment variable {key}?', { key: row.key }),
onSuccess({ hide }) {
if (releaseGroup.deleteEnvironmentVariable.loading)
return;
@@ -904,9 +905,9 @@ export default {
}
),
{
- loading: '正在删除环境变量...',
+ loading: t('Deleting environment variable...'),
success: () =>
- `环境变量 ${row.key} 已删除`,
+ t('Environment variable {key} deleted', { key: row.key }),
error: e => getToastErrorMessage(e)
}
);
@@ -926,8 +927,8 @@ export default {
return [
{
- label: '模拟组所有者',
- title: '模拟组所有者',
+ label: t('Impersonate Group Owner'),
+ title: t('Impersonate Group Owner'),
slots: {
icon: defineAsyncComponent(() =>
import('~icons/lucide/venetian-mask')
@@ -941,8 +942,8 @@ export default {
},
{
label: group.pg?.deploy_information?.last_deploy
- ? '有可用更新'
- : '立即部署',
+ ? t('Update Available')
+ : t('Deploy Now'),
slots: {
prefix: group.pg?.deploy_information?.last_deploy
? icon(LucideHardDriveDownload)
@@ -971,8 +972,8 @@ export default {
);
} else {
confirmDialog({
- title: '部署',
- message: "立即部署吗?",
+ title: t('Deploy'),
+ message: t("Deploy now?"),
onSuccess({ hide }) {
toast.promise(
group.initialDeploy.submit(null, {
@@ -982,9 +983,9 @@ export default {
}
}),
{
- success: '部署计划成功',
- error: '部署计划失败',
- loading: '正在计划部署...'
+ success: t('Deployment scheduled successfully'),
+ error: t('Deployment scheduling failed'),
+ loading: t('Scheduling deployment...')
}
);
}
@@ -993,7 +994,7 @@ export default {
}
},
{
- label: '部署进行中',
+ label: t('Deployment in Progress'),
slots: {
prefix: () => h(LoadingIndicator, { class: 'w-4 h-4' })
},
@@ -1005,11 +1006,11 @@ export default {
}
},
{
- label: '选项',
+ label: t('Options'),
condition: () => team.pg?.is_desk_user,
options: [
{
- label: '在 Desk 中查看',
+ label: t('View in Desk'),
icon: icon('external-link'),
condition: () => team.pg?.is_desk_user,
onClick() {
diff --git a/jcloud/translations/zh.csv b/jcloud/translations/zh.csv
index 9d5a604..de7ab98 100644
--- a/jcloud/translations/zh.csv
+++ b/jcloud/translations/zh.csv
@@ -43,6 +43,7 @@ Cancel Update,取消更新,
Cancelled,取消,
Cancelling update...,正在取消更新...,
Card,卡,
+China,中国大陆,
China Mainland,中国大陆,
Category,类别,
Change Plan,更改计划,
@@ -491,6 +492,69 @@ Switch Team,切换团队,
Logout,退出登录,
Suspended,已暂停,
Broken,损坏,
+China,中国大陆,
+Apps in this bench may have been patched,此工作台中的应用可能已打补丁,
+Bench has been updated in place,工作台已就地更新,
+Benches,工作台,
+Processes,进程,
+Uptime,运行时间,
+times,次,
+Fetch Latest Updates,获取最新更新,
+Fetching latest updates for {app}...,正在为 {app} 获取最新更新...,
+Latest updates fetched for {app},已为 {app} 获取最新更新,
+Change Branch,更改分支,
+Remove App,移除应用,
+Visit Repository,访问仓库,
+Add App,添加应用,
+Replacing app...,替换应用中...,
+Adding app...,添加应用中...,
+App {app} updated,应用 {app} 已更新,
+App {app} added,应用 {app} 已添加,
+App removed,应用已移除,
+Deploy,部署,
+Deploy Candidate,部署候选,
+Deploy Now,立即部署,
+Deployment in Progress,部署进行中,
+Deployment scheduled successfully,部署计划成功,
+Deployment scheduling failed,部署计划失败,
+Deploying...,正在部署...,
+Changes deployed,更改已部署,
+Deployment in progress. Please wait for it to complete.,部署正在进行中。请等待其完成。,
+Deploy without app updates?,无需应用更新即可部署?,
+No app updates detected. Deployment will apply dependency and environment variable changes.,未检测到应用更新。部署时将应用依赖项和环境变量的更改。,
+Deployed at {date},部署于 {date},
+Attention required!,需要关注!,
+Deployer,部署者,
+Config,配置,
+Config Name,配置名称,
+Config Value,配置值,
+Add Config,添加配置,
+Delete Config,删除配置,
+Deleting config...,正在删除配置...,
+Config {key} deleted,配置 {key} 已删除,
+Custom Version,自定义版本,
+Dependencies,依赖项,
+Environment,环境,
+Environment Variable Name,环境变量名称,
+Environment Variable Value,环境变量值,
+Add Environment Variable,添加环境变量,
+Delete Environment Variable,删除环境变量,
+Deleting environment variable...,正在删除环境变量...,
+Environment variable {key} deleted,环境变量 {key} 已删除,
+Impersonate Group Owner,模拟组所有者,
+Name,名称,
+Not Deployed,未部署,
+Add Region,添加区域,
+Repository,仓库,
+Branch,分支,
+Scheduling deployment...,正在计划部署...,
+What is this?,这是什么?,
+Action Required,需要操作,
+Builds suspended: Updates will be scheduled after builds resume.,构建已暂停:更新将在构建恢复后计划运行。,
+Are you sure you want to remove app {app}?,确定要移除应用 {app} 吗?,
+Removing app...,正在移除应用...,
+Are you sure you want to delete config {key}?,确定要删除配置 {key} 吗?,
+Are you sure you want to delete environment variable {key}?,确定要删除环境变量 {key} 吗?,