关于在自行封装的组件库中(使用vue-class-component)使用Vue-i18n无法正常翻译的解决办法

news2024/12/25 11:32:14

文章目录

  • 介绍
  • 背景
  • 现象1
    • 解决办法
  • 现象2
    • 原因分析
    • 解决办法
  • 最终方案

介绍

大家或多或少都用过别人封装的组件库,甚至有人或者公司内有自行封装的一些公用组件库,而国际化翻译现在已经是各大项目中必不可少的一个插件了,但组件库中使用 i18n 进行翻译的时候可能遇到过以下这种问题,希望我的一点分析可以帮到你们

背景

因公司历史项目使用 Angular 框架原因,所以在做架构迁移时,我们采用了 vue-class-component(github链接) 和 vue-property-decorator(github链接) 平滑的迁移到了 Vue 框架

部分代码如下:

<template>
    <div class="pager">
        <span class="grid-pager">
            {{$t('pager.total', {total: totalRows})}}
        </span>
        <div class="grid-pager">
            <a-pagination 
                size="small" 
                v-model:current="index"
                :total="totalRows" 
                v-model:pageSize="size"
                :pageSizeOptions="pageSizeOptions"
                :showSizeChanger="showSizeChanger"
                :showQuickJumper="showQuickJumper"
                :simple="simple"
                @change="change" />
        </div>
    </div>
</template>

<script lang="ts">
import { Options, Vue, setup } from 'vue-class-component';
import { Composer, useI18n } from 'vue-i18n';
import { Emit, Prop, Watch } from 'vue-property-decorator';
@Options({
    name: 'pager'
})
export default class Pager extends Vue {
    @Prop({ type: Number, }) public totalRows: number;
    @Prop({ type: Number, }) public pagerSize: number;
    @Prop({ type: Number, }) public pageIndex: number;
    @Prop({ type: Array, default: ['20', '50', '100']}) public pageSizeOptions: Array<number>;
    @Prop({ type: Boolean, default: false }) public showSizeChanger: boolean;
    @Prop({ type: Boolean, default: true }) public showQuickJumper: boolean;
    @Prop({ type: Boolean, default: false }) public simple: boolean;
    public index: number = 1;
    public size: number = 20;

    /**
     * change事件
     *
     * @param {number} index 下标
     * @param {number} size 每页条数
     * @returns {{index: number, size: number}} 返回分页器当前页码与当前每页条数
     */
    @Emit()
    public change(index: number, size: number): {index: number, size: number} {
        return {
            index: index - 1,
            size
        };
    }

    /**
     * 监听 pageIndex 属性变化
     *
     * @param {number} newValue 新值
     */
    @Watch('pageIndex')
    public pageIndexChange(newValue: number): void {
        if (newValue >= 0) {
            this.index = newValue + 1;
        }
    }

    /**
     * 监听 pagerSize 属性变化
     * 
     * @param {number} newValue 新值
     */
    @Watch('pagerSize', { immediate: true })
    public pageSizeChange(newValue: number): void {
        if (newValue) {
            this.size = newValue;
        }
    }

    /**
     * 钩子函数
     */
    public mounted(): void {
        if (this.pageIndex || this.pageIndex === 0) {
            this.index = this.pageIndex + 1;
        }
    }
}
</script>
<style lang="scss" scoped></style>

因此我们封装的组件库也是基于此的,若您的项目不是使用该种方式开发的,那我觉得以下部分的内容对您的帮助应该是不多的

同时我们也没有做像现在主流的UI组件库的国际化翻译处理,如 Element UI组件库中既可以兼容 vu-i18n 又可以自行进行翻译(可参考这里)

目前我们只是简单的封装了一个组件到另一个库里,国际化翻译是依赖于本地项目中安装的 vue-i18n 插件进行翻译的,正是因为这个原因才会出现我下面说的情况(后期可能会像组件库一样有自行的翻译方法)

现象1

就以上面的代码为例,若引用该库的项目国际化文件中没有 pager.total 字段时,浏览器控制台就会报错

Not found 'pager.total' key in 'zh_CN' locale messages.

且无法正常翻译
在这里插入图片描述

这是因为在全局国际化翻译文件中没有找到该key值才翻译有误

解决办法

@options 注解中可以直接添加组件级的i18n配置,如下:

@Options({
    name: 'pager',
    i18n: {
        messages: {
            zh_CN: {
                'pager.total': '共{total}条'
            },
            zh_TW: {
                'pager.total': '共{total}條'
            },
            en_US: {
                'pager.total': '{total} entries in total'
            }
        }
    }
})

使用这种方式确实可以使用了,因此我们也没多注意,其实它只是在引用这个组件的项目中也使用 vue-class-component 时才可以生效

现象2

基于上面的修改,直到有一天我们有个新的项目使用了 vue3 + vite + setup 技术栈时,它又没有正常翻译了,浏览器控制台报错和现象1一致

然后就是和同事找原因,issue、google都找遍了,没有发现遇到同样问题的,无奈只好自行查阅源码分析原因

原因分析

首先使用 @options 进行注解对象中的内容,会挂载在 Vue实例的 $options 上,如下:
在这里插入图片描述
因此在注解中设置i18n时可以肯定的是可以传给Vue实例的,那么就得看看I18n中是如何处理这部分内容的了(因源码较复杂,以下只关注$options部分)

可以看到这里 就是处理Vue实例上$options的i18n配置的
在这里插入图片描述
可以看到是 这里 引入了该Mixin,因使用的是v9版本,查看 defineMixinNext 调用位置
在这里插入图片描述
可以看到注释以及if判断都明确指出是 Legacy模式下才会进入这部分处理逻辑,那么现在翻译不生效的原因就非常明了了

但是现在本地项目使用的是 Vue3 + Vite + setup suger 技术栈,那么就必选找一个可以兼容的方式呀,不然这组件库不是白封装了吗 (艹皿艹)

然后就是漫长的翻阅 vue-class-component github issue 的过程

解决办法

关注到这个 issue ,里面提到:

Composition functions are available in class property initializers by wrapping setup helper.

也就是说,在V8版本中我们是可以使用 setup 的

而翻阅 Vue-i18n 官网,可以看到在组合式API部分可以在setup中设置组件级的翻译内容
在这里插入图片描述
这样的话,方法就显而易见了,只要在 setup 中设置组件级的翻译内容即可,如下:

public componentSetup = setup(() => {
	const i18n: Composer = useI18n({
		locale: 'zh_CN',
		messages: {
			zh_CN: {
				'pager.total1': '共{total}条'
			},
			zh_TW: {
				'pager.total': '共{total}條'
			},
			en_US: {
				'pager.total': '{total} entries in total'
			}
		},
	});
	return {
		i18n
	};
});

结果一试,还是原来的问题,控制台报错,页面没有正常显示,继续翻文档可以看到 这里 提到:
在这里插入图片描述
大致意思就是如果你在 useI18n 中 设置了那些字段时,添加 useScope: 'global' 配置将会将这些字段合并到全局作用域中,这不就是我们想要的吗! (✪ω✪)

public componentSetup = setup(() => {
	const i18n: Composer = useI18n({
		locale: 'zh_CN',
		messages: {
			zh_CN: {
				'pager.total1': '共{total}条'
			},
			zh_TW: {
				'pager.total': '共{total}條'
			},
			en_US: {
				'pager.total': '{total} entries in total'
			}
		},
		useScope: 'global'
	});
	return {
		i18n
	};
});

运行项目并打印全局i18n实例,你会发现组件中的翻译已成功加入全局翻译文件中,且页面也显示正常了
在这里插入图片描述
在这里插入图片描述

最终方案

根据现象1分析得出如果使用 Legacy 模式时可以读取 @Options 注解中的翻译内容
根据现象2分析得出如果使用 Composition 模式时可以读取 setup() 回调中的翻译内容

那么就可以兼容这两种方式使该组件以任何模式引入时都可以正常翻译了,完整代码如下:

<template>
    <div class="pager">
        <span class="grid-pager">
            {{$t('pager.total', {total: totalRows})}}
        </span>
        <div class="grid-pager">
            <a-pagination 
                size="small" 
                v-model:current="index"
                :total="totalRows" 
                v-model:pageSize="size"
                :pageSizeOptions="pageSizeOptions"
                :showSizeChanger="showSizeChanger"
                :showQuickJumper="showQuickJumper"
                :simple="simple"
                @change="change" />
        </div>
    </div>
</template>

<script lang="ts">
import { Options, Vue, setup } from 'vue-class-component';
import { Composer, useI18n } from 'vue-i18n';
import { Emit, Prop, Watch } from 'vue-property-decorator';
@Options({
    name: 'pager',
    // 兼容选项式API模式
    i18n: {
        messages: {
            zh_CN: {
                'pager.total': '共{total}条'
            },
            zh_TW: {
                'pager.total': '共{total}條'
            },
            en_US: {
                'pager.total': '{total} entries in total'
            }
        }
    }
})
export default class Pager extends Vue {
    @Prop({ type: Number, }) public totalRows: number;
    @Prop({ type: Number, }) public pagerSize: number;
    @Prop({ type: Number, }) public pageIndex: number;
    @Prop({ type: Array, default: ['20', '50', '100']}) public pageSizeOptions: Array<number>;
    @Prop({ type: Boolean, default: false }) public showSizeChanger: boolean;
    @Prop({ type: Boolean, default: true }) public showQuickJumper: boolean;
    @Prop({ type: Boolean, default: false }) public simple: boolean;
    public index: number = 1;
    public size: number = 20;

	// 兼容组合式API模式
	public componentSetup = setup(() => {
        const i18n: Composer = useI18n({
            locale: 'zh_CN',
            messages: {
                zh_CN: {
                    'pager.total': '共{total}条'
                },
                zh_TW: {
                    'pager.total': '共{total}條'
                },
                en_US: {
                    'pager.total': '{total} entries in total'
                }
            },
            // 将当前翻译内容合并到全局翻译文件中
            useScope: 'global'
        });
        return {
            i18n
        };
    });

    /**
     * change事件
     *
     * @param {number} index 下标
     * @param {number} size 每页条数
     * @returns {{index: number, size: number}} 返回分页器当前页码与当前每页条数
     */
    @Emit()
    public change(index: number, size: number): {index: number, size: number} {
        return {
            index: index - 1,
            size
        };
    }
}
</script>
<style lang="scss" scoped></style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1907663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI视频创作一条龙!达摩院“寻光”平台炸场WAIC,突破可控编辑难题

卡奥斯智能交互引擎是卡奥斯基于海尔近40年工业生产经验积累和卡奥斯7年工业互联网平台建设的最佳实践&#xff0c;基于大语言模型和RAG技术&#xff0c;集合海量工业领域生态资源方优质产品和知识服务&#xff0c;旨在通过智能搜索、连续交互&#xff0c;实时生成个性化的内容…

基于Java+SpringMvc+Vue技术的实验室管理系统设计与实现(6000字以上论文参考)

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

正态、威布尔、指数分布、伽马分布、对数正态分布介绍

目录 正态、威布尔、指数分布、3.1 概念介绍概率密度函数&#xff08;PDF&#xff09;累积分布函数&#xff08;CDF&#xff09;性质应用 3.2 参数及绘图参数概率密度函数&#xff08;PDF&#xff09;累积分布函数&#xff08;CDF&#xff09;绘图图像解读 3.3 指数分布拟合代码…

Java面经知识点汇总版

Java面经知识点汇总版 算法 14. 最长公共前缀&#xff08;写出来即可&#xff09; Java 计算机基础 数据库 基础 SQL SELECT first_name, last_name, salary FROM employees WHERE department Sales AND salary > (SELECT AVG(salary)FROM employeesWHERE department Sal…

阶段三:项目开发---大数据开发运行环境搭建:任务6:安装配置HBase

任务描述 知识点&#xff1a;安装配置HBase 重 点&#xff1a; 安装配置HBase 难 点&#xff1a;无 内 容&#xff1a; 本阶段任务是安装配置HBase&#xff0c;实时飞行数据是保存在HBase中的&#xff0c;因为HBase具有高效的读写能力&#xff0c;在当前项目中我们是…

网络资源模板--Android Studio 外卖点餐App

目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 原创外卖点餐&#xff1a;基于Android studio 实现外卖(点)订餐系统 非原创奶茶点餐&#xff1a;网络资源模板--基于 Android Studio 实现的奶茶点餐App报告 一、项目演示 网络资源模板--基于Android …

通义千问Qwen-VL-Chat大模型本地部署(一)

目录 前言 环境准备 软件安装 其它库安装启动项目 FASTAPI 小结 前言 人工智能大模型是一种能够利用大数据和神经网络来模拟人类思维和创造力的人工智能算法。它利用海量的数据和深度学习技术来理解、生成和预测新内容&#xff0c;通常情况下有数十亿乃至数百亿个参数&#xf…

关于Web开发的详细介绍

目录 一、什么是Web&#xff1f; 二、Web网站的工作流程和开发模式 &#xff08;1&#xff09;简单介绍 &#xff08;2&#xff09;工作流程 1、第一步 2、第二步 &#xff08;3&#xff09;Web网站的开发模式 1、前后端分离开发模式 ​编辑2、混合开发模式 三、开发W…

36.哀家要长脑子了!--前缀和差分

前缀和 1.一维的795. 前缀和 - AcWing题库 前缀和公式 s[i] a[1] a[2] a[3] ... a[i] 即 s[i] s[i-1] a[i] #include<iostream> using namespace std;const int N 1e5 10; int a[N], s[N];int main(){int m, n;cin >> n >> m;for(int i 1; i <…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【加解密(ArkTS)】

加解密(ArkTS) 以AES 128密钥为例&#xff0c;完成加解密。具体的场景介绍及支持的算法规格。 开发步骤 生成密钥 指定密钥别名。初始化密钥属性集。调用[generateKeyItem]生成密钥&#xff0c;具体请参考[密钥生成]。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/l…

数据库之MQL

1&#xff0c;查询所有 mysql> select * from grade;2&#xff0c; mysql> select id,firstname,lastname from grade;3&#xff0c; mysql> select firstname,lastname from grade where id > 4;4&#xff0c; mysql> select * from grade where sex f;5&…

pandas数据分析(8)

描述性统计量和数据聚合 描述性统计量 描述性统计量通过量化数据来概括数据集。DataFrame和Series可以通过sum、mean、count等方法来获取各种描述性统计量。在默认情况下会按照axis0返回一个Series&#xff0c;也就是说会得到一个有关列的统计量&#xff1a; 如果要计算行的统…

【解决Windows11系统Windows Hello不能使用的问题】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Windows Hello是什么&#xff1f;二、使用步骤1.购买一个摄像头2.开始配置 三、异常解决1.内置管理员不能使用2.没找到合适的摄像头3.摄像头需要专用驱动4.…

CSS技巧 - 一日一例 (1):会讨好的热情按钮

题外话: 从今天开始,我准备开设一个新的专栏,专门写 使用CSS实现各种酷炫按钮的方法,本专栏目前准备写40篇左右,大概会完成如下按钮效果: 今天,我来介绍第一个按钮的实现方法:会讨好的热情按钮。为什么我给它起这样的名字呢?你看它像不像一个不停摇尾巴的小黄?当你鼠…

Java---包装类与泛型

1.包装类 1.1 包装类 在Java中&#xff0c;由于基本数据类型不是继承Object类&#xff0c;为了在泛型代码中可以支持基本数据类型&#xff0c;Java给每个基本数据类型各自提供了一个包装类。 如下图 除了char和int基本数据类型的包装类型有点特别&#xff0c;其他的都是首字…

百川工作手机实现销售管理微信监控系统

在瞬息万变的商业战场中&#xff0c;每一分效率的提升都是企业制胜的关键。传统销售管理模式已难以满足现代企业对精准、高效、合规的迫切需求。今天&#xff0c;让我们一同探索如何利用工作手机这一创新工具&#xff0c;为您的销售团队装上智能翅膀&#xff0c;开启销售管理的…

ELfK logstash filter模块常用的插件 和ELFK部署

ELK之filter模块常用插件 logstash filter模块常用的插件&#xff1a; filter&#xff1a;表示数据处理层&#xff0c;包括对数据进行格式化处理、数据类型转换、数据过滤等&#xff0c;支持正则表达式 grok 对若干个大文本字段进行再分割成一些小字段 (?<字段名…

ROS中不同文件之间的引用小结

在比较大的一些程序中&#xff0c;往往会涉及到一些不同模块的调用&#xff0c;如果这些东西放在一个.cpp文件内&#xff0c;这个文件会变的特别长&#xff0c;因此会使用多个文件互相引用。那么如何在ROS下进行这种不同文件下的引用呢&#xff0c;根据最近所学&#xff0c;简单…

【大模型LLM面试合集】大语言模型基础_Word2Vec

Word2Vec 文章来源&#xff1a;Word2Vec详解 - 知乎 (zhihu.com) 1.Word2Vec概述 Word2Vec是google在2013年推出的一个NLP工具&#xff0c;它的特点是能够将单词转化为向量来表示&#xff0c;这样词与词之间就可以定量的去度量他们之间的关系&#xff0c;挖掘词之间的联系。 …

世界商用飞机机型大全-使用Java抓取FlightAware后的答案

目录 前言 一、数据说明 1、实时航班飞机机型数据 2、网页结构分析 二、使用Java进行信息抓取 1、定义页面PageVO对象 2、爬取属性定义 3、启动信息抓取组件 三、成果分析 1、商业飞行的飞机机型的种类 2、飞机种类排名前十名 3、航班数排名后十名 4、看中国国产大飞…