记一次前端Vue项目国际化解决方案
背景
有一个vue项目,要实现国际化功能,能够切换中英文显示,因为该项目系统的用户包括了国内和国外用户。
需求
1、页面表单上的所有中文标签要国际化,包括表单属性标签、表格列头标签等, title=“数量”;
2、输入框的提示内容需要国际化,如 placeholder=“选择日期”
3、js代码中的提示信息需要国际化,如 message(“请勾选批量设置”)、confirm(‘您确定要设置业务损耗吗?’)、title: ‘删除错误’ 等;
解决方案
1、开发流程,一开始开发过程中,我们不考虑国际化,等代码基本完成后,最后再进行国际化;
2、考虑日后还可能由其他语种,所以这里我们做国际化词语库时,国际化编码使用5位数字,对应多种语言值,即一对多;
3、前端我们重新封装一个全局方法 $lang(param1, param2) 来支持国际化,param1是国际化编码,param2是默认值(如果国际化编码没找到对应的语言单词,则默认用param2,且去掉左右两边的 ‘~’符号);
(其实后来又分析了下,如果一开始前端开发人员把所有需要国际化的中文词语,都写成 $lang(‘中文词语’) , $lang方法逻辑再修改下,如果没有第二个参数并且第一个参数对应的国际化词语也没有,则直接显示第一个参数字符串,而且这样的话,到后面再提取代码中的需要国际化的内容时就会很精确了。)
4、国际化流程:
- 从前端代码文件中将所有的中文提取出来,形成一个数组放到一个json文件中,并且数组需要去重一下;
- 使用第三方的翻译接口,来对导出的中文进行翻译,生成一个中英文对照键值对json文件;
- 校对中英文对照表,因为有的翻译不一定准确;
- 根据校对后的中英文对照表,生成国际化编码库,并创建两个国际化文件;
- 根据校对后的中英文对照表,并分析代码规则,将程序代码中的中文进行国际化处理;
国际化流程实施
在国际化流程实施中,我使用编写js脚本代码来实现相关的处理,使用node环境来执行脚本;
1、提取中文
从前端代码文件中将所有的中文提取出来,形成一个数组放到一个json文件中,并且数组需要去重一下;
下面的代码,是用来提取文件代码中的中文的,我们可以将代码文件命名为extractChinese.js,使用node来执行该脚本;
代码中要国际化的路径设置的是当前目录下的src下的 components和pages文件夹
const fs = require('fs');
const path = require('path');
const chineseRegex = /[一-龥]+/g; function extractChineseFromFile(filePath) { const content = fs.readFileSync(filePath, 'utf-8'); const chineseWords = content.match(chineseRegex); return chineseWords || [];
} function processDirectory(directoryPath) { const files = fs.readdirSync(directoryPath); const chineseSentences = []; files.forEach((fileName) => { const filePath = path.join(directoryPath, fileName); const stats = fs.statSync(filePath);if (stats.isDirectory()) { chineseSentences.push(...processDirectory(filePath)); } else if (stats.isFile() && ['.js', '.vue'].indexOf(path.extname(filePath)) > -1) {const chineseWords = extractChineseFromFile(filePath); chineseSentences.push(...chineseWords); } }); return chineseSentences;
} function main() { const srcDirectory = path.join(__dirname, 'src'); const componentsDirectory = path.join(srcDirectory, 'components'); const pagesDirectory = path.join(srcDirectory, 'pages'); const componentsChineseSentences = processDirectory(componentsDirectory); const pagesChineseSentences = processDirectory(pagesDirectory); const allChineseSentences = [...componentsChineseSentences, ...pagesChineseSentences]; //const allChineseSentences = componentsChineseSentences; const outputPath = path.join(__dirname, 'output.json'); // 使用 Set 对象来去重 let backString = Array.from(new Set(allChineseSentences)); // 对去重后的数组进行排序 backString.sort();fs.writeFileSync(outputPath, JSON.stringify(backString, null, 2), 'utf-8'); console.log('提取到的中文单词或语句已保存到output.json文件中。');
} main();
2、翻译中文
使用第三方的翻译接口,来对导出的中文进行翻译,生成一个中英文对照键值对json文件;
翻译接口,这里我们用的是百度翻译,至于如何去使用百度翻译,这里就不再说了,自己去百度看吧;
该步骤需要用到第一步生成的 output.json 文件,然后翻译结果是存在 translated_zh_en.json 中。
const fs = require('fs');
const axios = require('axios');
const appId = '123456789'; // 替换成你的百度翻译的APP ID
const secretKey = '999999999'; // 替换成你的百度翻译的密钥 const crypto = require('crypto');
axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8";function md5Hash(input) { // 创建一个哈希对象 const hash = crypto.createHash('md5'); // 更新哈希对象的内容 hash.update(input); // 获取哈希值的二进制表示 const hashBuffer = hash.digest(); // 将二进制转换为十六进制表示 const hashHex = hashBuffer.toString('hex'); // 返回小写的哈希值 return hashHex.toLowerCase();
} // 使用百度翻译API进行翻译
async function translateToEnglish(text) { const params = { q: text, appid: appId, salt: Date.now(), from: 'zh', to: 'en', sign: '' };// 计算签名 params.sign = md5Hash(params.appid + params.q + params.salt + secretKey);// 请求翻译 const url = `http://api.fanyi.baidu.com/api/trans/vip/translate?q=${encodeURIComponent(params.q)}&from=zh&to=en&appid=${params.appid}&salt=${params.salt}&sign=${params.sign}`;const response = await axios.get(url); //console.log(url);//console.log(response.data)// 返回翻译结果 return response.data.trans_result[0].dst;
} function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms));
} async function mysleep() { console.log('休息1秒......................'); await sleep(1000); // 暂停 1 秒 console.log('休息完成...');
} async function process() { // 读取json文件 const data = JSON.parse(fs.readFileSync('output.json', 'utf8')); // 存储翻译结果的对象 let translationData = {}; let execNumber = 1;// 遍历中文字符串数组,进行翻译for (let i = 0; i < data.length; i++) { const chineseString = data[i]; const englishString = await translateToEnglish(chineseString); // 将原中文字符串和英文字符串形成键值对存储到translationData对象中 translationData[chineseString] = englishString;if (execNumber >= 120) { // 如果不想全部执行,则执行多少场退出break;} else if (i == execNumber*20) { // 每执行20次接口调用,就休息1秒execNumber++;await mysleep()}}// 将翻译结果写入translate.json文件中 fs.writeFileSync('translated_zh_en.json', JSON.stringify(translationData, null, 2));
} process().catch(error => { console.error(error);
});
3、校对翻译
校对中英文对照表,因为有的翻译不一定准确;(找个行业英语水平高点的人,自己去校对吧)
4、创建国际化库
根据校对后的中英文对照表,生成国际化编码库,并创建两个国际化文件;
const fs = require('fs'); // 读取原始 JSON 文件
const data = JSON.parse(fs.readFileSync('translated_zh_en.json', 'utf8')); // 中文和英文的 JSON 文件
const chineseData = {};
const englishData = {}; let serialNumber = 00001; // 遍历原始数据,生成新的键值对
for (let chinese in data) { const english = data[chinese]; // 生成新的键值对,序号为 5 位数字 const key = `N${String(serialNumber).padStart(5, '0')}`; chineseData[key] = chinese; englishData[key] = english; serialNumber++;
} // 将中文和英文的 JSON 数据写入文件
fs.writeFileSync('cn.json', JSON.stringify(chineseData, null, 2));
fs.writeFileSync('en.json', JSON.stringify(englishData, null, 2));
5、代码国际化处理
根据第4步生成的中文国际化文件 cn.json ,并分析代码规则,将程序代码中的中文进行国际化处理;
首先要分析程序需要国际化的代码规则,因为这个替换不是简单的去就把中文替换,可能代码都由变化,我们分析项目代码中目前的规则如下:
场 景
**代码示例****
**查找内容****
**替换内容****
作为组件元素内容的
<vxe-button @click="closeModel">取消</vxe-button> <span style="color: red;">如调整了颜色尺码,保存后请务必核对检查数量和配色数据!</span> <div class="title">尺码信息</div>
>取消<
>{{$lang(‘10000’, ‘取消’)}}<
作为组件元素属性值的
<vxe-table-column field="odgc_pcs" title="数量" width="100" header-align="center" align="right"> <el-date-picker v-if="row.type == 'date'" type="date" placeholder="选择日期" v-model="row.value">
title="数量"placeholder=“选择日期”
:title=“ l a n g ( ′ 1000 1 ′ , ′ 数量 ′ ) " : p l a c e h o l d e r = " lang(‘10001’, ‘数量’)“:placeholder=” lang(′10001′,′数量′)":placeholder="lang(‘Ph_select_data’, ‘选择日期’)”
组件模板代码中三元运算结果
<el-button size="mini" @click="alterConsumption(row)">{{onlyShow?'查看':'修改'}}</el-button>
‘查看’:‘查看’ ::‘修改’: ‘修改’
l a n g ( ′ 1000 2 ′ , ′ 查看 ′ ) 同上 lang(‘10002’, ‘查看’)同上 lang(′10002′,′查看′)同上lang(‘10003’, ‘修改’)同上
js 中方法参数值
this.$XModal.message("请勾选批量设置", "error"); this.$XModal.confirm('您确定要设置吗?') this.$confirm("确定要删除此记录吗 ?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", })
message("请勾选批量设置"message('请勾选要批量设置’confirm("您确定要设置吗?"confirm(‘您确定要设置吗?’“提示”,confirmButtonText: “确定”,cancelButtonText: “取消”,
message(this. l a n g ( ′ 1000 4 ′ , ′ 请勾选批量设置 ′ ) 同上 c o n f i r m ( t h i s . lang(‘10004’, ‘请勾选批量设置’)同上confirm(this. lang(′10004′,′请勾选批量设置′)同上confirm(this.lang(‘10005’, ‘您确定要设置吗?’)同上this. l a n g ( ′ 1000 6 ′ , ′ 提示 ′ ) , c o n f i r m B u t t o n T e x t : t h i s . lang(‘10006’, ‘提示’),confirmButtonText: this. lang(′10006′,′提示′),confirmButtonText:this.lang(‘10007’, ‘确定’)cancelButtonText: this.$lang(‘10008’, ‘取消’)
js 中对象属性赋值
`this. X M o d a l . m e s s a g e ( m e s s a g e : " 保存失败 " , s t a t u s : " e r r o r " ) ; t h i s . XModal.message({ message: “保存失败”, status: “error” }); this. XModal.message(message:“保存失败”,status:“error”);this.message({ message: ‘请选择要设置的物料!’, type: ‘warning’ }); this. X M o d a l . a l e r t ( m e s s a g e : " 请选择附件分类 " , s t a t u s : " w a r n i n g " , ) ; t h i s . XModal.alert({ message: “请选择附件分类”, status: “warning”, }); this. XModal.alert(message:“请选择附件分类”,status:“warning”,);this.XModal.alert({ status: “error”, title: “删除错误”, message: response.msg
“服务器删除发生错误”, });`
js 中 || 赋值
`this.$XModal.alert({ status: “error”, title: “删除错误”, message: response.msg
“服务器删除发生错误”, });`
替换的脚本代码如下:
const fs = require('fs');
const path = require('path');// 读取 cn.json 文件并解析 JSON 数据
function loadTranslations() {const cnJsonPath = path.join(__dirname, 'src', 'lang', 'cn.json');const content = fs.readFileSync(cnJsonPath, 'utf-8');return JSON.parse(content);
}// 判断字符串是否以指定前缀开头
function startsWith(str, prefix) {return str.startsWith(prefix);
}/*** 每个键值对的场景匹配* @param {String} fileContent 文件内容* @param {String} key 国际化变量名* @param {String} value 中文字符串*/
function replaceAllScene(fileContent, key, value) {// 场景:>取消<let searchValue = `>${value}<`;let replaceValue = `>{{$lang('${key}', '~${value}~')}}<`;fileContent = fileContent.split(searchValue).join(replaceValue); // 场景:title="数量"searchValue = `title="${value}"`;replaceValue = `:title="$lang('${key}', '~${value}~')"`;fileContent = fileContent.split(searchValue).join(replaceValue);// 场景:placeholder="选择日期"searchValue = `placeholder="${value}"`;replaceValue = `:placeholder="$lang('${key}', '~${value}~')"`;fileContent = fileContent.split(searchValue).join(replaceValue);// 场景:message("请勾选批量设置"searchValue = `message("${value}"`;replaceValue = `message(this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);// 场景:message('请勾选批量设置'searchValue = `message('${value}'`;replaceValue = `message(this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);// 场景:confirm("您确定要设置业务损耗吗?"searchValue = `confirm("${value}"`;replaceValue = `confirm(this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);// 场景:confirm('您确定要设置业务损耗吗?'searchValue = `confirm('${value}'`;replaceValue = `confirm(this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);// confirmButtonText: "确定",searchValue = `confirmButtonText: "${value}",`;replaceValue = `confirmButtonText: this.$lang('${key}', '~${value}~'),`;fileContent = fileContent.split(searchValue).join(replaceValue);// cancelButtonText: "取消",searchValue = `cancelButtonText: "${value}",`;replaceValue = `cancelButtonText: this.$lang('${key}', '~${value}~'),`;fileContent = fileContent.split(searchValue).join(replaceValue);// message: "保存失败"searchValue = `message: "${value}"`;replaceValue = `message: this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);// message: '保存失败''searchValue = `message: '${value}'`;replaceValue = `message: this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);// title: "删除错误"searchValue = `title: "${value}"`;replaceValue = `title: this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);// title: '删除错误'searchValue = `title: '${value}'`;replaceValue = `title: this.$lang('${key}', '~${value}~')`;fileContent = fileContent.split(searchValue).join(replaceValue);return fileContent;
}// 在给定文件中替换指定的字符串
function replaceStringsInFile(filePath, replacements) {const content = fs.readFileSync(filePath, 'utf-8');let newContent = content;for (const [key, value] of Object.entries(replacements)) {// 如果匹配到的字符串前面存在 "message(",则去掉左右两边的双引号 //const searchValue = startsWith(value, 'message("') ? value.slice(8, -1) : value;newContent = replaceAllScene(newContent, key, value);//newContent = newContent.split(searchValue).join("$lang('" + key + "',')" + searchValue + "'");}if (newContent !== content) {fs.writeFileSync(filePath, newContent, 'utf-8');console.log(`Replaced strings in ${filePath}`);}
}// 在指定目录下处理所有文件
function processDirectory(directoryPath, replacements) {const files = fs.readdirSync(directoryPath);files.forEach((fileName) => {const filePath = path.join(directoryPath, fileName);const stats = fs.statSync(filePath);if (stats.isDirectory()) {processDirectory(filePath, replacements);} else if (stats.isFile()) {replaceStringsInFile(filePath, replacements);}});
}function main() {const translations = loadTranslations();const componentsDirectory = path.join(__dirname, 'src', 'components');const pagesDirPath = path.join(__dirname, 'src', 'pages');processDirectory(componentsDirectory, translations);processDirectory(pagesDirPath, translations);
}main();
到此,我们就完成了前端代码的国际化实现;
我们为什么要把原中文作为 国际化方法 $lang 的第二个参数呢?
因为,如果代码文件中看不到中文,修改代码的时候太难找了,你只能看到国际化数字编码。
建议
建议是在前端一开始开发的时候,就把需要国际化的地方都写成 $lang(‘中文’),包括模板代码和js代码中,
这样后期替换更精确,而且一开始开发人员也不用去管国际化,
并且,我们在提取代码中文时,就可以按 $lang(‘中文’) 这个格式精确提取了,国际化处理后就变成 $lang(‘国际化编码’,‘中文’) ,这样我们在第二次再提取时,就不会重复提取已经国际化处理后的代码中文了。
相关文章:
记一次前端Vue项目国际化解决方案
背景 有一个vue项目,要实现国际化功能,能够切换中英文显示,因为该项目系统的用户包括了国内和国外用户。 需求 1、页面表单上的所有中文标签要国际化,包括表单属性标签、表格列头标签等, title“数量”;…...
JS进阶-手写Promise
一、什么是Promise 在Promise A规范中规定,Promise是一个有一个符合规范的then方法的对象或者函数。 1.关于then then接收onFulfilled和onRejected两个可选参数;then必须返回一个新的Promise对象;如果onFulfilled是一个函数 在状态切换为f…...
PCL点云库入门——PCL库点云滤波算法之直通滤波(PassThrough)和条件滤波(ConditionalRemoval)
0、滤波算法概述 PCL点云库中的滤波算法是处理点云数据不可或缺的一部分,它们能够有效地去除噪声、提取特征或进行数据降维。例如,使用体素网格滤波(VoxelGrid)可以减少点云数据量,同时保留重要的形状特征。此外&#…...
ioctl回顾
一、ioctl协议的命令组成 cmd本质为一个32位的数字,共分为四段: [31-30]:读写方向dir,分为无数据(_IO)、读数据(_IOR)、写数据(_IOW)、读写数据(_IOWR)四种模式; [29-16]:传递数据的大小size,一般利用其宏_IO、_IOR…...
jquery-validate在前端数据校验中的应用以及remote异步调用实践-以若依为例
目录 前言 一、关于Jquery Validate组件 1、validate是什么 2、内置验证方式及触发方式 3、自定义验证规则 二、基本验证实战以及Remote验证 1、基本验证实现 2、remote校验方式 三、总结 前言 随着技术的不断演进,在我们的日常开发过程中,大家一…...
如何重新设置VSCode的密钥环密码?
故障现象: 忘记了Vscode的这个密码: Enter password to unlock An application wants access to the keyring “Default ke... Password: The unlock password was incorrect Cancel Unlock 解决办法: 1.任意terminal下,输入如下…...
Android--java实现手机亮度控制
文章目录 1、开发需求2、运行环境3、主要文件4、布局文件信息5、手机界面控制代码6、debug 1、开发需求 需求:开发一个Android apk实现手机亮度控制 2、运行环境 Android studio最新版本 3、主要文件 app\src\main\AndroidManifest.xml app\src\main\res\layou…...
原点安全再次入选信通院 2024 大数据“星河”案例
近日,中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会(CCSA TC601)共同组织开展的 2024 大数据“星河(Galaxy)”案例征集活动结果正式公布。由工银瑞信基金管理有限公司、北京原点数安科技有限公司联…...
torch.nn.init 模块介绍
PyTorch 的 torch.nn.init 模块提供了一组用于初始化张量或模型参数的函数。这些初始化方法对深度学习模型的训练收敛速度和性能有显著影响,正确选择初始化方法可以避免梯度消失或爆炸等问题。 模块功能 torch.nn.init 提供了一系列函数,用于对张量(如权重或偏置)进行初始…...
人工智能与物联网:从智慧家居到智能城市的未来蓝图
引言:未来已来,智能化的世界 想象一下,一个早晨,智能闹钟根据你的睡眠状态自动调整叫醒时间,咖啡机早已备好热腾腾的咖啡,窗帘缓缓拉开,迎接清晨的阳光。这不是科幻小说中的场景,而是…...
极狐GitLab 17.7正式发布,可从 GitLab 丝滑迁移至极狐GitLab【一】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...
纯Dart Flutter库适配HarmonyOS
纯Dart Flutter库适配HarmonyOS介绍: Flutter基本组件、Flutter布局组件、Flutter图片组件、Flutter字体、Flutter图标、Fluter路由、flutter动画、 Flutter表单、flutter异步等,纯Dart库无需任何处理,可以直接编译成HarmonyOs应用。 具体步…...
【R语言遥感技术】“R+遥感”的水环境综合评价方法
R语言在遥感领域中是一个强大的工具,它提供了一系列的功能和优势,使得遥感数据的分析和应用更加高效和灵活。以下是R语言在遥感中的具体应用: 数据处理:R语言可以处理和清洗遥感数据,包括数据转换、滤波处理、去噪和数…...
软件工程三 需求获取与结构化分析方法(需求分析、功能建模、数据建模、行为建模、数据字典等)
包括内容如下: 1. 需求获取与需求分析阶段的任务 2. 结构化分析方法 3. 系统需求规格说明 4. 需求评审 5. 需求管理 3.1 需求获取与需求分析阶段的任务 3.1包括: 需求获取的任务和原则 需求获取的过程 软件需求分析阶段的任务 3.1.1需求获取的任…...
Python 抽象基类 ABC :从实践到优雅
今天我们来聊聊 Python 中的抽象基类(Abstract Base Class,简称 ABC)。虽然这个概念在 Python 中已经存在很久了,但在日常开发中,很多人可能用得并不多,或者用得不够优雅。 让我们从一个实际场景开始&…...
Elasticsearch检索方案之一:使用from+size实现分页
前面两篇文章介绍了elasticsearch以及Kibana的安装,检索引擎以及可视化工具都已经安装完成,接下来介绍下如何使用golang的sdk实现简单的分页查询。 1、下载Elastic官方golang sdk 在讲解elasticsearch检索之前,需要先把golang的环境安装好&…...
知识图谱+大模型:打造全新智慧城市底层架构
在数字化时代,智慧城市的建设正迎来新一轮的变革。本文将探讨如何结合知识图谱和大模型技术,构建智慧城市的全新底层架构,以应对日益增长的数据量和复杂性,提升城市管理的智能化水平。 知识图谱:智慧城市的知识库 知识…...
Flutter开发HarmonyOS 鸿蒙App的好处、能力以及把Flutter项目打包成鸿蒙应用
Flutter开发HarmonyOS的好处: Flutter是谷歌公司开发的一款开源、免费的UI框架,可以让我们快速的在Android和iOS上构建高质量App。它最大的特点就是跨平台、以及高性能。 目前 Flutter 已经支持 iOS、Android、Web、Windows、macOS、Linux 的跨平台开发…...
vscode安装fortran插件配置
本章教程,主要介绍如何在vscode上安装fortran插件,以便于使用vscode运行fortran编写的程序。 一、安装插件 首先在插件商店安装这个扩展插件 然后再把Code Runner扩展插件装上 二、下载mingw64 通过网盘分享的文件:mingw64 链接: https://pan.baidu.com/s/1fwS-CwC7dgI...
容器化平台Docker初识
Docker 是一个容器化平台,可以让你打包、分发和运行应用程序。它的核心思想是通过容器技术,让应用程序在任何环境下都能以一致的方式运行。 通俗易懂的理解 快餐盒的比喻: 假设你做了一顿饭(开发了一个应用程序)&#…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
