@koishi-ce/client 1.0.4 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,7 +25,7 @@ Koishi 控制台的浏览器端运行时与组件基座,移植自上游 [koish
25
25
 
26
26
  - CLI:`koishi-console build [root]`(`src/bin.ts`)——带 root 时构建单个插件的前端,否则执行宿主控制台总装(产物硬编码到 `plugins/webui/console/dist`,含共享 chunk 与样式);
27
27
  - 编程式:`build(root, config)` 构建单个插件前端,`createServer(baseDir)` 创建 Vite 开发中间件;
28
- - `collectWorkspaceAliases()`:读取根 package.json 的 workspaces,为每个工作区包名建立指向其 `client/index.ts` 的别名——未被依赖的 workspace 包不会出现在 node_modules 链接里,必须显式映射才能被 bundler 解析。
28
+ - `collectWorkspaceAliases()`(内部机制,未从包导出):读取根 package.json 的 workspaces,为每个工作区包名建立指向其 `client/index.ts` 的别名——未被依赖的 workspace 包不会出现在 node_modules 链接里,必须显式映射才能被 bundler 解析。
29
29
 
30
30
  ## 许可证
31
31
 
@@ -49,7 +49,7 @@ The package is consumed as source code rather than shipped as a standalone runti
49
49
 
50
50
  - CLI: `koishi-console build [root]` — builds a single plugin frontend when a root is given, otherwise performs the host console assembly (output hardcoded to `plugins/webui/console/dist`).
51
51
  - Programmatic: `build(root, config)` and `createServer(baseDir)` (a Vite dev middleware).
52
- - `collectWorkspaceAliases()` — maps every workspace package to its `client/index.ts`; workspace packages absent from node_modules links can only be resolved through this explicit alias map.
52
+ - `collectWorkspaceAliases()` (an internal mechanism, not exported from the package) — maps every workspace package to its `client/index.ts`; workspace packages absent from node_modules links can only be resolved through this explicit alias map.
53
53
 
54
54
  ## License
55
55
 
package/app/home/home.vue CHANGED
@@ -16,14 +16,17 @@
16
16
  </k-slot>
17
17
  </el-scrollbar>
18
18
  <div v-else>
19
- <k-card class="connect">正在连接到 Koishi 服务器……</k-card>
19
+ <k-card class="connect">{{ t('home.connecting') }}</k-card>
20
20
  </div>
21
21
  </k-layout>
22
22
  </template>
23
23
 
24
24
  <script lang="ts" setup>
25
25
  import { global, socket } from "@koishi-ce/client";
26
+ import { useI18n } from "vue-i18n";
26
27
  import Welcome from "./welcome.vue";
28
+
29
+ const { t } = useI18n();
27
30
  </script>
28
31
 
29
32
  <style lang="scss">
package/app/home/index.ts CHANGED
@@ -14,7 +14,8 @@ export default function (ctx: Context) {
14
14
  ctx.page({
15
15
  id: "home",
16
16
  path: "/",
17
- name: "欢迎",
17
+ // name 传 getter:渲染层解析(MaybeRefOrGetter),随界面语言切换
18
+ name: () => ctx.$i18n.t("pages.home"),
18
19
  icon: "activity:home",
19
20
  order: 1000,
20
21
  component: Home,
@@ -4,25 +4,25 @@
4
4
 
5
5
  <!--
6
6
  欢迎卡片:文档 / 社区论坛等入口链接。
7
- 自带 zh-CN 与 en-US 两套文案(局部 vue-i18n 实例,独立于全局语言包),
7
+ 文案走宿主全局词典(client/locales/,7 语种),
8
8
  其余扩展可通过 "welcome-choice" 插槽追加自定义入口。
9
9
  -->
10
10
  <template>
11
11
  <div class="k-card welcome">
12
- <h1>{{ t('title') }}</h1>
13
- <p>{{ t('description') }}</p>
12
+ <h1>{{ t('welcome.title') }}</h1>
13
+ <p>{{ t('welcome.description') }}</p>
14
14
  <div class="choices">
15
15
  <k-slot name="welcome-choice">
16
16
  <k-slot-item :order="1000">
17
17
  <a class="choice" href="https://koishi.chat" rel="noopener noreferer" target="_blank">
18
- <h2>{{ t('action.docs.title') }}</h2>
19
- <p>{{ t('action.docs.description') }}</p>
18
+ <h2>{{ t('welcome.docs.title') }}</h2>
19
+ <p>{{ t('welcome.docs.description') }}</p>
20
20
  </a>
21
21
  </k-slot-item>
22
22
  <k-slot-item :order="500">
23
23
  <a class="choice" href="https://k.ilharp.cc" rel="noopener noreferer" target="_blank">
24
- <h2>{{ t('action.forum.title') }}</h2>
25
- <p>{{ t('action.forum.description') }}</p>
24
+ <h2>{{ t('welcome.forum.title') }}</h2>
25
+ <p>{{ t('welcome.forum.description') }}</p>
26
26
  </a>
27
27
  </k-slot-item>
28
28
  </k-slot>
@@ -32,32 +32,9 @@
32
32
 
33
33
  <script lang="ts" setup>
34
34
  import { useI18n } from "vue-i18n";
35
- import enUS from "./welcome.en-US.yml";
36
- import zhCN from "./welcome.zh-CN.yml";
37
35
 
38
- // 组件私有的双语文案:不依赖全局 i18n,确保欢迎页在任何语言环境下可用
39
- const { t, setLocaleMessage } = useI18n({
40
- messages: {
41
- "zh-CN": zhCN,
42
- "en-US": enUS,
43
- },
44
- });
45
-
46
- // 开发模式下 yml 文案改动即时热替换,无需整页刷新
47
- if (import.meta.hot) {
48
- import.meta.hot.accept(
49
- "./welcome.zh-CN.yml",
50
- (module) => {
51
- setLocaleMessage("zh-CN", module.default);
52
- },
53
- );
54
- import.meta.hot.accept(
55
- "./welcome.en-US.yml",
56
- (module) => {
57
- setLocaleMessage("en-US", module.default);
58
- },
59
- );
60
- }
36
+ // 全局 composer:词典由宿主 $i18n 服务在挂载前注入
37
+ const { t } = useI18n();
61
38
  </script>
62
39
 
63
40
  <style lang="scss">
package/app/index.html CHANGED
@@ -3,6 +3,10 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <!-- 外链头像/图片(平台 CDN、gravatar 等)对携带本站 Referer 的请求
7
+ 回 403,CSS 背景图无法逐元素设置 referrerpolicy,全局不发送
8
+ upstream: koishijs/koishi#1377 -->
9
+ <meta name="referrer" content="no-referrer" />
6
10
  <title>Koishi 控制台</title>
7
11
  <link rel="icon" href="/assets/logo.png" />
8
12
  </head>
@@ -16,7 +16,8 @@ import Theme from "./theme.vue";
16
16
  export default function (ctx: Context) {
17
17
  ctx.page({
18
18
  path: "/settings/:name*",
19
- name: "用户设置",
19
+ // name / title 传 getter:渲染层解析,随界面语言切换
20
+ name: () => ctx.$i18n.t("pages.settings"),
20
21
  icon: "activity:settings",
21
22
  position: "bottom",
22
23
  order: -100,
@@ -31,7 +32,7 @@ export default function (ctx: Context) {
31
32
 
32
33
  ctx.settings({
33
34
  id: "status",
34
- title: "状态栏设置",
35
+ title: () => ctx.$i18n.t("settings.status"),
35
36
  order: 800,
36
37
  });
37
38
 
@@ -5,12 +5,12 @@
5
5
  <!--
6
6
  用户设置页:左侧 el-tree 分组导航 + 右侧当前分组的内容。
7
7
  分组内的条目三种形态二选一渲染:组件型(component)、表单型(schema),
8
- disabled 的条目直接跳过。
8
+ disabled 的条目直接跳过。分区标题支持 getter(随界面语言实时切换)。
9
9
  -->
10
10
  <template>
11
11
  <k-layout main="page-settings">
12
12
  <template #header>
13
- {{ ctx.internal.settings[path][0]?.title }}
13
+ {{ toValue(ctx.internal.settings[path][0]?.title) }}
14
14
  </template>
15
15
 
16
16
  <template #left>
@@ -38,7 +38,7 @@
38
38
 
39
39
  <script lang="ts" setup>
40
40
  import { useConfig, useContext } from "@koishi-ce/client";
41
- import { computed } from "vue";
41
+ import { computed, toValue } from "vue";
42
42
  import { useRoute, useRouter } from "vue-router";
43
43
 
44
44
  const route = useRoute();
@@ -59,7 +59,7 @@ const data = computed(() =>
59
59
  Object.entries(ctx.internal.settings).map<Tree>(
60
60
  ([id, [{ title }]]) => ({
61
61
  id,
62
- label: title,
62
+ label: toValue(title),
63
63
  }),
64
64
  ),
65
65
  );
@@ -9,7 +9,7 @@
9
9
  <template>
10
10
  <k-status v-if="progress < 1">
11
11
  <el-progress :indeterminate="!store.entry" :percentage="progress * 100">
12
- 正在加载页面组件
12
+ {{ t('loading.page') }}
13
13
  </el-progress>
14
14
  </k-status>
15
15
  </template>
@@ -17,8 +17,10 @@
17
17
  <script lang="ts" setup>
18
18
  import { store, useContext } from "@koishi-ce/client";
19
19
  import { computed } from "vue";
20
+ import { useI18n } from "vue-i18n";
20
21
 
21
22
  const ctx = useContext();
23
+ const { t } = useI18n();
22
24
 
23
25
  const progress = computed(() => {
24
26
  const states = Object.values(ctx.$loader.extensions);
@@ -6,10 +6,16 @@
6
6
  <template>
7
7
  <div class="blank">
8
8
  <h1>404</h1>
9
- <p>如果你看到了这个页面,说明控制台插件已经加载成功,但你尚未安装任何控制台扩展。</p>
9
+ <p>{{ t('blank.hint') }}</p>
10
10
  </div>
11
11
  </template>
12
12
 
13
+ <script lang="ts" setup>
14
+ import { useI18n } from "vue-i18n";
15
+
16
+ const { t } = useI18n();
17
+ </script>
18
+
13
19
  <style lang="scss" scoped>
14
20
 
15
21
  .blank {
@@ -39,7 +39,8 @@ export default function (ctx: Context) {
39
39
  // label: '活动栏设置',
40
40
  // }, {
41
41
  id: ".reset",
42
- label: "重置活动栏",
42
+ // label 支持 getter(menu-item 渲染层 toValue 解析),随界面语言切换
43
+ label: () => ctx.$i18n.t("menu.resetActivity"),
43
44
  },
44
45
  ]);
45
46
  }
@@ -14,7 +14,7 @@
14
14
  <component :is="Component"></component>
15
15
  </keep-alive>
16
16
  </router-view>
17
- <div class="loading" v-else v-loading="true" element-loading-text="正在加载数据……"></div>
17
+ <div class="loading" v-else v-loading="true" :element-loading-text="t('loading.data')"></div>
18
18
  <status-bar></status-bar>
19
19
  <menu-list></menu-list>
20
20
  </template>
@@ -22,11 +22,13 @@
22
22
  <script lang="ts" setup>
23
23
  import { store } from "@koishi-ce/client";
24
24
  import { computed } from "vue";
25
+ import { useI18n } from "vue-i18n";
25
26
  import { useRoute } from "vue-router";
26
27
  import ActivityBar from "./activity/index.vue";
27
28
  import MenuList from "./menu/index.vue";
28
29
  import StatusBar from "./status.vue";
29
30
 
31
+ const { t } = useI18n();
30
32
  const route = useRoute();
31
33
 
32
34
  // 页面就绪判定:未声明所需字段的页面恒为已就绪;
@@ -18,19 +18,19 @@
18
18
  <k-icon name="chevron-right"/>
19
19
  </span>
20
20
  <span class="button bottom" @click.stop>
21
- <el-tooltip placement="top" content="缩小" :offset="20">
21
+ <el-tooltip placement="top" :content="t('overlay.zoomOut')" :offset="20">
22
22
  <k-icon name="search-minus" @click="scale -= 0.2"/>
23
23
  </el-tooltip>
24
- <el-tooltip placement="top" content="放大" :offset="20">
24
+ <el-tooltip placement="top" :content="t('overlay.zoomIn')" :offset="20">
25
25
  <k-icon name="search-plus" @click="scale += 0.2"/>
26
26
  </el-tooltip>
27
- <el-tooltip placement="top" content="复原" :offset="20">
27
+ <el-tooltip placement="top" :content="t('overlay.reset')" :offset="20">
28
28
  <k-icon name="expand" @click="scale = 1, rotate = 0"/>
29
29
  </el-tooltip>
30
- <el-tooltip placement="top" content="逆时针旋转" :offset="20">
30
+ <el-tooltip placement="top" :content="t('overlay.rotateLeft')" :offset="20">
31
31
  <k-icon name="undo" @click="rotate -= 90"/>
32
32
  </el-tooltip>
33
- <el-tooltip placement="top" content="逆时针旋转" :offset="20">
33
+ <el-tooltip placement="top" :content="t('overlay.rotateRight')" :offset="20">
34
34
  <k-icon name="redo" @click="rotate += 90"/>
35
35
  </el-tooltip>
36
36
  </span>
@@ -49,8 +49,11 @@ import {
49
49
  ref,
50
50
  watch,
51
51
  } from "vue";
52
+ import { useI18n } from "vue-i18n";
52
53
  import { shared } from "./utils";
53
54
 
55
+ const { t } = useI18n();
56
+
54
57
  // 用户手动调整的缩放与旋转量(复原即回到 1 / 0)
55
58
  const scale = ref(1);
56
59
  const rotate = ref(0);
@@ -0,0 +1,47 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ pages:
6
+ home: Willkommen
7
+ settings: Benutzereinstellungen
8
+ home:
9
+ connecting: Verbindung zum Koishi-Server wird hergestellt...
10
+ welcome:
11
+ title: Willkommen bei Koishi!
12
+ description: Ihr Chatbot ist einsatzbereit. Wählen Sie eine der Optionen unten, um Ihre Koishi-Reise zu beginnen.
13
+ docs:
14
+ title: Dokumentation
15
+ description: Lesen Sie die offizielle Dokumentation. Sie enthält alles, was Sie wissen möchten.
16
+ forum:
17
+ title: Forum
18
+ description: Tauschen Sie sich im Forum mit anderen Nutzern aus. Wenn Probleme auftreten, finden Sie dort Hilfe.
19
+ settings:
20
+ general: Allgemeine Einstellungen
21
+ appearance: Erscheinungsbild
22
+ status: Statusleisten-Einstellungen
23
+ loading:
24
+ page: Seitenkomponenten werden geladen
25
+ data: Daten werden geladen...
26
+ blank:
27
+ hint: Wenn Sie diese Seite sehen, wurde das Konsolen-Plugin erfolgreich geladen, aber es sind noch keine Konsolen-Erweiterungen installiert.
28
+ menu:
29
+ resetActivity: Aktivitätsleiste zurücksetzen
30
+ overlay:
31
+ zoomOut: Verkleinern
32
+ zoomIn: Vergrößern
33
+ reset: Zurücksetzen
34
+ rotateLeft: Gegen den Uhrzeigersinn drehen
35
+ rotateRight: Im Uhrzeigersinn drehen
36
+ schema:
37
+ settings:
38
+ $description: Allgemeine Einstellungen
39
+ locale: Spracheinstellungen.
40
+ theme:
41
+ $description: Design-Einstellungen
42
+ mode: Design-Präferenz.
43
+ mode-auto: System folgen
44
+ mode-dark: Dunkel
45
+ mode-light: Hell
46
+ dark: Dunkles Design.
47
+ light: Helles Design.
@@ -0,0 +1,47 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ pages:
6
+ home: Welcome
7
+ settings: User Settings
8
+ home:
9
+ connecting: Connecting to the Koishi server...
10
+ welcome:
11
+ title: Welcome to Koishi!
12
+ description: Your chatbot is ready to go. Pick any option below to start your Koishi journey.
13
+ docs:
14
+ title: Documentation
15
+ description: Read the official documentation, which contains everything you want to know.
16
+ forum:
17
+ title: Forum
18
+ description: Talk with other users on the forum. If you run into problems, you can always ask for help there.
19
+ settings:
20
+ general: General Settings
21
+ appearance: Appearance
22
+ status: Status Bar Settings
23
+ loading:
24
+ page: Loading page components
25
+ data: Loading data...
26
+ blank:
27
+ hint: If you are seeing this page, the console plugin has loaded successfully, but no console extensions are installed yet.
28
+ menu:
29
+ resetActivity: Reset Activity Bar
30
+ overlay:
31
+ zoomOut: Zoom out
32
+ zoomIn: Zoom in
33
+ reset: Reset
34
+ rotateLeft: Rotate counterclockwise
35
+ rotateRight: Rotate clockwise
36
+ schema:
37
+ settings:
38
+ $description: General Settings
39
+ locale: Language settings.
40
+ theme:
41
+ $description: Theme Settings
42
+ mode: Theme preference.
43
+ mode-auto: Follow system
44
+ mode-dark: Dark
45
+ mode-light: Light
46
+ dark: Dark theme.
47
+ light: Light theme.
@@ -0,0 +1,47 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ pages:
6
+ home: Accueil
7
+ settings: Paramètres utilisateur
8
+ home:
9
+ connecting: Connexion au serveur Koishi...
10
+ welcome:
11
+ title: Bienvenue sur Koishi !
12
+ description: Votre chatbot est prêt. Choisissez une option ci-dessous pour commencer votre aventure Koishi.
13
+ docs:
14
+ title: Documentation
15
+ description: "Lisez la documentation officielle : elle contient tout ce que vous voulez savoir."
16
+ forum:
17
+ title: Forum
18
+ description: Échangez avec d'autres utilisateurs sur le forum. Si vous rencontrez des problèmes, vous pouvez y demander de l'aide.
19
+ settings:
20
+ general: Paramètres généraux
21
+ appearance: Apparence
22
+ status: Paramètres de la barre d'état
23
+ loading:
24
+ page: Chargement des composants de la page
25
+ data: Chargement des données...
26
+ blank:
27
+ hint: Si vous voyez cette page, le plugin console s'est chargé avec succès, mais aucune extension de console n'est installée.
28
+ menu:
29
+ resetActivity: Réinitialiser la barre d'activités
30
+ overlay:
31
+ zoomOut: Réduire
32
+ zoomIn: Agrandir
33
+ reset: Réinitialiser
34
+ rotateLeft: Pivoter dans le sens antihoraire
35
+ rotateRight: Pivoter dans le sens horaire
36
+ schema:
37
+ settings:
38
+ $description: Paramètres généraux
39
+ locale: Paramètres de langue.
40
+ theme:
41
+ $description: Paramètres du thème
42
+ mode: Préférence de thème.
43
+ mode-auto: Suivre le système
44
+ mode-dark: Sombre
45
+ mode-light: Clair
46
+ dark: Thème sombre.
47
+ light: Thème clair.
@@ -0,0 +1,47 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ pages:
6
+ home: ホーム
7
+ settings: ユーザー設定
8
+ home:
9
+ connecting: Koishi サーバーに接続しています……
10
+ welcome:
11
+ title: Koishi へようこそ!
12
+ description: ボットの準備が整いました。以下の選択肢からどれかを選んで、Koishi の旅を始めましょう。
13
+ docs:
14
+ title: ドキュメントを読む
15
+ description: 公式ドキュメントを読み始めましょう。知りたいことがすべて書かれています。
16
+ forum:
17
+ title: フォーラムへ
18
+ description: フォーラムで他のユーザーと交流しましょう。問題が起きたときは、ここで助けを求めることができます。
19
+ settings:
20
+ general: 一般設定
21
+ appearance: 外観設定
22
+ status: ステータスバー設定
23
+ loading:
24
+ page: ページコンポーネントを読み込み中
25
+ data: データを読み込み中……
26
+ blank:
27
+ hint: このページが表示されている場合、コンソールプラグインの読み込みには成功していますが、コンソール拡張がまだインストールされていません。
28
+ menu:
29
+ resetActivity: アクティビティバーをリセット
30
+ overlay:
31
+ zoomOut: 縮小
32
+ zoomIn: 拡大
33
+ reset: リセット
34
+ rotateLeft: 反時計回りに回転
35
+ rotateRight: 時計回りに回転
36
+ schema:
37
+ settings:
38
+ $description: 一般設定
39
+ locale: 言語設定。
40
+ theme:
41
+ $description: テーマ設定
42
+ mode: テーマの設定。
43
+ mode-auto: システムに従う
44
+ mode-dark: ダーク
45
+ mode-light: ライト
46
+ dark: ダークテーマ。
47
+ light: ライトテーマ。
@@ -0,0 +1,47 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ pages:
6
+ home: Главная
7
+ settings: Настройки пользователя
8
+ home:
9
+ connecting: Подключение к серверу Koishi...
10
+ welcome:
11
+ title: Добро пожаловать в Koishi!
12
+ description: Ваш бот готов к работе. Выберите любой пункт ниже, чтобы начать путешествие по Koishi.
13
+ docs:
14
+ title: Документация
15
+ description: Начните читать официальную документацию — в ней есть всё, что вы хотите узнать.
16
+ forum:
17
+ title: Форум
18
+ description: Общайтесь с другими пользователями на форуме. Если возникнут проблемы, здесь можно обратиться за помощью.
19
+ settings:
20
+ general: Общие настройки
21
+ appearance: Оформление
22
+ status: Настройки строки состояния
23
+ loading:
24
+ page: Загрузка компонентов страницы
25
+ data: Загрузка данных...
26
+ blank:
27
+ hint: Если вы видите эту страницу, плагин консоли загрузился успешно, но расширения консоли ещё не установлены.
28
+ menu:
29
+ resetActivity: Сбросить панель активности
30
+ overlay:
31
+ zoomOut: Уменьшить
32
+ zoomIn: Увеличить
33
+ reset: Сбросить
34
+ rotateLeft: Повернуть против часовой стрелки
35
+ rotateRight: Повернуть по часовой стрелке
36
+ schema:
37
+ settings:
38
+ $description: Общие настройки
39
+ locale: Настройки языка.
40
+ theme:
41
+ $description: Настройки темы
42
+ mode: Предпочтительная тема.
43
+ mode-auto: Как в системе
44
+ mode-dark: Тёмная
45
+ mode-light: Светлая
46
+ dark: Тёмная тема.
47
+ light: Светлая тема.
@@ -0,0 +1,47 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ pages:
6
+ home: 欢迎
7
+ settings: 用户设置
8
+ home:
9
+ connecting: 正在连接到 Koishi 服务器……
10
+ welcome:
11
+ title: 欢迎使用 Koishi!
12
+ description: 你的机器人已经准备就绪。点击下面的任一选项,开启你的 Koishi 之旅吧。
13
+ docs:
14
+ title: 阅读文档
15
+ description: 开始阅读官方文档。官方文档里包含了你想要了解的全部内容。
16
+ forum:
17
+ title: 前往论坛
18
+ description: 前往论坛与其他用户交流。如果你遇到了问题,可以在这里寻求帮助。
19
+ settings:
20
+ general: 通用设置
21
+ appearance: 外观设置
22
+ status: 状态栏设置
23
+ loading:
24
+ page: 正在加载页面组件
25
+ data: 正在加载数据……
26
+ blank:
27
+ hint: 如果你看到了这个页面,说明控制台插件已经加载成功,但你尚未安装任何控制台扩展。
28
+ menu:
29
+ resetActivity: 重置活动栏
30
+ overlay:
31
+ zoomOut: 缩小
32
+ zoomIn: 放大
33
+ reset: 复原
34
+ rotateLeft: 逆时针旋转
35
+ rotateRight: 顺时针旋转
36
+ schema:
37
+ settings:
38
+ $description: 通用设置
39
+ locale: 语言设置。
40
+ theme:
41
+ $description: 主题设置
42
+ mode: 主题偏好。
43
+ mode-auto: 跟随系统
44
+ mode-dark: 深色
45
+ mode-light: 浅色
46
+ dark: 深色主题。
47
+ light: 浅色主题。
@@ -0,0 +1,47 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ # Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ pages:
6
+ home: 歡迎
7
+ settings: 使用者設定
8
+ home:
9
+ connecting: 正在連線到 Koishi 伺服器……
10
+ welcome:
11
+ title: 歡迎使用 Koishi!
12
+ description: 你的機器人已經準備就緒。點擊下面的任一選項,開啟你的 Koishi 之旅吧。
13
+ docs:
14
+ title: 閱讀文件
15
+ description: 開始閱讀官方文件。官方文件裡包含了你想要了解的全部內容。
16
+ forum:
17
+ title: 前往論壇
18
+ description: 前往論壇與其他使用者交流。如果你遇到了問題,可以在這裡尋求幫助。
19
+ settings:
20
+ general: 一般設定
21
+ appearance: 外觀設定
22
+ status: 狀態列設定
23
+ loading:
24
+ page: 正在載入頁面元件
25
+ data: 正在載入資料……
26
+ blank:
27
+ hint: 如果你看到了這個頁面,說明控制檯插件已經載入成功,但你尚未安裝任何控制檯擴充功能。
28
+ menu:
29
+ resetActivity: 重置活動欄
30
+ overlay:
31
+ zoomOut: 縮小
32
+ zoomIn: 放大
33
+ reset: 復原
34
+ rotateLeft: 逆時針旋轉
35
+ rotateRight: 順時針旋轉
36
+ schema:
37
+ settings:
38
+ $description: 一般設定
39
+ locale: 語言設定。
40
+ theme:
41
+ $description: 主題設定
42
+ mode: 主題偏好。
43
+ mode-auto: 跟隨系統
44
+ mode-dark: 深色
45
+ mode-light: 淺色
46
+ dark: 深色主題。
47
+ light: 淺色主題。
@@ -2,9 +2,17 @@
2
2
  // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
3
  // Copyright (c) 2026-present Koishi-CE contributors.
4
4
 
5
+ import { type Dict, valueMap } from "cosmokit";
5
6
  import { watchEffect } from "vue";
6
7
  import { createI18n } from "vue-i18n";
7
8
  import type { Context } from "../context";
9
+ import deDE from "../locales/de-DE.yml";
10
+ import enUS from "../locales/en-US.yml";
11
+ import frFR from "../locales/fr-FR.yml";
12
+ import jaJP from "../locales/ja-JP.yml";
13
+ import ruRU from "../locales/ru-RU.yml";
14
+ import zhCN from "../locales/zh-CN.yml";
15
+ import zhTW from "../locales/zh-TW.yml";
8
16
  import { Service } from "../utils";
9
17
  import { useConfig } from "./setting";
10
18
 
@@ -14,22 +22,69 @@ declare module "../context" {
14
22
  }
15
23
  }
16
24
 
17
- // 客户端本地配置(未与服务端同步前的原始值)
18
- const config = useConfig();
25
+ /** 宿主自身的全局词典:随服务启动注入,键结构见 client/locales/zh-CN.yml */
26
+ export const localeMessages: Dict<Dict> = {
27
+ "de-DE": deDE,
28
+ "en-US": enUS,
29
+ "fr-FR": frFR,
30
+ "ja-JP": jaJP,
31
+ "ru-RU": ruRU,
32
+ "zh-CN": zhCN,
33
+ "zh-TW": zhTW,
34
+ };
35
+
36
+ /**
37
+ * 从一组 `{ locale: 词典 }` 映射中按路径摘取各语种的同一子树,
38
+ * 构造 Schemastery `.i18n()` 的入参形态(`{ locale: 子树 }`)。
39
+ * 宿主与扩展的 schema 词典均可经此摘取;yaml 为动态数据,
40
+ * 收窄集中在这一处。
41
+ */
42
+ export function pickFrom(
43
+ messages: Dict<unknown>,
44
+ ...path: string[]
45
+ ): Dict {
46
+ return valueMap(messages, (data) => {
47
+ let node: unknown = data;
48
+ for (const key of path) {
49
+ node = (node as Dict<unknown> | undefined)?.[key];
50
+ }
51
+ return node;
52
+ });
53
+ }
54
+
55
+ /**
56
+ * 从宿主词典中按路径摘取各语种的同一子树(`pickFrom` 的宿主词典
57
+ * 专用便捷形式),叶节点上亦可取得 `Dict<string>`(const 选项的
58
+ * 显示名直接传 description)。
59
+ */
60
+ export function pickMessages<T = unknown>(
61
+ ...path: string[]
62
+ ): Dict<T> {
63
+ return pickFrom(localeMessages, ...path) as Dict<T>;
64
+ }
19
65
 
20
66
  /**
21
67
  * 国际化服务:持有 vue-i18n 实例(默认回退语言 zh-CN),
22
68
  * 并监听本地配置中的 locale 变化实时切换界面语言。
23
- * 各扩展自身的语言包由 loader 加载时注入该实例。
69
+ *
70
+ * 宿主词典在实例创建时整体传入;各扩展自身的语言包由其 client
71
+ * 入口调用 `ctx.$i18n.extend()` 深合并注入(vue-i18n 已被构建别名
72
+ * 钉到宿主单实例,扩展与宿主共享同一 composer)。
24
73
  */
25
74
  export default class I18nService extends Service {
26
75
  public i18n = createI18n({
27
76
  legacy: false,
28
77
  fallbackLocale: "zh-CN",
78
+ messages: localeMessages,
29
79
  });
30
80
 
31
81
  constructor(ctx: Context) {
32
82
  super(ctx, "$i18n", true);
83
+ instance = this;
84
+
85
+ // 客户端本地配置(在构造期获取而非模块顶层,避免与
86
+ // setting.ts 的循环依赖在模块求值顺序上踩 TDZ)
87
+ const config = useConfig();
33
88
 
34
89
  // flush: "post" 确保在语言切换引起重渲染前完成 locale 写入
35
90
  ctx.effect(() =>
@@ -44,4 +99,64 @@ export default class I18nService extends Service {
44
99
  ),
45
100
  );
46
101
  }
102
+
103
+ /**
104
+ * 注入扩展语言包:按 locale 深合并进全局词典。
105
+ * 扩展应把自身的键收纳在独立命名空间(如 `explorer.*`)下,
106
+ * 避免与其他扩展或宿主词典冲突。
107
+ */
108
+ extend(locale: string, messages: Dict) {
109
+ this.i18n.global.mergeLocaleMessage(locale, messages);
110
+ }
111
+
112
+ /**
113
+ * 全局翻译函数(非组件上下文使用,如 activity 名的 getter 与
114
+ * echarts 等纯 ts 模块)。可携带位置插值参数。
115
+ */
116
+ t(key: string, args?: unknown[]) {
117
+ return this.i18n.global.t(key, args ?? []);
118
+ }
119
+ }
120
+
121
+ // 服务实例的模块级引用:仅用于开发期 HMR 回调中定位热替换目标
122
+ let instance: I18nService | undefined;
123
+
124
+ // 开发模式下宿主词典 yml 改动即时热替换(accept 的路径须为静态字面量);
125
+ // 用 merge 而非 set,以免覆盖扩展此前经 extend() 注入的同语种词条
126
+ if (import.meta.hot) {
127
+ const replace = (locale: string) => (module: unknown) => {
128
+ // 热替换模块的 default 导出即该语种的完整词典
129
+ const messages = (
130
+ module as { default?: Dict } | undefined
131
+ )?.default;
132
+ instance?.extend(locale, messages ?? {});
133
+ };
134
+ import.meta.hot.accept(
135
+ "../locales/de-DE.yml",
136
+ replace("de-DE"),
137
+ );
138
+ import.meta.hot.accept(
139
+ "../locales/en-US.yml",
140
+ replace("en-US"),
141
+ );
142
+ import.meta.hot.accept(
143
+ "../locales/fr-FR.yml",
144
+ replace("fr-FR"),
145
+ );
146
+ import.meta.hot.accept(
147
+ "../locales/ja-JP.yml",
148
+ replace("ja-JP"),
149
+ );
150
+ import.meta.hot.accept(
151
+ "../locales/ru-RU.yml",
152
+ replace("ru-RU"),
153
+ );
154
+ import.meta.hot.accept(
155
+ "../locales/zh-CN.yml",
156
+ replace("zh-CN"),
157
+ );
158
+ import.meta.hot.accept(
159
+ "../locales/zh-TW.yml",
160
+ replace("zh-TW"),
161
+ );
47
162
  }
@@ -17,6 +17,7 @@ import { type Dict, remove } from "cosmokit";
17
17
  import {
18
18
  type Component,
19
19
  computed,
20
+ type MaybeRefOrGetter,
20
21
  markRaw,
21
22
  reactive,
22
23
  ref,
@@ -29,6 +30,7 @@ import {
29
30
  import type { Config } from "..";
30
31
  import type { Context } from "../context";
31
32
  import { insert, type Ordered, Service } from "../utils";
33
+ import { pickMessages } from "./i18n";
32
34
 
33
35
  declare module "../context" {
34
36
  interface Context {
@@ -46,8 +48,8 @@ declare module "../context" {
46
48
  interface SettingOptions extends Ordered {
47
49
  /** 所属设置分区 id(空串为通用设置) */
48
50
  id: string;
49
- /** 分区标题 */
50
- title?: string;
51
+ /** 分区标题(支持 getter,随界面语言实时切换) */
52
+ title?: MaybeRefOrGetter<string>;
51
53
  disabled?: () => boolean;
52
54
  /** 该分区的配置 schema(渲染为表单) */
53
55
  schema?: Schema;
@@ -157,14 +159,23 @@ export default class SettingService extends Service {
157
159
 
158
160
  this.settings({
159
161
  id: "",
160
- title: "通用设置",
162
+ title: () => this.ctx.$i18n.t("settings.general"),
161
163
  order: 1000,
162
164
  schema: Schema.object({
165
+ // 语言列表覆盖宿主词典支持的全部语种;
166
+ // 显示名采用各语言的本地名称(CLDR 惯例),无需翻译
163
167
  locale: Schema.union([
164
- "zh-CN",
165
- "en-US",
168
+ Schema.const("zh-CN").description("简体中文"),
169
+ Schema.const("zh-TW").description("繁體中文"),
170
+ Schema.const("en-US").description("English"),
171
+ Schema.const("ja-JP").description("日本語"),
172
+ Schema.const("fr-FR").description("Français"),
173
+ Schema.const("de-DE").description("Deutsch"),
174
+ Schema.const("ru-RU").description("Русский"),
166
175
  ]).description("语言设置。"),
167
- }).description("通用设置"),
176
+ })
177
+ .description("通用设置")
178
+ .i18n(pickMessages("schema", "settings")),
168
179
  });
169
180
 
170
181
  // 汇总所有分区 schema 为一个相交对象,作为 resolved 的解释器
@@ -21,6 +21,7 @@ import {
21
21
  import { Schema } from "../../../components/client/index.ts";
22
22
  import type { Context } from "../context";
23
23
  import { Service } from "../utils";
24
+ import { pickMessages } from "./i18n";
24
25
  import { useConfig } from "./setting";
25
26
 
26
27
  declare module "../context" {
@@ -79,14 +80,38 @@ export default class ThemeService extends Service {
79
80
 
80
81
  ctx.settings({
81
82
  id: "appearance",
82
- title: "外观设置",
83
+ title: () => this.ctx.$i18n.t("settings.appearance"),
83
84
  order: 900,
84
85
  schema: Schema.object({
85
86
  theme: Schema.object({
87
+ // const 选项的显示名不受 .i18n() 递归覆盖(其词典
88
+ // 递归会滤掉 $ 键),故以 Dict 形态经 extra 写入
89
+ // meta.description(description() 方法仅收 string)
86
90
  mode: Schema.union([
87
- Schema.const("auto").description("跟随系统"),
88
- Schema.const("dark").description("深色"),
89
- Schema.const("light").description("浅色"),
91
+ Schema.const("auto").extra(
92
+ "description",
93
+ pickMessages<string>(
94
+ "schema",
95
+ "theme",
96
+ "mode-auto",
97
+ ),
98
+ ),
99
+ Schema.const("dark").extra(
100
+ "description",
101
+ pickMessages<string>(
102
+ "schema",
103
+ "theme",
104
+ "mode-dark",
105
+ ),
106
+ ),
107
+ Schema.const("light").extra(
108
+ "description",
109
+ pickMessages<string>(
110
+ "schema",
111
+ "theme",
112
+ "mode-light",
113
+ ),
114
+ ),
90
115
  ])
91
116
  .default("auto")
92
117
  .description("主题偏好。"),
@@ -99,7 +124,7 @@ export default class ThemeService extends Service {
99
124
  .default("default-light")
100
125
  .description("浅色主题。"),
101
126
  }).description("主题设置"),
102
- }),
127
+ }).i18n(pickMessages("schema", "theme")),
103
128
  });
104
129
 
105
130
  ctx.effect(() =>
package/lib/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as build } from "./src-BOakssV3.mjs";
2
+ import { t as build } from "./src-HOy7ib_t.mjs";
3
3
  import { existsSync } from "node:fs";
4
4
  import { resolve } from "node:path";
5
5
  //#endregion
@@ -16,7 +16,7 @@ import { resolve } from "node:path";
16
16
  const { version } = {
17
17
  name: "@koishi-ce/client",
18
18
  description: "Koishi Console Client",
19
- version: "1.0.4",
19
+ version: "1.1.1",
20
20
  type: "module",
21
21
  main: "client/index.ts",
22
22
  exports: {
package/lib/index.d.ts CHANGED
@@ -6,7 +6,7 @@ import * as vite from "vite";
6
6
  * @param root 插件目录(无 `client/` 子目录时视为该插件没有前端,直接跳过)
7
7
  * @param config 额外的 vite 配置,逐层合并覆盖下方默认值
8
8
  */
9
- declare function build(root: string, config?: vite.UserConfig): Promise<void>;
9
+ export declare function build(root: string, config?: vite.UserConfig): Promise<void>;
10
10
  /**
11
11
  * 创建开发模式下的 vite dev server(middlewareMode,不监听独立端口,
12
12
  * 由宿主 HTTP 服务挂载到 `/vite/` 前缀下)。
@@ -15,6 +15,5 @@ declare function build(root: string, config?: vite.UserConfig): Promise<void>;
15
15
  * @param config 额外的 vite 配置
16
16
  * @returns vite 的 ViteDevServer 实例
17
17
  */
18
- declare function createServer(baseDir: string, config?: vite.InlineConfig): Promise<vite.ViteDevServer>;
19
- //#endregion
20
- export { build, createServer };
18
+ export declare function createServer(baseDir: string, config?: vite.InlineConfig): Promise<vite.ViteDevServer>;
19
+ //#endregion
package/lib/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as createServer, t as build } from "./src-BOakssV3.mjs";
1
+ import { n as createServer, t as build } from "./src-HOy7ib_t.mjs";
2
2
  export { build, createServer };
@@ -2,7 +2,6 @@ import { t as yaml } from "./yaml-UzQbvquH.mjs";
2
2
  import { existsSync } from "node:fs";
3
3
  import { mkdir, rm } from "node:fs/promises";
4
4
  import { resolve } from "node:path";
5
- import { pathToFileURL } from "node:url";
6
5
  import vue from "@vitejs/plugin-vue";
7
6
  import * as vite from "vite";
8
7
  //#region src/index.ts
@@ -46,7 +45,7 @@ async function build(root, config = {}) {
46
45
  if (!existsSync(`${root}/client`)) return;
47
46
  const overridePath = `${root}/build/client.ts`;
48
47
  if (existsSync(overridePath)) {
49
- const mod = await import(pathToFileURL(overridePath).href);
48
+ const mod = await import(Bun.pathToFileURL(overridePath).href);
50
49
  config = vite.mergeConfig(config, mod.default ?? mod);
51
50
  }
52
51
  const outDir = `${root}/dist`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@koishi-ce/client",
3
3
  "description": "Koishi Console Client",
4
- "version": "1.0.4",
4
+ "version": "1.1.1",
5
5
  "type": "module",
6
6
  "main": "client/index.ts",
7
7
  "exports": {
@@ -48,7 +48,7 @@
48
48
  "build"
49
49
  ],
50
50
  "dependencies": {
51
- "@koishi-ce/components": "^1.0.1",
51
+ "@koishi-ce/components": "^1.0.2",
52
52
  "@satorijs/protocol": "^1.7.0",
53
53
  "@vitejs/plugin-vue": "^6.0.8",
54
54
  "@vueuse/core": "^14.4.0",
@@ -64,7 +64,7 @@
64
64
  "vue-router": "^5.2.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@koishi-ce/koishi": "^1.0.6",
68
- "@koishi-ce/plugin-console": "^1.0.4"
67
+ "@koishi-ce/koishi": "^1.0.11",
68
+ "@koishi-ce/plugin-console": "^1.2.0"
69
69
  }
70
70
  }
package/src/index.ts CHANGED
@@ -14,7 +14,6 @@
14
14
  import { existsSync } from "node:fs";
15
15
  import { mkdir, rm } from "node:fs/promises";
16
16
  import { resolve } from "node:path";
17
- import { pathToFileURL } from "node:url";
18
17
  import vue from "@vitejs/plugin-vue";
19
18
  import * as vite from "vite";
20
19
  import { yaml } from "./yaml.ts";
@@ -110,7 +109,7 @@ export async function build(
110
109
  const overridePath = `${root}/build/client.ts`;
111
110
  if (existsSync(overridePath)) {
112
111
  const mod = await import(
113
- pathToFileURL(overridePath).href
112
+ Bun.pathToFileURL(overridePath).href
114
113
  );
115
114
  config = vite.mergeConfig(config, mod.default ?? mod);
116
115
  }
@@ -1,13 +0,0 @@
1
- # SPDX-License-Identifier: AGPL-3.0-only
2
- # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
- # Copyright (c) 2026-present Koishi-CE contributors.
4
-
5
- title: Welcome to Koishi!
6
- description: Your chatbot is ready to go. 点击下面的任一选项,开启你的 Koishi 之旅吧。
7
- action:
8
- docs:
9
- title: Documentation
10
- description: The official documentation contains everything you want to know.
11
- forum:
12
- title: Forum
13
- description: If you run into problems, you can always ask for help on the forum.
@@ -1,13 +0,0 @@
1
- # SPDX-License-Identifier: AGPL-3.0-only
2
- # Copyright (c) 2019-present Shigma and Koishijs contributors.
3
- # Copyright (c) 2026-present Koishi-CE contributors.
4
-
5
- title: 欢迎使用 Koishi!
6
- description: 你的机器人已经准备就绪。点击下面的任一选项,开启你的 Koishi 之旅吧。
7
- action:
8
- docs:
9
- title: 阅读文档
10
- description: 开始阅读官方文档。官方文档里包含了你想要了解的全部内容。
11
- forum:
12
- title: 前往论坛
13
- description: 前往论坛与其他用户交流。如果你遇到了问题,可以在这里寻求帮助。