前端不使用i18n该如何优雅的实现多语言

JavaScript/前端
290
0
0
2023-07-23
目录
  • 前言:
  • 一、业务需求
  • 1. 确定业务
  • 2. 实现思路
  • 二、实现步骤  
  • 1. 新建 VFrame.js 文件 
  • 2. main.js 中 使用我们新建的 VFrame.js
  • 3. html js 中使用我们配置好的多语言 
  • 三、微信小程序中使用 
  • 1. 完成上方基础 VFrame 创建,main.js 中挂载,但是不要在用 new vue 勿动(不然会报错)!
  • 2. 预加载页面的制作 
  • 3. 切换语言
  • 结语:

前言:

关于ERP管理系统的多语言,或者其他应用的多语言一直是我们比较麻烦的问题,大部分是使用 i18n 在代码里进行配置,如果想要修改语言就要自己去改代码!

今天我们分享一下如何不使用 i18n 去实现多语言,用到以下的技术栈

ERP管理系统:Vue + 若依 + Element-ui

 微信小程序 :uniapp + uView

一、业务需求

1. 确定业务

最近要实现多语言的 ERP,以及对应的H5、小程序;但是因为语言可能不确定,需求是客户自己去添加、修改自己想要的语言,所以就需要在 ERP 上实现 一个叫 language(可以定义语言) 的功能,可以去给客户提供一个可以添加默认语言、添加新的语言、修改、删除当前语言的平台,如果当前语言没有加过翻译则用 [ key ] 这种方式去提醒客户,该 key 未加多语言!然后我们的ERP、H5、小程序就可以使用当前的全局多语言...

2. 实现思路

思路其实比较简单,但是复杂的需求往往离不开后端大佬支持!下面我们一起来捋一捋我们的实现思路(假设我们现在的需求是三个地区的语言:简体中文、繁体中文、英文)!

① 既然我们再 ERP 上需要有一个 language 的功能供客户去操作语言,考虑到客户还可能设置默认语言,添加多种不同地区的语言,所以我们的 UI 可以像下图设计的那样;

② 新增完地区语言以后我们该如何让去修改、新增的不同地区的语言呢?我们这个时候就需要 一个子页面,负责新增当前 key 的多语言,查询当前 key 的多语言去做修改,看完下图你就会明白啦!!! 

③经过以上两个步骤我们已经成功的让客户实现了想要的效果,但是我们要怎么把后端大哥保存好的东西拿过来用呢

1. 保存时的数据结构:

{
 key: "Status"
 texts:[
   {
     "languageName": "en-GB",
     "text": "Status"
   },
   {
     "languageName": "zh-CN",
     "text": "状态"
   },
   {
     "languageName": "zh-HK",
     "text": "狀態"
   }
 ]
}

关于ERP 查询时的数据结构这里就不一一列举了,和上面类似!

2. ERP | H5 | 微信小程序 用时查询的数据结构:

// ERP | H | 微信小程序 用时的查询
// language_texts 是我们再 SQL Table 中存储的所有多语言集合
 
language_texts : {
    key : 当前语言的value, // 如何知道当前语言,我们稍后再说
    key : value,
    key : value,
    ... : ...
    ... : ...
}

3. 前端如何使用已经保存好的数据结构呢?

其实有了上面查询我们就可以用到 language_texts[ key ] 的方式去取出来我们想要的语言数据;使用的话也比较简单,js html 中的不同使用方法下面给大家一 一讲解 ;

值得我们思考的问题是 我们该何时把 当前语言集全部查询出来?

我这里采用的方案是在 new Vue 之前去调用我们当前的 VFrame方法,这样我们可以确保 我们的多语言所有的 [ key ] [ value ] 可以成功加载,具体步骤请看下方!

二、实现步骤  

1. 新建 VFrame.js 文件 

我这里的 VFrame.js 是直接建在Src 目录下的

import axiso from 'axios'
import { getAll } from '@/api/uxretail/application.js'
const format = require('string-format')
 
 
export default class VFrame {
 
    static host;
    static wsUrl;
 
    /**
     * 在vue实例化之前调用异步start方法
     */
    static start() {
        return new Promise((resolve, reject) => {
            axiso.request({
                url: "/static/appconfig.json",
                method: "get",
            }).then(result => {
                VFrame.host = (result.host || window.location.host);
                VFrame.wsUrl = "ws://" + VFrame.host + ":" + result.wsPort + "/ws/notify/";
                VFrame.getAll(resolve);
            }).catch(error => {
                reject(error);
            });
        });
    }
 
    static getAll(resolve) {
        getAll().then(res => {
            VFrame.applicationResource = res
            resolve()
        });
    }
 
 
    static l(key, ...args) {
        if (localStorage.getItem("ux_debug_language")) {
            return `[${key}]`
        }
 
        if (!VFrame.applicationResource) {
            return key
        }
        if (VFrame.applicationResource.language_texts) {
            let res = VFrame.applicationResource.language_texts[key];
            if (res) {
                return format(res, args);
            } else {
                return `[${key}]`
            }
        }
        return key;
    }
}

2. main.js 中 使用我们新建的 VFrame.js

上面我们说到 VFrame 的执行时机,我们可以看下面代码! 
import Vue from "vue";
import App from "./App";
import store from "./store";
import router from "./router";
import VFrame from "./framework/VFrame";
 
Vue.prototype.l = VFrame.l;
 
// 这个为了方便用 VFrame ,并没有其他用途
Vue.prototype.$ux = {
  v: VFrame
}
 
// 重要的就是这一步,VFrame 的执行时机
VFrame.start().then(() => {
  //set layout to store
  store.commit("dragLayout/initLayout")
  new Vue({
    el: "#app",
    router,
    store,
    render: h => h(App)
  });
});

3. html js 中使用我们配置好的多语言 

Html 中使用: 【 l("Key") 】

<div> {{ l('ManyLanguage') }} </div>
 
<el-form :model="item" :rules="tableRules">
    <el-form-item :label="l("ManyLanguage")" prop="language">
        <el-input v-model="item.language"></el-input>
    </el-form-item>
 
</el-form>

js 中使用: 【 this.l("Key") 】

export default{
 
  data(){
    return {
        language:this.l("ManyLanguage")
    }
  },
 
  created(){
  
    console.log(this.l("MyLanguage"))
  },
}

经过上面一番操作我们已经可以在管理系统中成功的调用当前多语言了;

那么我们 VFrame 中的 getAll() API 调用时 如何返回我们刚好我们想要的语言呢?

我们就要处理 【 request.js 】中的封装,在请求拦截中去携带我们自定义的请求头信息,后端大佬去对应的处理即可!

import Cookies from "js-cookie";
import axios from "axios";
 
 
axios.defaults.headers["Content-Type"] = "application/json;charset=utf-";
// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: process.env.VUE_APP_BASE_API,
  // 超时
  timeout:
});
 
 
service.interceptors.request.use(
  config => {
 
    var cultureName = Cookies.get(CONSTANT.UX_LANGUAGE);
    if (cultureName) {
      config.headers[CONSTANT.UX_LANGUAGE] = cultureName; // 让每个请求携带自定义token 请根据实际情况自行修改
    }
 
})

三、微信小程序中使用 

实现步骤就像上面写的步骤一样,那么注意点是什么呢( 本文用的是uniapp去开发的微信小程序,也是基于 vue 的语法 )?
在 uniapp 里面我们不能写的像我们上面那样,因为 uniapp 是不支持的,那么这个时候我们如何去确保页面加载完成时,多语言可以全部返回呢?
答:做一个预加载页,当我们多语言成功返回以后跳转至业务页面

1. 完成上方基础 VFrame 创建,main.js 中挂载,但是不要在用 new vue 勿动(不然会报错)!

因为小程序有 Tabbar 所以我们的 VFrame.js 需要多一个方法; 

import {
	request
} from "util/request.js";
export default class VFrame {
	static applicationResource;
 
	/**
	 * 在vue实例化之前调用异步start方法
	 */
	static start() {
		return new Promise((resolve, reject) => {
			request({
				url: '/application/getAppAll', //activityType= 查询全部
				method: 'GET',
				hideLoading: true,
				data: {
					orgId: uni.getStorageSync('companyOrgId')
				}
			}).then(res => {
				VFrame.applicationResource = res.data.result
				resolve(res)
			});
		});
	}
 
	static l(key, args) {
		if (VFrame.applicationResource && VFrame.applicationResource.language_texts) {
			let res = VFrame.applicationResource.language_texts[key];
			if (res) {
				return res;
			} else {
				return `[${key}]`
			}
		}
		return key;
	}
 
	static tabbarLanguage(tabbarList) {
		let language = uni.getStorageSync('language') || 'zh-CN'
		if (language) {
			switch (language) {
				case 'en-GB':
					tabbarList[].text = "Coupons"
					tabbarList[].text = "Stamp"
					tabbarList[].text = "Account"
					tabbarList[].text = "Discount"
					tabbarList[].text = "Favourite"
					break
				case 'zh-CN':
					tabbarList[].text = "电子优惠券"
					tabbarList[].text = "电子印花"
					tabbarList[].text = "我的账户"
					tabbarList[].text = "优惠奖赏"
					tabbarList[].text = "我的最爱"
					break
				case 'zh-HK':
					tabbarList[].text = "電子優惠券"
					tabbarList[].text = "電子印花"
					tabbarList[].text = "我的賬戶"
					tabbarList[].text = "優惠獎賞"
					tabbarList[].text = "我的最愛"
					break
			}
		}
		return tabbarList
	}
}

2. 预加载页面的制作 

新建 preload.vue 

<template>
	<view class="preload">fashion-cue</view>
</template>
 
<script>
import { mapMutations } from 'vuex';
import VFrame from '@/util/VFrame';
export default {
	name: 'Preload', // 预加载动画页面
	data() {
		return {};
	},
	methods: {
		...mapMutations(['setAppAll'])
	},
	onLoad(e) {
		console.log('onLoad', e);
		VFrame.start().then(res => {
			if (res && res.data.result.brandList && res.data.result.categoryList) {
				this.setAppAll(res.data.result);
			}
			getApp().globalData.isLoaded = true;
			uni.switchTab({
				url: e.redirect ? '/' + e.redirect : '/platforms/mp-weixin/discount/discount'
			});
		});
	}
};
</script>
 
<style>
.preload {
	width:%;
	font-weight: bold;
	font-family: 'Arial', 'Microsoft YaHei', '黑体', '宋体', sans-serif;
	font-size:.5rem;
	text-align: center;
	background-image: -webkit-linear-gradient(left, black, transparent%, black 50%, transparent 75%, black);
	-webkit-text-fill-color: transparent;
	-webkit-background-clip: text;
	-webkit-background-size:% 100%;
	-webkit-animation: masked-animations infinite linear;
	position: absolute;
	top:%;
	left:%;
	transform: translate(-%, -140%);
}
@-webkit-keyframes masked-animation {% {
		background-position: 0;
	}% {
		background-position: -% 0;
	}
}
</style>

3. 切换语言

切换语言是再次调用我们的 VFrame.js 中的 start() 方法即可;

但是小程序切换要注意几个点:

① 自定义请求头的特殊处理

② 切换完成以后需要刷新当前页面

③ Tabbar 页面需要再 onShow 时处理

结语:

其实实现步骤略微有些繁琐,但是并不难,主要还是后端大佬的配合!