From 481ec5ecf7a502daa9b91eacbcb4eda7e24f8408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?mac=C2=B7ufutx?= Date: Wed, 11 Jun 2025 20:15:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=2020250611=20=E9=85=8D=E7=BD=AE=20@intlif?= =?UTF-8?q?y/unplugin-vue-i18n=EF=BC=8C=E5=9B=BD=E9=99=85=E5=8C=96I18n?= =?UTF-8?q?=E5=A4=9A=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + src/locales/en/common.json | 20 +++++ src/locales/en/home.json | 18 +++++ src/locales/en/i18nDemo.json | 28 +++++++ src/locales/i18n.ts | 61 +++++++++++++++ src/locales/zh-CN/common.json | 20 +++++ src/locales/zh-CN/home.json | 18 +++++ src/locales/zh-CN/i18nDemo.json | 29 ++++++++ src/main.ts | 25 ++++++- src/router/routes.ts | 9 +++ src/views/I18nDemo.vue | 127 ++++++++++++++++++++++++++++++++ tsconfig.app.json | 2 +- 12 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 src/locales/en/common.json create mode 100644 src/locales/en/home.json create mode 100644 src/locales/en/i18nDemo.json create mode 100644 src/locales/i18n.ts create mode 100644 src/locales/zh-CN/common.json create mode 100644 src/locales/zh-CN/home.json create mode 100644 src/locales/zh-CN/i18nDemo.json create mode 100644 src/views/I18nDemo.vue diff --git a/package.json b/package.json index 5836258..8613b16 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,13 @@ "pinia": "^2.1.7", "postcss-px-to-viewport": "^1.1.1", "vue": "^3.5.13", + "vue-i18n": "^9.8.0", "vue-router": "^4.5.1" }, "devDependencies": { "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", + "@intlify/unplugin-vue-i18n": "^6.0.8", "@types/node": "^20.19.0", "@typescript-eslint/eslint-plugin": "6.13.2", "@typescript-eslint/parser": "6.13.2", diff --git a/src/locales/en/common.json b/src/locales/en/common.json new file mode 100644 index 0000000..865eb24 --- /dev/null +++ b/src/locales/en/common.json @@ -0,0 +1,20 @@ +{ + "button": { + "submit": "Submit", + "cancel": "Cancel", + "back": "Back", + "next": "Next" + }, + "message": { + "welcome": "Welcome to our application", + "loading": "Loading...", + "success": "Operation successful", + "error": "An error occurred" + }, + "navigation": { + "home": "Home", + "about": "About", + "contact": "Contact", + "profile": "Profile" + } +} diff --git a/src/locales/en/home.json b/src/locales/en/home.json new file mode 100644 index 0000000..4774718 --- /dev/null +++ b/src/locales/en/home.json @@ -0,0 +1,18 @@ +{ + "title": "Home Page", + "subtitle": "Welcome to our website", + "hero": { + "heading": "Discover our products", + "description": "Explore our range of high-quality solutions", + "cta": "Learn More" + }, + "features": { + "title": "Key Features", + "items": [ + "Fast Performance", + "Easy Integration", + "Secure Transactions", + "24/7 Support" + ] + } +} diff --git a/src/locales/en/i18nDemo.json b/src/locales/en/i18nDemo.json new file mode 100644 index 0000000..2759c39 --- /dev/null +++ b/src/locales/en/i18nDemo.json @@ -0,0 +1,28 @@ +{ + "page": { + "title": "Internationalization Demo" + }, + "demo": { + "date": { + "title": "Date Formatting", + "current": "Current Date" + }, + "number": { + "title": "Number Formatting", + "price": "Product Price" + }, + "plural": { + "title": "Pluralization", + "message": "{count, plural, zero {No messages} one {One message} other {# messages}}" + }, + "nested": { + "title": "Nested Translation", + "content": "This is a nested translation example with {placeholder}." + }, + "router": { + "title": "Router Links", + "home": "Home", + "about": "About" + } + } +} \ No newline at end of file diff --git a/src/locales/i18n.ts b/src/locales/i18n.ts new file mode 100644 index 0000000..c20eef7 --- /dev/null +++ b/src/locales/i18n.ts @@ -0,0 +1,61 @@ +// src/locales/i18n.ts +import { createI18n } from 'vue-i18n' +import en from './en/common.json' +console.log('Manual import:', en) + +// 关键:正确加载语言文件(假设语言文件直接放在 locales 目录下) +// src/locales/i18n.ts +const loadLocaleMessages = () => { + const messages: Record = {} + + const localeFiles = import.meta.glob('./**/*.json', { eager: true }) + + for (const path in localeFiles) { + const pathParts = path.split('/').slice(1) + const locale = pathParts[0] // 'en' 或 'zh-CN' + const module = pathParts[1].replace('.json', '') // 'i18nDemo' + + if (!messages[locale]) { + messages[locale] = {} + } + messages[locale][module] = localeFiles[path] + } + + // 合并模块到语言层级(而非顶层) + const mergedMessages: Record = {} + for (const locale in messages) { + mergedMessages[locale] = {} + for (const module in messages[locale]) { + Object.assign(mergedMessages[locale], messages[locale][module]) + } + } + + console.log('Final messages:', mergedMessages) + return mergedMessages +} + +const i18n = createI18n({ + legacy: false, + + locale: 'zh-CN', + fallbackLocale: 'en', + messages: loadLocaleMessages(), + datetimeFormats: { + en: { + short: { year: 'numeric', month: 'short', day: 'numeric' } + }, + 'zh-CN': { + short: { year: 'numeric', month: '2-digit', day: '2-digit' } + } + }, + numberFormats: { + en: { + currency: { style: 'currency', currency: 'USD' } + }, + 'zh-CN': { + currency: { style: 'currency', currency: 'CNY' } + } + } +}) as any // 暂时忽略类型检查 + +export default i18n diff --git a/src/locales/zh-CN/common.json b/src/locales/zh-CN/common.json new file mode 100644 index 0000000..505d0e0 --- /dev/null +++ b/src/locales/zh-CN/common.json @@ -0,0 +1,20 @@ +{ + "button": { + "submit": "提交", + "cancel": "取消", + "back": "返回", + "next": "下一步" + }, + "message": { + "welcome": "欢迎使用我们的应用", + "loading": "加载中...", + "success": "操作成功", + "error": "发生错误" + }, + "navigation": { + "home": "首页", + "about": "关于我们", + "contact": "联系我们", + "profile": "个人资料" + } +} diff --git a/src/locales/zh-CN/home.json b/src/locales/zh-CN/home.json new file mode 100644 index 0000000..5097959 --- /dev/null +++ b/src/locales/zh-CN/home.json @@ -0,0 +1,18 @@ +{ + "title": "首页", + "subtitle": "欢迎访问我们的网站", + "hero": { + "heading": "发现我们的产品", + "description": "探索我们的高品质解决方案系列", + "cta": "了解更多" + }, + "features": { + "title": "核心功能", + "items": [ + "快速性能", + "简单集成", + "安全交易", + "全天候支持" + ] + } +} diff --git a/src/locales/zh-CN/i18nDemo.json b/src/locales/zh-CN/i18nDemo.json new file mode 100644 index 0000000..422a0b2 --- /dev/null +++ b/src/locales/zh-CN/i18nDemo.json @@ -0,0 +1,29 @@ +{ + "page": { + "title": "国际化演示" + }, + "demo": { + "date": { + "title": "日期格式化", + "current": "当前日期" + }, + "number": { + "title": "数字格式化", + "price": "产品价格" + }, + "plural": { + "title": "复数形式", + "message": "{count, plural, =0 {没有消息} =1 {一条消息} other {# 条消息}}" + }, + "nested": { + "title": "嵌套翻译", + "content": "这是一个包含 {placeholder} 的嵌套翻译示例。" + }, + "router": { + "title": "路由链接", + "home": "首页", + "about": "关于" + } + } +} + \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index f72708e..0968924 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,13 +3,34 @@ import { ViteSSG } from 'vite-ssg' import { createWebHistory } from 'vue-router' import App from './App.vue' import routes from './router/routes' +import i18n from './locales/i18n' // 导入i18n配置 import './style.css' - +// 修正:明确 meta.title 的类型为 string +declare module 'vue-router' { + interface RouteMeta { + title?: string // 明确指定为 string 类型 + requiresAuth?: boolean + } +} export const createApp = ViteSSG( App, { history: createWebHistory(), - routes + routes, + base: import.meta.env.BASE_URL || '/' + }, + ctx => { + // 安装 i18n 插件 + ctx.app.use(i18n) + + // 路由守卫:设置页面标题 + ctx.router.beforeEach((to, _from, next) => { + // 动态设置页面标题 + if (to.meta.title) { + document.title = to.meta.title || '默认标题' // 确保赋值为 string + } + next() + }) } // _ctx => { // // 确保路由插件被安装 diff --git a/src/router/routes.ts b/src/router/routes.ts index 3496a35..8bc365c 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -2,8 +2,17 @@ import type { RouteRecordRaw } from 'vue-router' // 添加type关键字 import Home from '@/views/Home.vue' import About from '@/views/About.vue' +import I18nDemo from '../views/I18nDemo.vue' const routes: RouteRecordRaw[] = [ + { + path: '/i18n-demo', + name: 'I18nDemo', + component: I18nDemo, + meta: { + title: '国际化演示' + } + }, { path: '/', name: 'Home', diff --git a/src/views/I18nDemo.vue b/src/views/I18nDemo.vue new file mode 100644 index 0000000..0114401 --- /dev/null +++ b/src/views/I18nDemo.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/tsconfig.app.json b/tsconfig.app.json index b2c4573..67c6c1c 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -17,7 +17,7 @@ // 支持 Vue 的 JSX 语法 "types": [ "vite/client", - "vue" + "vue","vue-i18n" ], "incremental": true, // 添加这一行 // 新增 Vue 类型声明