乐趣区

关于javascript:对国际化-i18n-项目的一点思考

国际化是什么?

国际化 对应的英文单词为 Internationalization,又称 i18n

  • i 为单词的【第一个】字母
  • 18 为【in 之间】单词的个数
  • n 代表这个单词的【最初一个】字母

如果你的我的项目是 Vue,那么置信你在实现国际化性能时,也必不可少的会应用到 vue-i18n 这个库,接下来本文也是通过这个库搭配 Vue 实现最根本的国际化性能,但关注点并不是如何应用这个库,而是在实现的过程中思考 可优化的点

实现根本国际化性能

这里就不再多余演示 demo 我的项目的创立过程了,并且文中只演示最根本的 中英文 切换,好了当初直奔外围吧!

集成 vue-i18n

装置依赖

相熟的命令:npm install vue-i18n -S

配置 vue-i18n

  • src 目录下创立 language 目录用于放弃和语言切换相干的内容
  • language 目录下创立 lang 目录用于保留不同语言的映射关系,如中文对应 zh.js、英文对应 en.js
  • language 目录下创立 index.js 作为默认导出,并在其中创立 i18n 对象

     import {createI18n} from "vue-i18n";
      import zh from './lang/zh';
      import en from './lang/en';
      const i18n = createI18n({
        legacy: false,
        locale: "zh", // 初始化配置语言
        messages: {
          zh,
          en,
        },
      });
    
      export default i18n;

    main.js 注册 i18n

    内容非常简单,间接上代码:

    import {createApp} from "vue";
    import i18n from "./language";
    import store from "./store";
    import App from "./App.vue";
    
    createApp(App)
      .use(store)
      .use(i18n)
      .mount("#app");

    实际上在通过 use(i18n) 时,会调用 i18n.install() 办法,大略内容如下:

  • 通过 app.provide(app.__VUE_I18N_SYMBOL__, i18n)i18n 对象提供给利用中的所有后辈组件可通过 inject 注入
  • 通过 app.config.globalProperties.xxx = xxx 的形式为利用增加 全局属性 / 办法,实际上是对 Vue2 中 Vue.prototype 应用形式的一种代替

    • 常见全局属性,如 $i18n 通过 app.config.globalProperties.$i18n = i18n 增加到全局
    • 常见全局办法,如:$t, $rt, $d, $n, $tm 通过 Object.defineProperty(app.config.globalProperties, `$${method}`, desc) 增加到全局
  • 通过 aplly(...) 办法注册罕用的 全局指令 v-t全局组件 i18n
  • 在根组件卸载时移除 / 开释 i18n 相干内容

    const unmountApp = app.unmount;
    app.unmount = () => {i18n.dispose();
        unmountApp();};
  • 注册 vue-devtools 的相干插件

    依据数据信息填充国际化内容

    页面渲染

    假如须要渲染如下数据对应的列表,并且要实现国际化:

    const data = [
    {
      url: vueImg,
      title: 'Vue',
      describe: '渐进式 JavaScript 框架'
    },
    {
      url: reactImg,
      title: 'React',
      describe: '用于构建用户界面的 JavaScript 库'
    },
    {
      url: angularImg,
      title: 'Angular',
      describe: '古代 Web 开发平台'
    },
    {
      url: nodeImg,
      title: 'Node',
      describe: 'Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时'
    },
    {
      url: webpackImg,
      title: 'Webpack',
      describe: 'webpack 是一个用于古代 JavaScript 应用程序的动态模块打包工具'
    },
    ];

    对应 App.vue 组件模板内容如下:

    <template>
    <button class="btn" @click="changeLang">{{$t("中 / 英") }}</button>
    <List :data="data" />
    </template>

对应的页面成果如下:

填充 lang 目录下映射关系

lang/zh.js 文件中:

export default {
  "渐进式 JavaScript 框架": "渐进式 JavaScript 框架",

  "用于构建用户界面的 JavaScript 库": "用于构建用户界面的 JavaScript 库",

  "古代 Web 开发平台": "古代 Web 开发平台",

  "Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时":
    "Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时",

  "webpack 是一个用于古代 JavaScript 应用程序的动态模块打包工具":
    "webpack 是一个用于古代 JavaScript 应用程序的动态模块打包工具",

  "中 / 英": "中 / 英",
};

lang/zh.js 文件中:

export default {
  "渐进式 JavaScript 框架": "Progressive JavaScript framework",

  "用于构建用户界面的 JavaScript 库":
    "JavaScript library for building user interface",

  "古代 Web 开发平台": "Modern web development platform",

  "Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时":
    "Node.js is a JavaScript runtime based on the chrome V8 engine",

  "webpack 是一个用于古代 JavaScript 应用程序的动态模块打包工具":
    "Webpack is a static module packaging tool for modern JavaScript applications",

  "中 / 英": "Chinese / English",
};

<List /> 组件中进行翻译解决

翻译解决可通过如下形式解决:

  • 应用 $t(...) 办法
  • 应用 v-t 指令
  • 应用 <i18n-t></i18n-t> 组件

这里抉择第一种,因为它更灵便,能应用的范畴也更广,指令和组件模式限定在了 template 中:

成果演示

优化 i18n 配置

基于以上简略的例子,曾经可能实现了国际化切换性能,但其中须要思考的优化点还不少,上面的内容仅属于 抛转引玉,不肯定全面。

优化翻译文件中的 key

当初很显著的一点,就是 zh.js、en.js 文件中用于映射的 key 太长了,导致整个文件看起来会很多、很乱,因而咱们能够将对应的 key 进行精简,如下:

// zh.js
export default {
  "Vue 简介": "渐进式 JavaScript 框架",
  
  "React 简介": "用于构建用户界面的 JavaScript 库",

  "Angular 简介": "古代 Web 开发平台",

  "Node 简介": "Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时",

  "Webpack 简介":
    "webpack 是一个用于古代 JavaScript 应用程序的动态模块打包工具",

  "中 / 英": "中 / 英",
};

// en.js
export default {
  "Vue 简介": "Progressive JavaScript framework",

  "React 简介": "JavaScript library for building user interface",

  "Angular 简介": "Modern web development platform",

  "Node 简介": "Node.js is a JavaScript runtime based on the chrome V8 engine",

  "Webpack 简介":
    "Webpack is a static module packaging tool for modern JavaScript applications",

  "中 / 英": "Chinese / English",
};

那么对应到内部传入须要渲染的数据源 data 就能够简写为:

const data = [
  {
    url: vueImg,
    title: "Vue",
    describe: "Vue 简介",
  },
  {
    url: reactImg,
    title: "React",
    describe: "React 简介",
  },
  {
    url: angularImg,
    title: "Angular",
    describe: "Angular 简介",
  },
  {
    url: nodeImg,
    title: "Node",
    describe: "Node 简介",
  },
  {
    url: webpackImg,
    title: "Webpack",
    describe: "Webpack 简介",
  },
];

另一种精简形式就是将 key 用对应的相似于 变量命名 的形式来定义,但我集体不是很喜爱这种形式,首先在语义上很难间接读出相干信息,而且很难用一两个英文单词去概括文字内容,而且在前期须要排查对应问题并须要定位 tmeplate 时是极其不不便.

转换翻译文件类型

.js 转 .json

上述的翻译文件是 .js 文件,因而,为了可能让其能被其余文件导入,咱们不得不在文件中应用 export defualtexport 将对应文件内容向外导出,但其实咱们能够将 .js 文件转换为 .json 文件间接应用,如下:

excel 转 .json

在理论我的项目中翻译的内容通常是由业务专门找对应的翻译人员提供的,而真正到了开发者手中往往就是一个 excel 类型的表格文件,如果咱们应用的是后面的纯 json 形式,免不了要本人一个一个从表格中复制对应的内容到咱们对应的 .json 文件中,而且是别离填充到 zh.jsonen.json 中,值得注意的是当初才是仅反对两个国家的语言,如果后续反对的国家变多,那么手动复制的形式岂不是要

因而,最好的做法是咱们依据业务方提供的表格主动转成 json 格局的数据,防止不必要的手动操作,用命令帮咱们解决这个内容:

  • 装置依赖 npm install xlsx-to-json
  • 将对应的转换操作封装 excel2json.js 文件中,基于第三方库简略封装即可

    const xlsx2json = require("xlsx-to-json");
    const path = require("path");
    
    xlsx2json(
      {input: path.join(__dirname, "./i18n.xlsx"),
        output: path.join(__dirname, "./i18n.json"),
      },
      function (err, result) {if (err) {console.error(err);
        } else {console.log(result);
        }
      }
    );
    • 批改 i18n 配置的入口文件 src\language\index.js

        import {createI18n} from "vue-i18n";
        import i18njson from "./i18n.json";
      
        // 动静获取 message 信息
        function getMessage() {
       const messages = {zh: {},
         en: {}};
       i18njson.forEach(({Short, Chinese, English}) => {messages.zh[Short] = Chinese;
         messages.en[Short] = English;
       });
       return messages;
        }
      
        const i18n = createI18n({
       legacy: false,
       locale: "zh", // 初始化配置语言
       messages: getMessage(),});
      
        export default i18n;
    • 提供 i18n.xlsx 文件作为数据源

    • package.json 问文件中增加对应转换命令

        "scripts": {
            "dev": "vite",
            "build": "vite build",
            "preview": "vite preview",
            "i18n": "node ./src/language/excel2json.js"
          }

      具体成果如下:

降级为 i18n 零碎

下面曾经将对应的翻译包从 *.xlsx 转成了 *.json 的模式,这样肯定水平上能缩小反复劳动力,但在 复用 / 合作 方面还是不够现实,因而能够从更高的维度将这整个内容降级到 i18n 零碎,并提供对应的翻译包上传、主动解析、去重、增加命名空间等性能,再加上对应的列表治理性能,重点是可供多人员、多零碎进行 复用 / 合作 ,对前端来讲就能够通过 接口 获取对应的翻译包数据,也能缩小前端最终构建产物的体积。

解决翻译文件中反复的内容

什么叫反复的内容呢?其实很简略,比方有个文字内容为 确认 按钮,它在 A 页面须要翻译为 Confirm,它在 B 页面须要翻译为 OK,而它的中文内容就是 确认,意味着它对应的数据内容为:

[{ "Short": "确认", "Chinese": "确认", "English": "Confirm"},
  {"Short": "确认", "Chinese": "确认", "English": "OK"}
]

但这样其实是不行的,这样最终会被前面的内容笼罩掉,即 A、B 页面最终的翻译内容都为 OK,因为数据中的 Short 其实就是最终的不同语言映射的 key,如下:

const messages = {
    zh: {
     "确认": "确认",
     "确认": "确认"
    },
    en: {
     "确认": "Confirm",
     "确认": "OK"
    }
};

既然这样,那么其实咱们只有为不同的翻译内容设置不同的 Short 值即可,如下:

[{ "Short": "确认 1", "Chinese": "确认", "English": "Confirm"},
  {"Short": "确认 2", "Chinese": "确认", "English": "OK"}
]

这样变动小,并且也没有失落掉本来的语义。

思考不同语言的款式

因为不同语言的表现形式不同,内容长度也不统一,因而在前端进行展现时,就必须要思考到最终的显式问题,否则一旦切换语言环境那么肯定会导致页面的布局展现呈现问题,解决形式无非几种:

  • 容许文字内容换行展现,在文字产生换行时,要通过 CSS 设置按残缺词换行
  • 不容许换行的,就要管制固定宽度,超出局部打点展现,鼠标移入展现全部内容等
  • 独自为不同语言环境设置款式,具体还是得看展现需要,如须要思考不同语言环境下文字的对齐形式、文字间距等
  • 针对难以解决的翻译内容,能够通过和业务沟通是否能够替换翻译内容、缩减文字长度等等

    思考后端接口语言环境变更

    一个我的项目的国际化不可能都是前端来实现的,一些接口动静返回的内容也是须要后端去解决的,通常接口的申请头中会存储一个用于标识以后页面语言环境的字段,而后再决定返回给前端页面的具体内容。

基于后面解决的国家化切换性能,自身是会基于 vue 的响应式来切换翻译内容的,即不会刷新页面,因而在切换对应翻译内容后,同样须要批改后续接口申请头中的语言环境。

但这样还是有问题的,曾经通过接口返回的数据内容,此时没有方法切换成对应的翻译内容,因为以后的国际化切换是基于页面的变动,但基于接口变动的局部还没从新申请获取新的内容,那怎么解决呢?

  • 前端切换语言环境后,从新刷新页面,让接口也从新获取新的内容
  • 后端在返回数据时,将对应的不同语言环境的翻译内容一起返回,由前端依据语言环境决定如何渲染
  • 将所有的翻译内容全副交由前端治理,一开始就初始化好各个语言环境对应的翻译内容

    最初

    以上内容是基于国际化性能的一点思考,文中对应的思考点是笔者本人在我的项目中遇到的点,并不一定适宜所有我的项目,当然也期待评论区给出更多、更好的计划。

本文参加了 SegmentFault 思否写作挑战赛,欢送正在浏览的你也退出。

退出移动版