diff --git a/package.json b/package.json index aab5da5..d13cc1f 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "vue": "^3.4.21", - "vue-router": "^4.3.0", - "vue-i18n": "^9.13.1" + "vue-i18n": "^9.13.1", + "vue-router": "^4.3.0" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.4", diff --git a/src/components/LanguageSwitcher.vue b/src/components/LanguageSwitcher.vue index 0eb83c2..5177c9d 100644 --- a/src/components/LanguageSwitcher.vue +++ b/src/components/LanguageSwitcher.vue @@ -2,6 +2,7 @@ import { useI18nLazy } from '../composables/useI18nLazy.js' const { current, switching, switchLocale } = useI18nLazy() +// current 直接派生自全局 locale,无需 useI18n const locales = [ { code: 'zh-CN', label: '中' }, diff --git a/src/composables/useI18nLazy.js b/src/composables/useI18nLazy.js index 45396a8..581ed2f 100644 --- a/src/composables/useI18nLazy.js +++ b/src/composables/useI18nLazy.js @@ -1,17 +1,19 @@ -import { ref } from 'vue' -import { loadLocaleAsync } from '../i18n/index.js' +import { ref, computed } from 'vue' +import i18n, { loadLocaleAsync } from '../i18n/index.js' -const current = ref('zh-CN') const switching = ref(false) // 语言懒加载切换逻辑 +// current 派生自全局 i18n composer 的 locale(composition 模式下在 i18n.global), +// 与各组件内 useI18n({ useScope: 'global' }) 读到的是同一个 ref。 export function useI18nLazy() { + const current = computed(() => i18n.global.locale.value) + async function switchLocale(locale) { if (switching.value || current.value === locale) return switching.value = true try { await loadLocaleAsync(locale) - current.value = locale } finally { switching.value = false } diff --git a/src/i18n/index.js b/src/i18n/index.js index 7d72872..0b22a46 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -22,9 +22,10 @@ const loadedLocales = new Set(['zh-CN']) * 之后切换仅在已加载集合内完成(无网络请求)。 */ export async function loadLocaleAsync(locale) { - if (i18n.locale.value === locale) return + // composition 模式下响应式 locale 在 i18n.global 上 + if (i18n.global.locale.value === locale) return if (loadedLocales.has(locale)) { - i18n.locale.value = locale + i18n.global.locale.value = locale document.documentElement.setAttribute('lang', locale) return } @@ -35,14 +36,14 @@ export async function loadLocaleAsync(locale) { messages = (await import('./locales/en.js')).default } else { // 未知语言回退到中文 - i18n.locale.value = 'zh-CN' + i18n.global.locale.value = 'zh-CN' document.documentElement.setAttribute('lang', 'zh-CN') return } - i18n.mergeLocaleMessage(locale, messages) + i18n.global.mergeLocaleMessage(locale, messages) loadedLocales.add(locale) - i18n.locale.value = locale + i18n.global.locale.value = locale document.documentElement.setAttribute('lang', locale) } diff --git a/src/modules/mobile-game/MobileGame.vue b/src/modules/mobile-game/MobileGame.vue index 565aa50..276920b 100644 --- a/src/modules/mobile-game/MobileGame.vue +++ b/src/modules/mobile-game/MobileGame.vue @@ -6,7 +6,7 @@ import VersionTimeline from './components/VersionTimeline.vue' import TeamCard from './components/TeamCard.vue' import Leaderboard from './components/Leaderboard.vue' -const { locale } = useI18n() +const { locale } = useI18n({ useScope: 'global' }) const L = (zh, en) => (locale.value === 'zh-CN' ? zh : en) // 站点根绝对路径,用绑定避免被 Vite 当作构建期静态资源解析 const videoPoster = '/448/2.jpg' @@ -28,14 +28,14 @@ const videoSrc = '/448/ml/449ml.mp4'

{{ L( - '项目跨越 448 与 449 两门课程。448 用 Unity 开发游戏本体,产出可浏览器试玩的 WebGL 构建与可安装的 Android APK;449 搭建展示网站与基于 PHP+MySQL 的玩家分数排行榜。主要贡献者 Zikai Wang(zikai-ui / main / Roll-a-Ball 系列)与 Hongxiang He(2D DobyAdventure)。', - 'The project spans courses 448 and 449. 448 built the game with Unity, producing browser-playable WebGL builds and installable Android APKs; 449 built the showcase site and a PHP+MySQL player leaderboard. Main contributors: Zikai Wang (zikai-ui / main / Roll-a-Ball series) and Hongxiang He (2D DobyAdventure).' + '项目跨越 448 与 449 两门课程。448 用 Unity 开发游戏本体,产出可浏览器试玩的 WebGL 构建与可安装的 Android APK;449 搭建展示网站与基于 PHP+MySQL 的玩家分数排行榜。游戏经历多条并行演进线,从 2D 角色控制器与平台跳跃,最终转向手机陀螺仪操控的 3D 滚球,并尝试用 ML-Agents 做强化学习实验。', + 'The project spans courses 448 and 449. 448 built the game with Unity, producing browser-playable WebGL builds and installable Android APKs; 449 built the showcase site and a PHP+MySQL player leaderboard. The game evolved along several parallel lines, from 2D character controllers and platformers, ultimately shifting to a gyroscope-controlled 3D rolling ball, with ML-Agents reinforcement-learning experiments.' ) }}

{{ L( - '玩法经过多版本迭代:早期是键盘控制生命/能量的角色控制器,最终版改为手机陀螺仪倾斜操控的 3D 滚球,含雪花砖掉落、黄色砖弹跳等机制。还用 Unity ML-Agents 做了强化学习实验。', - 'Gameplay iterated across versions: early builds were keyboard-controlled character controllers with health/power stats; the final version became a gyroscope-tilt 3D rolling-ball game with falling snowflake bricks and bouncing yellow bricks. ML-Agents reinforcement-learning experiments were also conducted.' + '玩法经过多版本迭代:早期是键盘控制生命/能量的 2D 角色控制器,最终版改为手机陀螺仪倾斜操控的 3D 滚球,含雪花砖掉落、黄色砖弹跳等机制。', + 'Gameplay iterated across versions: early builds were keyboard-controlled 2D character controllers with health/power stats; the final version became a gyroscope-tilt 3D rolling-ball game with falling snowflake bricks and bouncing yellow bricks.' ) }}

diff --git a/src/modules/mobile-game/components/HeroSection.vue b/src/modules/mobile-game/components/HeroSection.vue index 7e9ff63..53516f1 100644 --- a/src/modules/mobile-game/components/HeroSection.vue +++ b/src/modules/mobile-game/components/HeroSection.vue @@ -2,7 +2,7 @@ import Tag from '../../../components/ui/Tag.vue' import { useI18n } from 'vue-i18n' -const { locale } = useI18n() +const { locale } = useI18n({ useScope: 'global' }) const meta = { course: { zh: '迈阿密大学 · 毕设 448 / 449', en: 'Miami University · Capstone 448 / 449' }, diff --git a/src/modules/mobile-game/components/Leaderboard.vue b/src/modules/mobile-game/components/Leaderboard.vue index ba2f6f8..188ad55 100644 --- a/src/modules/mobile-game/components/Leaderboard.vue +++ b/src/modules/mobile-game/components/Leaderboard.vue @@ -2,7 +2,7 @@ import { ref, onMounted } from 'vue' import { useI18n } from 'vue-i18n' -const { locale, t } = useI18n() +const { locale, t } = useI18n({ useScope: 'global' }) const rows = ref([]) const state = ref('loading') // loading | ok | error diff --git a/src/modules/mobile-game/components/ScreenshotCarousel.vue b/src/modules/mobile-game/components/ScreenshotCarousel.vue index fcc693b..32df863 100644 --- a/src/modules/mobile-game/components/ScreenshotCarousel.vue +++ b/src/modules/mobile-game/components/ScreenshotCarousel.vue @@ -3,7 +3,7 @@ import { ref, onMounted, onUnmounted } from 'vue' import { useI18n } from 'vue-i18n' import { screenshots } from '../data/versions.js' -const { locale } = useI18n() +const { locale } = useI18n({ useScope: 'global' }) const current = ref(0) let timer = null diff --git a/src/modules/mobile-game/components/TeamCard.vue b/src/modules/mobile-game/components/TeamCard.vue index f4a5d5e..867a090 100644 --- a/src/modules/mobile-game/components/TeamCard.vue +++ b/src/modules/mobile-game/components/TeamCard.vue @@ -2,7 +2,7 @@ import { useI18n } from 'vue-i18n' import { team } from '../data/team.js' -const { locale } = useI18n() +const { locale } = useI18n({ useScope: 'global' }) const L = (obj) => (locale.value === 'zh-CN' ? obj.zh : obj.en) diff --git a/src/modules/mobile-game/components/VersionTimeline.vue b/src/modules/mobile-game/components/VersionTimeline.vue index ee7d05e..2aa6a35 100644 --- a/src/modules/mobile-game/components/VersionTimeline.vue +++ b/src/modules/mobile-game/components/VersionTimeline.vue @@ -1,49 +1,90 @@