Vue3 如何接入 i18n 实现国际化多语言

vue3,i18n · 浏览次数 : 10

小编点评

本文介绍了如何在 Vue.js 3 项目中实现网页的国际化多语言,主要使用了 vue-i18n 和 vue-i18n-routing 插件。首先,通过安装 vue-i18n 和 vue-i18n-routing 并配置 vite.config.js 文件,实现多语言支持。接着,定义了路由和导航栏切换按钮,以及实现自动切换语言的功能。最后,提供了 SEO 和 head Meta 的设置方法。 1. 安装 vue-i18n 和 vue-i18n-routing: 使用 unplugin-vue-i18n 插件,并调整 vite.config.js 配置文件。 2. 定义路由和导航栏切换按钮: 在 router/index.ts 中定义支持的语言种类,并使用 vue-i18n-routing 自动生成所有支持语言的 routes。在导航栏上增加英语和中文切换按钮,使用 store 进行语言设置。 3. 实现自动切换语言功能: 在 store/lang.ts 中定义获取用户偏好的浏览器语言的方法,以及设置当前语言的方法。在 router.beforeEach 中使用 store 中的方法判断当前路径是否已经存在对应语言版本,若不存在,则自动跳转到偏好语言路径。 4. 设置 SEO 和 head Meta: 在 App.vue 中使用 useLocaleHead 和 useHead 函数,设置多语言支持的 SEO 信息和 head Meta。 通过以上步骤,可以实现一个基本的国际化多语言网站。同时,后端也需要根据需求进行相应的翻译工作。

正文

1. 基本方法

在 Vue.js 3 中实现网页的国际化多语言,最常用的包是 vue-i18n,通常我们会与 vue-i18n-routing 一起使用。

vue-i18n 负责根据当前页面的语言渲染文本占位符,例如:

<span>{{ t('Login') }}</span>

当语言设置为中文时,会将 Login 渲染为“登录”。

vue-i18n-routing 负责将页面语言与 URL 绑定,例如:

https://githubstar.pro/zh-CN/repo

表示访问中文版的 /repo 路径。

将不同语言的网页放在不同的 URL 下有助于 SEO,因为可以在 <head> 部分添加语言信息,增加不同语言被搜索引擎索引的概率。

Google 对于多语言 Vue 站点的爬取机制如下:

  1. 类似 Vue 站点的 JS 动态页面是可以被爬取的,不影响权重 (参见 Google SEO)。
  2. 与用户首选语言匹配的页面将优先展示 (参见 Google SEO)。

2. 基础实现

第一步,安装一个 Vite 下使用 <i18n> 标签的插件:unplugin-vue-i18n

然后调整 vite.config.js

import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import VueDevTools from 'vite-plugin-vue-devtools';
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';

export default defineConfig({
  plugins: [
    vue(),
    VueDevTools(),
    VueI18nPlugin({}),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
});

添加插件后,我们可以在组件内使用 <i18n> 块:

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

const { t, locale } = useI18n({ inheritLocale: true, useScope: 'local' });
</script>

<template>
  <span>{{ t('Login') }}</span>
</template>

<i18n lang="yaml">
en:
  Login: 'Login to web'
zh-CN:
  Login: '登录'
</i18n>

这里我们定义了两种不同的语言。

3. 路径绑定

接下来,我们需要定义使用 URL 作为当前语言,编辑 router/index.ts

import { createRouter as _createRouter, type RouteLocationNormalized } from 'vue-i18n-routing';
import { createWebHistory } from 'vue-router';
import HomeView from '@/views/HomeView.vue';

const locales = [
  {
    code: 'en',
    iso: 'en-US',
    name: 'English',
  },
  {
    code: 'zh-CN',
    iso: 'zh-CN',
    name: '中文',
  },
];

export function createRouter(i18n: any) {
  const router = _createRouter(i18n, {
    version: 4,
    locales: locales,
    defaultLocale: 'zh-CN',
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
      {
        path: '/home',
        name: 'home',
        component: HomeView,
      },
    ],
  });
  return router;
}

我们定义了支持的语言种类,并将原来的 routes 包装起来,vue-i18n-routing 会自动生成所有支持语言的 routes

  • /home = 中文
  • /en/home = 英文

由于我们设置了 defaultLocale: 'zh-CN',默认路径为中文。

然后,我们需要将源代码中涉及跳转的部分,例如:

router.push({ name: 'home' });

全部加上 localePath,表示是当前语言的 URL 路径下:

import { useLocalePath } from 'vue-i18n-routing';

const localePath = useLocalePath();

router.push(localePath({ name: 'home' }));

这样就完成了路径绑定。

4. 自动切换

有时,我们希望没有默认语言,而是根据用户的浏览器语言自动选择:

  • /zh-CN/home = 中文
  • /en/home = 英文
  • /home -> 重定向 (浏览器偏好中文) -> /zh-CN/home = 中文
  • /home -> 重定向 (浏览器偏好英文) -> /en/home = 英文

这时我们需要定义一个 store,这里使用 Pinia store,Vuex 同理。

import { usePreferredLanguages, useStorage } from '@vueuse/core';
import { defineStore } from 'pinia';

export const useLangStore = defineStore('lang', {
  state: () => {
    const savedLang = useStorage<string | null>('lang', null, undefined);
    const systemLang = usePreferredLanguages();
    return { savedLang, systemLang };
  },
  getters: {
    lang: (state) => {
      const lang = state.savedLang || state.systemLang[0];
      if (lang.startsWith('zh')) {
        return 'zh-CN';
      } else {
        return 'en';
      }
    },
  },
  actions: {
    setLang(l?: string) {
      if (!l) {
        this.savedLang = null;
      } else {
        this.savedLang = l;
      }
    },
  }
});

这段代码使用了 VueUse 中的 usePreferredLanguages 来获得用户偏好的浏览器语言,并用 useStorage 添加了一个 LocalStorage 中的存储项。

逻辑是:如果用户手动设定了语言(savedLang),则使用之;如果没有,则使用系统偏好的第一个语言。这样,我们只要取 lang 的值就可以得到最终的偏好语言是中文还是英文。

然后,我们需要定义一个路径守卫,以自动处理 URL 中没有语言的情况。

import { createRouter as _createRouter, type RouteLocationNormalized } from 'vue-i18n-routing';
import { createWebHistory } from 'vue-router';
import HomeView from '@/views/HomeView.vue';

const locales = [
  {
    code: 'en',
    iso: 'en-US',
    name: 'English',
  },
  {
    code: 'zh-CN',
    iso: 'zh-CN',
    name: '中文',
  },
  {
    code: '',
    iso: '',
    name: '',
  }
];

export function createRouter(i18n: any) {
  const router = _createRouter(i18n, {
    version: 4,
    locales: locales,
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
      {
        path: '/home',
        name: 'home',
        component: HomeView,
      },
    ],
  });
  router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized) => {
    const lang = useLangStore();
    const pathLocale = to.path.split('/')[1];
    if ((!pathLocale) || (!locales.some(locale => locale.code === pathLocale))) {
      return `/${lang.lang}${to.path}`;
    }
  });
  return router;
}

这里需要注意三点:

  1. 我们增加了一个新的空 locales,这样请求才能到达 router.beforeEach
  2. 我们去掉了 defaultLocale
  3. 使用刚才定义的 store:useLangStore() 这行代码必须放在 router.beforeEach 中,而不能放在模块顶端,因为加载模块时 Pinia 还没有启动。

这样,就实现了无语言路径自动跳转到当前偏好语言路径。

5. 导航栏切换按钮

然后,可以在导航栏增加一个按钮,来手动切换语言,例如:

<script setup lang="ts">
import { useLocalePath, useSwitchLocalePath } from 'vue-i18n-routing';
import { useLangStore } from '@/stores/lang';

const lang = useLangStore();
const { t, locale } = useI18n({ inheritLocale: true, useScope: 'local' });
</script>

<template>
<div
  @click="
    lang.setLang('en');
    router.push(switchLocalePath('en'));
    menuShown = '';
  "
  class="py-2 px-2 gap-2 flex items-center cursor-pointer hover:bg-slate-400/10"
  :class="{ 'text-sky-300': locale == 'en' }"
  role="option"
  tabindex="-1"
  :aria-selected="locale == 'en'"
>
  <IconEnglish class="w-5 h-5 text-slate-400 dark:text-slate-200" />
  English
</div>
<div
  @click="
    lang.setLang('zh-CN');
    router.push(switchLocalePath('zh-CN'));
    menuShown = '';
  "
  class="py-2 px-2 gap-2 flex items-center cursor-pointer hover:bg-slate-400/10"
  :class="{ 'text-sky-300': locale == 'zh-CN' }"
  role="option"
  tabindex="-1"
  :aria-selected="locale == 'zh-CN'"
>
  <IconChinese class="w-5 h-5 text-slate-400 dark:text-slate-200" />
  中文
</div>
</template>

这里,我们在刚才定义的 store 中存储当前手动设定的语言,同时使用 switchLocalePath 来实现路径和语言的切换。

6. SEO 和 Head Meta

同一内容的不同语言版本应该在 head 中进行标注,并指向所有其他替代页面(参见 Google SEO)。这里我们可以在 App.vue 中用 useLocaleHead 和来自 @unhead/vue 包的 useHead 进行设置:

import { useLocaleHead } from 'vue-i18n-routing';
import { useHead } from '@unhead/vue';

const i18nHead = useLocaleHead({ addSeoAttributes: true, defaultLocale: null, strategy: null });

onMounted(() => {
  useHead({
    htmlAttrs: computed(() => ({
      lang: i18nHead.value.htmlAttrs!.lang,
    })),
    link: computed(() => [...(i18nHead.value.link || [])]),
    meta: computed(() => [...(i18nHead.value.meta || [])]),
  });
});

这样就基本实现了一个多语言的国际化站点。可能在进行前端翻译的同时,后端也需要进行翻译,请期待下一期:Python Flask 后端如何接入 i18n 实现国际化多语言!

6. 案例分析

案例:GithubStar.Pro 的前端界面国际化多语言,是使用本文所述的方法实现的,各位可以看看效果。

也欢迎各位使用 GithubStar.Pro 互赞平台,提高您的开源项目知名度,收获更多用户。

与Vue3 如何接入 i18n 实现国际化多语言相似的内容:

Vue3 如何接入 i18n 实现国际化多语言

如何在现有 Vue 3.0 + Vite 项目中,引入 i18n 实现国际化多语言,可以手动切换,SEO友好,且完整可用的解决方案。

Flask API 如何接入 i18n 实现国际化多语言

​ 1. 介绍 上一篇文章分享了 Vue3 如何如何接入 i18n 实现国际化多语言,这里继续和大家分享 Flask 后端如何接入 i18n 实现国际化多语言。 用户请求 API 的多语言化其实有两种解决方案: 后端返回:"USER_ERROR" => 前端渲染:"用户错误" 后端接收请求中 "Ac

掉了两根头发后,我悟了!vue3的scoped原来是这样避免样式污染(下)

上篇文章中我们讲了使用scoped后,vue是如何给CSS选择器添加对应的属性选择器[data-v-x]。这篇文章我们来接着讲vue是如何给html增加自定义属性data-v-x

掉了两根头发后,我悟了!vue3的scoped原来是这样避免样式污染(下)

上篇文章中我们讲了使用scoped后,vue是如何给CSS选择器添加对应的属性选择器[data-v-x]。这篇文章我们来接着讲vue是如何给html增加自定义属性data-v-x

终于搞懂了!原来 Vue 3 的 generate 是这样生成 render 函数的

前言 在之前的 面试官:来说说vue3是怎么处理内置的v-for、v-model等指令? 文章中讲了transform阶段处理完v-for、v-model等指令后,会生成一棵javascript AST抽象语法树。这篇文章我们来接着讲generate阶段是如何根据这棵javascript AST抽象

在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载对象接口

在基于vue-next-admin 的 Vue3+TypeScript 前端项目中,可以整合自己的 .NET 后端,前端操作一些功能的时候,为了使用方便全局挂载的对象接口,以便能够快速处理一些特殊的操作,如消息提示、辅助函数、正则测试等等。本篇随笔介绍在Vue3+TypeScript 前端项目中全局挂载对象$u,获得相关 $u_interface 的统一入口的接口信息。这样在组件或者页面中就可以方

教你如何用Vue3搭配Spring Framework

摘要:在本文中,我们将介绍如何使用Vue3和Spring Framework进行开发,并创建一个简单的TodoList应用程序。 本文分享自华为云社区《Vue3搭配Spring Framework开发【Vue3应用程序实战】》,作者:黎燃。 一、介绍 Vue3和Spring Framework都是现

新知识get,vue3是如何实现在style中使用响应式变量?

前言 vue2的时候想必大家有遇到需要在style模块中访问script模块中的响应式变量,为此我们不得不使用css变量去实现。现在vue3已经内置了这个功能啦,可以在style中使用v-bind指令绑定script模块中的响应式变量,这篇文章我们来讲讲vue是如何实现在style中使用script

「AntV」X6 自定义vue节点(vue3)

官方文档 本篇文档只讲解vue3中如何使用,vue2的可以参考下官方文档 安装插件 @antv/x6-vue-shape 添加vue组件 既然使用vue节点,那么我们就需要准备一个vue的组件,这个组件就是节点的一些样式,根据你们的ui自行写代码即可 节点名称

彻底搞清楚vue3的defineExpose宏函数是如何暴露方法给父组件使用

前言 众所周知,当子组件使用setup后,父组件就不能像vue2那样直接就可以访问子组件内的属性和方法。这个时候就需要在子组件内使用defineExpose宏函数来指定想要暴露出去的属性和方法。这篇文章来讲讲defineExpose宏函数是如何暴露出去这些属性和方法给父组件使用。注:本文中使用的vu