From fc4152cf1952d179fcd0ea18e2b38d270f985fb3 Mon Sep 17 00:00:00 2001 From: jingrow Date: Mon, 29 Dec 2025 04:42:45 +0800 Subject: [PATCH] =?UTF-8?q?Benches=E8=8F=9C=E5=8D=95=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=A4=9A=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/components/NavigationItems.vue | 4 +- dashboard/src/objects/bench.ts | 59 +++--- dashboard/src/objects/group.js | 201 ++++++++++--------- jcloud/translations/zh.csv | 64 ++++++ 4 files changed, 197 insertions(+), 131 deletions(-) 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} 吗?,