国际化 (i18n) + vscode( i18n Ally) 使用指南

22 阅读2分钟

国际化 (i18n) 使用指南

概述

本项目使用 Vue i18n 进行国际化处理,配合 i18n ally 插件提供完整的开发体验。支持中文(zh_CN)和英文(en_US)两种语言。

目录结构

src/locales/
├── lang/                    # 语言文件目录
│   ├── zh_CN/              # 中文语言包
│   │   ├── index.ts        # 主入口文件
│   │   ├── components.ts   # 组件相关翻译
│   │   ├── layout.ts       # 布局相关翻译
│   │   └── pages/          # 页面相关翻译
│   │       ├── index.ts    # 页面翻译入口
│   │       ├── login.ts    # 登录页翻译
│   │       ├── dashboard-base.ts
│   │       └── ...
│   └── en_US/              # 英文语言包
│       ├── index.ts
│       ├── components.ts
│       ├── layout.ts
│       └── pages/
├── index.ts                # i18n 配置入口
├── useLocale.ts           # 语言切换 Hook
└── README.md              # 使用说明

基本使用

在 Vue 组件中使用

<template>
  <div>
    <!-- 使用全局 $t 方法 -->
    <h1>{{ $t('pages.login.loginTitle') }}</h1>
    
    <!-- 使用 Composition API -->
    <p>{{ t('layout.header.user') }}</p>
    
    <!-- 条件渲染 -->
    <div v-if="$te('layout.header.help')">
      {{ $t('layout.header.help') }}
    </div>
  </div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n';

// 解构出需要的方法
const { t, te, locale } = useI18n();

// 或者直接导入
import { t } from '@/locales';
</script>

在 TypeScript 中使用

import { t } from '@/locales';

// 基本翻译
const message = t('layout.header.logout');

// 在函数中使用
function showMessage() {
  console.log(t('components.commonTable.contractName'));
}

// 动态翻译
const getStatusText = (status: string) => {
  return t(`components.commonTable.contractStatusEnum.${status}`);
};

语言切换

使用 useLocale Hook

<script setup lang="ts">
import { useLocale } from '@/locales/useLocale';

const { changeLocale, locale } = useLocale();

// 切换到英文
const switchToEnglish = () => {
  changeLocale('en_US');
};

// 切换到中文
const switchToChinese = () => {
  changeLocale('zh_CN');
};
</script>

使用语言选择器

<template>
  <t-select v-model="currentLang" @change="handleLangChange">
    <t-option 
      v-for="lang in langList" 
      :key="lang.value" 
      :value="lang.value"
    >
      {{ lang.content }}
    </t-option>
  </t-select>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { langList } from '@/locales';
import { useLocale } from '@/locales/useLocale';

const { changeLocale, locale } = useLocale();
const currentLang = ref(locale.value);

const handleLangChange = (value: string) => {
  changeLocale(value);
  currentLang.value = value;
};
</script>

添加新的翻译

1. 在语言文件中添加翻译

// src/locales/lang/zh_CN/components.ts
export default {
  button: {
    confirm: '确认',
    cancel: '取消',
    save: '保存',
    delete: '删除',
  },
  message: {
    success: '操作成功',
    error: '操作失败',
    warning: '警告信息',
  },
  form: {
    required: '此字段为必填项',
    invalid: '格式不正确',
  },
};

2. 对应的英文翻译

// src/locales/lang/en_US/components.ts
export default {
  button: {
    confirm: 'Confirm',
    cancel: 'Cancel',
    save: 'Save',
    delete: 'Delete',
  },
  message: {
    success: 'Operation successful',
    error: 'Operation failed',
    warning: 'Warning message',
  },
  form: {
    required: 'This field is required',
    invalid: 'Invalid format',
  },
};

3. 在组件中使用

<template>
  <div>
    <t-button @click="handleSave">
      {{ $t('components.button.save') }}
    </t-button>
    
    <t-button @click="handleDelete" theme="danger">
      {{ $t('components.button.delete') }}
    </t-button>
  </div>
</template>

<script setup lang="ts">
import { MessagePlugin } from 'tdesign-vue-next';
import { t } from '@/locales';

const handleSave = () => {
  // 使用翻译的消息
  MessagePlugin.success(t('components.message.success'));
};

const handleDelete = () => {
  MessagePlugin.error(t('components.message.error'));
};
</script>

i18n ally 插件配置

VSCode 设置

项目已配置 .vscode/settings.json

{
  "i18n-ally.localesPaths": ["src/locales/lang"],
  "i18n-ally.enabledParsers": ["ts"],
  "i18n-ally.enabledFrameworks": ["vue"],
  "i18n-ally.displayLanguage": "zh-CN",
  "i18n-ally.sourceLanguage": "zh-CN",
  "i18n-ally.keystyle": "nested"
}

插件功能

1. 自动提取翻译
  • 选中需要翻译的文本
  • 右键选择 "i18n ally: Extract text"
  • 插件会自动生成 key 并添加到语言文件
2. 内联显示
  • 在代码中显示当前语言的翻译内容
  • 支持快速切换预览语言
  • 鼠标悬停显示翻译详情
3. 翻译管理
  • 侧边栏查看所有翻译 key
  • 快速跳转到翻译定义
  • 检测缺失的翻译
  • 批量编辑翻译
4. 实时验证
  • 检测未使用的翻译 key
  • 检测缺失的翻译
  • 语法错误提示

最佳实践

1. Key 命名规范

// ✅ 推荐:使用嵌套结构,语义化命名
{
  pages: {
    login: {
      title: '登录',
      form: {
        username: '用户名',
        password: '密码',
        submit: '登录'
      }
    }
  }
}

// ❌ 不推荐:扁平结构,难以维护
{
  'login-title': '登录',
  'login-username': '用户名',
  'login-password': '密码'
}

2. 文件组织

  • 按功能模块分组:页面、组件、布局分别管理
  • 保持结构一致:中英文文件结构完全对应
  • 及时同步更新:添加新翻译时同时更新所有语言

3. 翻译内容规范

// ✅ 推荐:简洁明了,符合语言习惯
{
  button: {
    save: '保存',
    cancel: '取消'
  },
  message: {
    saveSuccess: '保存成功'
  }
}

// ❌ 不推荐:过于冗长或不符合习惯
{
  button: {
    save: '点击此按钮进行保存操作',
    cancel: '点击取消按钮'
  }
}

4. 动态翻译

<script setup lang="ts">
import { computed } from 'vue';
import { t } from '@/locales';

// 动态状态翻译
const statusText = computed(() => {
  return t(`components.commonTable.contractStatusEnum.${status.value}`);
});

// 带参数的翻译(如果需要)
const welcomeMessage = computed(() => {
  return t('pages.user.welcome', { name: userName.value });
});
</script>

类型安全

全局类型定义

项目已配置 src/types/vue-i18n.d.ts

import type { VueI18n } from 'vue-i18n';

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    /**
     * 翻译函数
     * @param key 翻译键
     * @param values 插值参数
     */
    $t: (key: string, values?: Record<string, any>) => string;
    
    /**
     * 检查翻译键是否存在
     * @param key 翻译键
     */
    $te: (key: string) => boolean;
    
    /**
     * 复数翻译函数
     * @param key 翻译键
     * @param choice 数量
     * @param values 插值参数
     */
    $tc: (key: string, choice?: number, values?: Record<string, any>) => string;
    
    /**
     * 日期格式化函数
     * @param value 日期值
     * @param key 格式化键
     * @param locale 语言环境
     */
    $d: (value: number | Date, key?: string, locale?: string) => string;
    
    /**
     * 数字格式化函数
     * @param value 数字值
     * @param key 格式化键
     * @param locale 语言环境
     */
    $n: (value: number, key?: string, locale?: string) => string;
    
    /**
     * 当前语言环境
     */
    $i18n: VueI18n;
  }
}

export {};

使用 TypeScript

// 类型安全的翻译函数
const getTranslation = (key: string): string => {
  return t(key);
};

// 枚举翻译
enum ContractStatus {
  FAIL = 'fail',
  AUDIT = 'audit',
  EXECUTING = 'executing'
}

const getStatusText = (status: ContractStatus): string => {
  return t(`components.commonTable.contractStatusEnum.${status}`);
};

常见问题

1. 翻译不生效

问题:添加了翻译但页面不显示

解决方案

  1. 检查 key 是否正确
  2. 确认语言文件已正确导入
  3. 检查浏览器控制台错误
  4. 重启开发服务器

2. i18n ally 无法识别

问题:插件不显示翻译内容

解决方案

  1. 确保安装了 i18n ally 插件
  2. 检查 .vscode/settings.json 配置
  3. 重启 VSCode
  4. 确认文件路径正确

3. i18n ally 加载错误:Cannot find module 'tdesign-vue-next/es/locale/'

问题:i18n ally 插件报错无法找到 TDesign 语言包模块

解决方案

方法一:移除 TDesign 语言包导入(推荐)

项目已经移除了语言文件中的 TDesign 语言包导入,改为动态加载:

// 不再在语言文件中直接导入
// import componentsLocale from 'tdesign-vue-next/es/locale/zh_CN';

// 而是在 useLocale.ts 中动态导入
const getComponentsLocale = computed(() => {
  try {
    if (locale.value === 'zh_CN') {
      return import('tdesign-vue-next/es/locale/zh_CN');
    } else if (locale.value === 'en_US') {
      return import('tdesign-vue-next/es/locale/en_US');
    }
    return {};
  } catch (error) {
    console.warn('Failed to load TDesign locale:', error);
    return {};
  }
});
方法二:配置 i18n ally 忽略特定文件

.vscode/settings.json 中添加:

{
  "i18n-ally.ignoreFiles": [
    "**/node_modules/**",
    "**/dist/**",
    "**/.git/**",
    "**/index.ts"
  ]
}

4. 文件只读模式错误

问题:提示文件处于只读模式,无法编辑

解决方案

方法一:检查文件权限
# 检查文件权限
ls -la src/locales/

# 修改文件权限(如果需要)
chmod 644 src/locales/lang/*/index.ts
方法二:关闭可能占用文件的进程
  1. 关闭所有 VSCode 窗口
  2. 停止开发服务器 (Ctrl+C)
  3. 重启 VSCode
  4. 重新启动开发服务器
方法三:检查 Git 状态
# 检查文件是否被 Git 锁定
git status

# 如果有冲突,解决冲突
git add .
git commit -m "fix: resolve i18n configuration conflicts"
方法四:重启 TypeScript 服务
  1. 在 VSCode 中按 Ctrl+Shift+P(Mac: Cmd+Shift+P
  2. 输入 TypeScript: Restart TS Server
  3. 选择并执行

5. 类型错误:$t$te 等方法不存在

问题:TypeScript 报错 Property '$te' does not exist on type...

解决方案

方法一:确保类型定义文件存在
  1. 确保 src/types/vue-i18n.d.ts 文件存在且内容正确
  2. 重启 TypeScript 服务(VSCode: Ctrl+Shift+PTypeScript: Restart TS Server
  3. 重启开发服务器
方法二:使用 Composition API 替代
<template>
  <div>
    <!-- 使用 Composition API 方法 -->
    <div v-if="te('layout.header.help')">
      {{ t('layout.header.help') }}
    </div>
  </div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n';

// 解构出所有需要的方法
const { t, te, d, n, locale } = useI18n();
</script>
方法三:临时类型断言(不推荐)
<template>
  <div>
    <!-- 临时解决方案,不推荐长期使用 -->
    <div v-if="($te as any)('layout.header.help')">
      {{ $t('layout.header.help') }}
    </div>
  </div>
</template>
方法四:检查 tsconfig.json 配置

确保 tsconfig.json 包含了类型文件:

{
  "include": [
    "src/**/*.d.ts",
    "src/types/**/*.d.ts"
  ]
}

6. 动态导入问题

问题:语言文件动态导入失败

解决方案

  1. 检查文件路径是否正确
  2. 确认 import.meta.glob 语法正确
  3. 检查文件导出格式

7. 全局注入不生效

问题globalInjection: true 设置了但仍然无法使用 $t

解决方案

  1. 确保在 main.ts 中正确安装了 i18n 插件:
import { createApp } from 'vue';
import i18n from './locales';

const app = createApp(App);
app.use(i18n);
  1. 确保 i18n 配置中设置了 globalInjection: true
export const i18n = createI18n({
  legacy: false,
  globalInjection: true, // 确保这个设置为 true
  // ... 其他配置
});

开发工作流

1. 添加新页面翻译

# 1. 创建页面翻译文件
touch src/locales/lang/zh_CN/pages/new-page.ts
touch src/locales/lang/en_US/pages/new-page.ts

# 2. 在 pages/index.ts 中导入
# 3. 在组件中使用

2. 批量翻译管理

使用 i18n ally 插件:

  1. 打开侧边栏的 i18n ally 面板
  2. 查看所有翻译 key
  3. 批量编辑或导出翻译
  4. 检查缺失的翻译

3. 翻译验证

# 检查未使用的翻译 key
# 使用 i18n ally 插件的 "Unused keys" 功能

# 检查缺失的翻译
# 使用 i18n ally 插件的 "Missing keys" 功能

性能优化

1. 懒加载语言包

// 如果需要懒加载(当前项目使用 eager: true)
const langModules = import.meta.glob('./lang/*/index.ts', { 
  eager: false  // 改为懒加载
});

2. 缓存翻译结果

// 在组件中缓存翻译结果
const cachedTranslations = computed(() => ({
  title: t('pages.dashboard.title'),
  subtitle: t('pages.dashboard.subtitle'),
}));

扩展功能

1. 添加新语言

// 1. 创建新语言目录
mkdir src/locales/lang/ja_JP

// 2. 复制现有语言文件结构
// 3. 翻译内容
// 4. 在 langList 中添加新语言选项

2. 日期和数字格式化

<template>
  <!-- 如果需要格式化功能,可以添加配置 -->
  <p>{{ $d(new Date(), 'short') }}</p>
  <p>{{ $n(1234.56, 'currency') }}</p>
</template>

故障排除清单

当遇到国际化相关问题时,请按以下顺序检查:

类型错误排查

  1. 检查文件是否存在src/types/vue-i18n.d.ts
  2. 重启 TypeScript 服务:VSCode 中按 Ctrl+Shift+P,输入 TypeScript: Restart TS Server
  3. 重启开发服务器:停止并重新启动 npm run dev
  4. 检查 tsconfig.json:确保包含了 src/types/**/*.d.ts
  5. 检查 i18n 配置:确保 globalInjection: true
  6. 检查插件安装:确保在 main.ts 中使用了 app.use(i18n)
  7. 使用 Composition API:作为备选方案使用 useI18n() 解构方法

i18n ally 插件问题排查

  1. 检查插件是否安装:确保安装了 i18n ally 插件
  2. 检查 VSCode 设置:确认 .vscode/settings.json 配置正确
  3. 重启 VSCode:完全关闭并重新打开 VSCode
  4. 清除插件缓存:禁用并重新启用 i18n ally 插件
  5. 检查语言文件:确保语言文件没有导入错误
  6. 查看插件日志:打开 VSCode 输出面板,选择 i18n ally 查看错误日志

文件只读问题排查

  1. 检查文件权限ls -la src/locales/
  2. 关闭占用进程:停止开发服务器,关闭 VSCode
  3. 检查 Git 状态git status 查看是否有冲突
  4. 重启系统服务:重启 TypeScript 服务和开发服务器
  5. 修改权限chmod 644 src/locales/lang/*/index.ts

快速修复命令

# 1. 停止开发服务器
# Ctrl+C

# 2. 检查文件权限
ls -la src/locales/lang/

# 3. 修复权限(如果需要)
chmod -R 644 src/locales/lang/

# 4. 检查 Git 状态
git status

# 5. 重启开发服务器
npm run dev

应急解决方案

如果以上方法都无效,可以使用以下应急方案:

  1. 使用 Composition API
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
const { t, te } = useI18n();
</script>
  1. 临时禁用 i18n ally:在 VSCode 扩展中禁用 i18n ally 插件

  2. 使用类型断言

<template>
  <div v-if="($te as any)('key')">{{ ($t as any)('key') }}</div>
</template>

这个文档涵盖了项目中国际化的完整使用方法,包括基本用法、最佳实践、问题排查等,可以作为团队开发的参考指南。