本文正在参加「金石方案」

 自定义IDEA插件 → 划词翻译替换(上)

在上上节 《花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

 自定义IDEA插件 → 划词翻译替换(上)

感觉做了功用合并啥的,还支持Kotlin,尝尝鲜?点下OK却提示:

 自定义IDEA插件 → 划词翻译替换(上)

擦,需求Java 11,em…前阵子群里有人说一些插件晋级后,就用不了,估摸着跟这个有联系吧。杰哥还在用Java 8,妥妥是不能上的,只能下一个旧一点的IDEA版别了。

随手下了个 2021.3.3 的版别,新建项目后,编译报错,即使我手动指定了低版别的Gradle也于事无补:

 自定义IDEA插件 → 划词翻译替换(上)

报错原因:IDEA、gradle-intellij-plugin(Gradle插件)、gradle、JDK 有版别对应联系,不匹配就GG了。先经过 IDEA版别 → 找到Gradle版别:Third-Party Software and Licenses

 自定义IDEA插件 → 划词翻译替换(上)

选择当前版别,比如我的2021.3,然后页面搜 Gradle

 自定义IDEA插件 → 划词翻译替换(上)

用的Gradle 7.1,从7.0开始要求 最低要求的JDK版别为JDK 11,解法有两:

解法1:修改IDEA的Gradle JVM版别为11+

 自定义IDEA插件 → 划词翻译替换(上)

解法2:换运用Gradle 6.x的IDEA

笔者选用的解法2,终究定位到 2021.1.3 选用Gradle 6.8:

 自定义IDEA插件 → 划词翻译替换(上)

翻开官网 Other Versions 直接定位到 2021.1.3的社区版 下载安装。

新建项目后依然报错,默许选用最新的 org.jetbrains.intellij 需求手动指定下版别。

 自定义IDEA插件 → 划词翻译替换(上)

能够看到发行日期应该是2021.6.30,在 mvnrepository 搜 org.jetbrains.intellij 定位到大概的时刻点:

 自定义IDEA插件 → 划词翻译替换(上)

翻开build.gradle把版别号改为1.1:

 自定义IDEA插件 → 划词翻译替换(上)

然后就能够编译经过,开始写代码,但当我编写好Action预备运转时,又翻车了

 自定义IDEA插件 → 划词翻译替换(上)

问下Bing AI:

 自定义IDEA插件 → 划词翻译替换(上)

点开 platform-api.jar包的 MANIFEST.MF 文件

 自定义IDEA插件 → 划词翻译替换(上)

我服了,看来想运用Java8还得运用更旧的IDEA,又换成了 2020.2.4 的版别,终于能够了!


② 创立插件工程

创立方法有两种:

 自定义IDEA插件 → 划词翻译替换(上)

这儿选第二种:

 自定义IDEA插件 → 划词翻译替换(上)

点击OK,静待项目编译成功,看下 插件核心装备文件plugin.xml

 自定义IDEA插件 → 划词翻译替换(上)

不难看出它用于装备:插件称号作者信息插件介绍Action 等信息。

接着看下 项目依靠装备文件build.gradle

 自定义IDEA插件 → 划词翻译替换(上)

也不难看出它用于装备:第三方依靠插件版别插件版别更新记录 等信息。


③ 创立插件进口Action

Action是IDEA供给的 事件呼应处理器,能够经过它来自界说一些事件处理逻辑/动作。

依次:右键java目录 (没有自己手动建)NewPlugin DevKitAction

 自定义IDEA插件 → 划词翻译替换(上)

按需填写Action的相关装备项:

 自定义IDEA插件 → 划词翻译替换(上)

点击OK,会生成一个Action类:

 自定义IDEA插件 → 划词翻译替换(上)

plugin.xml 也会主动生成此Action的装备信息:

 自定义IDEA插件 → 划词翻译替换(上)

接着随便加个弹窗的代码玩玩:

 自定义IDEA插件 → 划词翻译替换(上)


④ 编译运转

依次:点击右侧Gradle面板TasksintellijrunIde,然后会发动一个默许安装了这个插件的IDEA~

 自定义IDEA插件 → 划词翻译替换(上)

但报错了:编码GBK的不可映射字符 解决方法:build.gradle中增加utf-8的编码方法

tasks.withType(JavaCompile) {
    options.encoding ="UTF-8"
}

再次编译,如愿发动了一个新的IDEA,点击 菜单栏的Tools 能够看到咱们界说的动作称号:

 自定义IDEA插件 → 划词翻译替换(上)

接着点击,会有一个弹窗:

 自定义IDEA插件 → 划词翻译替换(上)

o(╯□╰)o 才发现我把标题和内容参数传错了,Demo暂时体验到这,接着来写咱们的功用插件了~


0x2、完成自界说插件

想了一下,我这种 翻译替换文本的需求 其实能够归类为 字符串格式化,那插件称号就叫 CpStringFormat 吧。

别的,除了 翻译成果转小写加下划线 的需求外,有时还需求 翻译成果单词首字母大写,索性就弄个 有子级的右键菜单吧,今后有啥常用的字符串格式化,直接加~

① 带子级的右键菜单

在 《自界说 idea 插件开发》 这篇文章里,看到了这个:

 自定义IDEA插件 → 划词翻译替换(上)

不难看出 group标签 能完成咱们的需求,红框框住的也是三个关键,直接创立两个Action,简略装备下:

<actions>
    <action id="CnEnLowerULine" class="CnEnLowerULineAction" text="中-&gt;英 (小写+下划线)"
            description="中-&gt;英 (小写+下划线)">
        <!--  设置快捷键为ctrl+1  -->
        <keyboard-shortcut keymap="$default" first-keystroke="ctrl 1"/>
    </action>
    <action id="CnEnWord" class="CnEnWordAction" text="中-&gt;英(单词首字母大写)"
            description="中-&gt;英(单词首字母大写)">
        <!--  设置快捷键为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后右键看看作用:

 自定义IDEA插件 → 划词翻译替换(上)


② 获取选中文本

 自定义IDEA插件 → 划词翻译替换(上)

东西代码类代码与其满大街找,不如直接问ChatGPT:

 自定义IDEA插件 → 划词翻译替换(上)

不难看出 红框部分 就是获取选中文本的代码,加了个弹窗验证下,公然输出选中的文本~


③ 替换选中的文本

接着我又Copy了 黄框部分 代码尝试替换文本,但发现并 没有替换成功,有问了下ChatGPT:

 自定义IDEA插件 → 划词翻译替换(上)

供给的方案并不太行,后面在另一个开源项目:judasn/ChineseTypography-IDEA-Plugin 找到了解决方法:

 自定义IDEA插件 → 划词翻译替换(上)

再问ChatGPT:

 自定义IDEA插件 → 划词翻译替换(上)

给出的示例代码也和项目给出的代码千篇一律,最终还给出了一个主张:

 自定义IDEA插件 → 划词翻译替换(上)

当然,对这个主张持保留意见,2333,毕竟AI偶然也会瞎编~


④ 调用百度翻译API

之前的翻译脚本是依据Groovy开发的,现在得转成Java代码,这种繁琐的工作,同样能够交给ChatGPT:

 自定义IDEA插件 → 划词翻译替换(上)

但转化后的代码引入了第三方库Jackson,插件的话,当然是尽可能不依靠第三方的,这样体积也会更小。

 自定义IDEA插件 → 划词翻译替换(上)

那就让ChatGPT不要引入第三方库:

 自定义IDEA插件 → 划词翻译替换(上)

能够看到,它运用到了最原始的 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);
    }
}

⑤ 看下作用

万事俱备,接着就是运转验证了,直接上图:

 自定义IDEA插件 → 划词翻译替换(上)

前两个经过右键选中进行转化,后面直接用快捷键 ctrl+1/2 快速转化,简直不要太爽!!!此处应有掌声:

 自定义IDEA插件 → 划词翻译替换(上)


0x3、小结

本节简略记录了杰哥开发 划词翻译替换插件 的进程,先是踩了个 JDK版别问题的大坑,然后抄网上的教程写了个 简略Demo跑起来,最终 凭借ChatGPT快速完结了划词翻译替换功用。当看到插件用起来那一刻,仍是成就感满满的~

 自定义IDEA插件 → 划词翻译替换(上)

由于用到了百度翻译的API,Key是写死的,不太好Share。下节来给它写个设置页,答应填写读者自己的ID和Key,顺带演示下怎么到处插件包及上传至插件商场,敬请期待~