fix(i18n): 运行模式/权限/沙箱菜单选项改用 labelKey 动态渲染,切语言即时生效

state.ts 四个选项数组此前在模块顶层用 t() 固化翻译字符串,切换语言后不更新、需刷新页面。改为存 labelKey/descriptionKey,渲染处动态求值(模板 $t / computed t(matched.labelKey)),与 PersonalizationDrawer 既有规范模式对齐。tsc/build/i18n 审计均通过。
This commit is contained in:
JOJO 2026-08-29 10:09:08 +08:00
parent a2cf547400
commit 4aac18f7bb
4 changed files with 40 additions and 39 deletions

View File

@ -51,13 +51,13 @@ export const permissionMethods = {
void currentLocale.value;
const options = Array.isArray(this.permissionModeOptions) ? this.permissionModeOptions : [];
const hit = options.find((item) => item.value === mode);
return hit ? hit.label : mode || t('appUi.unknown');
return hit ? t(hit.labelKey) : mode || t('appUi.unknown');
},
getExecutionModeLabel(mode) {
void currentLocale.value;
const options = Array.isArray(this.executionModeOptions) ? this.executionModeOptions : [];
const hit = options.find((item) => item.value === mode);
return hit ? hit.label : mode || t('appUi.unknown');
return hit ? t(hit.labelKey) : mode || t('appUi.unknown');
},
async changePermissionMode(mode) {
const target = String(mode || '')

View File

@ -11,7 +11,7 @@ export const workModeMethods = {
void currentLocale.value;
const options = Array.isArray(this.workModeOptions) ? this.workModeOptions : [];
const hit = options.find((item) => item.value === mode);
return hit ? hit.label : mode || t('appUi.unknown');
return hit ? t(hit.labelKey) : mode || t('appUi.unknown');
},
async fetchWorkMode() {
try {

View File

@ -196,13 +196,13 @@ export function dataState() {
networkPermissionOptions: [
{
value: 'restricted',
label: t('appUi.networkRestricted'),
description: t('appCore.networkRestrictedDesc')
labelKey: 'appUi.networkRestricted',
descriptionKey: 'appCore.networkRestrictedDesc'
},
{
value: 'full',
label: t('appUi.networkFull'),
description: t('appCore.networkFullDesc')
labelKey: 'appUi.networkFull',
descriptionKey: 'appCore.networkFullDesc'
}
],
workModeMenuOpen: false,
@ -210,18 +210,18 @@ export function dataState() {
workModeOptions: [
{
value: 'plan',
label: t('appUi.workModePlan'),
description: t('appCore.workModePlanDesc')
labelKey: 'appUi.workModePlan',
descriptionKey: 'appCore.workModePlanDesc'
},
{
value: 'ask',
label: t('appUi.workModeAsk'),
description: t('appCore.workModeAskDesc')
labelKey: 'appUi.workModeAsk',
descriptionKey: 'appCore.workModeAskDesc'
},
{
value: 'execute',
label: t('appUi.workModeExecute'),
description: t('appCore.workModeExecuteDesc')
labelKey: 'appUi.workModeExecute',
descriptionKey: 'appCore.workModeExecuteDesc'
}
],
pathAuthorizationDialogOpen: false,
@ -256,35 +256,35 @@ export function dataState() {
permissionModeOptions: [
{
value: 'readonly',
label: t('personalization.permissionReadonly'),
description: t('appCore.permissionReadonlyDesc')
labelKey: 'personalization.permissionReadonly',
descriptionKey: 'appCore.permissionReadonlyDesc'
},
{
value: 'approval',
label: t('appCore.permissionApproval'),
description: t('appCore.permissionApprovalDesc')
labelKey: 'appCore.permissionApproval',
descriptionKey: 'appCore.permissionApprovalDesc'
},
{
value: 'auto_approval',
label: t('appCore.permissionAutoApproval'),
description: t('appCore.permissionAutoApprovalDesc')
labelKey: 'appCore.permissionAutoApproval',
descriptionKey: 'appCore.permissionAutoApprovalDesc'
},
{
value: 'unrestricted',
label: t('appCore.permissionUnrestricted'),
description: t('appCore.permissionUnrestrictedDesc')
labelKey: 'appCore.permissionUnrestricted',
descriptionKey: 'appCore.permissionUnrestrictedDesc'
}
],
executionModeOptions: [
{
value: 'sandbox',
label: t('input.executionSandbox'),
description: t('appCore.executionSandboxDesc')
labelKey: 'input.executionSandbox',
descriptionKey: 'appCore.executionSandboxDesc'
},
{
value: 'direct',
label: t('input.executionFullAccess'),
description: t('appCore.executionDirectDesc')
labelKey: 'input.executionFullAccess',
descriptionKey: 'appCore.executionDirectDesc'
}
],
pendingToolApprovals: [],

View File

@ -375,8 +375,8 @@
:class="{ 'is-active': option.value === currentWorkMode }"
@click.stop="$emit('change-work-mode', option.value)"
>
<span class="agent-type-switcher__menu-label">{{ option.label }}</span>
<span class="agent-type-switcher__menu-desc">{{ option.description }}</span>
<span class="agent-type-switcher__menu-label">{{ $t(option.labelKey) }}</span>
<span class="agent-type-switcher__menu-desc">{{ $t(option.descriptionKey) }}</span>
</button>
</div>
</div>
@ -546,8 +546,8 @@
:disabled="permissionLockedByPlan"
@click="$emit('change-permission-mode', option.value)"
>
<span class="permission-switcher__item-label">{{ option.label }}</span>
<span class="permission-switcher__item-desc">{{ option.description }}</span>
<span class="permission-switcher__item-label">{{ $t(option.labelKey) }}</span>
<span class="permission-switcher__item-desc">{{ $t(option.descriptionKey) }}</span>
</button>
</div>
<div
@ -572,9 +572,9 @@
<span
class="permission-switcher__item-label"
:class="{ 'permission-switcher__item-label--warn': option.value === 'direct' }"
>{{ option.label }}</span
>{{ $t(option.labelKey) }}</span
>
<span class="permission-switcher__item-desc">{{ option.description }}</span>
<span class="permission-switcher__item-desc">{{ $t(option.descriptionKey) }}</span>
</button>
</div>
<div
@ -595,9 +595,9 @@
<span
class="permission-switcher__item-label"
:class="{ 'permission-switcher__item-label--warn': option.value === 'full' }"
>{{ option.label }}</span
>{{ $t(option.labelKey) }}</span
>
<span class="permission-switcher__item-desc">{{ option.description }}</span>
<span class="permission-switcher__item-desc">{{ $t(option.descriptionKey) }}</span>
</button>
</div>
</div>
@ -756,13 +756,13 @@ const props = defineProps<{
blockConversationReview?: boolean;
currentPermissionMode: 'readonly' | 'approval' | 'auto_approval' | 'unrestricted';
permissionMenuOpen: boolean;
permissionOptions: Array<{ value: string; label: string; description: string }>;
permissionOptions: Array<{ value: string; labelKey: string; descriptionKey: string }>;
executionModeEnabled?: boolean;
currentExecutionMode?: 'sandbox' | 'direct';
executionModeOptions?: Array<{ value: string; label: string; description: string }>;
executionModeOptions?: Array<{ value: string; labelKey: string; descriptionKey: string }>;
networkPermissionEnabled?: boolean;
currentNetworkPermission?: 'restricted' | 'full';
networkPermissionOptions?: Array<{ value: string; label: string; description: string }>;
networkPermissionOptions?: Array<{ value: string; labelKey: string; descriptionKey: string }>;
currentContextTokens: number;
versioningEnabled?: boolean;
runtimeQueuedMessages?: Array<{ id: string; text: string }>;
@ -790,7 +790,7 @@ const props = defineProps<{
/** 运行模式菜单是否展开 */
workModeMenuOpen?: boolean;
/** 运行模式选项 */
workModeOptions?: Array<{ value: string; label: string; description: string }>;
workModeOptions?: Array<{ value: string; labelKey: string; descriptionKey: string }>;
/** host/docker 项目模式下的工作区列表(/菜单「切换工作区」数据源) */
hostWorkspaces?: Array<{
workspace_id: string;
@ -3320,10 +3320,11 @@ const triggerQuickUpload = () => {
};
const currentPermissionLabel = computed(() => {
void currentLocale.value;
const matched = (props.permissionOptions || []).find(
(item) => item.value === props.currentPermissionMode
);
return matched ? matched.label : props.currentPermissionMode;
return matched ? t(matched.labelKey) : props.currentPermissionMode;
});
/** 对话类型选择器:选项定义(响应式:随语言切换刷新) */
@ -3364,7 +3365,7 @@ const workModeLabel = computed(() => {
const matched = (props.workModeOptions || []).find(
(item) => item.value === props.currentWorkMode
);
return matched ? matched.label : props.currentWorkMode || t('input.workModePlanFallback');
return matched ? t(matched.labelKey) : props.currentWorkMode || t('input.workModePlanFallback');
});
/** 计划模式下权限被锁定为只读UI 禁用 + 后端强制) */