feat(options/i18n): add i18n for Userscripts Manager UI (en, zh_CN)

Use chrome.i18n in App.vue and add locale messages
This commit is contained in:
hangwin
2025-10-09 11:23:16 +00:00
parent 09e07f276b
commit f481658f8d
3 changed files with 135 additions and 43 deletions
+49 -1
View File
@@ -442,5 +442,53 @@
"pagesUnit": {
"message": "pages",
"description": "Pages count unit"
}
},
"userscriptsManagerTitle": {
"message": "Userscripts Manager",
"description": "Options page title"
},
"emergencySwitchLabel": { "message": "Emergency Switch", "description": "Global disable switch" },
"createRunSectionTitle": {
"message": "Create / Run",
"description": "Create & run section title"
},
"nameLabel": { "message": "Name", "description": "Name input label" },
"runAtLabel": { "message": "Run At", "description": "runAt select label" },
"runAtAuto": { "message": "auto", "description": "runAt auto" },
"runAtDocumentStart": { "message": "document_start", "description": "runAt document_start" },
"runAtDocumentEnd": { "message": "document_end", "description": "runAt document_end" },
"runAtDocumentIdle": { "message": "document_idle", "description": "runAt document_idle" },
"worldLabel": { "message": "World", "description": "world select label" },
"worldAuto": { "message": "auto", "description": "world auto" },
"worldIsolated": { "message": "ISOLATED", "description": "ISOLATED world" },
"worldMain": { "message": "MAIN", "description": "MAIN world" },
"modeLabel": { "message": "Mode", "description": "mode select label" },
"modeAuto": { "message": "auto", "description": "mode auto" },
"modePersistent": { "message": "persistent", "description": "mode persistent" },
"modeCss": { "message": "css", "description": "mode css" },
"modeOnce": { "message": "once", "description": "mode once" },
"allFramesLabel": { "message": "All Frames", "description": "allFrames checkbox" },
"persistLabel": { "message": "Persist", "description": "persist checkbox" },
"dnrFallbackLabel": { "message": "DNR Fallback", "description": "dnr fallback checkbox" },
"matchesInputLabel": { "message": "Matches (comma-separated)", "description": "matches input" },
"excludesInputLabel": {
"message": "Excludes (comma-separated)",
"description": "excludes input"
},
"tagsInputLabel": { "message": "Tags (comma-separated)", "description": "tags input" },
"scriptLabel": { "message": "Script", "description": "script textarea label" },
"applyButton": { "message": "Apply", "description": "apply button" },
"runOnceButton": { "message": "Run Once (CDP)", "description": "run once button" },
"listSectionTitle": { "message": "List", "description": "list section title" },
"queryLabel": { "message": "Query", "description": "query input label" },
"statusAll": { "message": "all", "description": "status all" },
"statusEnabled": { "message": "enabled", "description": "status enabled" },
"statusDisabled": { "message": "disabled", "description": "status disabled" },
"domainLabel": { "message": "Domain", "description": "domain filter label" },
"exportAllButton": { "message": "Export All", "description": "export button" },
"tableHeaderName": { "message": "Name", "description": "table header name" },
"tableHeaderWorld": { "message": "World", "description": "table header world" },
"tableHeaderRunAt": { "message": "Run At", "description": "table header runAt" },
"tableHeaderUpdated": { "message": "Updated", "description": "table header updated" },
"deleteButton": { "message": "Delete", "description": "delete button" }
}
@@ -442,5 +442,44 @@
"pagesUnit": {
"message": "页",
"description": "页面计数单位"
}
},
"userscriptsManagerTitle": { "message": "脚本管理器", "description": "Options 页标题" },
"emergencySwitchLabel": { "message": "紧急开关", "description": "紧急关闭开关" },
"createRunSectionTitle": { "message": "创建 / 运行", "description": "创建与运行分区标题" },
"nameLabel": { "message": "名称", "description": "名称输入标签" },
"runAtLabel": { "message": "运行时机", "description": "runAt 选择标签" },
"runAtAuto": { "message": "自动", "description": "runAt auto" },
"runAtDocumentStart": { "message": "document_start", "description": "runAt document_start" },
"runAtDocumentEnd": { "message": "document_end", "description": "runAt document_end" },
"runAtDocumentIdle": { "message": "document_idle", "description": "runAt document_idle" },
"worldLabel": { "message": "执行上下文", "description": "world 选择标签" },
"worldAuto": { "message": "自动", "description": "world auto" },
"worldIsolated": { "message": "隔离 (ISOLATED)", "description": "ISOLATED world" },
"worldMain": { "message": "页面 (MAIN)", "description": "MAIN world" },
"modeLabel": { "message": "模式", "description": "模式选择标签" },
"modeAuto": { "message": "自动", "description": "mode auto" },
"modePersistent": { "message": "持久", "description": "mode persistent" },
"modeCss": { "message": "仅样式 (CSS)", "description": "mode css" },
"modeOnce": { "message": "一次运行 (CDP)", "description": "mode once" },
"allFramesLabel": { "message": "全部 frame", "description": "allFrames 复选框" },
"persistLabel": { "message": "持久化", "description": "persist 复选框" },
"dnrFallbackLabel": { "message": "DNR 回退", "description": "DNR fallback 复选框" },
"matchesInputLabel": { "message": "匹配(逗号分隔)", "description": "matches 输入" },
"excludesInputLabel": { "message": "排除(逗号分隔)", "description": "excludes 输入" },
"tagsInputLabel": { "message": "标签(逗号分隔)", "description": "tags 输入" },
"scriptLabel": { "message": "脚本", "description": "脚本文本标签" },
"applyButton": { "message": "应用", "description": "应用按钮" },
"runOnceButton": { "message": "一次运行(CDP)", "description": "一次运行按钮" },
"listSectionTitle": { "message": "脚本列表", "description": "列表分区标题" },
"queryLabel": { "message": "搜索", "description": "查询输入标签" },
"statusAll": { "message": "全部", "description": "状态-全部" },
"statusEnabled": { "message": "启用", "description": "状态-启用" },
"statusDisabled": { "message": "禁用", "description": "状态-禁用" },
"domainLabel": { "message": "域名", "description": "域名过滤标签" },
"exportAllButton": { "message": "导出全部", "description": "导出按钮" },
"tableHeaderName": { "message": "名称", "description": "表头-名称" },
"tableHeaderWorld": { "message": "执行上下文", "description": "表头-World" },
"tableHeaderRunAt": { "message": "运行时机", "description": "表头-RunAt" },
"tableHeaderUpdated": { "message": "更新时间", "description": "表头-更新时间" },
"deleteButton": { "message": "删除", "description": "删除按钮" }
}
@@ -1,115 +1,115 @@
<template>
<div class="page">
<header class="topbar">
<h1>Userscripts Manager</h1>
<h1>{{ m('userscriptsManagerTitle') }}</h1>
<div class="switch">
<label>
<input type="checkbox" v-model="emergencyDisabled" @change="saveEmergency" />
<span>Emergency OFF/ON</span>
<span>{{ m('emergencySwitchLabel') }}</span>
</label>
</div>
</header>
<section class="create">
<h2>Create / Run</h2>
<h2>{{ m('createRunSectionTitle') }}</h2>
<div class="grid">
<label>
Name
{{ m('nameLabel') }}
<input v-model="form.name" placeholder="optional" />
</label>
<label>
RunAt
{{ m('runAtLabel') }}
<select v-model="form.runAt">
<option value="auto">auto</option>
<option value="document_start">document_start</option>
<option value="document_end">document_end</option>
<option value="document_idle">document_idle</option>
<option value="auto">{{ m('runAtAuto') }}</option>
<option value="document_start">{{ m('runAtDocumentStart') }}</option>
<option value="document_end">{{ m('runAtDocumentEnd') }}</option>
<option value="document_idle">{{ m('runAtDocumentIdle') }}</option>
</select>
</label>
<label>
World
{{ m('worldLabel') }}
<select v-model="form.world">
<option value="auto">auto</option>
<option value="ISOLATED">ISOLATED</option>
<option value="MAIN">MAIN</option>
<option value="auto">{{ m('worldAuto') }}</option>
<option value="ISOLATED">{{ m('worldIsolated') }}</option>
<option value="MAIN">{{ m('worldMain') }}</option>
</select>
</label>
<label>
Mode
{{ m('modeLabel') }}
<select v-model="form.mode">
<option value="auto">auto</option>
<option value="persistent">persistent</option>
<option value="css">css</option>
<option value="once">once</option>
<option value="auto">{{ m('modeAuto') }}</option>
<option value="persistent">{{ m('modePersistent') }}</option>
<option value="css">{{ m('modeCss') }}</option>
<option value="once">{{ m('modeOnce') }}</option>
</select>
</label>
<label>
All Frames
{{ m('allFramesLabel') }}
<input type="checkbox" v-model="form.allFrames" />
</label>
<label>
Persist
{{ m('persistLabel') }}
<input type="checkbox" v-model="form.persist" />
</label>
<label>
DNR Fallback
{{ m('dnrFallbackLabel') }}
<input type="checkbox" v-model="form.dnrFallback" />
</label>
</div>
<label>
Matches (comma-separated)
{{ m('matchesInputLabel') }}
<input v-model="form.matches" placeholder="e.g. https://*.example.com/*" />
</label>
<label>
Excludes (comma-separated)
{{ m('excludesInputLabel') }}
<input v-model="form.excludes" placeholder="optional" />
</label>
<label>
Tags (comma-separated)
{{ m('tagsInputLabel') }}
<input v-model="form.tags" placeholder="optional" />
</label>
<label>
Script
{{ m('scriptLabel') }}
<textarea v-model="form.script" placeholder="Paste JS/CSS/TM here" rows="8" />
</label>
<div class="row">
<button :disabled="submitting" @click="apply('auto')">Apply</button>
<button :disabled="submitting" @click="apply('once')">Run Once (CDP)</button>
<button :disabled="submitting" @click="apply('auto')">{{ m('applyButton') }}</button>
<button :disabled="submitting" @click="apply('once')">{{ m('runOnceButton') }}</button>
<span class="hint" v-if="lastResult">{{ lastResult }}</span>
</div>
</section>
<section class="filters">
<h2>List</h2>
<h2>{{ m('listSectionTitle') }}</h2>
<div class="grid">
<label>
Query
{{ m('queryLabel') }}
<input v-model="filters.query" @input="reload()" />
</label>
<label>
Status
{{ m('statusLabel') }}
<select v-model="filters.status" @change="reload()">
<option value="">all</option>
<option value="enabled">enabled</option>
<option value="disabled">disabled</option>
<option value="">{{ m('statusAll') }}</option>
<option value="enabled">{{ m('statusEnabled') }}</option>
<option value="disabled">{{ m('statusDisabled') }}</option>
</select>
</label>
<label>
Domain
{{ m('domainLabel') }}
<input v-model="filters.domain" @input="reload()" placeholder="example.com" />
</label>
</div>
<div class="row">
<button @click="exportAll">Export All</button>
<button @click="exportAll">{{ m('exportAllButton') }}</button>
</div>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>World</th>
<th>RunAt</th>
<th>Updated</th>
<th>{{ m('tableHeaderName') }}</th>
<th>{{ m('statusLabel') }}</th>
<th>{{ m('tableHeaderWorld') }}</th>
<th>{{ m('tableHeaderRunAt') }}</th>
<th>{{ m('tableHeaderUpdated') }}</th>
<th></th>
</tr>
</thead>
@@ -126,7 +126,7 @@
<td>{{ it.runAt }}</td>
<td>{{ formatTime(it.updatedAt) }}</td>
<td class="actions">
<button @click="remove(it)">Delete</button>
<button @click="remove(it)">{{ m('deleteButton') }}</button>
</td>
</tr>
</tbody>
@@ -287,6 +287,11 @@ onMounted(async () => {
await loadEmergency();
await reload();
});
function m(key: string, substitutions?: string | string[]) {
const msg = (globalThis.chrome?.i18n?.getMessage(key, substitutions as any) || '').trim();
return msg || key;
}
</script>
<style scoped>