本文正在参加「金石方案」
在上上节 《花3分钟,重拾开发效率神器 → Live Templates》 中,杰哥整活写了个 自界说中文转英文的Expression。认为从此就能够告别编写布局xml时为View控件设置id的繁杂操作:
翻开翻译软件 → 输入控件称号 → 翻译 → Copy到xml中 → 单词字母全转化为小写 → 添加下划线
但在实际开发运用中,尽管能用,但存在下述问题:
- 细微卡顿:机制原因,每个文字都会触发一次转化,多个同步恳求导致呼应较慢。
- 不支持连续输入中文:输入完中文,主动转化后,再次输入,焦点会跳到外面进行输入。
- 没界说模板的View不支持:得覆盖一堆不常用的View,谁顶得住啊?
综上,Live Template并不能满意我日常的开发需求,需求另辟蹊径来补齐这些短板。其实,我要的并不多:
仅仅希望 中文能主动转化成英文 罢了~
在运用IDEA插件 Translation 划词翻译时,我忽然灵机一动,完全能够自己写个相似的插件:
- 1、选中要翻译的文本
- 2、右键(或快捷键)
- 3、对选中文本进行翻译
- 4、处理翻译成果
- 5、替换选中的文本
看着进程有点多,其实345是主动完结的,话不多说,直接着手完成一波~
0x1、插件开发初体验
先依照网上烂大街的基础教程,搭个环境,写个Demo跑起来,然后再自界说。不过上来就遇到一个大坑…
① 大坑:JDK版别问题
不主张用Android Studio开发IDEA插件,得装一堆乱七八糟的库,直接用IDEA,社区版(免费版) 也支持IDEA插件开发。笔者用的最新的IDEA,但在新建项目时并没有找到:InteliJ Platform Plugin,只找到一个 IDE Plugin:
感觉做了功用合并啥的,还支持Kotlin,尝尝鲜?点下OK却提示:
擦,需求Java 11,em…前阵子群里有人说一些插件晋级后,就用不了,估摸着跟这个有联系吧。杰哥还在用Java 8,妥妥是不能上的,只能下一个旧一点的IDEA版别了。
随手下了个 2021.3.3 的版别,新建项目后,编译报错,即使我手动指定了低版别的Gradle也于事无补:
报错原因:IDEA、gradle-intellij-plugin(Gradle插件)、gradle、JDK 有版别对应联系,不匹配就GG了。先经过 IDEA版别 → 找到Gradle版别:Third-Party Software and Licenses
选择当前版别,比如我的2021.3,然后页面搜 Gradle:
用的Gradle 7.1,从7.0开始要求 最低要求的JDK版别为JDK 11,解法有两:
解法1:修改IDEA的Gradle JVM版别为11+
解法2:换运用Gradle 6.x的IDEA
笔者选用的解法2,终究定位到 2021.1.3 选用Gradle 6.8:
翻开官网 Other Versions 直接定位到 2021.1.3的社区版 下载安装。
新建项目后依然报错,默许选用最新的 org.jetbrains.intellij 需求手动指定下版别。
能够看到发行日期应该是2021.6.30,在 mvnrepository 搜 org.jetbrains.intellij 定位到大概的时刻点:
翻开build.gradle把版别号改为1.1:
然后就能够编译经过,开始写代码,但当我编写好Action预备运转时,又翻车了:
问下Bing AI:
点开 platform-api.jar包的 MANIFEST.MF 文件
我服了,看来想运用Java8还得运用更旧的IDEA,又换成了 2020.2.4 的版别,终于能够了!
② 创立插件工程
创立方法有两种:
这儿选第二种:
点击OK,静待项目编译成功,看下 插件核心装备文件 → plugin.xml:
不难看出它用于装备:插件称号、作者信息、插件介绍、Action 等信息。
接着看下 项目依靠装备文件 → build.gradle:
也不难看出它用于装备:第三方依靠、插件版别、插件版别更新记录 等信息。
③ 创立插件进口Action
Action是IDEA供给的 事件呼应处理器,能够经过它来自界说一些事件处理逻辑/动作。
依次:右键java目录 (没有自己手动建) → New → Plugin DevKit → Action:
按需填写Action的相关装备项:
点击OK,会生成一个Action类:
plugin.xml 也会主动生成此Action的装备信息:
接着随便加个弹窗的代码玩玩:
④ 编译运转
依次:点击右侧Gradle面板 → Tasks → intellij → runIde,然后会发动一个默许安装了这个插件的IDEA~
但报错了:编码GBK的不可映射字符 解决方法:build.gradle中增加utf-8的编码方法:
tasks.withType(JavaCompile) {
options.encoding ="UTF-8"
}
再次编译,如愿发动了一个新的IDEA,点击 菜单栏的Tools 能够看到咱们界说的动作称号:
接着点击,会有一个弹窗:
o(╯□╰)o 才发现我把标题和内容参数传错了,Demo暂时体验到这,接着来写咱们的功用插件了~
0x2、完成自界说插件
想了一下,我这种 翻译替换文本的需求 其实能够归类为 字符串格式化,那插件称号就叫 CpStringFormat 吧。
别的,除了 翻译成果转小写加下划线 的需求外,有时还需求 翻译成果单词首字母大写,索性就弄个 有子级的右键菜单吧,今后有啥常用的字符串格式化,直接加~
① 带子级的右键菜单
在 《自界说 idea 插件开发》 这篇文章里,看到了这个:
不难看出 group标签 能完成咱们的需求,红框框住的也是三个关键,直接创立两个Action,简略装备下:
<actions>
<action id="CnEnLowerULine" class="CnEnLowerULineAction" text="中->英 (小写+下划线)"
description="中->英 (小写+下划线)">
<!-- 设置快捷键为ctrl+1 -->
<keyboard-shortcut keymap="$default" first-keystroke="ctrl 1"/>
</action>
<action id="CnEnWord" class="CnEnWordAction" text="中->英(单词首字母大写)"
description="中->英(单词首字母大写)">
<!-- 设置快捷键为ctrl+2 -->
<keyboard-shortcut keymap="$default" first-keystroke="ctrl 2"/>
</action>
<group popup="true" id="StringFormatMenu" text="CpStringFormat" icon="/icons/icon.png">
<reference ref="CnEnLowerULine"/>
<!-- 设置切割线 -->
<separator/>
<reference ref="CnEnWord"/>
<!-- 添加到右侧菜单栏,并置顶 -->
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
</group>
</actions>
我这儿还弄了一个 喵喵怪的logo (经屡次测试,发现 16×16 的显现作用加载),运转runIde后右键看看作用:
② 获取选中文本
东西代码类代码与其满大街找,不如直接问ChatGPT:
不难看出 红框部分 就是获取选中文本的代码,加了个弹窗验证下,公然输出选中的文本~
③ 替换选中的文本
接着我又Copy了 黄框部分 代码尝试替换文本,但发现并 没有替换成功,有问了下ChatGPT:
供给的方案并不太行,后面在另一个开源项目:judasn/ChineseTypography-IDEA-Plugin 找到了解决方法:
再问ChatGPT:
给出的示例代码也和项目给出的代码千篇一律,最终还给出了一个主张:
当然,对这个主张持保留意见,2333,毕竟AI偶然也会瞎编~
④ 调用百度翻译API
之前的翻译脚本是依据Groovy开发的,现在得转成Java代码,这种繁琐的工作,同样能够交给ChatGPT:
但转化后的代码引入了第三方库Jackson,插件的话,当然是尽可能不依靠第三方的,这样体积也会更小。
那就让ChatGPT不要引入第三方库:
能够看到,它运用到了最原始的 HttpURLConnection 发送恳求,Scanner 类读取呼应文本。
接着就是CV它给出的代码,修修剪剪 + 拆类 + 调试,得出真实的可用代码,先是 翻译的东西类 (TranslateUtil.java):
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.Scanner;
public class TranslateUtil {
private static final String appId = "xxx"; // 替换成自己的 App ID
private static final String appKey = "xxx"; // 替换成自己的 App Key
private static final String from = "zh"; // 原始言语
private static final String to = "en"; // 转化后的言语
static String fetchTranslateResult(String query) {
// 转化后的成果
String translation = null;
try {
// 结构salt参数
String salt = String.valueOf(System.currentTimeMillis());
// 结构Sign参数
String sign = generateMD5(appId + query + salt + appKey);
// 拼接恳求URL
String url = "http://api.fanyi.baidu.com/api/trans/vip/translate" +
"?q=" + URLEncoder.encode(query, "UTF-8") +
"&from=" + from +
"&to=" + to +
"&appid=" + appId +
"&salt=" + salt +
"&sign=" + sign;
// 呼应数据
String responseText = "";
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
Scanner scanner = new Scanner(connection.getInputStream(), "UTF-8");
responseText = scanner.useDelimiter("\\A").next();
scanner.close();
}
connection.disconnect();
if (responseText.contains("\"error_code\":")) {
translation = responseText;
} else {
// 解析翻译成果
int startIndex = responseText.indexOf("\"dst\":\"") + 7;
int endIndex = responseText.indexOf("\"", startIndex);
translation = responseText.substring(startIndex, endIndex);
}
} catch (Exception e) {
e.printStackTrace();
}
return translation;
}
// 生成MD5
public static String generateMD5(String string) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] hashBytes = messageDigest.digest(string.getBytes(StandardCharsets.UTF_8));
Formatter formatter = new Formatter();
for (byte b : hashBytes) {
formatter.format("%02x", b);
}
String hash = formatter.toString();
formatter.close();
return hash;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
然后是 翻译转小写然后拼接下划线的类 (CnEnLowerULineAction.java):
public class CnEnLowerULineAction extends AnAction {
@Override
public void actionPerformed(AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
SelectionModel selectionModel = editor.getSelectionModel();
String selectedText = selectionModel.getSelectedText();
if (selectedText == null) return;
Runnable writeAction = () -> {
String translation = TranslateUtil.fetchTranslateResult(selectedText).toLowerCase()
.replaceAll(" ", "_").replace(",", "");
editor.getDocument().replaceString(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd(), translation);
};
WriteCommandAction.runWriteCommandAction(editor.getProject(), writeAction);
}
}
最终是 翻译后单词首字母大写拼接的类 (CnEnWordAction.java):
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
public class CnEnWordAction extends AnAction {
@Override
public void actionPerformed(AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
SelectionModel selectionModel = editor.getSelectionModel();
String selectedText = selectionModel.getSelectedText();
if (selectedText == null) return;
Runnable writeAction = () -> {
// 依据空格切割成单词数组,遍历首字母大写拼接
String[] words = TranslateUtil.fetchTranslateResult(selectedText).split(" ");
if (words.length == 0) return;
StringBuilder sb = new StringBuilder();
for (String word : words) sb.append(word.substring(0, 1).toUpperCase()).append(word.substring(1));
editor.getDocument().replaceString(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd(),
sb.toString().replace(",", ""));
};
WriteCommandAction.runWriteCommandAction(editor.getProject(), writeAction);
}
}
⑤ 看下作用
万事俱备,接着就是运转验证了,直接上图:
前两个经过右键选中进行转化,后面直接用快捷键 ctrl+1/2 快速转化,简直不要太爽!!!此处应有掌声:
0x3、小结
本节简略记录了杰哥开发 划词翻译替换插件 的进程,先是踩了个 JDK版别问题的大坑,然后抄网上的教程写了个 简略Demo跑起来,最终 凭借ChatGPT快速完结了划词翻译替换功用。当看到插件用起来那一刻,仍是成就感满满的~
由于用到了百度翻译的API,Key是写死的,不太好Share。下节来给它写个设置页,答应填写读者自己的ID和Key,顺带演示下怎么到处插件包及上传至插件商场,敬请期待~