Browse Source

国际化基础设置基本完成

Huanyi 1 week ago
parent
commit
0575320f7f

+ 185 - 0
CHANGELOG_I18N.md

@@ -0,0 +1,185 @@
+# 国际化目录结构变更日志
+
+## 2025-12-01 - 目录结构重组
+
+### 变更说明
+
+将语言包组织方式从**按语言分组**调整为**按功能模块分组**,以提升可维护性和扩展性。
+
+### 变更前后对比
+
+#### 旧结构(按语言分组)
+```
+locales/
+├── zh-CN/
+│   ├── common.js
+│   ├── home.js
+│   └── index.js
+├── en-US/
+│   ├── common.js
+│   ├── home.js
+│   └── index.js
+└── index.js
+```
+
+#### 新结构(按功能模块分组)
+```
+locales/
+├── common/
+│   ├── zh_CN.js
+│   └── en_US.js
+├── pages/
+│   └── home/
+│       ├── zh_CN.js
+│       └── en_US.js
+├── index.js
+├── README.md
+└── STRUCTURE.md
+```
+
+### 主要变更
+
+1. **目录组织**
+   - ✅ 每个功能模块独立成目录
+   - ✅ 同一模块的不同语言文件放在一起
+   - ✅ 文件命名从 `zh-CN` 改为 `zh_CN`(使用下划线)
+
+2. **导入方式**
+   ```javascript
+   // 旧方式
+   import zhCN from './zh-CN'
+   import enUS from './en-US'
+   
+   // 新方式
+   import commonZhCN from './common/zh_CN'
+   import commonEnUS from './common/en_US'
+   import homeZhCN from './pages/home/zh_CN'
+   import homeEnUS from './pages/home/en_US'
+   ```
+
+3. **导出结构**
+   ```javascript
+   // 旧方式
+   export const messages = {
+     'zh-CN': zhCN,
+     'en-US': enUS
+   }
+   
+   // 新方式
+   export const messages = {
+     'zh-CN': {
+       common: commonZhCN,
+       home: homeZhCN
+     },
+     'en-US': {
+       common: commonEnUS,
+       home: homeEnUS
+     }
+   }
+   ```
+
+### 新结构的优势
+
+1. **更易维护**
+   - 同一模块的不同语言翻译在同一目录下
+   - 可以同时打开对比编辑,确保键的一致性
+   - 便于发现翻译遗漏或不一致
+
+2. **更易扩展**
+   - 添加新语言:只需在每个模块目录下添加新文件
+   - 添加新模块:创建新目录,包含所有语言文件
+   - 不需要维护多个语言目录的平行结构
+
+3. **更清晰的职责**
+   - 每个模块独立管理自己的翻译
+   - 模块化开发更友好
+   - 减少目录层级,结构更扁平
+
+4. **团队协作更友好**
+   - 减少文件冲突(不同模块在不同目录)
+   - 代码审查更容易(对比同目录下的文件)
+   - 新成员更容易理解结构
+
+### 迁移影响
+
+#### 对现有代码的影响
+
+**无影响** - 以下部分保持不变:
+
+- ✅ 组件中的使用方式(`t('home.title')`)
+- ✅ i18n 配置和初始化
+- ✅ store 的使用方式
+- ✅ API 接口(`useI18n`、`useLocaleStore`)
+
+#### 需要更新的部分
+
+- ✅ `locales/index.js` - 已更新导入和导出逻辑
+- ✅ 相关文档 - 已更新所有示例和说明
+- ✅ 旧文件 - 已删除
+
+### 文档更新
+
+已更新以下文档以反映新结构:
+
+- ✅ `I18N_GUIDE.md` - 国际化完整指南
+- ✅ `locales/README.md` - 语言包管理文档
+- ✅ `locales/STRUCTURE.md` - 目录结构说明(新增)
+- ✅ `i18n/README.md` - i18n 使用文档
+
+### 快速上手
+
+#### 添加新模块(如 user 模块)
+
+```bash
+# 1. 创建目录和文件
+mkdir -p locales/pages/user
+touch locales/pages/user/zh_CN.js
+touch locales/pages/user/en_US.js
+
+# 2. 编写翻译内容(略)
+
+# 3. 在 locales/index.js 中注册
+# 导入
+import userZhCN from './pages/user/zh_CN'
+import userEnUS from './pages/user/en_US'
+
+# 注册
+export const messages = {
+  'zh-CN': {
+    // ...
+    user: userZhCN
+  },
+  'en-US': {
+    // ...
+    user: userEnUS
+  }
+}
+```
+
+#### 添加新语言(如日语)
+
+```bash
+# 为每个现有模块添加日语文件
+touch locales/common/ja_JP.js
+touch locales/pages/home/ja_JP.js
+
+# 在 locales/index.js 中注册(参考文档)
+```
+
+### 注意事项
+
+1. **文件命名**:使用下划线 `zh_CN.js`,不是连字符 `zh-CN.js`
+2. **模块键名**:在 `locales/index.js` 中注册时,键名要与翻译键的第一级保持一致
+3. **同步维护**:确保同一模块下所有语言文件的键结构完全相同
+
+### 相关链接
+
+- [目录结构详细说明](./locales/STRUCTURE.md)
+- [语言包管理文档](./locales/README.md)
+- [国际化完整指南](./I18N_GUIDE.md)
+
+---
+
+**变更日期**:2025-12-01  
+**变更类型**:目录结构重组  
+**向后兼容**:是(对使用方无影响)

+ 485 - 0
I18N_GUIDE.md

@@ -0,0 +1,485 @@
+# 国际化完整指南
+
+## 📦 已完成的功能
+
+✅ vue-i18n 集成
+✅ 模块化语言包管理
+✅ 中英文双语支持
+✅ 语言切换功能
+✅ 持久化存储
+✅ Pinia 状态管理
+✅ 可复用组件
+✅ 完整文档
+
+## 📁 项目结构
+
+```
+intelligent-etmf-system-applet/
+├── i18n/                          # i18n 配置
+│   ├── index.js                  # i18n 初始化
+│   └── README.md                 # 使用文档
+├── locales/                       # 语言包(按模块分组)
+│   ├── common/                   # 通用模块
+│   │   ├── zh_CN.js             # 中文翻译
+│   │   └── en_US.js             # 英文翻译
+│   ├── pages/                    # 页面模块
+│   │   └── home/                # 首页
+│   │       ├── zh_CN.js         # 中文翻译
+│   │       └── en_US.js         # 英文翻译
+│   ├── components/               # 组件模块
+│   │   └── languageSwitcher/    # 语言切换组件
+│   │       ├── zh_CN.js         # 中文翻译
+│   │       └── en_US.js         # 英文翻译
+│   ├── index.js                  # 语言包总导出
+│   ├── README.md                 # 语言包管理文档
+│   ├── STRUCTURE.md              # 目录结构说明
+│   └── COMPONENT_I18N.md         # 组件国际化指南
+├── store/                         # 状态管理
+│   └── locale.js                 # 语言切换 store
+├── utils/                         # 工具函数
+│   └── i18n.js                   # i18n 工具函数
+├── components/                    # 组件
+│   └── LanguageSwitcher/         # 语言切换组件
+│       └── index.vue
+└── main.js                        # 已集成 i18n
+```
+
+## 🚀 快速开始
+
+### 1. 在页面中使用国际化
+
+```vue
+<template>
+  <view>
+    <!-- 基础用法 -->
+    <text>{{ t('home.title') }}</text>
+    
+    <!-- 嵌套键 -->
+    <text>{{ t('common.button.submit') }}</text>
+    
+    <!-- 带参数 -->
+    <text>{{ t('common.greeting', { name: userName }) }}</text>
+  </view>
+</template>
+
+<script setup>
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+const userName = '张三'
+</script>
+```
+
+### 2. 在组件中使用国际化
+
+```vue
+<template>
+  <view class="user-card">
+    <!-- 使用组件专属翻译 -->
+    <text>{{ t('components.userCard.title') }}</text>
+    
+    <!-- 使用通用翻译 -->
+    <button>{{ t('common.button.confirm') }}</button>
+    
+    <!-- 动态键 -->
+    <text>{{ t(`components.userCard.status.${user.status}`) }}</text>
+  </view>
+</template>
+
+<script setup>
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+const props = defineProps({
+  user: Object
+})
+</script>
+```
+
+### 3. 使用语言切换组件
+
+```vue
+<template>
+  <view class="header">
+    <!-- 方式1:使用封装的组件 -->
+    <LanguageSwitcher />
+    
+    <!-- 方式2:自定义切换逻辑 -->
+    <button @click="changeLanguage">切换语言</button>
+  </view>
+</template>
+
+<script setup>
+import { useLocaleStore } from '@/store/locale'
+import LanguageSwitcher from '@/components/LanguageSwitcher/index.vue'
+
+const localeStore = useLocaleStore()
+
+const changeLanguage = () => {
+  // 切换到下一个语言
+  localeStore.toggleLocale()
+  
+  // 或切换到指定语言
+  // localeStore.setLocale('en-US')
+}
+</script>
+```
+
+### 4. 在 JS 中使用(非组件环境)
+
+```javascript
+import { t } from '@/utils/i18n'
+
+// 在 API 请求中使用
+const showError = () => {
+  uni.showToast({
+    title: t('common.message.error'),
+    icon: 'none'
+  })
+}
+
+// 在工具函数中使用
+const formatStatus = (status) => {
+  return t(`order.status.${status}`)
+}
+```
+
+## 🎨 为组件添加国际化
+
+### 为什么要为组件创建专门的翻译?
+
+- 组件有特定的业务术语
+- 组件需要在多处复用
+- 便于独立维护和版本管理
+
+### 快速示例
+
+创建 UserCard 组件的翻译:
+
+1. 创建翻译文件:`locales/components/userCard/zh_CN.js` 和 `en_US.js`
+2. 在 `locales/index.js` 中注册到 `components` 下
+3. 在组件中使用:`t('components.userCard.title')`
+
+**详细指南**:请查看 [组件国际化指南](./locales/COMPONENT_I18N.md)
+
+## 📝 添加新的翻译模块
+
+### 页面模块示例
+
+在对应目录下创建语言文件。例如创建产品管理页面模块:
+
+创建 `locales/pages/product/zh_CN.js`:
+```javascript
+export default {
+  title: '产品管理',
+  list: {
+    title: '产品列表',
+    empty: '暂无产品'
+  },
+  detail: {
+    name: '产品名称',
+    price: '价格',
+    stock: '库存'
+  },
+  action: {
+    add: '添加产品',
+    edit: '编辑',
+    delete: '删除'
+  }
+}
+```
+
+创建 `locales/pages/product/en_US.js`:
+```javascript
+export default {
+  title: 'Product Management',
+  list: {
+    title: 'Product List',
+    empty: 'No products'
+  },
+  detail: {
+    name: 'Product Name',
+    price: 'Price',
+    stock: 'Stock'
+  },
+  action: {
+    add: 'Add Product',
+    edit: 'Edit',
+    delete: 'Delete'
+  }
+}
+```
+
+### 步骤 2:注册模块
+
+在 `locales/index.js` 中导入新模块:
+```javascript
+// 导入通用模块
+import commonZhCN from './common/zh_CN'
+import commonEnUS from './common/en_US'
+
+// 导入页面模块
+import homeZhCN from './pages/home/zh_CN'
+import homeEnUS from './pages/home/en_US'
+import productZhCN from './pages/product/zh_CN'  // 新增
+import productEnUS from './pages/product/en_US'  // 新增
+
+// 导入组件模块
+import languageSwitcherZhCN from './components/languageSwitcher/zh_CN'
+import languageSwitcherEnUS from './components/languageSwitcher/en_US'
+
+export const messages = {
+  'zh-CN': {
+    common: commonZhCN,
+    home: homeZhCN,
+    product: productZhCN,  // 新增
+    components: {
+      languageSwitcher: languageSwitcherZhCN
+    }
+  },
+  'en-US': {
+    common: commonEnUS,
+    home: homeEnUS,
+    product: productEnUS,  // 新增
+    components: {
+      languageSwitcher: languageSwitcherEnUS
+    }
+  }
+}
+```
+
+**注意**:
+- 页面模块直接注册在第一层
+- 组件模块注册在 `components` 对象下
+
+### 步骤 3:使用新模块
+
+```vue
+<template>
+  <view>
+    <text>{{ t('product.title') }}</text>
+    <text>{{ t('product.detail.name') }}</text>
+  </view>
+</template>
+```
+
+## 🌍 添加新语言
+
+### 步骤 1:创建语言文件
+
+为每个现有模块创建新语言的翻译文件:
+
+```
+locales/
+  ├── common/
+  │   ├── zh_CN.js
+  │   ├── en_US.js
+  │   └── ja_JP.js        # 新增日文
+  └── pages/
+      └── home/
+          ├── zh_CN.js
+          ├── en_US.js
+          └── ja_JP.js    # 新增日文
+```
+
+### 步骤 2:更新配置
+
+在 `locales/index.js` 中:
+```javascript
+import commonZhCN from './common/zh_CN'
+import commonEnUS from './common/en_US'
+import commonJaJP from './common/ja_JP'  // 新增
+
+import homeZhCN from './pages/home/zh_CN'
+import homeEnUS from './pages/home/en_US'
+import homeJaJP from './pages/home/ja_JP'  // 新增
+
+export const messages = {
+  'zh-CN': {
+    common: commonZhCN,
+    home: homeZhCN
+  },
+  'en-US': {
+    common: commonEnUS,
+    home: homeEnUS
+  },
+  'ja-JP': {  // 新增
+    common: commonJaJP,
+    home: homeJaJP
+  }
+}
+
+export const localeList = [
+  { label: '简体中文', value: 'zh-CN' },
+  { label: 'English', value: 'en-US' },
+  { label: '日本語', value: 'ja-JP' }  // 新增
+]
+```
+
+## 💡 最佳实践
+
+### 1. 模块划分原则
+
+- **common/**:通用文本(按钮、消息、错误提示等),所有语言文件都在 `locales/common/` 下
+- **pages/**:页面模块,每个页面一个子目录(如 `pages/home/`、`pages/user/`),每个子目录下包含各语言版本
+- **其他业务模块**:可根据需要创建其他模块目录(如 `api/`、`components/` 等)
+
+**优势**:
+- 同一功能的不同语言翻译在同一目录下,便于对比和维护
+- 添加新语言只需在每个模块目录下添加新的语言文件
+- 模块划分清晰,职责明确
+
+### 2. 命名规范
+
+```javascript
+// ✅ 推荐:清晰的层级结构
+{
+  user: {
+    profile: {
+      title: '个人资料',
+      field: {
+        name: '姓名',
+        email: '邮箱'
+      }
+    }
+  }
+}
+
+// ❌ 不推荐:扁平结构
+{
+  userProfileTitle: '个人资料',
+  userProfileFieldName: '姓名'
+}
+```
+
+### 3. 参数化文本
+
+```javascript
+// 定义
+{
+  welcome: '欢迎,{name}!',
+  itemCount: '共 {count} 项'
+}
+
+// 使用
+t('common.welcome', { name: '张三' })
+t('common.itemCount', { count: 10 })
+```
+
+### 4. 保持同步
+
+确保同一模块下所有语言文件的翻译键完全一致:
+```javascript
+// ✅ 正确:locales/pages/user/zh_CN.js 和 locales/pages/user/en_US.js 键名相同
+// zh_CN.js
+{ user: { name: '姓名' } }
+
+// en_US.js
+{ user: { name: 'Name' } }
+
+// ❌ 错误:键名不一致
+// zh_CN.js
+{ user: { name: '姓名' } }
+
+// en_US.js
+{ user: { userName: 'Name' } }  // 键名不同!
+```
+
+**提示**:建议同时打开同一模块的不同语言文件进行对比编辑
+
+## 🔧 API 参考
+
+### useLocaleStore
+
+```javascript
+const localeStore = useLocaleStore()
+
+// 状态
+localeStore.currentLocale        // 当前语言:'zh-CN' | 'en-US'
+localeStore.availableLocales     // 可用语言列表
+
+// 方法
+localeStore.setLocale('en-US')   // 切换到指定语言
+localeStore.toggleLocale()       // 切换到下一个语言
+localeStore.getCurrentLocaleName() // 获取当前语言显示名称
+```
+
+### useI18n
+
+```javascript
+const { t, locale, te } = useI18n()
+
+t('home.title')                  // 翻译文本
+t('common.greeting', { name: 'John' }) // 带参数翻译
+locale.value                     // 当前语言
+te('home.title')                 // 检查键是否存在
+```
+
+### 工具函数
+
+```javascript
+import { t, getLocale, setLocale, hasKey } from '@/utils/i18n'
+
+t('home.title')                  // 翻译文本
+getLocale()                      // 获取当前语言
+setLocale('en-US')              // 设置语言
+hasKey('home.title')            // 检查键是否存在
+```
+
+## 🐛 常见问题
+
+### 问题 1:翻译不生效
+
+**原因**:翻译键不存在或拼写错误
+
+**解决**:
+1. 检查翻译键是否正确
+2. 确认模块是否正确导入
+3. 使用浏览器开发工具查看警告信息
+
+### 问题 2:语言切换后页面未更新
+
+**原因**:使用了非响应式的方式获取翻译
+
+**解决**:
+```javascript
+// ❌ 错误:在 setup 外部获取
+const title = t('home.title')
+
+// ✅ 正确:在模板中使用或使用 computed
+{{ t('home.title') }}
+// 或
+const title = computed(() => t('home.title'))
+```
+
+### 问题 3:小程序环境报错
+
+**原因**:i18n 配置不兼容
+
+**解决**:确保使用以下配置
+```javascript
+createI18n({
+  legacy: false,          // 必须:使用 Composition API
+  globalInjection: true,  // 必须:全局注入
+  // ...
+})
+```
+
+## 📚 相关文档
+
+- [i18n 使用指南](./i18n/README.md)
+- [语言包管理](./locales/README.md)
+- [目录结构说明](./locales/STRUCTURE.md)
+- [组件国际化指南](./locales/COMPONENT_I18N.md)
+- [Vue I18n 官方文档](https://vue-i18n.intlify.dev/)
+
+## 🎯 TODO
+
+- [ ] 添加更多语言支持(日语、韩语等)
+- [ ] 实现语言包懒加载优化
+- [ ] 添加翻译缺失检测工具
+- [ ] 集成在线翻译服务
+
+## 📞 联系方式
+
+如有问题或建议,请联系项目维护者。

+ 57 - 0
components/LanguageSwitcher/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <view class="language-switcher" @click="handleSwitch">
+    <text class="language-icon">🌐</text>
+    <text class="language-text">{{ currentLocaleName }}</text>
+  </view>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useLocaleStore } from '@/store/locale'
+
+const { t } = useI18n()
+const localeStore = useLocaleStore()
+
+const currentLocaleName = computed(() => localeStore.getCurrentLocaleName())
+
+const handleSwitch = () => {
+  const success = localeStore.toggleLocale()
+  if (success) {
+    uni.showToast({
+      title: t('components.languageSwitcher.switchSuccess'),
+      icon: 'success',
+      duration: 1500
+    })
+  } else {
+    uni.showToast({
+      title: t('components.languageSwitcher.switchFailed'),
+      icon: 'error',
+      duration: 1500
+    })
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.language-switcher {
+  display: inline-flex;
+  align-items: center;
+  gap: 8rpx;
+  background: rgba(255, 255, 255, 0.2);
+  padding: 12rpx 24rpx;
+  border-radius: 30rpx;
+  backdrop-filter: blur(10rpx);
+  border: 1rpx solid rgba(255, 255, 255, 0.3);
+  
+  .language-icon {
+    font-size: 28rpx;
+  }
+  
+  .language-text {
+    font-size: 24rpx;
+    color: #ffffff;
+    font-weight: 500;
+  }
+}
+</style>

+ 311 - 0
i18n/README.md

@@ -0,0 +1,311 @@
+# 国际化使用指南
+
+## 目录结构
+
+```
+├── i18n/                   # i18n 配置目录
+│   ├── index.js           # i18n 初始化配置
+│   └── README.md          # 使用文档
+├── locales/               # 语言包目录(按模块分组)
+│   ├── common/           # 通用模块
+│   │   ├── zh_CN.js     # 中文翻译
+│   │   └── en_US.js     # 英文翻译
+│   ├── pages/            # 页面模块
+│   │   └── home/        # 首页模块
+│   │       ├── zh_CN.js # 中文翻译
+│   │       └── en_US.js # 英文翻译
+│   ├── index.js          # 语言包导出
+│   └── README.md         # 语言包管理文档
+├── store/                 # 状态管理
+│   └── locale.js         # 语言切换 store
+└── components/            # 组件
+    └── LanguageSwitcher/ # 语言切换组件
+```
+
+## 快速开始
+
+### 1. 在页面中使用
+
+```vue
+<template>
+  <view>
+    <!-- 使用 t 函数翻译 -->
+    <text>{{ t('home.title') }}</text>
+    
+    <!-- 使用嵌套的翻译键 -->
+    <text>{{ t('common.button.confirm') }}</text>
+  </view>
+</template>
+
+<script setup>
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+</script>
+```
+
+### 2. 在 JS 中使用
+
+```javascript
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
+
+// 使用翻译
+const message = t('common.message.success')
+
+// 使用参数
+const greeting = t('common.greeting', { name: '张三' })
+```
+
+### 3. 切换语言
+
+```vue
+<script setup>
+import { useLocaleStore } from '@/store/locale'
+
+const localeStore = useLocaleStore()
+
+// 切换到指定语言
+localeStore.setLocale('en-US')
+
+// 切换到下一个语言
+localeStore.toggleLocale()
+
+// 获取当前语言
+const currentLang = localeStore.currentLocale
+</script>
+```
+
+### 4. 使用语言切换组件
+
+```vue
+<template>
+  <view>
+    <!-- 直接使用组件 -->
+    <LanguageSwitcher />
+  </view>
+</template>
+
+<script setup>
+import LanguageSwitcher from '@/components/LanguageSwitcher/index.vue'
+</script>
+```
+
+## 添加新的语言模块
+
+### 1. 创建新模块文件
+
+在对应目录下创建语言文件:
+
+```javascript
+// locales/pages/user/zh_CN.js
+export default {
+  profile: '个人资料',
+  settings: '设置',
+  logout: '退出登录'
+}
+```
+
+```javascript
+// locales/pages/user/en_US.js
+export default {
+  profile: 'Profile',
+  settings: 'Settings',
+  logout: 'Logout'
+}
+```
+
+### 2. 在索引文件中导入
+
+```javascript
+// locales/index.js
+import commonZhCN from './common/zh_CN'
+import commonEnUS from './common/en_US'
+import homeZhCN from './pages/home/zh_CN'
+import homeEnUS from './pages/home/en_US'
+import userZhCN from './pages/user/zh_CN'  // 新增
+import userEnUS from './pages/user/en_US'  // 新增
+
+export const messages = {
+  'zh-CN': {
+    common: commonZhCN,
+    home: homeZhCN,
+    user: userZhCN  // 新增
+  },
+  'en-US': {
+    common: commonEnUS,
+    home: homeEnUS,
+    user: userEnUS  // 新增
+  }
+}
+```
+
+### 3. 在页面中使用
+
+```vue
+<template>
+  <text>{{ t('user.profile') }}</text>
+</template>
+```
+
+## 添加新的语言
+
+### 1. 创建新语言文件
+
+为每个现有模块添加新语言翻译:
+
+```
+locales/
+  ├── common/
+  │   ├── zh_CN.js
+  │   ├── en_US.js
+  │   └── ja_JP.js        # 新增日文
+  └── pages/
+      └── home/
+          ├── zh_CN.js
+          ├── en_US.js
+          └── ja_JP.js    # 新增日文
+```
+
+### 2. 更新语言配置
+
+```javascript
+// locales/index.js
+import commonZhCN from './common/zh_CN'
+import commonEnUS from './common/en_US'
+import commonJaJP from './common/ja_JP'  // 新增
+
+import homeZhCN from './pages/home/zh_CN'
+import homeEnUS from './pages/home/en_US'
+import homeJaJP from './pages/home/ja_JP'  // 新增
+
+export const messages = {
+  'zh-CN': {
+    common: commonZhCN,
+    home: homeZhCN
+  },
+  'en-US': {
+    common: commonEnUS,
+    home: homeEnUS
+  },
+  'ja-JP': {  // 新增
+    common: commonJaJP,
+    home: homeJaJP
+  }
+}
+
+export const localeList = [
+  { label: '简体中文', value: 'zh-CN' },
+  { label: 'English', value: 'en-US' },
+  { label: '日本語', value: 'ja-JP' }  // 新增
+]
+```
+
+## 最佳实践
+
+### 1. 命名规范
+
+- 使用小驼峰命名法:`userName`、`submitButton`
+- 使用有意义的名称,避免使用 `text1`、`label2` 等
+- 保持中英文翻译键一致
+
+### 2. 模块划分
+
+- 按功能模块划分:`common`、`home`、`user`、`order` 等
+- `common` 模块存放通用文本:按钮、提示信息等
+- 每个页面或功能一个独立模块
+
+### 3. 翻译键结构
+
+```javascript
+// ✅ 推荐
+{
+  user: {
+    profile: {
+      title: '个人资料',
+      name: '姓名',
+      email: '邮箱'
+    }
+  }
+}
+
+// ❌ 不推荐
+{
+  userProfileTitle: '个人资料',
+  userProfileName: '姓名',
+  userProfileEmail: '邮箱'
+}
+```
+
+### 4. 带参数的翻译
+
+```javascript
+// locales/zh-CN/common.js
+export default {
+  greeting: '你好,{name}!',
+  itemCount: '共 {count} 项'
+}
+
+// 使用
+t('common.greeting', { name: '张三' })
+t('common.itemCount', { count: 10 })
+```
+
+## API 文档
+
+### useLocaleStore
+
+```javascript
+const localeStore = useLocaleStore()
+
+// 属性
+localeStore.currentLocale        // 当前语言
+localeStore.availableLocales     // 可用语言列表
+
+// 方法
+localeStore.initLocale()         // 初始化语言设置
+localeStore.setLocale(locale)    // 设置语言
+localeStore.toggleLocale()       // 切换到下一个语言
+localeStore.getCurrentLocaleName() // 获取当前语言名称
+```
+
+### useI18n
+
+```javascript
+const { t, locale } = useI18n()
+
+// 翻译文本
+t('home.title')
+t('common.greeting', { name: 'John' })
+
+// 获取当前语言
+console.log(locale.value)
+```
+
+## 注意事项
+
+1. **uni-app 兼容性**:确保使用的 i18n 特性在小程序环境中可用
+2. **性能优化**:语言包会在应用启动时全部加载,注意控制大小
+3. **持久化**:语言设置会自动保存到本地存储
+4. **SSR 支持**:使用 `createSSRApp` 时需要特别配置(已完成)
+
+## 故障排查
+
+### 问题:翻译不生效
+
+检查:
+1. 是否正确导入了模块
+2. 翻译键是否正确
+3. i18n 是否正确注册
+
+### 问题:语言切换后部分文本未更新
+
+原因:可能使用了非响应式的方式获取翻译
+解决:确保使用 `t()` 函数或 `computed` 包装
+
+### 问题:小程序中报错
+
+检查:
+1. 确保使用 `legacy: false` 配置
+2. 确保正确配置了 `globalInjection`

+ 23 - 0
i18n/index.js

@@ -0,0 +1,23 @@
+import { createI18n } from 'vue-i18n'
+import { messages, defaultLocale } from '../locales'
+
+// 获取存储的语言设置
+const getStoredLocale = () => {
+  try {
+    return uni.getStorageSync('locale') || defaultLocale
+  } catch (e) {
+    return defaultLocale
+  }
+}
+
+const i18n = createI18n({
+  legacy: false, // 使用 Composition API 模式
+  locale: getStoredLocale(), // 当前语言
+  fallbackLocale: defaultLocale, // 回退语言
+  messages, // 语言包
+  globalInjection: true, // 全局注入 $t 函数
+  missingWarn: false, // 关闭警告
+  fallbackWarn: false
+})
+
+export default i18n

+ 34 - 0
locales/common/en_US.js

@@ -0,0 +1,34 @@
+export default {
+  app: {
+    name: 'Intelligent eTMF System',
+    welcome: 'Welcome'
+  },
+  button: {
+    confirm: 'Confirm',
+    cancel: 'Cancel',
+    submit: 'Submit',
+    reset: 'Reset',
+    save: 'Save',
+    delete: 'Delete',
+    edit: 'Edit',
+    add: 'Add',
+    search: 'Search',
+    back: 'Back',
+    next: 'Next',
+    previous: 'Previous',
+    close: 'Close',
+    start: 'Get Started'
+  },
+  message: {
+    success: 'Operation successful',
+    error: 'Operation failed',
+    loading: 'Loading...',
+    noData: 'No data',
+    networkError: 'Network error, please try again later'
+  },
+  language: {
+    zh: '简体中文',
+    en: 'English',
+    switchSuccess: 'Language switched successfully'
+  }
+}

+ 34 - 0
locales/common/zh_CN.js

@@ -0,0 +1,34 @@
+export default {
+  app: {
+    name: '智能eTMF系统',
+    welcome: '欢迎使用'
+  },
+  button: {
+    confirm: '确定',
+    cancel: '取消',
+    submit: '提交',
+    reset: '重置',
+    save: '保存',
+    delete: '删除',
+    edit: '编辑',
+    add: '添加',
+    search: '搜索',
+    back: '返回',
+    next: '下一步',
+    previous: '上一步',
+    close: '关闭',
+    start: '开始使用'
+  },
+  message: {
+    success: '操作成功',
+    error: '操作失败',
+    loading: '加载中...',
+    noData: '暂无数据',
+    networkError: '网络错误,请稍后重试'
+  },
+  language: {
+    zh: '简体中文',
+    en: 'English',
+    switchSuccess: '语言切换成功'
+  }
+}

+ 7 - 0
locales/components/languageSwitcher/en_US.js

@@ -0,0 +1,7 @@
+export default {
+  title: 'Language Switcher',
+  tooltip: 'Click to switch language',
+  switchSuccess: 'Language switched successfully',
+  switchFailed: 'Failed to switch language',
+  currentLanguage: 'Current Language'
+}

+ 7 - 0
locales/components/languageSwitcher/zh_CN.js

@@ -0,0 +1,7 @@
+export default {
+  title: '语言切换',
+  tooltip: '点击切换语言',
+  switchSuccess: '语言切换成功',
+  switchFailed: '语言切换失败',
+  currentLanguage: '当前语言'
+}

+ 43 - 0
locales/index.js

@@ -0,0 +1,43 @@
+// 导入通用模块
+import commonZhCN from './common/zh_CN'
+import commonEnUS from './common/en_US'
+
+// 导入页面模块
+import homeZhCN from './pages/home/zh_CN'
+import homeEnUS from './pages/home/en_US'
+
+// 导入组件模块
+import languageSwitcherZhCN from './components/languageSwitcher/zh_CN'
+import languageSwitcherEnUS from './components/languageSwitcher/en_US'
+
+// 按语言组织翻译数据
+export const messages = {
+  'zh-CN': {
+    common: commonZhCN,
+    home: homeZhCN,
+    components: {
+      languageSwitcher: languageSwitcherZhCN
+    }
+  },
+  'en-US': {
+    common: commonEnUS,
+    home: homeEnUS,
+    components: {
+      languageSwitcher: languageSwitcherEnUS
+    }
+  }
+}
+
+export const localeList = [
+  {
+    label: '简体中文',
+    value: 'zh-CN'
+  },
+  {
+    label: 'English',
+    value: 'en-US'
+  }
+]
+
+// 默认语言
+export const defaultLocale = 'zh-CN'

+ 11 - 0
locales/pages/home/en_US.js

@@ -0,0 +1,11 @@
+export default {
+  title: 'Intelligent eTMF System',
+  subtitle: 'Welcome',
+  systemInfo: 'System Information',
+  framework: 'Framework Version',
+  platform: 'Platform',
+  frameworkValue: 'Vue 3 + uni-app',
+  platformValue: 'WeChat Mini Program',
+  startButton: 'Get Started',
+  welcomeMessage: 'Welcome to Intelligent eTMF System'
+}

+ 11 - 0
locales/pages/home/zh_CN.js

@@ -0,0 +1,11 @@
+export default {
+  title: '智能eTMF系统',
+  subtitle: '欢迎使用',
+  systemInfo: '系统信息',
+  framework: '框架版本',
+  platform: '平台',
+  frameworkValue: 'Vue 3 + uni-app',
+  platformValue: '微信小程序',
+  startButton: '开始使用',
+  welcomeMessage: '欢迎使用智能eTMF系统'
+}

+ 2 - 0
main.js

@@ -1,5 +1,6 @@
 import { createSSRApp } from 'vue'
 import { createPinia } from 'pinia'
+import i18n from './i18n'
 import App from './App.vue'
 
 export function createApp() {
@@ -7,6 +8,7 @@ export function createApp() {
   const pinia = createPinia()
   
   app.use(pinia)
+  app.use(i18n)
   
   return {
     app

+ 66 - 17
package-lock.json

@@ -13,7 +13,8 @@
         "@dcloudio/uni-h5": "^3.0.0-alpha-4020220360004",
         "@dcloudio/uni-mp-weixin": "^3.0.0-alpha-4020220360004",
         "pinia": "^2.3.1",
-        "vue": "^3.4.0"
+        "vue": "^3.4.0",
+        "vue-i18n": "^9.14.5"
       },
       "devDependencies": {
         "@dcloudio/types": "^3.4.0",
@@ -4519,7 +4520,6 @@
       "version": "2.5.1",
       "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
       "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
-      "dev": true,
       "hasInstallScript": true,
       "license": "MIT",
       "optional": true,
@@ -4559,7 +4559,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4580,7 +4579,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4601,7 +4599,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4622,7 +4619,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4643,7 +4639,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4664,7 +4659,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4685,7 +4679,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4706,7 +4699,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4727,7 +4719,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4748,7 +4739,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4769,7 +4759,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4790,7 +4779,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -4811,7 +4799,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "os": [
@@ -7274,7 +7261,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
       "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
-      "dev": true,
       "license": "Apache-2.0",
       "optional": true,
       "bin": {
@@ -11175,7 +11161,6 @@
       "version": "7.1.1",
       "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
       "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
-      "dev": true,
       "license": "MIT",
       "optional": true
     },
@@ -13680,6 +13665,70 @@
         }
       }
     },
+    "node_modules/vue-i18n": {
+      "version": "9.14.5",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
+      "integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/core-base": "9.14.5",
+        "@intlify/shared": "9.14.5",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/vue-i18n/node_modules/@intlify/core-base": {
+      "version": "9.14.5",
+      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
+      "integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "9.14.5",
+        "@intlify/shared": "9.14.5"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/vue-i18n/node_modules/@intlify/message-compiler": {
+      "version": "9.14.5",
+      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
+      "integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "9.14.5",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/vue-i18n/node_modules/@intlify/shared": {
+      "version": "9.14.5",
+      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
+      "integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
     "node_modules/vue-router": {
       "version": "4.4.4",
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.4.tgz",

+ 2 - 1
package.json

@@ -16,7 +16,8 @@
     "@dcloudio/uni-h5": "^3.0.0-alpha-4020220360004",
     "@dcloudio/uni-mp-weixin": "^3.0.0-alpha-4020220360004",
     "pinia": "^2.3.1",
-    "vue": "^3.4.0"
+    "vue": "^3.4.0",
+    "vue-i18n": "^9.14.5"
   },
   "devDependencies": {
     "@dcloudio/types": "^3.4.0",

+ 50 - 10
pages/index/index.vue

@@ -1,36 +1,58 @@
 <template>
   <view class="container">
     <view class="header">
-      <text class="title">智能eTMF系统</text>
-      <text class="subtitle">欢迎使用</text>
+      <view class="language-switch" @click="handleLanguageSwitch">
+        <text class="language-text">{{ currentLocaleName }}</text>
+      </view>
+      <text class="title">{{ t('home.title') }}</text>
+      <text class="subtitle">{{ t('home.subtitle') }}</text>
     </view>
     
     <view class="content">
       <view class="card">
-        <text class="card-title">系统信息</text>
+        <text class="card-title">{{ t('home.systemInfo') }}</text>
         <view class="info-item">
-          <text class="label">框架版本:</text>
-          <text class="value">Vue 3 + uni-app</text>
+          <text class="label">{{ t('home.framework') }}:</text>
+          <text class="value">{{ t('home.frameworkValue') }}</text>
         </view>
         <view class="info-item">
-          <text class="label">平台:</text>
-          <text class="value">微信小程序</text>
+          <text class="label">{{ t('home.platform') }}:</text>
+          <text class="value">{{ t('home.platformValue') }}</text>
         </view>
       </view>
       
       <view class="action-section">
-        <button type="primary" @click="handleClick">开始使用</button>
+        <button type="primary" @click="handleClick">{{ t('home.startButton') }}</button>
       </view>
     </view>
   </view>
 </template>
 
 <script setup>
-import { ref } from 'vue'
+import { computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { useLocaleStore } from '@/store/locale'
+
+const { t } = useI18n()
+const localeStore = useLocaleStore()
+
+// 获取当前语言名称
+const currentLocaleName = computed(() => localeStore.getCurrentLocaleName())
+
+// 切换语言
+const handleLanguageSwitch = () => {
+  const success = localeStore.toggleLocale()
+  if (success) {
+    uni.showToast({
+      title: t('common.language.switchSuccess'),
+      icon: 'success'
+    })
+  }
+}
 
 const handleClick = () => {
   uni.showToast({
-    title: '欢迎使用智能eTMF系统',
+    title: t('home.welcomeMessage'),
     icon: 'success'
   })
 }
@@ -46,6 +68,24 @@ const handleClick = () => {
 .header {
   text-align: center;
   padding: 60rpx 0;
+  position: relative;
+  
+  .language-switch {
+    position: absolute;
+    top: 20rpx;
+    right: 20rpx;
+    background: rgba(255, 255, 255, 0.2);
+    padding: 12rpx 24rpx;
+    border-radius: 30rpx;
+    backdrop-filter: blur(10rpx);
+    border: 1rpx solid rgba(255, 255, 255, 0.3);
+    
+    .language-text {
+      font-size: 24rpx;
+      color: #ffffff;
+      font-weight: 500;
+    }
+  }
   
   .title {
     display: block;

+ 70 - 0
store/locale.js

@@ -0,0 +1,70 @@
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { defaultLocale, localeList } from '../locales'
+import i18n from '../i18n'
+
+export const useLocaleStore = defineStore('locale', () => {
+  // 当前语言
+  const currentLocale = ref(defaultLocale)
+  
+  // 可用语言列表
+  const availableLocales = ref(localeList)
+  
+  // 初始化语言设置
+  const initLocale = () => {
+    try {
+      const storedLocale = uni.getStorageSync('locale')
+      if (storedLocale) {
+        currentLocale.value = storedLocale
+        i18n.global.locale.value = storedLocale
+      }
+    } catch (e) {
+      console.error('Failed to load locale:', e)
+    }
+  }
+  
+  // 切换语言
+  const setLocale = (locale) => {
+    if (!locale || !localeList.find(item => item.value === locale)) {
+      console.error('Invalid locale:', locale)
+      return false
+    }
+    
+    try {
+      // 更新 i18n 语言
+      i18n.global.locale.value = locale
+      // 更新 store 状态
+      currentLocale.value = locale
+      // 持久化到本地存储
+      uni.setStorageSync('locale', locale)
+      
+      return true
+    } catch (e) {
+      console.error('Failed to set locale:', e)
+      return false
+    }
+  }
+  
+  // 切换到下一个语言
+  const toggleLocale = () => {
+    const currentIndex = localeList.findIndex(item => item.value === currentLocale.value)
+    const nextIndex = (currentIndex + 1) % localeList.length
+    const nextLocale = localeList[nextIndex].value
+    return setLocale(nextLocale)
+  }
+  
+  // 获取当前语言的显示名称
+  const getCurrentLocaleName = () => {
+    const locale = localeList.find(item => item.value === currentLocale.value)
+    return locale ? locale.label : ''
+  }
+  
+  return {
+    currentLocale,
+    availableLocales,
+    initLocale,
+    setLocale,
+    toggleLocale,
+    getCurrentLocaleName
+  }
+})

+ 41 - 0
utils/i18n.js

@@ -0,0 +1,41 @@
+import i18n from '@/i18n'
+
+/**
+ * 在非组件环境中使用的翻译函数
+ * @param {string} key - 翻译键
+ * @param {object} params - 参数
+ * @returns {string} 翻译后的文本
+ */
+export function t(key, params) {
+  return i18n.global.t(key, params)
+}
+
+/**
+ * 获取当前语言
+ * @returns {string} 当前语言代码
+ */
+export function getLocale() {
+  return i18n.global.locale.value
+}
+
+/**
+ * 设置语言
+ * @param {string} locale - 语言代码
+ */
+export function setLocale(locale) {
+  i18n.global.locale.value = locale
+  try {
+    uni.setStorageSync('locale', locale)
+  } catch (e) {
+    console.error('Failed to save locale:', e)
+  }
+}
+
+/**
+ * 检查是否存在翻译键
+ * @param {string} key - 翻译键
+ * @returns {boolean} 是否存在
+ */
+export function hasKey(key) {
+  return i18n.global.te(key)
+}

+ 1 - 1
utils/request.js

@@ -6,7 +6,7 @@
 const CLIENT_ID = '2f847927afb2b3ebeefc870c13d623f2'
 
 // 基础 URL(根据实际情况修改)
-const BASE_URL = ''
+const BASE_URL = 'http://127.0.0.1:8080'
 
 /**
  * 封装的请求方法