在现代前端开发中,国际化(i18n)是提升用户体验的重要功能。本文将详细介绍如何在 Vue 3 + TypeScript 项目中优雅地封装和使用 Vue-i18n。

1. 安装依赖

首先安装必要的依赖包:

# 安装 vue-i18n 及其类型支持
npm install vue-i18n@next @intlify/vue-i18n-loader -D

2. 类型定义

创建翻译键的类型定义文件:

// types/i18n.d.ts
import { DefineLocaleMessage } from "vue-i18n";

declare module "vue-i18n" {
  export interface DefineLocaleMessage {
    message: {
      hello: string;
      welcome: string;
    };
    navbar: {
      action: {
        locale: string;
      };
    };
    language: {
      "zh-CN": string;
      "en-US": string;
    };
  }
}

3. 语言包配置

创建语言文件:

// src/locales/zh-CN.ts
export default {
  message: {
    hello: "你好",
    welcome: "欢迎",
  },
  navbar: {
    action: {
      locale: "切换语言",
    },
  },
  language: {
    "zh-CN": "简体中文",
    "en-US": "English",
  },
};

// src/locales/en-US.ts
export default {
  message: {
    hello: "Hello",
    welcome: "Welcome",
  },
  navbar: {
    action: {
      locale: "Switch Language",
    },
  },
  language: {
    "zh-CN": "Chinese",
    "en-US": "English",
  },
};

4. i18n 实例配置

// src/i18n/index.ts
import { createI18n, type I18nOptions } from "vue-i18n";
import zh from "../locales/zh-CN";
import en from "../locales/en-US";

type MessageSchema = {
  message: {
    hello: string;
    welcome: string;
  };
  navbar: {
    action: {
      locale: string;
    };
  };
  language: {
    "zh-CN": string;
    "en-US": string;
  };
};

const options: I18nOptions = {
  legacy: false, // 启用组合式API
  locale: localStorage.getItem("locale") || "zh-CN",
  fallbackLocale: "en-US",
  messages: {
    "zh-CN": zh,
    "en-US": en,
  },
};

const i18n = createI18n<[MessageSchema], "zh-CN" | "en-US">(options);

export default i18n;

5. Hook 封装

// src/hooks/useLocale.ts
import { useI18n } from "vue-i18n";
import { computed, ref } from "vue";
import type { Ref } from "vue";

export function useLocale() {
  const { locale, t, availableLocales: locales } = useI18n();
  const loading = ref(false);

  const currentLocale = computed(() => locale.value);

  // 支持的语言列表
  const availableLocales = computed(() => locales);

  // 切换语言
  const changeLocale = async (value: string) => {
    loading.value = true;
    try {
      // 动态导入语言包
      const messages = await import(`../locales/${value}.ts`);
      i18n.global.setLocaleMessage(value, messages.default);

      locale.value = value;
      localStorage.setItem("locale", value);

      // 可选: 修改 HTML lang 属性
      document.querySelector("html")?.setAttribute("lang", value);
    } catch (error) {
      console.error("Failed to load locale:", error);
    } finally {
      loading.value = false;
    }
  };

  return {
    t,
    loading,
    currentLocale,
    availableLocales,
    changeLocale,
  };
}

6. 在 Vue 应用中使用

// src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import i18n from "./i18n";

const app = createApp(App);
app.use(i18n);
app.mount("#app");

7. 组件中的使用示例

<!-- src/components/LanguageSwitcher.vue -->
<script setup lang="ts">
import { useLocale } from "@/hooks/useLocale";

const { t, loading, currentLocale, availableLocales, changeLocale } =
  useLocale();

const switchLang = async () => {
  const nextLocale = currentLocale.value === "zh-CN" ? "en-US" : "zh-CN";
  await changeLocale(nextLocale);
};
</script>

<template>
  <div class="lang-switcher">
    <!-- 按钮切换方式 -->
    <button @click="switchLang" :disabled="loading" class="switch-btn">
      {{ loading ? "切换中..." : t("navbar.action.locale") }}
    </button>

    <!-- 下拉框切换方式 -->
    <select
      v-model="currentLocale"
      @change="changeLocale($event.target.value)"
      :disabled="loading"
      class="lang-select"
    >
      <option v-for="locale in availableLocales" :key="locale" :value="locale">
        {{ t(`language.${locale}`) }}
      </option>
    </select>
  </div>
</template>

<style scoped>
.lang-switcher {
  display: flex;
  gap: 1rem;
}

.switch-btn {
  padding: 0.5rem 1rem;
  border-radius: 4px;
  border: 1px solid #ddd;
  cursor: pointer;
}

.switch-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.lang-select {
  padding: 0.5rem;
  border-radius: 4px;
  border: 1px solid #ddd;
}
</style>

8. Vite 配置优化

// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: [
      {
        find: "vue-i18n",
        replacement: "vue-i18n/dist/vue-i18n.cjs.js",
      },
    ],
  },
});

9. 最佳实践总结

  1. 类型安全:
  • 使用 TypeScript 定义翻译键类型
  • 为 i18n 实例添加泛型约束

2.性能优化:

    • 支持语言包的按需加载
    • 使用 computed 属性优化响应式数据

3.用户体验:

    • 提供加载状态反馈
    • 支持多种切换语言的方式
    • 持久化语言选择

4.可维护性:

    • 模块化的语言包管理
    • 统一的类型定义
    • 清晰的目录结构

5.扩展性:

    • 支持动态添加新的语言包
    • 灵活的 Hook 封装
    • 可复用的组件设计

通过以上配置和实践,我们可以在 Vue 3 + TypeScript 项目中实现一个类型安全、性能优良、用户体验好的国际化解决方案。这种方案既保持了代码的简洁性,又具有良好的可维护性和扩展性

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐