HOW - 前端国际化之多语言通用方案

时间:2024-10-04 14:05:26

目录

  • 一、国际化
  • 二、多语言支持
    • 1. i18n 库或插件
    • 2. 存在的问题
      • 2.1 全量语言包影响打包体积
      • 2.2 语言切换的流畅性
      • 2.3 SEO 问题
  • 三、多语言通用方案:不再需要硬编码、重新打包和部署才生效
    • 1. 独立的文案文件
    • 2. 语言包管理平台
    • 3. 版本管理
      • 3.1 问题
      • 3.2 具体流程设计
  • 四、通用翻译服务

一、国际化

前端项目开发中,避免不了要做国际化。

当我们谈论前端国际化时,我们指的是一种将网站或应用程序设计成可以适应不同地区不同语言和文化习惯的过程。前端国际化的目标是确保用户体验在不同语言和文化环境下都能够尽可能一致和友好。

前端国际化涉及以下几个方面:

  1. 多语言支持:前端国际化需要支持多种语言,使得用户可以在他们熟悉的语言环境下使用应用程序。这意味着所有的文本内容,包括按钮、标签、错误信息等,都应该可以根据用户的语言偏好进行翻译和显示。

  2. 本地化格式:不同国家和地区有不同的日期时间货币和数字格式。前端国际化需要考虑这些差异,并确保应用程序能够根据用户的地区设置正确地显示日期、时间和货币等信息。

  3. 文化习惯:不同文化背景下,用户的习惯和偏好也会有所不同。例如,阅读习惯颜色偏好布局等都可能因文化差异而变化。因此,在前端国际化中,需要考虑这些因素,并确保应用程序的设计和内容能够符合不同文化用户的需求。

  4. 图形和图标:某些图形和图标可能在不同文化中有不同的含义或象征,因此在前端国际化中需要考虑这些因素,并确保图形和图标的使用不会造成误解或混淆。

  5. 多语言文档和帮助:除了应用程序本身的界面外,前端国际化还需要考虑文档和帮助文档的多语言支持,以便用户可以在他们熟悉的语言中获取相关信息。

总的来说,前端国际化旨在使应用程序能够适应不同地区和语言环境,提供一致的用户体验,并尊重不同文化背景下用户的习惯和偏好。

二、多语言支持

今天这篇文章的主题着重于多语言支持。

注意,下面的阐述都是基于不同语言之间共用一个页面的场景下,即关注语言切换,而不是不同语言有不同的 url。

1. i18n 库或插件

目前开发者想要在项目里引入多语言支持,一般会采用 i18n 库或插件来管理多语言。以 vue3 和 vite 项目为例:

  1. 安装 Vue I18n
npm install vue-i18n@next
  • 1
  1. 创建语言文件

在项目中创建一个文件夹来存放多语言相关的配置文件,比如 locales,然后在该文件夹下创建每种语言的 JSON 文件,例如 , 等,用来存放对应语言的键值对。每个 JSON 文件中的键表示要翻译的文本,值表示对应语言的翻译文本。

示例

{
  "hello": "Hello",
  "welcome": "Welcome to our website!"
}
  • 1
  • 2
  • 3
  • 4

示例

{
  "hello": "你好",
  "welcome": "欢迎来到我们的网站!"
}
  • 1
  • 2
  • 3
  • 4
  1. 创建 Vue I18n 实例

Vue 3 项目中,可以在 (或者是入口文件)中创建 Vue I18n 实例,并将其注入到 Vue 应用中。

import { createApp } from 'vue';
import App from './';
import { createI18n } from 'vue-i18n';
const i18n = createI18n({
  legacy: false, // 使用 Composition API
  locale: 'en', // 默认语言
  messages: {
    en: require('./locales/'), // 英文
    zh: require('./locales/'), // 中文
    // 其他语言
  },
});
createApp(App).use(i18n).mount('#app');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. 在组件中使用翻译

在需要翻译的组件中使用 $t 函数来获取对应的翻译文本。

<div>
  <p>{{ $t('hello') }}</p>
  <p>{{ $t('welcome') }}</p>
</div>
  • 1
  • 2
  • 3
  • 4

这样,在不同的语言环境下,Vue I18n 会根据当前语言加载对应的翻译文本,从而实现多语言支持。

在实际应用场景,我们一般会提供给用户一个语言选择 select 功能,开发者可以通过监听 select 元素的变化来动态改变 Vue I18n 实例中的 locale,从而实现语言切换功能。示例如下:

<select v-model="selectedLanguage" @change="changeLanguage">
  <option value="en">English</option>
  <option value="zh">中文</option>
</select>
  • 1
  • 2
  • 3
  • 4
// 使用 Composition API
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
export default {
  setup() {
    const { t, locale } = useI18n();
    const selectedLanguage = ref(locale.value);
    const changeLanguage = () => {
      // 修改 Vue I18n 实例的 locale
      locale.value = selectedLanguage.value;
    };
    return { selectedLanguage, changeLanguage };
  },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这样,用户就可以通过 select 元素来切换系统语言了。

当你在 Vue 应用中切换 locale 时,Vue I18n 会在后台自动完成所有的翻译处理。Vue 组件会监听 locale 的变化,并且在 locale 变化时重新渲染涉及到翻译的部分。

2. 存在的问题

尽管上述方式在简单的场景下能够实现语言切换功能,但在实际应用中可能存在一些不便之处:

2.1 全量语言包影响打包体积

使用上述方式切换语言时,通常会将所有语言包与其他代码都打包在一起,这可能会影响用户体验,尤其是对于大型单页应用而言。项目打包体检变大,可能会影响页面初始加载速度,尤其在移动设备或网络连接较慢的情况下。

对于该问题,我们可以采取按需加载的优化措施来改善用户体验:按需异步加载语言文件

下面是一个示例:

<div>
  <select v-model="selectedLanguage" @change="changeLanguage">
    <option value="en">English</option>
    <option value="zh">中文</option>
  </select>
  <p>{{ $t('hello') }}</p>
  <p>{{ $t('welcome') }}</p>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
export default {
  setup() {
    const { t, locale, setLocaleMessage } = useI18n();
    const selectedLanguage = ref(locale.value);
    const changeLanguage = async () => {
      // 异步加载对应语言的语言文件 可以替换成 https cdn 文件
      const langFile = await import(`./locales/${selectedLanguage.value}.json`);
      // 更新语言文件
      setLocaleMessage(selectedLanguage.value, langFile.default);
      // 更新 Vue I18n 实例的 locale
      locale.value = selectedLanguage.value;
    };
    return { selectedLanguage, changeLanguage };
  },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这个示例中,我们使用了异步加载语言文件的方式来优化语言切换体验。当用户切换语言时,我们首先异步加载对应语言的语言文件,然后使用 setLocaleMessage 方法更新 Vue I18n 实例中的语言文件,最后再更新 locale 属性来实现语言切换。

在实际应用中,我们也非常推荐用这种方式来加载语言包,并且可以利用 cdn预加载缓存机制来优化语言包的请求加载。

2.2 语言切换的流畅性

对于页面中文案内容较多的场景下,切换语言可能会导致页面上大量文本的重新渲染,这可能会造成页面内容的闪烁或者短暂的空白,给用户带来视觉上的不适。

对于这种问题,在语言切换时开发者可以添加过渡动画,使页面内容的变化更加平滑和自然。 提供了过渡动画的支持,你可以使用 <transition> 组件来包裹需要过渡的元素,并添加合适的 CSS 过渡效果,以实现页面内容的平滑切换。

<div>
  <select v-model="selectedLanguage" @change="changeLanguage">
    <option value="en">English</option>
    <option value="zh">中文</option>
  </select>
  <transition name="fade">
    <div v-if="isLoaded" key="content">
      <p>{{ $t('hello') }}</p>
      <p>{{ $t('welcome') }}</p>
    </div>
  </transition>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
const isLoaded = ref(false);
const changeLanguage = async () => {
  const langFile = await import(`./locales/${selectedLanguage.value}.json`);
  setLocaleMessage(selectedLanguage.value, langFile.default);
  locale.value = selectedLanguage.value;
  // 设置为加载完毕,触发过渡动画
  isLoaded.value = true;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在 changeLanguage 方法中,我们在语言切换完成后将 isLoaded 的值设置为 true,从而触发过渡动画。这样,用户在切换语言时就会看到内容平滑地淡入淡出,提升了页面切换的视觉体验。

2.3 SEO 问题

在之前一篇文章里我们也详细介绍了 SEO(搜索引擎优化)

因为搜索引擎可能无法正确解析和索引使用 JavaScript 动态生成的内容。

对于我们的网站,当然是希望不同语言在不同地区或国家都能得到很好的 seo 支持!

其实对于想要支持 seo 的网站,我们应该:

  • 支持服务端渲染 SSR
  • 为不同语言的 html 页面支持独立的 url

接着,从如下几个方面进行优化:

  • 语言和地区标记(Hreflang 标签):使用 <link rel="alternate" hreflang="x" href="url"> 标签来指定不同语言或地区版本的网页。这有助于搜索引擎正确地将用户重定向到其偏好的语言版本的页面。
  • URL 结构:使用清晰的 URL 结构来表示不同语言版本的页面。例如,可以在 URL 中包含语言或地区代码,以便搜索引擎和用户可以轻松地识别不同版本的页面。
  • HTML lang 属性:在每个页面的 HTML 标签中使用 lang 属性来指定页面的语言。这可以帮助搜索引擎正确地识别页面的语言,并将其与用户的语言偏好匹配。
  • 站点地图和索引:为每个语言版本的页面创建独立的 XML 站点地图,并将其提交给搜索引擎。这有助于搜索引擎更好地理解和索引网站的多语言内容。

三、多语言通用方案:不再需要硬编码、重新打包和部署才生效

1. 独立的文案文件

对于支持多语言的项目,当文案发生变更时,通常需要重新打包并发布新版本。这是因为文案通常是直接嵌入到前端代码中,而不是从外部文件动态加载。因此,即使只有文案发生变化,也需要重新打包整个应用程序才能更新文案。

然而,为了减少因文案变更而导致的频繁发布,你可以考虑使用独立的文案文件:将文案内容单独存放在外部文件中,而不是直接嵌入到代码中。前面我们也介绍过按需异步加载语言包的方案。这样,在文案发生变化时,你只需更新外部文件,而不需要重新打包整个应用程序。

通常情况下,当采用独立的文案文件时,每个语言会有一个独立的 JSON 文件,其中包含该语言的所有文案。这样做的好处是维护和管理起来更加方便,而且每个语言版本的文案都可以独立地进行修改和更新。

一个典型的 JSON 文件格式可能如下所示:

// 
{
  "hello": "Hello",
  "welcome": "Welcome to our website",
  "button": {
    "submit": "Submit",
    "cancel": "Cancel"
  }
}

// 
{
  "hello": "你好",
  "welcome": "欢迎访问我们的网站",
  "button": {
    "submit": "提交",
    "cancel": "取消"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这个例子中,有两个 JSON 文件,分别代表英文()和中文()版本的文案。每个 JSON 文件中包含了对应语言的文案内容,通过键值对的方式进行存储。对于需要嵌套的文案,可以使用对象的形式进行表示,如上例中的 “button”。

下面都会基于多语言多个 json 的形式来设计。

2. 语言包管理平台

当使用独立的文案文件,我们就需要构建一个通用的语言包维护和管理机制。

可以通过语言包管理平台来支持:

  1. 支持不同项目注册和管理
  2. 支持项目创建自定义的语言包在线编辑模块。即选择需要绑定的项目、选择需要支持的多种语言、选择默认语言、以 table 形式展示当前 messages
  3. 语言包在线编辑模块。应该支持如下功能:新增 message、删除 message、编辑 message、发布
  4. 支持 json 语言包导入。对于旧项目,可以通过导入已有语言包来切换成在线维护模式
  5. 支持不同环境语言配置同步。即支持开发环境配置好的语言包同步到正式环境

3. 版本管理

3.1 问题

在前面我们也提到过,在实际应用中,我们也非常推荐用这种按需异步加载方式来加载语言包,并且可以利用 cdn预加载缓存机制来优化语言包的请求加载。

通过语言包管理平台维护的语言包,在发布之后,会根据项目和不同语言生成独立的文案文件,并发布到 cdn 上。

那就有如下问题:

  1. 项目不同环境(如 dev、test、stg、prod 环境)如何绑定不同版本的语言包
  2. 项目不同版本如何绑定正确的语言包版本
  3. 本地缓存的语言包版本与前端项目需要的版本不一致怎么处理

因此,语言包管理平台还应该支持:

  1. 语言包版本管理
  2. 项目不同环境与语言包绑定管理
  3. 项目不同版本与语言包绑定管理

3.2 具体流程设计

在语言包管理平台中:

  1. 项目 messages 都翻译完成后或者更新后
  2. 点击发布,不同语言会分别上传 cdn 生成对应版本语言包 url,并用一个语言包版本id维护
  3. 可在语言包版本管理里查看不同版本语言包以及对应的多个语言的 cdn url
  4. 根据需要,将不同项目环境或项目版本绑定对应版本语言包(通过项目id、项目env和语言包版本id)

在浏览器中:

  1. 前端项目初始化
  2. 通过接口获取当前环境和版本(一般默认取最新)对应的最新语言包版本配置信息
  3. 将最新语言包配置信息缓存到本地
  4. 正常切换语言,获取所需语言包 cdn url,并缓存不同语言包文件到本地(注意是协商缓存),供下次获取
  5. 当此时某个语言包发生变更,有两种情况需要考虑,用户刷新页面的话,比较简单,将重新执行第 1 步;假如是用户进行了语言切换,其实会加载对应的资源,基于协商缓存机制,后台将通知前端应该加载最新的语言包文件
  6. 当前端发生了迭代,即版本变更,假如用户不刷新页面,那访问的依然是旧版的前端代码和语言包数据;当用户刷新页面,将请求到最新版本对应的语言包配置,将以新配置进行数据获取

至此,我们就可以愉快地进行前端版本变更、语言包变更而用户无感了。

四、通用翻译服务

在之前的多语言开发模式下,基本流程如下:

  1. 设计师和产品确认 ux,包括布局、交互和文案
  2. 产品与翻译沟通,根据 ux 每个位置提供文案翻译
  3. 开发基于 ux 和文案翻译,维护和使用语言包

有没有一种方案,可以减少第二步的工作量,并且让设计师、产品、翻译和开发都能同步看到目前的语言包状态,进行高效协同。

接下来我们设计一个通用翻译服务。主要功能如下:

  1. 固定文案主动翻译。调用接口获取文案译文,并且支持批量翻译
  2. 支持同步翻译和异步翻译。对于同步翻译,调用接口实时返回译文结果;对于异步翻译,调用接口支持可选实时返回还是落地存储,如果是后者,可以通过 guid 来获取对应译文
  3. 支持自定义固定术语(如:“ceo” => "tony"
  4. 支持自动识别源语种

除此之外,不仅为了服务于多语言场景,最好也可以支持图片文字识别翻译和常用文件格式(word、pdf等)翻译场景。

通用翻译服务,因为存在落地存储的数据,可作为语言包管理平台的一个子模块进一步管理历史翻译记录。其支持:

  1. 历史翻译记录查看
  2. 历史翻译详情查看、编辑、删除。支持后续请求使用