feat(skill-market): bilingual skill display names (nameZh / nameEn)

This commit is contained in:
matevip 2026-04-24 06:55:20 +08:00
parent a8f4236d90
commit 58ec60a5b8
10 changed files with 161 additions and 3 deletions

View File

@ -139,6 +139,9 @@ public class BuiltinSkillSeedService implements ApplicationRunner {
row.setIcon(stringFromFrontmatter(parsed, "icon", DEFAULT_ICON));
row.setAuthor(stringFromFrontmatter(parsed, "author", DEFAULT_AUTHOR));
row.setTags(tagsFromFrontmatter(parsed, parsed.getName()));
// RFC-042 §2.2 optional bilingual display names from frontmatter.
row.setNameZh(stringFromFrontmatter(parsed, "nameZh", null));
row.setNameEn(stringFromFrontmatter(parsed, "nameEn", null));
row.setConfigJson(buildConfigJson(parsed));
LocalDateTime now = LocalDateTime.now();
row.setCreateTime(now);
@ -190,6 +193,19 @@ public class BuiltinSkillSeedService implements ApplicationRunner {
dirty = true;
}
// RFC-042 §2.2 bilingual display names. Frontmatter wins; if silent,
// preserve whatever the SQL seed or a manual UI edit populated.
String nameZh = stringFromFrontmatter(parsed, "nameZh", null);
if (nameZh != null && !Objects.equals(existing.getNameZh(), nameZh)) {
existing.setNameZh(nameZh);
dirty = true;
}
String nameEn = stringFromFrontmatter(parsed, "nameEn", null);
if (nameEn != null && !Objects.equals(existing.getNameEn(), nameEn)) {
existing.setNameEn(nameEn);
dirty = true;
}
String configJson = buildConfigJson(parsed);
if (!Objects.equals(existing.getConfigJson(), configJson)) {
existing.setConfigJson(configJson);

View File

@ -18,9 +18,23 @@ public class SkillEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/** 技能名称 */
/** 技能名称 — immutable slug, used as primary identifier */
private String name;
/**
* RFC-042 §2.2 locale-specific display name for zh-CN.
* {@code null} UI falls back to {@code name}.
*/
@TableField(value = "name_zh", updateStrategy = FieldStrategy.ALWAYS)
private String nameZh;
/**
* RFC-042 §2.2 locale-specific display name for en-US.
* {@code null} UI falls back to {@code name}.
*/
@TableField(value = "name_en", updateStrategy = FieldStrategy.ALWAYS)
private String nameEn;
/** 技能描述 */
private String description;

View File

@ -538,6 +538,30 @@ MERGE INTO mate_skill (id, name, description, skill_type, icon, version, author,
KEY (id)
VALUES (1000000019, 'multi_agent_collaboration', 'When a task requires the professional capabilities of multiple Agents, orchestrate parallel or serial multi-agent collaboration and integrate results.', 'builtin', '🤝', '1.4.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'multi-agent,collaboration,orchestration,parallel', NOW(), NOW(), 0);
-- RFC-042 §2.2 — bilingual display names for the 19 builtin skills.
-- Identical across all four data-*.sql files because name_zh / name_en are
-- permanent attributes, not locale-conditional. The UI picks which one to
-- show based on the active i18n locale and falls back to `name` when null.
UPDATE mate_skill SET name_zh = '定时任务', name_en = 'Cron Jobs' WHERE name = 'cron';
UPDATE mate_skill SET name_zh = '文件阅读器', name_en = 'File Reader' WHERE name = 'file_reader';
UPDATE mate_skill SET name_zh = '钉钉渠道接入', name_en = 'DingTalk Channel' WHERE name = 'dingtalk_channel_connect';
UPDATE mate_skill SET name_zh = '邮件管理', name_en = 'Email (Himalaya)' WHERE name = 'himalaya';
UPDATE mate_skill SET name_zh = '新闻查询', name_en = 'News' WHERE name = 'news';
UPDATE mate_skill SET name_zh = 'PDF 处理', name_en = 'PDF' WHERE name = 'pdf';
UPDATE mate_skill SET name_zh = 'Word 文档', name_en = 'Word Document' WHERE name = 'docx';
UPDATE mate_skill SET name_zh = 'PPT 演示', name_en = 'PowerPoint' WHERE name = 'pptx';
UPDATE mate_skill SET name_zh = 'Excel 表格', name_en = 'Excel' WHERE name = 'xlsx';
UPDATE mate_skill SET name_zh = '可见浏览器', name_en = 'Visible Browser' WHERE name = 'browser_visible';
UPDATE mate_skill SET name_zh = '浏览器 CDP', name_en = 'Browser CDP' WHERE name = 'browser_cdp';
UPDATE mate_skill SET name_zh = '安装指引', name_en = 'Setup Guidance' WHERE name = 'guidance';
UPDATE mate_skill SET name_zh = '源码索引', name_en = 'Source Index' WHERE name = 'mateclaw_source_index';
UPDATE mate_skill SET name_zh = 'SQL 查询', name_en = 'SQL Query' WHERE name = 'sql_query';
UPDATE mate_skill SET name_zh = '乔布斯视角', name_en = 'Steve Jobs Perspective' WHERE name = 'steve_jobs_perspective';
UPDATE mate_skill SET name_zh = '制定计划', name_en = 'Make Plan' WHERE name = 'make_plan';
UPDATE mate_skill SET name_zh = '咨询智能体', name_en = 'Chat with Agent' WHERE name = 'chat_with_agent';
UPDATE mate_skill SET name_zh = '渠道推送', name_en = 'Channel Push' WHERE name = 'channel_message';
UPDATE mate_skill SET name_zh = '多智能体协作', name_en = 'Multi-Agent Collaboration' WHERE name = 'multi_agent_collaboration';
-- Populate skill_content for key built-in skills (SKILL.md execution protocol)
-- NOTE: For pdf/docx/pptx/xlsx/himalaya, the authoritative SKILL.md is bundled in
-- classpath:skills/{name}/ and auto-synced to workspace on startup.

View File

@ -576,6 +576,30 @@ INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author
VALUES (1000000019, 'multi_agent_collaboration', 'When a task requires the professional capabilities of multiple Agents, orchestrate parallel or serial multi-agent collaboration and integrate results.', 'builtin', '🤝', '1.4.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'multi-agent,collaboration,orchestration,parallel', NOW(), NOW(), 0)
ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), skill_type=VALUES(skill_type), icon=VALUES(icon), version=VALUES(version), author=VALUES(author), config_json=VALUES(config_json), enabled=VALUES(enabled), builtin=VALUES(builtin), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted);
-- RFC-042 §2.2 — bilingual display names for the 19 builtin skills.
-- Identical across all four data-*.sql files because name_zh / name_en are
-- permanent attributes, not locale-conditional. The UI picks which one to
-- show based on the active i18n locale and falls back to `name` when null.
UPDATE mate_skill SET name_zh = '定时任务', name_en = 'Cron Jobs' WHERE name = 'cron';
UPDATE mate_skill SET name_zh = '文件阅读器', name_en = 'File Reader' WHERE name = 'file_reader';
UPDATE mate_skill SET name_zh = '钉钉渠道接入', name_en = 'DingTalk Channel' WHERE name = 'dingtalk_channel_connect';
UPDATE mate_skill SET name_zh = '邮件管理', name_en = 'Email (Himalaya)' WHERE name = 'himalaya';
UPDATE mate_skill SET name_zh = '新闻查询', name_en = 'News' WHERE name = 'news';
UPDATE mate_skill SET name_zh = 'PDF 处理', name_en = 'PDF' WHERE name = 'pdf';
UPDATE mate_skill SET name_zh = 'Word 文档', name_en = 'Word Document' WHERE name = 'docx';
UPDATE mate_skill SET name_zh = 'PPT 演示', name_en = 'PowerPoint' WHERE name = 'pptx';
UPDATE mate_skill SET name_zh = 'Excel 表格', name_en = 'Excel' WHERE name = 'xlsx';
UPDATE mate_skill SET name_zh = '可见浏览器', name_en = 'Visible Browser' WHERE name = 'browser_visible';
UPDATE mate_skill SET name_zh = '浏览器 CDP', name_en = 'Browser CDP' WHERE name = 'browser_cdp';
UPDATE mate_skill SET name_zh = '安装指引', name_en = 'Setup Guidance' WHERE name = 'guidance';
UPDATE mate_skill SET name_zh = '源码索引', name_en = 'Source Index' WHERE name = 'mateclaw_source_index';
UPDATE mate_skill SET name_zh = 'SQL 查询', name_en = 'SQL Query' WHERE name = 'sql_query';
UPDATE mate_skill SET name_zh = '乔布斯视角', name_en = 'Steve Jobs Perspective' WHERE name = 'steve_jobs_perspective';
UPDATE mate_skill SET name_zh = '制定计划', name_en = 'Make Plan' WHERE name = 'make_plan';
UPDATE mate_skill SET name_zh = '咨询智能体', name_en = 'Chat with Agent' WHERE name = 'chat_with_agent';
UPDATE mate_skill SET name_zh = '渠道推送', name_en = 'Channel Push' WHERE name = 'channel_message';
UPDATE mate_skill SET name_zh = '多智能体协作', name_en = 'Multi-Agent Collaboration' WHERE name = 'multi_agent_collaboration';
-- Populate skill_content for key built-in skills (SKILL.md execution protocol)
-- NOTE: For pdf/docx/pptx/xlsx/himalaya, the authoritative SKILL.md is bundled in
-- classpath:skills/{name}/ and auto-synced to workspace on startup.

View File

@ -578,6 +578,30 @@ INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author
VALUES (1000000019, 'multi_agent_collaboration', '当任务需要多个 Agent 的专业能力协同完成时,编排多 Agent 并行或串行协作,整合各方结果。', 'builtin', '🤝', '1.4.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'multi-agent,collaboration,orchestration,parallel', NOW(), NOW(), 0)
ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), skill_type=VALUES(skill_type), icon=VALUES(icon), version=VALUES(version), author=VALUES(author), config_json=VALUES(config_json), enabled=VALUES(enabled), builtin=VALUES(builtin), tags=VALUES(tags), update_time=VALUES(update_time), deleted=VALUES(deleted);
-- RFC-042 §2.2 — bilingual display names for the 19 builtin skills.
-- Identical across all four data-*.sql files because name_zh / name_en are
-- permanent attributes, not locale-conditional. The UI picks which one to
-- show based on the active i18n locale and falls back to `name` when null.
UPDATE mate_skill SET name_zh = '定时任务', name_en = 'Cron Jobs' WHERE name = 'cron';
UPDATE mate_skill SET name_zh = '文件阅读器', name_en = 'File Reader' WHERE name = 'file_reader';
UPDATE mate_skill SET name_zh = '钉钉渠道接入', name_en = 'DingTalk Channel' WHERE name = 'dingtalk_channel_connect';
UPDATE mate_skill SET name_zh = '邮件管理', name_en = 'Email (Himalaya)' WHERE name = 'himalaya';
UPDATE mate_skill SET name_zh = '新闻查询', name_en = 'News' WHERE name = 'news';
UPDATE mate_skill SET name_zh = 'PDF 处理', name_en = 'PDF' WHERE name = 'pdf';
UPDATE mate_skill SET name_zh = 'Word 文档', name_en = 'Word Document' WHERE name = 'docx';
UPDATE mate_skill SET name_zh = 'PPT 演示', name_en = 'PowerPoint' WHERE name = 'pptx';
UPDATE mate_skill SET name_zh = 'Excel 表格', name_en = 'Excel' WHERE name = 'xlsx';
UPDATE mate_skill SET name_zh = '可见浏览器', name_en = 'Visible Browser' WHERE name = 'browser_visible';
UPDATE mate_skill SET name_zh = '浏览器 CDP', name_en = 'Browser CDP' WHERE name = 'browser_cdp';
UPDATE mate_skill SET name_zh = '安装指引', name_en = 'Setup Guidance' WHERE name = 'guidance';
UPDATE mate_skill SET name_zh = '源码索引', name_en = 'Source Index' WHERE name = 'mateclaw_source_index';
UPDATE mate_skill SET name_zh = 'SQL 查询', name_en = 'SQL Query' WHERE name = 'sql_query';
UPDATE mate_skill SET name_zh = '乔布斯视角', name_en = 'Steve Jobs Perspective' WHERE name = 'steve_jobs_perspective';
UPDATE mate_skill SET name_zh = '制定计划', name_en = 'Make Plan' WHERE name = 'make_plan';
UPDATE mate_skill SET name_zh = '咨询智能体', name_en = 'Chat with Agent' WHERE name = 'chat_with_agent';
UPDATE mate_skill SET name_zh = '渠道推送', name_en = 'Channel Push' WHERE name = 'channel_message';
UPDATE mate_skill SET name_zh = '多智能体协作', name_en = 'Multi-Agent Collaboration' WHERE name = 'multi_agent_collaboration';
-- 为关键 builtin skill 填充 skill_contentSKILL.md 执行协议)
-- NOTE: For pdf/docx/pptx/xlsx/himalaya, the authoritative SKILL.md is bundled in
-- classpath:skills/{name}/ and auto-synced to workspace on startup.

View File

@ -544,6 +544,30 @@ MERGE INTO mate_skill (id, name, description, skill_type, icon, version, author,
KEY (id)
VALUES (1000000019, 'multi_agent_collaboration', '当任务需要多个 Agent 的专业能力协同完成时,编排多 Agent 并行或串行协作,整合各方结果。', 'builtin', '🤝', '1.4.0', 'MateClaw', '{"upstream":"mateclaw","entryFile":"SKILL.md"}', TRUE, TRUE, 'multi-agent,collaboration,orchestration,parallel', NOW(), NOW(), 0);
-- RFC-042 §2.2 — bilingual display names for the 19 builtin skills.
-- Identical across all four data-*.sql files because name_zh / name_en are
-- permanent attributes, not locale-conditional. The UI picks which one to
-- show based on the active i18n locale and falls back to `name` when null.
UPDATE mate_skill SET name_zh = '定时任务', name_en = 'Cron Jobs' WHERE name = 'cron';
UPDATE mate_skill SET name_zh = '文件阅读器', name_en = 'File Reader' WHERE name = 'file_reader';
UPDATE mate_skill SET name_zh = '钉钉渠道接入', name_en = 'DingTalk Channel' WHERE name = 'dingtalk_channel_connect';
UPDATE mate_skill SET name_zh = '邮件管理', name_en = 'Email (Himalaya)' WHERE name = 'himalaya';
UPDATE mate_skill SET name_zh = '新闻查询', name_en = 'News' WHERE name = 'news';
UPDATE mate_skill SET name_zh = 'PDF 处理', name_en = 'PDF' WHERE name = 'pdf';
UPDATE mate_skill SET name_zh = 'Word 文档', name_en = 'Word Document' WHERE name = 'docx';
UPDATE mate_skill SET name_zh = 'PPT 演示', name_en = 'PowerPoint' WHERE name = 'pptx';
UPDATE mate_skill SET name_zh = 'Excel 表格', name_en = 'Excel' WHERE name = 'xlsx';
UPDATE mate_skill SET name_zh = '可见浏览器', name_en = 'Visible Browser' WHERE name = 'browser_visible';
UPDATE mate_skill SET name_zh = '浏览器 CDP', name_en = 'Browser CDP' WHERE name = 'browser_cdp';
UPDATE mate_skill SET name_zh = '安装指引', name_en = 'Setup Guidance' WHERE name = 'guidance';
UPDATE mate_skill SET name_zh = '源码索引', name_en = 'Source Index' WHERE name = 'mateclaw_source_index';
UPDATE mate_skill SET name_zh = 'SQL 查询', name_en = 'SQL Query' WHERE name = 'sql_query';
UPDATE mate_skill SET name_zh = '乔布斯视角', name_en = 'Steve Jobs Perspective' WHERE name = 'steve_jobs_perspective';
UPDATE mate_skill SET name_zh = '制定计划', name_en = 'Make Plan' WHERE name = 'make_plan';
UPDATE mate_skill SET name_zh = '咨询智能体', name_en = 'Chat with Agent' WHERE name = 'chat_with_agent';
UPDATE mate_skill SET name_zh = '渠道推送', name_en = 'Channel Push' WHERE name = 'channel_message';
UPDATE mate_skill SET name_zh = '多智能体协作', name_en = 'Multi-Agent Collaboration' WHERE name = 'multi_agent_collaboration';
-- 为关键 builtin skill 填充 skill_contentSKILL.md 执行协议)
-- NOTE: For pdf/docx/pptx/xlsx/himalaya, the authoritative SKILL.md is bundled in
-- classpath:skills/{name}/ and auto-synced to workspace on startup.

View File

@ -1636,6 +1636,8 @@ export default {
},
fields: {
name: 'Name',
nameZh: 'Chinese display name',
nameEn: 'English display name',
type: 'Type',
icon: 'Icon',
version: 'Version',
@ -1648,6 +1650,8 @@ export default {
},
placeholders: {
name: 'Skill name',
nameZh: '可选 — 留空则显示原始 name',
nameEn: 'Optional — leave blank to show the slug',
icon: '🛠️ (emoji or URL)',
version: '1.0.0',
author: 'Author name',

View File

@ -1646,6 +1646,8 @@ export default {
},
fields: {
name: '名称',
nameZh: '中文显示名',
nameEn: '英文显示名',
type: '类型',
icon: '图标',
version: '版本',
@ -1658,6 +1660,8 @@ export default {
},
placeholders: {
name: '技能名称',
nameZh: '可选 — 留空则显示原始 name',
nameEn: 'Optional — leave blank to show the slug',
icon: '🛠️ (emoji 或 URL)',
version: '1.0.0',
author: '作者名称',

View File

@ -194,7 +194,11 @@ export interface MessageContentPart {
// ==================== 技能 ====================
export interface Skill {
id: string | number
/** Slug / immutable identifier */
name: string
/** RFC-042 §2.2 — locale display names; null = fall back to `name` */
nameZh?: string
nameEn?: string
description?: string
skillType: string
icon?: string

View File

@ -66,7 +66,9 @@
<span class="skill-icon">{{ skill.icon || getSkillIcon(skill.skillType) }}</span>
</div>
<div class="skill-meta">
<h3 class="skill-name">{{ skill.name }}</h3>
<h3 class="skill-name">{{ resolveSkillName(skill) }}</h3>
<!-- RFC-042 §2.2.4 show the underlying slug below the i18n display name -->
<div v-if="hasI18nName(skill)" class="skill-slug">{{ skill.name }}</div>
<div class="skill-meta-row">
<span class="skill-type-badge" :class="getSkillTypeBadge(skill.skillType)">
{{ getSkillTypeLabel(skill.skillType) }}
@ -240,6 +242,15 @@
<input v-model="form.name" class="form-input" :placeholder="t('skills.placeholders.name')"
:disabled="isBuiltinEditing" />
</div>
<!-- RFC-042 §2.2.6 optional bilingual display names -->
<div class="form-group">
<label class="form-label">{{ t('skills.fields.nameZh') }}</label>
<input v-model="form.nameZh" class="form-input" :placeholder="t('skills.placeholders.nameZh')" />
</div>
<div class="form-group">
<label class="form-label">{{ t('skills.fields.nameEn') }}</label>
<input v-model="form.nameEn" class="form-input" :placeholder="t('skills.placeholders.nameEn')" />
</div>
<div class="form-group">
<label class="form-label">{{ t('skills.fields.type') }}</label>
<select v-model="form.skillType" class="form-input" :disabled="isBuiltinEditing">
@ -312,8 +323,10 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { skillApi } from '@/api/index'
import type { Skill, SkillRuntimeStatus, SkillSecurityFinding } from '@/types/index'
import ImportHubDialog from '@/components/skill/ImportHubDialog.vue'
import { useSkillName } from '@/composables/useSkillName'
const { t } = useI18n()
const { resolveSkillName, hasI18nName } = useSkillName()
const skills = ref<Skill[]>([])
const total = ref(0)
const counts = ref<Record<string, number>>({})
@ -346,6 +359,9 @@ const categoryTabs = computed(() => [
const defaultForm = () => ({
name: '',
// RFC-042 §2.2.6 optional bilingual display names
nameZh: '',
nameEn: '',
description: '',
skillType: 'dynamic' as string,
icon: '',
@ -469,6 +485,8 @@ function openEditModal(skill: Skill) {
editingSkill.value = skill
form.value = {
name: skill.name,
nameZh: skill.nameZh || '',
nameEn: skill.nameEn || '',
description: skill.description || '',
skillType: skill.skillType,
icon: skill.icon || '',
@ -942,7 +960,9 @@ html.dark .scan-finding-item { background: rgba(255, 255, 255, 0.05); }
.bg-gray { background: var(--mc-bg-sunken); }
.skill-icon { font-size: 20px; }
.skill-meta { flex: 1; overflow: hidden; }
.skill-name { font-size: 16px; font-weight: 700; color: var(--mc-text-primary); margin: 0 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.skill-name { font-size: 16px; font-weight: 700; color: var(--mc-text-primary); margin: 0 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* RFC-042 §2.2 — slug printed under the i18n display name when they differ */
.skill-slug { font-size: 11px; color: var(--mc-text-tertiary); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; margin: 0 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.skill-meta-row { display: flex; align-items: center; gap: 6px; }
.skill-type-badge { padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 500; }
.badge-blue { background: var(--mc-primary-bg); color: var(--mc-primary); }