From 32d7ccc8a11206a42a4642673c53e7ac0bd21a74 Mon Sep 17 00:00:00 2001 From: Bob Date: Thu, 2 Jul 2026 14:34:00 +0000 Subject: [PATCH 1/4] feat(i18n): add vue-i18n infrastructure with Simplified Chinese (zh-CN) locale - Install vue-i18n@8 (Vue 2 compatible) - Add src/i18n.js: VueI18n instance, setLocale() helper, SUPPORTED_LOCALES list - Add src/locales/en.json and src/locales/zh-CN.json with translations for nav, timeline, buckets, settings, home and language switcher - Wire i18n into the Vue root in src/main.js - Migrate nav strings in Header.vue to $t() calls (Activity, Timeline, Stopwatch, Tools, Search, Work Report, Raw Data, Settings, and more) - Migrate Timeline.vue: page title + filter labels (Host, Client, Duration, AFK, All) - Migrate Buckets.vue: page title, "last updated", "no events", "this device" - Migrate Home.vue: page title - Migrate Settings.vue: page title + all group labels/help text via $t() - Add LanguageSettings.vue: a dropdown in Settings > Language to switch locales with localStorage persistence (key: aw-locale) Locale falls back to English for untranslated keys. Language preference persists across page reloads via localStorage. --- package-lock.json | 8 +++ package.json | 1 + src/components/Header.vue | 36 +++++++------- src/i18n.js | 29 +++++++++++ src/locales/en.json | 66 +++++++++++++++++++++++++ src/locales/zh-CN.json | 66 +++++++++++++++++++++++++ src/main.js | 4 ++ src/views/Buckets.vue | 8 +-- src/views/Home.vue | 2 +- src/views/Timeline.vue | 18 +++---- src/views/settings/LanguageSettings.vue | 28 +++++++++++ src/views/settings/Settings.vue | 30 ++++++----- 12 files changed, 253 insertions(+), 43 deletions(-) create mode 100644 src/i18n.js create mode 100644 src/locales/en.json create mode 100644 src/locales/zh-CN.json create mode 100644 src/views/settings/LanguageSettings.vue diff --git a/package-lock.json b/package-lock.json index 13d1cd31..cf8a78db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "vue-color": "^2.8.1", "vue-d3-sunburst": "git+https://github.com/ErikBjare/Vue.D3.sunburst.git#patch-1", "vue-datetime": "^1.0.0-beta.13", + "vue-i18n": "^8.28.2", "vuedraggable": "^2.24.3", "weekstart": "^1.0.1", "xss": "^1.0.14" @@ -23563,6 +23564,13 @@ "dev": true, "license": "MIT" }, + "node_modules/vue-i18n": { + "version": "8.28.2", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.28.2.tgz", + "integrity": "sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA==", + "deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html", + "license": "MIT" + }, "node_modules/vue-loader": { "version": "17.4.2", "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.4.2.tgz", diff --git a/package.json b/package.json index 6ac792ee..a45d8c8f 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "vue-color": "^2.8.1", "vue-d3-sunburst": "git+https://github.com/ErikBjare/Vue.D3.sunburst.git#patch-1", "vue-datetime": "^1.0.0-beta.13", + "vue-i18n": "^8.28.2", "vuedraggable": "^2.24.3", "weekstart": "^1.0.1", "xss": "^1.0.14" diff --git a/src/components/Header.vue b/src/components/Header.vue index c7f37db3..1407a8d2 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -15,21 +15,21 @@ div(:class="{'fixed-top-padding': fixedTopMenu}") b-nav-item(v-if="activityViews && activityViews.length === 1", v-for="view in activityViews", :key="view.name", :to="view.pathUrl") div.px-2.px-lg-1 icon(name="calendar-day") - | Activity + | {{ $t('nav.activity') }} // If multiple (or no) activity views are available b-nav-item-dropdown(v-if="!activityViews || activityViews.length !== 1") template(slot="button-content") div.d-inline.px-2.px-lg-1 icon(name="calendar-day") - | Activity + | {{ $t('nav.activity') }} b-dropdown-item(v-if="activityViews === null", disabled) - span.text-muted Loading... + span.text-muted {{ $t('nav.loading') }} br b-dropdown-item(v-else-if="activityViews && activityViews.length <= 0", disabled) - | No activity reports available + | {{ $t('nav.noActivityReports') }} br - small Make sure you have both an AFK and window watcher running + small {{ $t('nav.noActivityReportsHint') }} b-dropdown-item(v-for="view in activityViews", :key="view.name", :to="view.pathUrl") icon(:name="view.icon") | {{ view.name }} @@ -37,12 +37,12 @@ div(:class="{'fixed-top-padding': fixedTopMenu}") b-nav-item(to="/timeline" style="font-color: #000;") div.px-2.px-lg-1 icon(name="stream") - | Timeline + | {{ $t('nav.timeline') }} b-nav-item(to="/stopwatch") div.px-2.px-lg-1 icon(name="stopwatch") - | Stopwatch + | {{ $t('nav.stopwatch') }} // Brand on large screens (centered) b-navbar-nav.abs-center.d-none.d-lg-block @@ -55,41 +55,41 @@ div(:class="{'fixed-top-padding': fixedTopMenu}") template(slot="button-content") div.d-inline.px-2.px-lg-1 icon(name="tools") - | Tools + | {{ $t('nav.tools') }} b-dropdown-item(to="/search") icon(name="search") - | Search + | {{ $t('nav.search') }} b-dropdown-item(to="/work-report") icon(name="briefcase") - | Work Report + | {{ $t('nav.workReport') }} b-dropdown-item(to="/trends" v-if="devmode") icon(name="chart-line") - | Trends + | {{ $t('nav.trends') }} b-dropdown-item(to="/report" v-if="devmode") icon(name="chart-pie") - | Report + | {{ $t('nav.report') }} b-dropdown-item(to="/alerts" v-if="devmode") icon(name="flag-checkered") - | Alerts + | {{ $t('nav.alerts') }} b-dropdown-item(to="/timespiral" v-if="devmode") icon(name="history") - | Timespiral + | {{ $t('nav.timespiral') }} b-dropdown-item(to="/query") icon(name="code") - | Query + | {{ $t('nav.query') }} b-dropdown-item(to="/graph" v-if="devmode") // TODO: use circle-nodes instead in the future icon(name="project-diagram") - | Graph + | {{ $t('nav.graph') }} b-nav-item(to="/buckets") div.px-2.px-lg-1 icon(name="database") - | Raw Data + | {{ $t('nav.rawData') }} b-nav-item(to="/settings") div.px-2.px-lg-1 icon(name="cog") - | Settings + | {{ $t('nav.settings') }}