天天看點

idea插件開發--服務-翻譯插件

gitee位址:https://gitee.com/jyq_18792721831/studyplugin.git

idea插件開發入門

idea插件開發–配置

idea插件開發–服務-翻譯插件

idea插件開發–元件–程式設計久坐提醒

idea插件開發--服務-翻譯插件

  • ​​介紹​​
  • ​​服務​​
  • ​​輕量級服務​​
  • ​​服務定義​​
  • ​​服務擷取​​
  • ​​執行個體​​
  • ​​目标​​
  • ​​分解​​
  • ​​準備線上翻譯資訊​​
  • ​​有道翻譯​​
  • ​​必應翻譯​​
  • ​​百度翻譯​​
  • ​​建立插件項目​​
  • ​​建立配置界面​​
  • ​​引入第三方依賴​​
  • ​​定義存儲的服務​​
  • ​​定義配置界面​​
  • ​​建立Action​​
  • ​​封裝抽象RestAPI​​
  • ​​有道翻譯​​
  • ​​百度翻譯​​
  • ​​廠商擴充​​
  • ​​編寫Action後續操作​​
  • ​​效果​​
  • ​​打包​​
  • ​​最後的最後​​
  • ​​總結​​

介紹

本次主要介紹idea中服務的相關内容,包括服務的種類,服務的定義,服務的擷取,以及服務的使用。

之後綜合idea插件的Action和簡單配置,實作一個較為實用的翻譯小插件,以此複習和鞏固idea插件的Action和簡單配置。

服務

在spring中,服務一般是單例的,使用起來也比較友善,自動注入。

idea插件平台也提供了類似的解決方案,允許我們建立單例的服務,然後在使用的時候擷取。

在idea插件項目中,通過​

​com.intellij.openapi.components.ComponentManager​

​​接口擷取,服務會在第一次調用的時候建立一個執行個體,而且在作用域範圍内,保證隻有一個執行個體。服務應該實作​

​Disposable​

​接口,用于登出服務。

​ComponentManager​

​接口有這幾個實作類

idea插件開發--服務-翻譯插件

最長用的也就是Application了

idea提供三種類型的服務:application,prject和module級别的服務。其作用域分别是全局,項目和子產品。子產品級别的服務需要慎用,因為當項目中子產品比較多的時候,會占用較多的記憶體和資源。

對于project和module級别的服務,可以注入project和module的對象,注入方式為在構造函數中增加Project和Module參數。因為這個注入的構造函數主要是用于參數注入,是以在使用的時候,盡可能避免在自己的代碼中調用。

輕量級服務

在2019.3版本之後,增加了另一種輕量級的服務,輕量級服務不需要在​

​plugin.xml​

​​中定義,隻需要增加​

​@Service​

​注解即可。

  • 輕量級服務必須是​

    ​final​

    ​修飾
  • 輕量級服務不推薦使用構造函數注入(根據文檔給出的示例,project對象還是能夠使用構造函數注入)
  • 如果服務用于存儲(​

    ​PersistentStateComponent​

    ​​,那麼需要增加參數​

    ​roamingType=RoamingtType.DISABLED​

    ​)

服務定義

如果不是輕量級服務,那麼需要在​

​plugin.xml​

​​中定義,定義需要在​

​extensions​

​​節點下定義。定義不同作用域的服務,使用不同的标簽:​

​applicationService,projectService,moduleService​

定義服務,接口不是必須的,如果沒有接口,把接口和執行個體屬性設定成實作類就行。

服務擷取

可以使用​

​ComponentManager​

​​接口的執行個體擷取接口,常使用​

​ApplicationManager​

​擷取。

執行個體

目标

實作一個翻譯插件,說實話,本人英語水準是在有限,是以在開發編碼的時候,有時候給變量起名字,就需要翻譯好,在拷貝過來。

當然,現在在插件市場上也有許許多多的翻譯插件,做的功能齊全,使用友善。

我們這主要是學習插件開發,翻譯插件邏輯也不複雜,正好作為一個練手的項目。

需求:在編輯視窗,選中需要翻譯的中文,按下快捷鍵,翻譯為英文,并轉為駝峰形式,替換選中的中文。

分解

  1. 我們需要增加編輯視窗的Action,而且需要有快捷鍵
  2. 需要擷取選中的中文
  3. 需要翻譯接口
  4. 配置線上翻譯接口的參數
  5. 得到翻譯的英文,處理為駝峰形式
  6. 替換編輯視窗選中的中文

準備線上翻譯資訊

有道翻譯

在​​有道智雲AI開放平台 (youdao.com)​​新增賬號,自己玩足夠了。

然後建立文本翻譯的應用

idea插件開發--服務-翻譯插件

在個人資訊裡能看到應用id和秘鑰

idea插件開發--服務-翻譯插件

必應翻譯

在​​Bing for Partners helps businesses and developers succeed​​新增賬號,必應線上文本翻譯每月有免費的數量,個人使用完全足夠

idea插件開發--服務-翻譯插件

有了賬号後,根據​​快速入門:Translator 入門 - Azure Cognitive Services | Microsoft Docs​​選擇文本翻譯即可

idea插件開發--服務-翻譯插件

注冊需要visa卡等進行驗證,如果沒有就跳過(我就沒有)

idea插件開發--服務-翻譯插件

百度翻譯

在​​百度翻譯開放平台 (baidu.com)​​新增賬號,選擇通用翻譯

idea插件開發--服務-翻譯插件

這裡需要進行實名認證,并注冊為個人開發者,然後在控制台就能看到自己選擇的服務了

idea插件開發--服務-翻譯插件

在最下面有應用id和秘鑰

idea插件開發--服務-翻譯插件

建立插件項目

建立如下項目

idea插件開發--服務-翻譯插件

在​

​plugin.xml​

​中定義好插件的資訊

idea插件開發--服務-翻譯插件

建立配置界面

在ui包下建立配置的資訊

idea插件開發--服務-翻譯插件

然後通過拖動的方式增加控件

idea插件開發--服務-翻譯插件

需要注意,使用密碼輸入框,而不是文本輸入框

記得選擇生成源代碼

idea插件開發--服務-翻譯插件

在源代碼中,我們增加方法,用于擷取資料,這樣就不把控件進行暴露了

編譯才會生成源代碼

然後生成測試ui的main方法(需要給最外層的JPanel設定屬性名字)

idea插件開發--服務-翻譯插件

運作main方法就可以看看我們的界面效果了

idea插件開發--服務-翻譯插件

需要注意,我們需要将最外層的JPannel暴露到外面,雖然自己生成了一個暴露最外層JPannel的方法,但是不介意使用。

如果使用者是修改配置,我們還需要增加方法,用于設定控件的值

idea插件開發--服務-翻譯插件

引入第三方依賴

我們使用lombok注解進行暴露,要是用lombok就需要在項目中加入lombok的依賴。

還記得我們的項目結構中,有個lib的檔案夾。

lib檔案夾就是放第三方依賴的jar包的。

首選需要在​​Maven Central Repository Search​​搜尋lombok插件,然後下載下傳jar包,并将jar拷貝到lib目錄下。

idea插件開發--服務-翻譯插件

然後将增加的jar包加入項目

idea插件開發--服務-翻譯插件

當然,其他第三方jar包也是這樣增加的。

定義存儲的服務

我們使用之前說的最簡單的存儲方式,然後對這種方式進行封裝。服務使用輕量級的服務,直接使用注解,也不需要實作登出的方法。

import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.components.Service;

@Service
public final class TranslateAppInfoService {

    private final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();

    public void save(String key, String value) {
        propertiesComponent.setValue(key, value);
    }

    public String get(String key, String defaultValue) {
        return propertiesComponent.getValue(key, defaultValue);
    }

    public String get(String key) {
        return get(key, "");
    }

}      

我們封裝三個方法,一個是存儲,一個是擷取,一個是帶有預設值的擷取。

很簡單,這裡定義的存儲服務,會在​

​SearchableConfiguable​

​的實作類中使用。

定義配置界面

我們建立好了配置界面的UI後,還需要配置到setting下,idea插件開發–配置_a18792721831的部落格

首先建立​

​SearchableConfigurable​

​接口的實作類,傳輸配置id,配置名字。

在定義配置界面的時候,首先從存儲服務中擷取已經儲存的配置,然後把配置放入控件中,因為使用者可能隻想修改一部分,如果不設定,就會被空值覆寫,而且不設定,使用者也不知道哪些已經配置過了。是以需要在建立好控件from後,擷取已有配置,設定到控件。

如果使用者根本無修改,此時給​

​isModified​

​方法傳回false,表示應用按鈕不可用,無修改,無需儲存,無需調用apply方法。

在apply方法中,則是将控件中輸入的值,調用存儲服務,存儲起來。

完整代碼如下

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.text.StringUtil;
import com.study.plugin.translate.service.TranslateAppInfoService;
import com.study.plugin.translate.ui.TranslateConfigUI;
import com.study.plugin.translate.utils.PluginAppKeys;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.JComponent;

public class TranslateAppInfoConfig implements SearchableConfigurable, PluginAppKeys {

    private TranslateConfigUI ui = new TranslateConfigUI();

    private TranslateAppInfoService appInfoService = ApplicationManager.getApplication().getService(TranslateAppInfoService.class);

    @Override
    public @NotNull
    @NonNls
    String getId() {
        return PLUGIN_CONFIG_ID;
    }

    @Override
    public @NlsContexts.ConfigurableName String getDisplayName() {
        return PLUGIN_CONFIG_NAME;
    }

    @Override
    public @Nullable
    JComponent createComponent() {
        ui.setYoudaoAppId(appInfoService.get(YOUDAO_APP_ID_SAVE_KEY, ""));
        return ui.getRootJPanel();
    }

    @Override
    public boolean isModified() {
        if (!appInfoService.get(YOUDAO_APP_ID_SAVE_KEY).equals(ui.getYoudaoAppId()) ||
                !appInfoService.get(YOUDAO_APP_SECRET_SAVE_KEY).equals(ui.getYoudaoAppSecret()) ||
                !appInfoService.get(BIYING_APP_ID_SAVE_KEY).equals(ui.getBiyingAppId()) ||
                !appInfoService.get(BIYING_APP_SECRET_SAVE_KEY).equals(ui.getBiyingAppSecret()) ||
                !appInfoService.get(BAIDU_APP_ID_SAVE_KEY).equals(ui.getBaiduAppId()) ||
                !appInfoService.get(BAIDU_APP_SECRET_SAVE_KEY).equals(ui.getBaiduAppSecret())) {
            return true;
        }
        return false;
    }

    @Override
    public void apply() throws ConfigurationException {
        String youdaoAppId = ui.getYoudaoAppId();
        String youdaoAppSecret = ui.getYoudaoAppSecret();
        if (StringUtil.isNotEmpty(youdaoAppId) && StringUtil.isNotEmpty(youdaoAppSecret)) {
            appInfoService.save(YOUDAO_APP_ID_SAVE_KEY, youdaoAppId);
            appInfoService.save(YOUDAO_APP_SECRET_SAVE_KEY, youdaoAppSecret);
        }
        String biyingAppId = ui.getBiyingAppId();
        String biyingAppSecret = ui.getBiyingAppSecret();
        if (StringUtil.isNotEmpty(biyingAppId) && StringUtil.isNotEmpty(biyingAppSecret)) {
            appInfoService.save(BIYING_APP_ID_SAVE_KEY, biyingAppId);
            appInfoService.save(BIYING_APP_SECRET_SAVE_KEY, biyingAppSecret);
        }
        String baiduAppId = ui.getBaiduAppId();
        String baiduAppSecret = ui.getBaiduAppSecret();
        if (StringUtil.isNotEmpty(baiduAppId) && StringUtil.isNotEmpty(baiduAppSecret)) {
            appInfoService.save(BAIDU_APP_ID_SAVE_KEY, baiduAppId);
            appInfoService.save(BAIDU_APP_SECRET_SAVE_KEY, baiduAppSecret);
        }
    }
}      

最後别忘記在​

​plugin.xml​

​中注冊

<extensions defaultExtensionNs="com.intellij">
    <!-- Add your extensions here -->
    <notificationGroup displayType="BALLOON" id="simpleconfig.notification.balloon" isLogByDefault="false"/>
    <applicationConfigurable parentId="tools" instance="com.study.plugin.translate.config.TranslateAppInfoConfig"
                             id="com.study.plugin.translate。setting.config.id" displayName="線上翻譯資訊"/>
  </extensions>      

當做好這些後,就可以調試一下之前寫的代碼了

idea插件開發--服務-翻譯插件

還是很不錯的,簡單明了,記得測試下存儲服務是否正常。

建立Action

建立Action很簡單,之前就用過:idea插件開發入門_a18792721831的部落格

我們建立Action,快捷鍵還是使用​

​ctrl+alt+;​

idea插件開發--服務-翻譯插件

在觸發後,首先擷取選中的文本,然後調用翻譯的服務(假設我們已經寫好了一個翻譯的RestAPI)

idea插件開發--服務-翻譯插件

以此來觸發翻譯,等翻譯至少有一個可用時,在回頭基礎開發這裡的操作。

封裝抽象RestAPI

因為我們使用的都是線上API的方式請求的,是以需要使用URL請求。

為了使用更加友善,我們使用spring的restTemplate接口進行請求。

首先從maven倉庫下載下傳spring-beans,spring-context,spring-web,spring-core四個依賴,并加入項目。

idea插件開發--服務-翻譯插件

然後封裝Rest請求的抽象類。

抽象類中主要是restTemplate的對象和存儲服務的對象,因為對所有的各個廠商的線上翻譯平台來說,我們的restTemplate和存儲服務使用同一個就可以了,而且我們定義子類必須實作翻譯方法,翻譯方法傳入待翻譯的中文,傳回翻譯後的英文或者空串。

一些公共的工具方法,也可以放在抽象類中,比如加密

import com.intellij.openapi.application.ApplicationManager;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public abstract class TranslateRestService {

    protected RestTemplate restTemplate;

    protected volatile AtomicBoolean isInit = new AtomicBoolean(Boolean.FALSE);

    protected TranslateAppInfoService appInfoService = ApplicationManager.getApplication().getService(TranslateAppInfoService.class);

    protected synchronized void init() {
        // 如果已經初始化了,直接結束
        if (isInit.get()) {
            return;
        }
        // 連接配接池
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(4);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(2);
        // 我們目前隻有2個線上翻譯可用,每個翻譯2個線程用于Rest請求,是以設定最大連接配接4,每個翻譯api是2個并發
        // 用戶端構造器
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
        // 建立restTemplate
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setHttpClient(httpClientBuilder.build());
        httpRequestFactory.setConnectTimeout(6000);
        httpRequestFactory.setConnectTimeout(6000);
        httpRequestFactory.setReadTimeout(12000);
        RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
        this.restTemplate = restTemplate;
        isInit.compareAndSet(Boolean.FALSE, Boolean.TRUE);
    }

    /**
     * 加密
     *
     * @param string
     * @return
     */
    protected static String getDigest(String string, String key) {
        if (string == null) {
            return null;
        }
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        byte[] btInput = string.getBytes(StandardCharsets.UTF_8);
        try {
            MessageDigest mdInst = MessageDigest.getInstance(key);
            mdInst.update(btInput);
            byte[] md = mdInst.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    public abstract String translate(String word);
}      

當抽象方法完成後,就需要針對各個廠商實作翻譯的子類。

有道翻譯

根據有道翻譯的api​​産品文檔-自然語言翻譯服務 (youdao.com)​​,根據裡面的示例程式,拷貝相關的請求參數封裝的代碼到子類中,然後調用父類的存儲服務,進行請求app_id,app_secret的讀取,并使用父類的restTemplate進行請求,并傳回。

import com.intellij.openapi.components.Service;
import com.study.plugin.translate.beans.YoudaoTranslateResult;
import com.study.plugin.translate.utils.PluginAppKeys;
import java.util.HashMap;
import java.util.Map;

@Service
public final class YoudaoTranslateRestService extends TranslateRestService implements PluginAppKeys {

    private String HOST = "https://openapi.youdao.com/api";

    private String APP_ID = appInfoService.get(YOUDAO_APP_ID_SAVE_KEY);

    private String APP_SECRET = appInfoService.get(YOUDAO_APP_SECRET_SAVE_KEY);

    private String DIGEST_KEY = "SHA-256";

    public YoudaoTranslateRestService() {
        super();
        if (!isInit.get()) {
            super.init();
        }
    }


    @Override
    public String translate(String word) {
        Map<String, String> params = getParams(word);
        StringBuilder builder = new StringBuilder(HOST + "?");
        params.entrySet().forEach(ent -> {
            builder.append(ent.getKey() + "=" + ent.getValue() + "&");
        });
        String requestUrl = builder.toString();
        requestUrl = requestUrl.substring(0, requestUrl.length() - 1);
        YoudaoTranslateResult result = restTemplate.getForObject(requestUrl, YoudaoTranslateResult.class);
        if (result.getErrorCode().equals("0")) {
            return result.getTranslation().get(0);
        }
        return null;
    }

    private Map<String, String> getParams(String word) {
        Map<String, String> params = new HashMap<>();
        String salt = String.valueOf(System.currentTimeMillis());
        params.put("from", "auto");
        params.put("to", "en");
        params.put("signType", "v3");
        String curtime = String.valueOf(System.currentTimeMillis() / 1000);
        params.put("curtime", curtime);
        String signStr = APP_ID + truncate(word) + salt + curtime + APP_SECRET;
        String sign = getDigest(signStr, DIGEST_KEY);
        params.put("appKey", APP_ID);
        params.put("q", word);
        params.put("salt", salt);
        params.put("sign", sign);
        return params;
    }

    public static String truncate(String q) {
        if (q == null) {
            return null;
        }
        int len = q.length();
        return len <= 20 ? q : (q.substring(0, 10) + len + q.substring(len - 10, len));
    }
}      

不要忘記把子類定義為輕量級的服務。這裡我們還沒有做英文單詞的駝峰化。

這裡需要将傳回值封裝為對象,根據文檔中給出的傳回資訊,我們隻需要處理一定傳回的項目即可。

是以,增加有道傳回的對象:

idea插件開發--服務-翻譯插件

調用這些接口,可能出現各種問題,需要找廠商的客服進行調試。

百度翻譯

百度翻譯也差不多,根據​​百度翻譯開放平台 (baidu.com)​​文檔,找到示例程式,拷貝到子類,進行調用。

import com.intellij.openapi.components.Service;
import com.study.plugin.translate.beans.BaiduTranslateResult;
import com.study.plugin.translate.utils.PluginAppKeys;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.springframework.util.CollectionUtils;

@Service
public final class BaiduTranslateRestService extends TranslateRestService implements PluginAppKeys {

    private String HOST = "http://api.fanyi.baidu.com/api/trans/vip/translate";

    private String APP_ID = appInfoService.get(BAIDU_APP_ID_SAVE_KEY);

    private String APP_SECRET = appInfoService.get(BAIDU_APP_SECRET_SAVE_KEY);

    private String DIGEST_KEY = "MD5";

    public BaiduTranslateRestService() {
        super();
        if (!isInit.get()) {
            super.init();
        }
    }

    @Override
    public String translate(String word) {
        Map<String, String> params = getParams(word);
        StringBuilder builder = new StringBuilder(HOST + "?");
        params.entrySet().forEach(ent -> {
            builder.append(ent.getKey() + "=" + ent.getValue() + "&");
        });
        String requestUrl = builder.toString();
        requestUrl = requestUrl.substring(0, requestUrl.length() - 1);
        BaiduTranslateResult result = restTemplate.getForObject(requestUrl, BaiduTranslateResult.class);
        if (Objects.isNull(result.getError_code()) && !CollectionUtils.isEmpty(result.getTrans_result())) {
            return result.getTrans_result().get(0).getDst();
        }
        return null;
    }

    private Map<String, String> getParams(String word) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("q", word);
        params.put("from", "zh");
        params.put("to", "en");
        params.put("appid", APP_ID);
        // 随機數
        String salt = String.valueOf(System.currentTimeMillis());
        params.put("salt", salt);
        // 簽名
        String src = APP_ID + word + salt + APP_SECRET; // 加密前的原文
        params.put("sign", getDigest(src, DIGEST_KEY).toLowerCase());
        return params;
    }

}      

百度翻譯的傳回對象定義

idea插件開發--服務-翻譯插件
idea插件開發--服務-翻譯插件

廠商擴充

上面兩個廠商的免費額度有限,或者說因為各種原因,無法使用,那麼可以選擇另外其他的廠商。

是以廠商的擴充就很有必要。

以DeepL翻譯為例,這是一個提供機器翻譯的網站,根據介紹是使用機器學習,實作的線上翻譯。

當然也需要新增賬號資訊,擷取app_id和app_secret。​​DeepL翻譯API|機器翻譯技術​​

其技術文檔在這裡:​​DeepL API​​

每增加一個廠商,就需要同步增加配置資訊。

是以我們根據廠商要求,增加相應的配置界面。

idea插件開發--服務-翻譯插件

然後在界面中增加資料設定和讀取的方法

idea插件開發--服務-翻譯插件

接着和其他配置相同的處理,在初始化界面時,将已有的值放入,判斷是否修改,然後進行儲存

idea插件開發--服務-翻譯插件

然後實作抽象的RestAPI類,定義deepl的子類翻譯

import com.intellij.openapi.components.Service;
import com.study.plugin.translate.beans.DeeplResult;
import com.study.plugin.translate.utils.PluginAppKeys;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.groovy.util.Maps;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

@Service
public final class DeeplTranslateRestService extends TranslateRestService implements PluginAppKeys {

    private String HOST = "https://api-free.deepl.com/v2/translate";

    private String APP_SECRET = appInfoService.get(DEEPL_APP_SECRET_SAVE_KEY);

    public DeeplTranslateRestService() {
        super();
        if (!isInit.get()) {
            init();
        }
    }

    @Override
    public String translate(String word) {
        Map<String, String> params = getParams(word);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Content-Type", "application/x-www-form-urlencoded");
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        params.entrySet().forEach(ent -> {
            map.put(ent.getKey(), Collections.singletonList(ent.getValue()));
        });
        RequestEntity<MultiValueMap<String, String>> request = new RequestEntity<>(map, httpHeaders, HttpMethod.POST, URI.create(HOST));
        DeeplResult result = restTemplate.postForObject(HOST, request, DeeplResult.class, Maps.of("auth_key", APP_SECRET));
        if (Objects.nonNull(result) && !CollectionUtils.isEmpty(result.getTranslations())) {
            return result.getTranslations().get(0).getText();
        }
        return null;
    }

    private Map<String, String> getParams(String word) {
        Map<String, String> params = new HashMap<>();
        params.put("text", word);
        // 非必填
        params.put("source_lang", "ZH");
        params.put("target_lang", "EN-US");
        params.put("auth_key", APP_SECRET);
        return params;
    }
}      

需要注意的是我們使用的​

​Service​

​注解是idea-platfrom的,而不是spring的。

然後在Action中調用即可。

idea插件開發--服務-翻譯插件

暫時我們隻是将翻譯的結果使用通知輸出,實際在Action中還應該對英文單詞結果做駝峰化,以及多個廠商之間的排程操作,還有就是需要替換選中的中文。現在還剩下這些未完成,當然這些前提是你至少有一個廠商能進行翻譯。

編寫Action後續操作

首先我們有多個用于翻譯的RestApi,是以我們建立一個排程工具,排程工具也非常簡單,就是輪訓。

idea插件開發--服務-翻譯插件

當我們得到了翻譯後的英文語句後,需要轉為駝峰形式

因為我們翻譯可能翻譯的是一個詞組,當翻譯的是詞組的時候,傳回的就不是單詞,而是短語,短語是通過空格分割的,是以我們需要将傳回的英文字元串根據空格拆分,然後第一個單詞轉為小寫,取餘單詞的第一個首字母大寫,然後拼接起來就行了

idea插件開發--服務-翻譯插件

接着我們需要控制什麼時候可用翻譯功能,當使用者沒有選中字元的時候,是不能使用字元的

idea插件開發--服務-翻譯插件

最後一步,我們需要使用翻譯後的英文字元串,并且是轉為駝峰形式的字元串替換掉選中的中文字元

idea插件開發--服務-翻譯插件

效果

idea插件開發--服務-翻譯插件
idea插件開發--服務-翻譯插件

打包

打包直接使用ide的打包功能即可

idea插件開發--服務-翻譯插件

打包後的zip包就可以釋出給其他人使用了

idea插件開發--服務-翻譯插件

最後的最後

配置的地方增加點說明,告訴使用者該去哪裡注冊。

idea插件開發--服務-翻譯插件

增加的文本區不可編輯。

總結

通過這個小插件,學習了idea插件中服務的定義,服務的擷取和使用。

通過調用線上翻譯API,學習了restTemplate的使用和配置。