├── .gitignore ├── AndroidLocalizationer 2 └── lib │ ├── AndroidLocalizationer.jar │ └── xswingx.jar ├── AndroidLocalizationer.iml ├── AndroidLocalizationer.zip ├── AndroidLocalizationer └── lib │ └── xswingx.jar ├── README.md ├── img ├── install.png └── setkey.png ├── libs ├── jsoup-1.13.1.jar └── xswingx.jar ├── resources └── META-INF │ └── plugin.xml ├── src ├── action │ ├── AndroidLocalization.java │ └── TestMain.java ├── data │ ├── Key.java │ ├── Log.java │ ├── SerializeUtil.java │ ├── StorageDataKey.java │ └── task │ │ └── GetTranslationTask.java ├── icons │ ├── globe.png │ └── globe@2x.png ├── language_engine │ ├── HttpUtils.java │ ├── TranslationEngineType.java │ ├── baidu │ │ ├── BaiduTranslationApi.java │ │ ├── HttpGet.java │ │ └── MD5.java │ └── google │ │ ├── GoogleTranslationApi.java │ │ └── GoogleTranslationer.java ├── module │ ├── AndroidString.java │ ├── AndroidStringArrayEntity.java │ ├── FilterRule.java │ ├── SimpleNameValuePair.java │ ├── StringArrayItem.java │ └── SupportedLanguages.java ├── settings │ └── SettingConfigurable.java ├── ui │ ├── AddFilterRuleDialog.java │ ├── GoogleAlertDialog.java │ └── MultiSelectDialog.java └── util │ ├── Logger.java │ └── MyURLEncoder.java ├── values-en └── strings.xml └── values └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | out 3 | .idea/workspace.xml 4 | .DS_Store -------------------------------------------------------------------------------- /AndroidLocalizationer 2/lib/AndroidLocalizationer.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/AndroidLocalizationer 2/lib/AndroidLocalizationer.jar -------------------------------------------------------------------------------- /AndroidLocalizationer 2/lib/xswingx.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/AndroidLocalizationer 2/lib/xswingx.jar -------------------------------------------------------------------------------- /AndroidLocalizationer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AndroidLocalizationer.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/AndroidLocalizationer.zip -------------------------------------------------------------------------------- /AndroidLocalizationer/lib/xswingx.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/AndroidLocalizationer/lib/xswingx.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 国际化插件 2 | 改进自[wujushan/AndroidLocalizationer](https://github.com/wujushan/AndroidLocalizationer)使用方式不变: 3 | 4 | ## 代码的事,哪能没有bug是吧。 5 | 我也就简单的测试了一下,如果遇到什么问题,欢迎大家提issue。 6 | #### 已知问题: 7 | 1. 翻译内容中包含的特殊内容翻译错误。如 < font color = "ා" >(百度) 8 | 2. 如1中的某些标签会多出空格(百度) 9 | 3. 起始的部分标签会被忽略。 10 | ``` 11 | 12 | 设置->设备信息页面“扫码激活”后,跑步机才能正常使用。 14 | ]]> 15 | 16 | ``` 17 | 只会获取到"此操作将会锁定设...正常使用。" 18 | 4. 部分html符号会被去掉 19 | 如"浏  览"会被翻译成"Browse"(百度) 20 | ## 使用说明 21 | 1. 本地安装插件 22 | 23 | [AndroidLocalizationer](https://github.com/DaveBoy/AndroidLocalizationer/blob/master/AndroidLocalizationer.zip) 24 | 下载后解压出 **AndroidLocalizationer.jar**文件,再如下图进行安装 25 | ![安装](https://github.com/DaveBoy/AndroidLocalizationer/blob/master/img/install.png) 26 | 2. 然后申请对应翻译的key填入设置 27 | 28 | 2.1 申请 29 | 包含google和百度两种翻译,目前百度翻译是基本版免费,google也有免费的额度,但是google的翻译弄起来比较麻烦。 30 | 31 | 百度:[申请百度翻译api](http://api.fanyi.baidu.com/doc/12) 32 | 33 | google:[google翻译(基本版)快速入门](https://cloud.google.com/translate/docs/basic/setup-basic) 34 | 35 | 2.2 设置key 36 | 37 | ![设置key](https://github.com/DaveBoy/AndroidLocalizationer/blob/master/img/setkey.png) 38 | 3. 选中string.xml右键选中"Convert to other languages",选择对应的语言,点击就可以生成了 39 | 40 | ## 更新日志 41 | 42 | #### V0.0.5 43 | 1. 解决urlEncode和decode导致的解码失败 44 | #### V0.0.4 45 | 1. 增加语言种类选择时的中英文展示设置,毕竟我英语学的不好。 46 | #### V0.0.3 47 | 1. 根据百度翻译最新文档接入百度翻译 48 | 49 | [通用翻译API接入文档](http://api.fanyi.baidu.com/doc/21) 50 | 51 | 2. 为什么我的请求会返回54003? 52 | 53 | 54003表示请求频率超限,请降低您的请求频率。 54 | 55 | 对于标准版服务,您的QPS(每秒请求量)=1,如需更大频率,请先进行身份认证,认证通过后可切换为高级版(适用于个人,QPS=10)或尊享版(适用于企业,QPS=100) 56 | 57 | #### V0.0.2 58 | 1. 根据google翻译最新文档接入google翻译 59 | 60 | [翻译文本(基本版)](https://cloud.google.com/translate/docs/basic/translating-text#translate_translate_text-drest) 61 | 62 | [创建API 密钥](https://cloud.google.com/docs/authentication/api-keys) 63 | 64 | [启用api](https://console.developers.google.com/apis/api/translate.googleapis.com/overview) 65 | 66 | -------------------------------------------------------------------------------- /img/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/img/install.png -------------------------------------------------------------------------------- /img/setkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/img/setkey.png -------------------------------------------------------------------------------- /libs/jsoup-1.13.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/libs/jsoup-1.13.1.jar -------------------------------------------------------------------------------- /libs/xswingx.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/libs/xswingx.jar -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidTranslation 3 | AndroidTranslation 4 | 0.0.5 5 | 6 | 7 | string resources(e.g. strings.xml) to your target languages automactically.
9 | Help developers localize their Android app easily, with just one click.

10 | Use multiple Translation APIs to translate strings into other languages.

11 | . 12 | ]]>
13 | 14 | 17 |
  • Publish project
  • 18 |
  • add Baidu Translation
  • 19 | 20 | ]]> 21 |
    22 | 23 | 24 | 25 | 26 | 28 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
    56 | -------------------------------------------------------------------------------- /src/action/AndroidLocalization.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package action; 18 | 19 | import com.intellij.ide.util.PropertiesComponent; 20 | import com.intellij.openapi.actionSystem.AnAction; 21 | import com.intellij.openapi.actionSystem.AnActionEvent; 22 | import com.intellij.openapi.actionSystem.CommonDataKeys; 23 | import com.intellij.openapi.project.Project; 24 | import com.intellij.openapi.ui.Messages; 25 | import com.intellij.openapi.util.IconLoader; 26 | import com.intellij.openapi.vfs.VirtualFile; 27 | import data.Log; 28 | import data.StorageDataKey; 29 | import data.task.GetTranslationTask; 30 | import language_engine.TranslationEngineType; 31 | import module.AndroidString; 32 | import module.SupportedLanguages; 33 | import org.jetbrains.annotations.Nullable; 34 | import ui.MultiSelectDialog; 35 | import util.Logger; 36 | 37 | import java.io.IOException; 38 | import java.util.List; 39 | 40 | /** 41 | * Created by Wesley Lin on 11/26/14. 42 | */ 43 | public class AndroidLocalization extends AnAction implements MultiSelectDialog.OnOKClickedListener { 44 | 45 | private static final String LOCALIZATION_TITLE = "Choose alternative string resources"; 46 | private static final String LOCALIZATION_MSG = "Warning: " + 47 | "The string resources are translated by %s, " + 48 | "try keeping your string resources simple, so that the result is more satisfied."; 49 | private static final String OVERRIDE_EXITS_STRINGS = "Override the existing strings"; 50 | 51 | private Project project; 52 | private List androidStringsInStringFile = null; 53 | 54 | public TranslationEngineType defaultTranslationEngine = TranslationEngineType.Baidu; 55 | 56 | private VirtualFile clickedFile; 57 | 58 | public AndroidLocalization() { 59 | super("Convert to other languages", null, IconLoader.getIcon("/icons/globe.png")); 60 | 61 | } 62 | 63 | @Override 64 | public void update(AnActionEvent e) { 65 | final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); 66 | 67 | boolean isStringXML = isStringXML(file); 68 | e.getPresentation().setEnabled(isStringXML); 69 | e.getPresentation().setVisible(isStringXML); 70 | } 71 | 72 | 73 | @Override 74 | public void actionPerformed(AnActionEvent e) { 75 | project = CommonDataKeys.PROJECT.getData(e.getDataContext()); 76 | Logger.init(getClass().getSimpleName(), Logger.DEBUG); 77 | if (project == null) { 78 | return; 79 | } 80 | 81 | clickedFile = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); 82 | Log.i("clicked file: " + clickedFile.getPath()); 83 | 84 | if (PropertiesComponent.getInstance().isValueSet(StorageDataKey.SettingLanguageEngine)) { 85 | defaultTranslationEngine = TranslationEngineType.fromName( 86 | PropertiesComponent.getInstance().getValue(StorageDataKey.SettingLanguageEngine)); 87 | } 88 | 89 | try { 90 | // androidStringsInStringFile = AndroidString.getAndroidStringsList(clickedFile.contentsToByteArray()); 91 | androidStringsInStringFile = AndroidString.getAndroidStrings(clickedFile.getInputStream()); 92 | } catch (IOException e1) { 93 | e1.printStackTrace(); 94 | } 95 | 96 | 97 | if (androidStringsInStringFile == null || androidStringsInStringFile.isEmpty()) { 98 | showErrorDialog(project, "Target file does not contain any strings or is not valid xml file."); 99 | return; 100 | } 101 | 102 | // show dialog 103 | MultiSelectDialog multiSelectDialog = new MultiSelectDialog(project, 104 | String.format(LOCALIZATION_MSG, defaultTranslationEngine.getDisplayName()), 105 | LOCALIZATION_TITLE, 106 | OVERRIDE_EXITS_STRINGS, 107 | PropertiesComponent.getInstance(project).getBoolean(StorageDataKey.OverrideCheckBoxStatus, false), 108 | defaultTranslationEngine, 109 | false); 110 | multiSelectDialog.setOnOKClickedListener(this); 111 | multiSelectDialog.show(); 112 | } 113 | 114 | @Override 115 | public void onClick(List selectedLanguages, boolean overrideChecked) { 116 | // set consistence data 117 | PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project); 118 | propertiesComponent.setValue(StorageDataKey.OverrideCheckBoxStatus, String.valueOf(overrideChecked)); 119 | 120 | List allData = SupportedLanguages.getAllSupportedLanguages(defaultTranslationEngine); 121 | 122 | for (SupportedLanguages language : allData) { 123 | propertiesComponent.setValue(StorageDataKey.SupportedLanguageCheckStatusPrefix + language.getLanguageCode(), 124 | String.valueOf(selectedLanguages.contains(language))); 125 | } 126 | 127 | new GetTranslationTask(project, "Translation in progress, using " + defaultTranslationEngine.getDisplayName(), 128 | selectedLanguages, androidStringsInStringFile, defaultTranslationEngine, overrideChecked, clickedFile) 129 | .setCancelText("Translation has been canceled").queue(); 130 | } 131 | 132 | public static void showErrorDialog(Project project, String msg) { 133 | Messages.showErrorDialog(project, msg, "Error"); 134 | } 135 | 136 | public static void showSuccessDialog(Project project, String msg) { 137 | Messages.showMessageDialog(project, msg, "Success", null); 138 | } 139 | 140 | private static boolean isStringXML(@Nullable VirtualFile file) { 141 | if (file == null) 142 | return false; 143 | 144 | if (!file.getName().equals("strings.xml")) 145 | return false; 146 | 147 | if (file.getParent() == null) 148 | return false; 149 | 150 | /* // only show popup menu for English strings 151 | 152 | if (!file.getParent().getName().equals("values") && !file.getParent().getName().startsWith("values-en")) 153 | return false;*/ 154 | 155 | return true; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/action/TestMain.java: -------------------------------------------------------------------------------- 1 | package action; 2 | 3 | import module.*; 4 | import org.jsoup.Jsoup; 5 | import org.jsoup.nodes.Document; 6 | import org.jsoup.nodes.Element; 7 | import org.jsoup.nodes.TextNode; 8 | import org.jsoup.select.Elements; 9 | 10 | import java.io.File; 11 | import java.io.FileInputStream; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import static module.AndroidString.getAndroidStrings; 18 | 19 | 20 | public class TestMain { 21 | public static void main(String[]args) throws IOException { 22 | File file=new File("/Users/pc/IdeaProjects/AndroidLocalizationer/values/strings.xml"); 23 | InputStream input=new FileInputStream(file); 24 | List androidStrings = getAndroidStrings(input); 25 | if(androidStrings==null) return; 26 | for (int i = 0; i < androidStrings.size(); i++) { 27 | System.out.println(androidStrings.get(i).toString()); 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/data/Key.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package data; 18 | 19 | /** 20 | * Created by Wesley Lin on 11/29/14. 21 | */ 22 | public class Key { 23 | 24 | // bing, clientId and clientSecret can be set by users 25 | public static final String BING_CLIENT_ID = "android_localizationer"; 26 | 27 | public static final String BING_CLIENT_SECRET = "eQiD1XOQCKToGLWMl0GXuWZb2cQJqYIwid8UPhln5CY="; 28 | public static final String BING_CLIENT_SCOPE = "http://api.microsofttranslator.com"; 29 | public static final String BING_CLIENT_GRANT_TYPE = "client_credentials"; 30 | 31 | public static final String BAIDU_CLIENT_ID = ""; 32 | public static final String BAIDU_CLIENT_SECRET = ""; 33 | 34 | // other than api keys 35 | public static final String NO_NEED_TRANSLATION_ANDROID_STRING_PREFIX = "NAL_"; 36 | } 37 | -------------------------------------------------------------------------------- /src/data/Log.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package data; 18 | 19 | /** 20 | * Created by Wesley Lin on 12/3/14. 21 | */ 22 | public class Log { 23 | public static void i(String... params) { 24 | if (params == null) 25 | return; 26 | String out = ""; 27 | for (int i = 0; i < params.length; i++) { 28 | out += params[i] + "\n"; 29 | } 30 | System.out.println(out); 31 | } 32 | 33 | public static void i(Object... params) { 34 | if (params == null) 35 | return; 36 | String out = ""; 37 | for (int i = 0; i < params.length; i++) { 38 | out += params[i].toString() + "\n"; 39 | } 40 | System.out.println(out); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/data/SerializeUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package data; 18 | 19 | import module.FilterRule; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Created by Wesley Lin on 12/17/14. 26 | */ 27 | public class SerializeUtil { 28 | 29 | public static String serializeFilterRuleList(List rules) { 30 | StringBuilder sb = new StringBuilder(); 31 | for (int i = 0; i < rules.size(); i++) { 32 | if (i != 0) 33 | sb.append("\n"); 34 | 35 | sb.append(rules.get(i).getFilterRuleType().toName()) 36 | .append("<>") 37 | .append(rules.get(i).getFilterString()); 38 | } 39 | return sb.toString(); 40 | } 41 | 42 | public static List deserializeFilterRuleList(String ruleString) { 43 | List rules = new ArrayList(); 44 | String[] tokens = ruleString.split("\n"); 45 | for (int i = 0; i < tokens.length; i++) { 46 | String[] values = tokens[i].split("<>"); 47 | if (values.length == 2) { 48 | rules.add(new FilterRule(FilterRule.FilterRuleType.fromName(values[0]), values[1])); 49 | } 50 | } 51 | return rules; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/data/StorageDataKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package data; 18 | 19 | /** 20 | * Created by Wesley Lin on 11/30/14. 21 | */ 22 | public class StorageDataKey { 23 | public static final String OverrideCheckBoxStatus = "AL_OverrideCheckBoxStatus"; 24 | 25 | public static final String SupportedLanguageCheckStatusPrefix = "AL_SupportedLanguageCheckStatus_"; 26 | 27 | // setting 28 | public static final String SettingLanguageEngine = "SettingLanguageEngine"; 29 | public static final String SettingLanguageShowWhenChoose = "SettingLanguageShowWhenChoose"; 30 | public static final String BingClientIdStored = "BingClientIdStored"; 31 | public static final String BingClientSecretStored = "BingClientSecretStored"; 32 | 33 | public static final String SettingFilterRules = "SettingFilterRules"; 34 | 35 | public static final String GoogleApiKeyStored = "GoogleAPIKeyStored"; 36 | public static final String GoogleAlertMsgShownSetting = "GoogleAlertMsgShownSetting"; 37 | 38 | public static final String BaiduClientIdStored = "BaiduClientIdStored"; 39 | public static final String BaiduClientSecretStored = "BaiduClientSecretStored"; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/data/task/GetTranslationTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package data.task; 18 | 19 | import action.AndroidLocalization; 20 | import com.intellij.ide.util.PropertiesComponent; 21 | import com.intellij.openapi.application.ApplicationManager; 22 | import com.intellij.openapi.fileEditor.FileEditorManager; 23 | import com.intellij.openapi.progress.ProgressIndicator; 24 | import com.intellij.openapi.progress.Task; 25 | import com.intellij.openapi.project.Project; 26 | import com.intellij.openapi.vfs.LocalFileSystem; 27 | import com.intellij.openapi.vfs.VirtualFile; 28 | import data.Log; 29 | import data.SerializeUtil; 30 | import data.StorageDataKey; 31 | import language_engine.TranslationEngineType; 32 | import language_engine.baidu.BaiduTranslationApi; 33 | 34 | import language_engine.google.GoogleTranslationApi; 35 | import module.*; 36 | import org.jetbrains.annotations.NotNull; 37 | import org.jetbrains.annotations.Nullable; 38 | import util.Logger; 39 | 40 | import java.io.*; 41 | import java.util.ArrayList; 42 | import java.util.List; 43 | 44 | /** 45 | * Created by Wesley Lin on 12/1/14. 46 | */ 47 | public class GetTranslationTask extends Task.Backgroundable { 48 | 49 | private List selectedLanguages; 50 | private final List androidStrings; 51 | private double indicatorFractionFrame; 52 | private TranslationEngineType translationEngineType; 53 | private boolean override; 54 | private VirtualFile clickedFile; 55 | 56 | private static final String GoogleErrorUnknown = "Error, please check API key in the settings panel."; 57 | private static final String GoogleDailyLimitError = "Daily Limit Exceeded, please note that Google Translation API " + 58 | "is a paid service."; 59 | 60 | private String errorMsg = null; 61 | 62 | public GetTranslationTask(Project project, String title, 63 | List selectedLanguages, 64 | List androidStrings, 65 | TranslationEngineType translationEngineType, 66 | boolean override, 67 | VirtualFile clickedFile) { 68 | super(project, title); 69 | this.selectedLanguages = selectedLanguages; 70 | this.androidStrings = androidStrings; 71 | this.translationEngineType = translationEngineType; 72 | this.indicatorFractionFrame = 1.0d / (double) (this.selectedLanguages.size()); 73 | this.override = override; 74 | this.clickedFile = clickedFile; 75 | } 76 | 77 | @Override 78 | public void run(ProgressIndicator indicator) { 79 | try { 80 | for (int i = 0; i < selectedLanguages.size(); i++) { 81 | 82 | SupportedLanguages language = selectedLanguages.get(i); 83 | 84 | if (language != null && !"".equals(language) /*&& !language.equals(SupportedLanguages.English)*/) { 85 | 86 | List androidStringList = filterAndroidString(androidStrings, language, override); 87 | 88 | List> filteredAndSplittedString 89 | = splitAndroidString(androidStringList, translationEngineType); 90 | 91 | List translationResult = new ArrayList(); 92 | for (int j = 0; j < filteredAndSplittedString.size(); j++) { 93 | 94 | List strings = getTranslationEngineResult( 95 | filteredAndSplittedString.get(j), 96 | language, 97 | SupportedLanguages.AUTO_BAIDU, 98 | translationEngineType 99 | ); 100 | 101 | if (strings == null) { 102 | Log.i("language===" + language); 103 | continue; 104 | } 105 | translationResult.addAll(strings); 106 | indicator.setFraction(indicatorFractionFrame * (double) (i) 107 | + indicatorFractionFrame / filteredAndSplittedString.size() * (double) (j)); 108 | indicator.setText("Translating to " + language.getLanguageEnglishDisplayName() 109 | + " (" + language.getLanguageDisplayName() + ")"); 110 | } 111 | String fileName = getValueResourcePath(language); 112 | Logger.info("output path:" + fileName); 113 | List fileContent = getTargetAndroidStrings(androidStrings, translationResult, fileName, override); 114 | writeAndroidStringToLocal(myProject, fileName, fileContent); 115 | } 116 | } 117 | }catch (Exception e){ 118 | Logger.error(e.getLocalizedMessage()); 119 | } 120 | } 121 | 122 | 123 | @Override 124 | public void onSuccess() { 125 | 126 | if (errorMsg == null || errorMsg.isEmpty()) 127 | return; 128 | AndroidLocalization.showSuccessDialog(getProject(), "translation Success"); 129 | } 130 | 131 | private String getValueResourcePath(SupportedLanguages language) { 132 | String resPath = clickedFile.getParent().getParent().getPath(); 133 | 134 | /* String resPath = clickedFile.getPath().substring(0, 135 | clickedFile.getPath().indexOf("/res/") + "/res/".length());*/ 136 | 137 | return resPath + "/values-" + language.getAndroidStringFolderNameSuffix() 138 | + "/" + clickedFile.getName(); 139 | } 140 | 141 | // todo: if got error message, should break the background task 142 | private List getTranslationEngineResult(@NotNull List needToTranslatedString, 143 | @NotNull SupportedLanguages targetLanguageCode, 144 | @NotNull SupportedLanguages sourceLanguageCode, 145 | TranslationEngineType translationEngineType) { 146 | 147 | List querys = AndroidString.getAndroidStringValues(needToTranslatedString); 148 | Log.i(querys.toString()); 149 | 150 | List result = null; 151 | 152 | switch (translationEngineType) { 153 | case Baidu: 154 | result = BaiduTranslationApi.getTranslationJSON(querys,targetLanguageCode,sourceLanguageCode); 155 | break; 156 | case Bing: 157 | break; 158 | case Google: 159 | result = GoogleTranslationApi.getTranslationJSON(querys, targetLanguageCode, sourceLanguageCode); 160 | if (result == null) { 161 | errorMsg = GoogleErrorUnknown; 162 | return null; 163 | } else if (result.isEmpty() && !querys.isEmpty()) { 164 | errorMsg = GoogleDailyLimitError; 165 | return null; 166 | } 167 | break; 168 | } 169 | if (result == null || result.size() <= 0){ 170 | return null; 171 | } 172 | 173 | List translatedAndroidStrings = new ArrayList<>(); 174 | // Logger.error(needToTranslatedString.size()); 175 | // Logger.info("needToTranslatedString.size(): " + needToTranslatedString.size()+ 176 | // "result.size(): " + result.size()); 177 | for (int i = 0,j=0; i < needToTranslatedString.size()&&j child = ((AndroidStringArrayEntity) oldAndroidString).getChild(); 184 | for(StringArrayItem item:child ){ 185 | if(item.isLink()){ 186 | androidStringArrayEntity.addChild(item); 187 | }else { 188 | androidStringArrayEntity.addChild(new StringArrayItem(result.get(i))); 189 | i++; 190 | } 191 | } 192 | translatedAndroidStrings.add(androidStringArrayEntity); 193 | }else{ 194 | if(oldAndroidString.isLink()){ 195 | translatedAndroidStrings.add(oldAndroidString); 196 | }else { 197 | translatedAndroidStrings.add(new AndroidString( 198 | oldAndroidString.getKey(), result.get(i))); 199 | i++; 200 | } 201 | } 202 | 203 | } 204 | return translatedAndroidStrings; 205 | } 206 | 207 | private List> splitAndroidString(List origin, TranslationEngineType engineType) { 208 | 209 | List> splited = new ArrayList>(); 210 | int splitFragment = 50; 211 | switch (engineType) { 212 | case Baidu: 213 | splitFragment = 50; 214 | break; 215 | case Bing: 216 | splitFragment = 50; 217 | break; 218 | case Google: 219 | splitFragment = 50; 220 | break; 221 | } 222 | 223 | if (origin != null && origin.size() > 0) { 224 | if (origin.size() <= splitFragment) { 225 | splited.add(origin); 226 | } else { 227 | int count = (origin.size() % splitFragment == 0) ? (origin.size() / splitFragment) : (origin.size() / splitFragment + 1); 228 | for (int i = 1; i <= count; i++) { 229 | int end = i * splitFragment; 230 | if (end > origin.size()) { 231 | end = origin.size(); 232 | } 233 | 234 | splited.add(origin.subList((i - 1) * splitFragment, end)); 235 | } 236 | } 237 | } 238 | 239 | return splited; 240 | } 241 | 242 | private List filterAndroidString(List origin, 243 | SupportedLanguages language, 244 | boolean override) { 245 | List result = new ArrayList(); 246 | 247 | 248 | 249 | 250 | String rulesString = PropertiesComponent.getInstance().getValue(StorageDataKey.SettingFilterRules); 251 | List filterRules = new ArrayList(); 252 | if (rulesString == null) { 253 | filterRules.add(FilterRule.DefaultFilterRule); 254 | } else { 255 | filterRules = SerializeUtil.deserializeFilterRuleList(rulesString); 256 | } 257 | // Log.i("targetAndroidString: " + targetAndroidStrings.toString()); 258 | for (AndroidString androidString : origin) { 259 | // filter rules 260 | if (FilterRule.inFilterRule(androidString.getKey(), filterRules)) 261 | continue; 262 | 263 | // override 264 | /*if (!override && !targetAndroidStrings.isEmpty()) { 265 | // check if there is the androidString in this file 266 | // if there is, filter it 267 | if (isAndroidStringListContainsKey(targetAndroidStrings, androidString.getKey())) { 268 | continue; 269 | } 270 | }*/ 271 | 272 | result.add(androidString); 273 | } 274 | 275 | return result; 276 | } 277 | 278 | private static List getTargetAndroidStrings(List sourceAndroidStrings, 279 | List translatedAndroidStrings, 280 | String fileName, 281 | boolean override) { 282 | 283 | if (translatedAndroidStrings == null) { 284 | translatedAndroidStrings = new ArrayList(); 285 | } 286 | 287 | VirtualFile existenceFile = LocalFileSystem.getInstance().findFileByPath(fileName); 288 | List existenceAndroidStrings = null; 289 | if (existenceFile != null && !override) { 290 | try { 291 | // existenceAndroidStrings = AndroidString.getAndroidStringsList(existenceFile.contentsToByteArray()); 292 | existenceAndroidStrings = AndroidString.getAndroidStrings(existenceFile.getInputStream()); 293 | } catch (IOException e) { 294 | e.printStackTrace(); 295 | } 296 | } else { 297 | existenceAndroidStrings = new ArrayList(); 298 | } 299 | 300 | Log.i("sourceAndroidStrings: " + sourceAndroidStrings, 301 | "translatedAndroidStrings: " + translatedAndroidStrings, 302 | "existenceAndroidStrings: " + existenceAndroidStrings); 303 | 304 | List targetAndroidStrings = new ArrayList(); 305 | 306 | for (int i = 0; i < sourceAndroidStrings.size(); i++) { 307 | AndroidString string = sourceAndroidStrings.get(i); 308 | AndroidString resultString ; 309 | if(string instanceof AndroidStringArrayEntity) resultString= new AndroidStringArrayEntity(string.getKey()); else resultString=new AndroidString(string); 310 | replaceValueOrChildren(translatedAndroidStrings, resultString); 311 | // if override is checked, skip setting the existence value, for performance issue 312 | if (!override) {//不覆盖原有的 313 | replaceValueOrChildren(existenceAndroidStrings, resultString); 314 | } 315 | targetAndroidStrings.add(resultString); 316 | } 317 | Log.i("targetAndroidStrings: " + targetAndroidStrings); 318 | return targetAndroidStrings; 319 | } 320 | 321 | 322 | 323 | private static void writeAndroidStringToLocal(final Project myProject, String filePath, List fileContent) { 324 | File file = new File(filePath); 325 | final VirtualFile virtualFile; 326 | boolean fileExits = true; 327 | try { 328 | file.getParentFile().mkdirs(); 329 | if (!file.exists()) { 330 | fileExits = false; 331 | file.createNewFile(); 332 | } 333 | //Change by GodLikeThomas FIX: Appeared Messy code under windows --start; 334 | //FileWriter fileWriter = new FileWriter(file.getAbsoluteFile()); 335 | //BufferedWriter writer = new BufferedWriter(fileWriter); 336 | //writer.write(getFileContent(fileContent)); 337 | //writer.close(); 338 | FileOutputStream fos = new FileOutputStream(file.getAbsoluteFile()); 339 | OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); 340 | osw.write(getFileContent(fileContent)); 341 | osw.close(); 342 | //Change by GodLikeThomas FIX: Appeared Messy code under windows --end; 343 | } catch (IOException e) { 344 | e.printStackTrace(); 345 | } 346 | 347 | if (fileExits) { 348 | virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file); 349 | if (virtualFile == null) 350 | return; 351 | virtualFile.refresh(true, false, new Runnable() { 352 | @Override 353 | public void run() { 354 | openFileInEditor(myProject, virtualFile); 355 | } 356 | }); 357 | } else { 358 | virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); 359 | openFileInEditor(myProject, virtualFile); 360 | } 361 | } 362 | 363 | private static void openFileInEditor(final Project myProject, @Nullable final VirtualFile file) { 364 | if (file == null) 365 | return; 366 | 367 | // run in UI thread: 368 | // https://theantlrguy.atlassian.net/wiki/display/~admin/Intellij+plugin+development+notes#Intellijplugindevelopmentnotes-GUIandthreads,backgroundtasks 369 | ApplicationManager.getApplication().invokeLater(new Runnable() { 370 | @Override 371 | public void run() { 372 | final FileEditorManager editorManager = FileEditorManager.getInstance(myProject); 373 | editorManager.openFile(file, true); 374 | } 375 | }); 376 | } 377 | 378 | private static String getFileContent(List fileContent) { 379 | String xmlHeader = "\n"; 380 | String stringResourceHeader = "\n\n"; 381 | String stringResourceTail = "\n"; 382 | 383 | StringBuilder sb = new StringBuilder(); 384 | sb.append(xmlHeader).append(stringResourceHeader); 385 | for (AndroidString androidString : fileContent) { 386 | sb.append("\t").append(androidString.toString()).append("\n"); 387 | } 388 | sb.append("\n").append(stringResourceTail); 389 | return sb.toString(); 390 | } 391 | 392 | 393 | 394 | public static AndroidString getAndroidStringInList(List androidStrings, String key) { 395 | for (AndroidString androidString : androidStrings) { 396 | if (androidString.getKey().equals(key)) { 397 | return androidString; 398 | } 399 | } 400 | return null; 401 | } 402 | private static void replaceValueOrChildren(List translatedAndroidStrings, AndroidString resultString) { 403 | AndroidString translatedValue = getAndroidStringInList(translatedAndroidStrings, resultString.getKey()); 404 | if (translatedValue != null) { 405 | resultString.setValue(translatedValue.getValue()); 406 | if(translatedValue instanceof AndroidStringArrayEntity){ 407 | ((AndroidStringArrayEntity) resultString).setChild( ((AndroidStringArrayEntity) translatedValue).getChild()); 408 | } 409 | } 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /src/icons/globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/src/icons/globe.png -------------------------------------------------------------------------------- /src/icons/globe@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveBoy/AndroidLocalizationer/ab06778e33d0d5fd47adf825da2fc1a5f1b738b5/src/icons/globe@2x.png -------------------------------------------------------------------------------- /src/language_engine/HttpUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package language_engine; 18 | 19 | import com.intellij.openapi.util.io.StreamUtil; 20 | import org.apache.http.*; 21 | import org.apache.http.client.HttpClient; 22 | import org.apache.http.client.entity.UrlEncodedFormEntity; 23 | import org.apache.http.client.methods.HttpGet; 24 | import org.apache.http.client.methods.HttpPost; 25 | import org.apache.http.entity.StringEntity; 26 | import org.apache.http.impl.client.DefaultHttpClient; 27 | import org.apache.http.message.BasicHeader; 28 | 29 | import java.io.InputStream; 30 | import java.util.List; 31 | 32 | /** 33 | * Created by Wesley Lin on 12/2/14. 34 | */ 35 | public class HttpUtils { 36 | 37 | public static String doHttpGet(String url) { 38 | try { 39 | HttpClient httpClient = new DefaultHttpClient(); 40 | HttpGet httpGet = new HttpGet(url); 41 | HttpResponse resp = httpClient.execute(httpGet); 42 | 43 | return StreamUtil.readText(resp.getEntity().getContent(), "UTF-8"); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | return null; 48 | } 49 | 50 | public static String doHttpGet(String url, Header[] headers) { 51 | try { 52 | HttpClient httpClient = new DefaultHttpClient(); 53 | HttpGet httpGet = new HttpGet(url); 54 | httpGet.setHeaders(headers); 55 | HttpResponse resp = httpClient.execute(httpGet); 56 | 57 | return StreamUtil.readText(resp.getEntity().getContent(), "UTF-8"); 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | return null; 62 | } 63 | 64 | public static String doHttpPost(String url, List params) { 65 | try { 66 | HttpClient httpClient = new DefaultHttpClient(); 67 | HttpPost httpPost = new HttpPost(url); 68 | httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); 69 | HttpResponse resp = httpClient.execute(httpPost); 70 | return StreamUtil.readText(resp.getEntity().getContent(), "UTF-8"); 71 | } catch (Exception e) { 72 | e.printStackTrace(); 73 | } 74 | return null; 75 | } 76 | 77 | public static String doHttpPost(String url, String xmlBody, Header[] headers) { 78 | try { 79 | HttpClient httpClient = new DefaultHttpClient(); 80 | HttpPost httpPost = new HttpPost(url); 81 | httpPost.setHeaders(headers); 82 | httpPost.setEntity(new StringEntity(xmlBody, "UTF-8")); 83 | HttpResponse resp = httpClient.execute(httpPost); 84 | return StreamUtil.readText(resp.getEntity().getContent(), "UTF-8"); 85 | } catch (Exception e) { 86 | e.printStackTrace(); 87 | } 88 | return null; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/language_engine/TranslationEngineType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package language_engine; 18 | 19 | /** 20 | * Created by Wesley Lin on 12/2/14. 21 | */ 22 | public enum TranslationEngineType { 23 | Baidu("Baidu Translator"), 24 | Bing("Microsoft Translator"), 25 | Google("Google Translation API"); 26 | 27 | private String displayName; 28 | 29 | TranslationEngineType(String displayName) { 30 | this.displayName = displayName; 31 | } 32 | 33 | public String getDisplayName() { 34 | return displayName; 35 | } 36 | 37 | public static TranslationEngineType[] getLanguageEngineArray() { 38 | return new TranslationEngineType[]{ 39 | Baidu, 40 | Bing, 41 | Google 42 | }; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return getDisplayName(); 48 | } 49 | 50 | public static TranslationEngineType fromName(String name) { 51 | if (name == null){ 52 | return Baidu; 53 | } 54 | for (TranslationEngineType type : values()) { 55 | if (type.name().equals(name)) { 56 | return type; 57 | } 58 | } 59 | return Baidu; 60 | } 61 | 62 | public String toName() { 63 | return name(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/language_engine/baidu/BaiduTranslationApi.java: -------------------------------------------------------------------------------- 1 | package language_engine.baidu; 2 | 3 | 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonObject; 7 | import com.google.gson.JsonParser; 8 | import com.intellij.ide.util.PropertiesComponent; 9 | import com.intellij.ui.ListUtil; 10 | import data.Key; 11 | import data.Log; 12 | import data.StorageDataKey; 13 | import language_engine.HttpUtils; 14 | import module.SupportedLanguages; 15 | import org.apache.commons.collections.ListUtils; 16 | import org.apache.http.message.BasicNameValuePair; 17 | import org.jetbrains.annotations.NotNull; 18 | import util.Logger; 19 | 20 | import java.net.URLDecoder; 21 | import java.net.URLEncoder; 22 | import java.util.*; 23 | 24 | public class BaiduTranslationApi { 25 | private static final String TRANS_API_HOST = "http://api.fanyi.baidu.com/api/trans/vip/translate"; 26 | private static final int MAX_BYTE = 6000; 27 | 28 | private String appid; 29 | private String securityKey; 30 | 31 | public BaiduTranslationApi(String appid, String securityKey) { 32 | this.appid = appid; 33 | this.securityKey = securityKey; 34 | } 35 | 36 | /** 37 | * @param querys 38 | * @param targetLanguageCode 39 | * @param sourceLanguageCode 40 | * @return 41 | */ 42 | public static List getTranslationJSON(@NotNull List querys, 43 | @NotNull SupportedLanguages targetLanguageCode, 44 | @NotNull SupportedLanguages sourceLanguageCode) { 45 | PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); 46 | 47 | if (querys.size() > 0) { 48 | try { 49 | Thread.sleep(1000);//http://api.fanyi.baidu.com/doc/21 对于标准版服务,您的QPS(每秒请求量)=1,如需更大频率,请先进行身份认证,认证通过后可切换为高级版 50 | } catch (InterruptedException e) { 51 | e.printStackTrace(); 52 | } 53 | StringBuilder qu = new StringBuilder(); 54 | for (int i = 0; i < querys.size(); i++) { 55 | if (i != 0) 56 | qu.append("\n"); 57 | qu.append(querys.get(i)); 58 | } 59 | String query = qu.toString(); 60 | List results = new ArrayList<>(); 61 | 62 | Map params = new HashMap<>(); 63 | params.put("q", query); 64 | params.put("from", sourceLanguageCode.getLanguageCode()); 65 | params.put("to", targetLanguageCode.getLanguageCode()); 66 | String appid = new BasicNameValuePair("client_id", 67 | propertiesComponent.getValue(StorageDataKey.BaiduClientIdStored, Key.BAIDU_CLIENT_ID)).getValue(); 68 | params.put("appid", appid); 69 | if (appid.isEmpty()) { 70 | Logger.error("Please input your Baidu APPID"); 71 | } 72 | // 随机数 73 | String salt = String.valueOf(System.currentTimeMillis()); 74 | params.put("salt", salt); 75 | 76 | String securityKey = new BasicNameValuePair("client_secret", 77 | propertiesComponent.getValue(StorageDataKey.BaiduClientSecretStored, Key.BAIDU_CLIENT_SECRET)).getValue(); 78 | if (securityKey.isEmpty()) { 79 | Logger.error("Please input your Baidu SecretKey"); 80 | } 81 | // 签名 82 | String src = appid + query + salt + securityKey; // 加密前的原文 83 | params.put("sign", MD5.md5(src)); 84 | String getResult = HttpGet.get(TRANS_API_HOST, params); 85 | Logger.info("Baidu Translation:" + getResult); 86 | if (getResult != null) { 87 | JsonObject resultObj = new JsonParser().parse(getResult).getAsJsonObject(); 88 | JsonElement errorElement = resultObj.get("error_code"); 89 | if (errorElement != null) { 90 | String errorCode = errorElement.getAsString(); 91 | String errorMsg = resultObj.get("error_msg").getAsString(); 92 | Logger.error(errorCode + " :" + errorMsg); 93 | return null; 94 | } else { 95 | JsonArray translations = resultObj.getAsJsonArray("trans_result"); 96 | if (translations != null) { 97 | for (int i = 0; i < translations.size(); i++) { 98 | String result = translations.get(i).getAsJsonObject().get("dst").getAsString(); 99 | results.add(/*URLDecoder.decode(*/result); 100 | } 101 | 102 | } 103 | } 104 | } else { 105 | return null; 106 | } 107 | 108 | return results; 109 | } 110 | // baiduTranslate(propertiesComponent,querys, targetLanguageCode, sourceLanguageCode); 111 | return null; 112 | } 113 | 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/language_engine/baidu/HttpGet.java: -------------------------------------------------------------------------------- 1 | package language_engine.baidu; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.Closeable; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.UnsupportedEncodingException; 9 | import java.net.HttpURLConnection; 10 | import java.net.MalformedURLException; 11 | import java.net.URL; 12 | import java.net.URLEncoder; 13 | import java.security.KeyManagementException; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.security.cert.CertificateException; 16 | import java.security.cert.X509Certificate; 17 | import java.util.Map; 18 | 19 | import javax.net.ssl.HttpsURLConnection; 20 | import javax.net.ssl.SSLContext; 21 | import javax.net.ssl.TrustManager; 22 | import javax.net.ssl.X509TrustManager; 23 | 24 | class HttpGet { 25 | protected static final int SOCKET_TIMEOUT = 10000; // 10S 26 | protected static final String GET = "GET"; 27 | 28 | public static String get(String host, Map params) { 29 | try { 30 | // 设置SSLContext 31 | SSLContext sslcontext = SSLContext.getInstance("TLS"); 32 | sslcontext.init(null, new TrustManager[] { myX509TrustManager }, null); 33 | 34 | String sendUrl = getUrlWithQueryString(host, params); 35 | 36 | // System.out.println("URL:" + sendUrl); 37 | 38 | URL uri = new URL(sendUrl); // 创建URL对象 39 | HttpURLConnection conn = (HttpURLConnection) uri.openConnection(); 40 | if (conn instanceof HttpsURLConnection) { 41 | ((HttpsURLConnection) conn).setSSLSocketFactory(sslcontext.getSocketFactory()); 42 | } 43 | 44 | conn.setConnectTimeout(SOCKET_TIMEOUT); // 设置相应超时 45 | conn.setRequestMethod(GET); 46 | int statusCode = conn.getResponseCode(); 47 | if (statusCode != HttpURLConnection.HTTP_OK) { 48 | System.out.println("Http错误码:" + statusCode); 49 | } 50 | 51 | // 读取服务器的数据 52 | InputStream is = conn.getInputStream(); 53 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 54 | StringBuilder builder = new StringBuilder(); 55 | String line = null; 56 | while ((line = br.readLine()) != null) { 57 | builder.append(line); 58 | } 59 | 60 | String text = builder.toString(); 61 | 62 | close(br); // 关闭数据流 63 | close(is); // 关闭数据流 64 | conn.disconnect(); // 断开连接 65 | 66 | return text; 67 | } catch (MalformedURLException e) { 68 | e.printStackTrace(); 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | } catch (KeyManagementException e) { 72 | e.printStackTrace(); 73 | } catch (NoSuchAlgorithmException e) { 74 | e.printStackTrace(); 75 | } 76 | 77 | return null; 78 | } 79 | 80 | public static String getUrlWithQueryString(String url, Map params) { 81 | if (params == null) { 82 | return url; 83 | } 84 | 85 | StringBuilder builder = new StringBuilder(url); 86 | if (url.contains("?")) { 87 | builder.append("&"); 88 | } else { 89 | builder.append("?"); 90 | } 91 | 92 | int i = 0; 93 | for (String key : params.keySet()) { 94 | String value = params.get(key); 95 | if (value == null) { // 过滤空的key 96 | continue; 97 | } 98 | 99 | if (i != 0) { 100 | builder.append('&'); 101 | } 102 | 103 | builder.append(key); 104 | builder.append('='); 105 | builder.append(encode(value)); 106 | 107 | i++; 108 | } 109 | 110 | return builder.toString(); 111 | } 112 | 113 | protected static void close(Closeable closeable) { 114 | if (closeable != null) { 115 | try { 116 | closeable.close(); 117 | } catch (IOException e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * 对输入的字符串进行URL编码, 即转换为%20这种形式 125 | * 126 | * @param input 原文 127 | * @return URL编码. 如果编码失败, 则返回原文 128 | */ 129 | public static String encode(String input) { 130 | if (input == null) { 131 | return ""; 132 | } 133 | 134 | try { 135 | return URLEncoder.encode(input, "utf-8").replaceAll("\\+","%20"); 136 | } catch (UnsupportedEncodingException e) { 137 | e.printStackTrace(); 138 | } 139 | 140 | return input; 141 | } 142 | 143 | private static TrustManager myX509TrustManager = new X509TrustManager() { 144 | 145 | @Override 146 | public X509Certificate[] getAcceptedIssuers() { 147 | return null; 148 | } 149 | 150 | @Override 151 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 152 | } 153 | 154 | @Override 155 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 156 | } 157 | }; 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/language_engine/baidu/MD5.java: -------------------------------------------------------------------------------- 1 | package language_engine.baidu; 2 | 3 | import java.io.*; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | /** 8 | * MD5编码相关的类 9 | * 10 | * @author wangjingtao 11 | * 12 | */ 13 | public class MD5 { 14 | // 首先初始化一个字符数组,用来存放每个16进制字符 15 | private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 16 | 'e', 'f' }; 17 | 18 | /** 19 | * 获得一个字符串的MD5值 20 | * 21 | * @param input 输入的字符串 22 | * @return 输入字符串的MD5值 23 | * 24 | */ 25 | public static String md5(String input) { 26 | if (input == null) 27 | return null; 28 | 29 | try { 30 | // 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”) 31 | MessageDigest messageDigest = MessageDigest.getInstance("MD5"); 32 | // 输入的字符串转换成字节数组 33 | byte[] inputByteArray = new byte[0]; 34 | try { 35 | inputByteArray = input.getBytes("utf-8"); 36 | } catch (UnsupportedEncodingException e) { 37 | e.printStackTrace(); 38 | } 39 | // inputByteArray是输入字符串转换得到的字节数组 40 | messageDigest.update(inputByteArray); 41 | // 转换并返回结果,也是字节数组,包含16个元素 42 | byte[] resultByteArray = messageDigest.digest(); 43 | // 字符数组转换成字符串返回 44 | return byteArrayToHex(resultByteArray); 45 | } catch (NoSuchAlgorithmException e) { 46 | return null; 47 | } 48 | } 49 | 50 | /** 51 | * 获取文件的MD5值 52 | * 53 | * @param file 54 | * @return 55 | */ 56 | public static String md5(File file) { 57 | try { 58 | if (!file.isFile()) { 59 | System.err.println("文件" + file.getAbsolutePath() + "不存在或者不是文件"); 60 | return null; 61 | } 62 | 63 | FileInputStream in = new FileInputStream(file); 64 | 65 | String result = md5(in); 66 | 67 | in.close(); 68 | 69 | return result; 70 | 71 | } catch (FileNotFoundException e) { 72 | e.printStackTrace(); 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | } 76 | 77 | return null; 78 | } 79 | 80 | public static String md5(InputStream in) { 81 | 82 | try { 83 | MessageDigest messagedigest = MessageDigest.getInstance("MD5"); 84 | 85 | byte[] buffer = new byte[1024]; 86 | int read = 0; 87 | while ((read = in.read(buffer)) != -1) { 88 | messagedigest.update(buffer, 0, read); 89 | } 90 | 91 | in.close(); 92 | 93 | String result = byteArrayToHex(messagedigest.digest()); 94 | 95 | return result; 96 | } catch (NoSuchAlgorithmException e) { 97 | e.printStackTrace(); 98 | } catch (FileNotFoundException e) { 99 | e.printStackTrace(); 100 | } catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | 104 | return null; 105 | } 106 | 107 | private static String byteArrayToHex(byte[] byteArray) { 108 | // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方)) 109 | char[] resultCharArray = new char[byteArray.length * 2]; 110 | // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去 111 | int index = 0; 112 | for (byte b : byteArray) { 113 | resultCharArray[index++] = hexDigits[b >>> 4 & 0xf]; 114 | resultCharArray[index++] = hexDigits[b & 0xf]; 115 | } 116 | 117 | // 字符数组组合成字符串返回 118 | return new String(resultCharArray); 119 | 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/language_engine/google/GoogleTranslationApi.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package language_engine.google; 18 | 19 | import com.google.gson.Gson; 20 | import com.google.gson.JsonArray; 21 | import com.google.gson.JsonObject; 22 | import com.google.gson.JsonParser; 23 | import com.intellij.ide.util.PropertiesComponent; 24 | import data.StorageDataKey; 25 | import language_engine.HttpUtils; 26 | import module.SimpleNameValuePair; 27 | import module.SupportedLanguages; 28 | import org.apache.http.HttpEntity; 29 | import org.apache.http.HttpResponse; 30 | import org.apache.http.NameValuePair; 31 | import org.apache.http.client.HttpClient; 32 | import org.apache.http.client.methods.HttpGet; 33 | import org.apache.http.impl.client.DefaultHttpClient; 34 | import org.apache.http.util.EntityUtils; 35 | import org.jetbrains.annotations.NotNull; 36 | import util.Logger; 37 | 38 | import java.io.IOException; 39 | import java.net.URLDecoder; 40 | import java.net.URLEncoder; 41 | import java.util.ArrayList; 42 | import java.util.IllegalFormatException; 43 | import java.util.List; 44 | 45 | 46 | /** 47 | * Created by Wesley Lin on 12/1/14. 48 | */ 49 | public class GoogleTranslationApi { 50 | private static final String BASE_TRANSLATION_URL = "https://translation.googleapis.com/language/translate/v2"; 51 | 52 | /** 53 | * @param querys 54 | * @param targetLanguageCode 55 | * @param sourceLanguageCode 56 | * @return 57 | */ 58 | public static List getTranslationJSON(@NotNull List querys, 59 | @NotNull SupportedLanguages targetLanguageCode, 60 | @NotNull SupportedLanguages sourceLanguageCode) { 61 | if (querys.isEmpty()) 62 | return null; 63 | 64 | 65 | for (int i=0;i para=new ArrayList<>(); 71 | para.add(new SimpleNameValuePair("key",PropertiesComponent.getInstance().getValue(StorageDataKey.GoogleApiKeyStored))); 72 | para.add(new SimpleNameValuePair("target",targetLanguageCode.getLanguageCode())); 73 | for (int i = querys.size() - 1; i >= 0; i--) { 74 | para.add(new SimpleNameValuePair("q",querys.get(i))); 75 | } 76 | 77 | 78 | 79 | String getResult = HttpUtils.doHttpPost(BASE_TRANSLATION_URL,para); 80 | Logger.info("Google Translation: " + getResult ); 81 | 82 | JsonObject jsonObject = new JsonParser().parse(getResult).getAsJsonObject(); 83 | if (jsonObject.get("error") != null) { 84 | JsonObject error = jsonObject.get("error").getAsJsonObject().get("errors").getAsJsonArray().get(0).getAsJsonObject(); 85 | if (error == null) 86 | return null; 87 | 88 | if (error.get("reason").getAsString().equals("dailyLimitExceeded")) 89 | return new ArrayList(); 90 | return null; 91 | } else { 92 | JsonObject data = jsonObject.get("data").getAsJsonObject(); 93 | JsonArray translations = data.get("translations").getAsJsonArray(); 94 | if (translations != null) { 95 | List result = new ArrayList(); 96 | for (int i = 0; i < translations.size(); i++) { 97 | result.add(translations.get(i).getAsJsonObject().get("translatedText").getAsString()); 98 | } 99 | return result; 100 | } 101 | } 102 | return null; 103 | } 104 | 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/language_engine/google/GoogleTranslationer.java: -------------------------------------------------------------------------------- 1 | package language_engine.google; 2 | 3 | import org.apache.http.HttpEntity; 4 | import org.apache.http.HttpResponse; 5 | import org.apache.http.client.HttpClient; 6 | import org.apache.http.client.methods.HttpGet; 7 | import org.apache.http.impl.client.DefaultHttpClient; 8 | import org.apache.http.util.EntityUtils; 9 | 10 | import javax.lang.model.util.Elements; 11 | import java.io.IOException; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.concurrent.CountDownLatch; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | 18 | public class GoogleTranslationer { 19 | private ExecutorService fixedThreadPool; 20 | private CountDownLatch countDownLatch; 21 | private List result; 22 | private static int COUNT = 0; 23 | 24 | public GoogleTranslationer(List urls) { 25 | this.fixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 26 | this.countDownLatch = new CountDownLatch(urls.size()); 27 | COUNT = urls.size(); 28 | this.result = new ArrayList<>(); 29 | } 30 | 31 | public List googleTranslate(List urls) { 32 | if (urls != null && urls.size() > 0) { 33 | for (String url : urls) { 34 | fixedThreadPool.execute(new HttpGetRunnable(url)); 35 | } 36 | try { 37 | countDownLatch.await(); 38 | } catch (InterruptedException e) { 39 | e.printStackTrace(); 40 | } 41 | return result; 42 | } 43 | return result; 44 | } 45 | 46 | 47 | private class HttpGetRunnable implements Runnable { 48 | 49 | String url; 50 | 51 | public HttpGetRunnable(String url) { 52 | this.url = url; 53 | } 54 | 55 | @Override 56 | public void run() { 57 | String resp = httpGet(url); 58 | if (resp != null){ 59 | } 60 | } 61 | } 62 | 63 | private static String httpGet(String url) { 64 | HttpClient httpClient = new DefaultHttpClient(); 65 | 66 | HttpGet httpGet = new HttpGet(url); 67 | HttpResponse httpResponse; 68 | try { 69 | httpResponse = httpClient.execute(httpGet); 70 | if (httpResponse.getStatusLine().getStatusCode() == 200) { 71 | HttpEntity entity = httpResponse.getEntity(); 72 | return EntityUtils.toString(entity, "utf-8"); 73 | } 74 | } catch (IOException e) { 75 | e.printStackTrace(); 76 | return null; 77 | } 78 | return null; 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/module/AndroidString.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package module; 18 | 19 | import org.jsoup.Jsoup; 20 | import org.jsoup.nodes.Element; 21 | import org.jsoup.select.Elements; 22 | import org.w3c.dom.Document; 23 | import org.w3c.dom.Node; 24 | import org.w3c.dom.NodeList; 25 | import org.xml.sax.SAXException; 26 | import util.Logger; 27 | 28 | import javax.xml.stream.XMLEventReader; 29 | import javax.xml.stream.XMLInputFactory; 30 | import javax.xml.stream.XMLStreamException; 31 | import javax.xml.stream.events.Attribute; 32 | import javax.xml.stream.events.EndElement; 33 | import javax.xml.stream.events.StartElement; 34 | import javax.xml.stream.events.XMLEvent; 35 | import javax.xml.parsers.DocumentBuilder; 36 | import javax.xml.parsers.DocumentBuilderFactory; 37 | import javax.xml.parsers.ParserConfigurationException; 38 | import javax.xml.xpath.XPath; 39 | import javax.xml.parsers.DocumentBuilder; 40 | import javax.xml.parsers.DocumentBuilderFactory; 41 | import javax.xml.parsers.ParserConfigurationException; 42 | import javax.xml.xpath.XPath; 43 | import javax.xml.xpath.XPathConstants; 44 | import javax.xml.xpath.XPathExpression; 45 | import javax.xml.xpath.XPathExpressionException; 46 | import javax.xml.xpath.XPathFactory; 47 | import javax.xml.xpath.XPathFactory; 48 | import java.io.IOException; 49 | import java.io.InputStream; 50 | import java.io.UnsupportedEncodingException; 51 | import java.net.URLEncoder; 52 | import java.util.ArrayList; 53 | import java.util.Iterator; 54 | import java.util.List; 55 | 56 | /** 57 | * Created by Wesley Lin on 11/29/14. 58 | */ 59 | public class AndroidString{ 60 | protected String key; 61 | protected String value; 62 | private boolean link; 63 | 64 | public AndroidString(String key, String value,boolean localLink) { 65 | this.key = key; 66 | this.value = value; 67 | this.link = localLink; 68 | } 69 | public AndroidString(String key, String value) { 70 | this.key = key; 71 | this.value = value; 72 | this.link = false; 73 | } 74 | 75 | public AndroidString(AndroidString androidString) { 76 | this.key = androidString.getKey(); 77 | this.value = androidString.getValue(); 78 | } 79 | 80 | public AndroidString() { 81 | 82 | } 83 | 84 | public String getKey() { 85 | return key; 86 | } 87 | 88 | public String getValue() { 89 | return value; 90 | } 91 | 92 | public void setValue(String value) { 93 | this.value = value; 94 | } 95 | 96 | public void setKey(String key) { 97 | this.key = key; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "" + 105 | value + 106 | ""; 107 | } 108 | 109 | 110 | 111 | public static List getAndroidStrings(InputStream xml) throws IOException { 112 | List androidStrings = new ArrayList<>(); 113 | if (xml != null) { 114 | org.jsoup.nodes.Document parse = Jsoup.parse(xml, "utf-8", ""); 115 | Element resources = parse.getElementsByTag("resources").first(); 116 | 117 | 118 | Elements strings = resources.children(); 119 | 120 | 121 | for (Element string : strings) { 122 | switch (string.tagName()){ 123 | case "string": 124 | String trans=string.attr("translatable"); 125 | if(!"false".equals(trans)) { 126 | String text=string.text(); 127 | androidStrings.add(new AndroidString(string.attr("name"), text,isLocalLink(text))); 128 | } 129 | break; 130 | case "string-array": 131 | AndroidStringArrayEntity array = new AndroidStringArrayEntity(string.attr("name")); 132 | androidStrings.add(array); 133 | getArrayItem(array,string); 134 | break; 135 | } 136 | } 137 | 138 | } 139 | return androidStrings; 140 | } 141 | 142 | private static void getArrayItem(AndroidStringArrayEntity array, Element string) { 143 | Elements children = string.children(); 144 | for (Element child : children) { 145 | String text=child.text(); 146 | array.addChild(new StringArrayItem(text,isLocalLink(text))); 147 | } 148 | } 149 | private static boolean isLocalLink(String str){ 150 | return str.startsWith("@string/"); 151 | } 152 | 153 | 154 | 155 | public static List getAndroidStringValues(List list) { 156 | List result = new ArrayList(); 157 | 158 | for (int i = 0; i < list.size(); i++) { 159 | AndroidString androidString = list.get(i); 160 | if(androidString instanceof AndroidStringArrayEntity){ 161 | for (StringArrayItem stringArrayItem : ((AndroidStringArrayEntity) androidString).getChild()) { 162 | if(!stringArrayItem.isLink()) 163 | result.add(stringArrayItem.getValue()); 164 | } 165 | }else { 166 | result.add(list.get(i).getValue()); 167 | } 168 | } 169 | return result; 170 | } 171 | 172 | public boolean isLink() { 173 | return link; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/module/AndroidStringArrayEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package module; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Created by Wesley Lin on 11/29/14. 24 | */ 25 | public class AndroidStringArrayEntity extends AndroidString{ 26 | private List children; 27 | 28 | public AndroidStringArrayEntity(String key) { 29 | this.key = key; 30 | } 31 | public void addChild(StringArrayItem item){ 32 | ArrayList list=new ArrayList(); 33 | if(children!=null){ 34 | list.addAll(children); 35 | } 36 | list.add(item); 37 | children=list; 38 | } 39 | public void setChild(List list){ 40 | children=list; 41 | } 42 | public List getChild(){ 43 | return children==null?new ArrayList<>():children; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | StringBuilder builder=new StringBuilder(); 49 | builder.append("\n"); 52 | for (StringArrayItem child : children) { 53 | builder.append(child.toString()); 54 | } 55 | builder.append(""); 56 | return builder.toString(); 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/module/FilterRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package module; 18 | 19 | import com.intellij.ide.util.PropertiesComponent; 20 | import data.SerializeUtil; 21 | import data.StorageDataKey; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | /** 27 | * Created by Wesley Lin on 12/14/14. 28 | */ 29 | public class FilterRule { 30 | 31 | private FilterRuleType filterRuleType; 32 | private String filterString; 33 | 34 | public FilterRule(FilterRuleType filterRuleType, String filterString) { 35 | this.filterRuleType = filterRuleType; 36 | this.filterString = filterString; 37 | } 38 | 39 | public FilterRuleType getFilterRuleType() { 40 | return filterRuleType; 41 | } 42 | 43 | public String getFilterString() { 44 | return filterString; 45 | } 46 | 47 | public void setFilterRuleType(FilterRuleType filterRuleType) { 48 | this.filterRuleType = filterRuleType; 49 | } 50 | 51 | public void setFilterString(String filterString) { 52 | this.filterString = filterString; 53 | } 54 | 55 | public static FilterRule DefaultFilterRule = new FilterRule(FilterRuleType.START_WITH, "NAL_"); 56 | 57 | public String toString() { 58 | return getFilterRuleType().toString() + " <" + getFilterString() + ">"; 59 | } 60 | 61 | public static List getFilterRulesFromLocal() { 62 | List result = new ArrayList(); 63 | 64 | String rules = PropertiesComponent.getInstance().getValue(StorageDataKey.SettingFilterRules); 65 | if (rules != null) { 66 | List ruleList = SerializeUtil.deserializeFilterRuleList(rules); 67 | result.addAll(ruleList); 68 | } else { 69 | result.add(DefaultFilterRule); 70 | } 71 | return result; 72 | } 73 | 74 | public static boolean inFilterRule(String key, List rules) { 75 | for (FilterRule rule : rules) { 76 | switch (rule.getFilterRuleType()) { 77 | case START_WITH: 78 | if (key.startsWith(rule.getFilterString())) { 79 | return true; 80 | } 81 | break; 82 | case EQUALS: 83 | if (key.equals(rule.getFilterString())) { 84 | return true; 85 | } 86 | break; 87 | case END_WITH: 88 | if (key.endsWith(rule.getFilterString())) { 89 | return true; 90 | } 91 | break; 92 | } 93 | } 94 | return false; 95 | } 96 | 97 | public enum FilterRuleType { 98 | START_WITH("Start with"), 99 | EQUALS("Equals"), 100 | END_WITH("End with"); 101 | 102 | private String displayName; 103 | 104 | FilterRuleType(String displayName) { 105 | this.displayName = displayName; 106 | } 107 | 108 | public String getDisplayName() { 109 | return displayName; 110 | } 111 | 112 | public String toString() { 113 | return getDisplayName(); 114 | } 115 | 116 | public static FilterRuleType fromName(String name) { 117 | if (name == null) 118 | return START_WITH; 119 | for (FilterRuleType type : values()) { 120 | if (type.name().equals(name)) { 121 | return type; 122 | } 123 | } 124 | return START_WITH; 125 | } 126 | 127 | public String toName() { 128 | return name(); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/module/SimpleNameValuePair.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | import org.apache.http.NameValuePair; 4 | 5 | public class SimpleNameValuePair implements NameValuePair { 6 | private String name; 7 | private String value; 8 | public SimpleNameValuePair(String name,String value) { 9 | this.name=name; 10 | this.value=value; 11 | } 12 | 13 | @Override 14 | public String getName() { 15 | return name; 16 | } 17 | 18 | @Override 19 | public String getValue() { 20 | return value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/module/StringArrayItem.java: -------------------------------------------------------------------------------- 1 | package module; 2 | 3 | public class StringArrayItem { 4 | private String value; 5 | private boolean link; 6 | 7 | public StringArrayItem(String value, boolean link) { 8 | this.value = value; 9 | this.link = link; 10 | } 11 | 12 | public StringArrayItem(String value) { 13 | this.value = value; 14 | this.link = false; 15 | } 16 | 17 | public String getValue() { 18 | return value; 19 | } 20 | 21 | public void setValue(String value) { 22 | this.value = value; 23 | } 24 | 25 | public boolean isLink() { 26 | return link; 27 | } 28 | 29 | public void setLink(boolean link) { 30 | this.link = link; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "\t" + 36 | value + 37 | "\n"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/module/SupportedLanguages.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package module; 18 | 19 | import language_engine.TranslationEngineType; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Created by Wesley Lin on 11/29/14. 26 | */ 27 | public enum SupportedLanguages { 28 | //https://cloud.google.com/translate/docs/languages 29 | Afrikaans("af", "Afrikaans", "Afrikaans","南非荷兰语"), 30 | Albanian("sq", "Shqiptar", "Albanian","阿尔巴尼亚语"), 31 | Amharic("am", "አማርኛ", "Amharic","阿姆哈拉语"), 32 | Arabic("ar", "العربية", "Arabic","阿拉伯语"), 33 | Armenian("ar", "Հայերեն", "Armenian","亚美尼亚语"), 34 | Azerbaijani("az", "Azərbaycan", "Azerbaijani","阿塞拜疆语"), 35 | Basque("eu", "Euskal", "Basque","巴斯克语"), 36 | Belarusian("be", "Беларускі", "Belarusian","白俄罗斯语"), 37 | Bengali("bn", "বাঙালি", "Bengali","孟加拉语"), 38 | Bosnian("bs", "Bosanski", "Bosnian","波斯尼亚语"), 39 | Bulgarian("bg", "Български", "Bulgarian","保加利亚语"), 40 | Catalan("ca", "Català", "Catalan","加泰罗尼亚语"), 41 | Cebuano("ceb", "Cebuano", "Cebuano","宿务语"), 42 | Chinese_Simplified("zh-CN", "简体中文", "Chinese Simplified","中文(简体)"), 43 | Chinese_Simplified_BING("zh-CHS", "简体中文", "Chinese Simplified","中文(简体)"), 44 | Chinese_Traditional("zh-TW", "正體中文", "Chinese Traditional","中文(繁体)"), 45 | Chinese_Traditional_BING("zh-CHT", "正體中文", "Chinese Traditional","中文(繁体)"), 46 | Corsican("co", "Corsu", "Corsican","科西嘉语"), 47 | Croatian("hr", "Hrvatski", "Croatian","克罗地亚语"), 48 | Czech("cs", "Čeština", "Czech","捷克语"), 49 | Danish("da", "Dansk", "Danish","丹麦语"), 50 | Dutch("nl", "Nederlands", "Dutch","荷兰语"), 51 | English("en", "English", "English","英语"), 52 | Esperanto("eo", "Esperanta", "Esperanto","世界语"), 53 | Estonian("et", "Eesti", "Estonian","爱沙尼亚语"), 54 | Filipino("tl", "Pilipino", "Filipino","菲律宾语"), 55 | Finnish("fi", "Suomi", "Finnish","芬兰语"), 56 | French("fr", "Français", "French","法语"), 57 | //Frisian("fy", "Frysk", "Frisian","弗里斯兰语"), 58 | Galician("gl", "Galego", "Galician","加利西亚语"), 59 | Georgian("ka", "ქართული", "Georgian","格鲁吉亚语"), 60 | German("de", "Deutsch", "German","德语"), 61 | Greek("el", "Ελληνικά", "Greek","希腊语"), 62 | Gujarati("gu", "ગુજરાતી", "Gujarati","古吉拉特语"), 63 | Haitian_Creole("ht", "Haitiancreole", "Haitian Creole","海地克里奥尔语"), 64 | Hausa("ha", "Hausa", "Hausa","豪萨语"), 65 | Hawaiian("haw", "ʻ .lelo Hawaiʻi", "Hawaiian","夏威夷语"), 66 | Hebrew("iw", "עברית", "Hebrew","希伯来语"), 67 | Hebrew_BING("he", "עברית", "Hebrew","希伯来语"), 68 | Hindi("hi", "हिंदी", "Hindi","印地语"), 69 | Hungarian("hu", "Magyar", "Hungarian","匈牙利语"), 70 | Icelandic("is", "Icelandic", "Icelandic","冰岛语"), 71 | Igbo("ig", "Ndi Igbo", "Igbo","伊博语"), 72 | Indonesian("id", "Indonesia", "Indonesian","印度尼西亚语"), 73 | Irish("ga", "Irish", "Irish","爱尔兰语"), 74 | Italian("it", "Italiano", "Italian","意大利语"), 75 | Japanese("ja", "日本語", "Japanese","日语"), 76 | Javanese("jv", "Basa Jawa", "Javanese","爪哇语"), 77 | Kannada("kn", "ಕನ್ನಡ", "Kannada","卡纳达语"), 78 | Korean("ko", "한국의", "Korean","韩语"), 79 | Latin("la", "Latina", "Latin","拉丁文"), 80 | Latvian("lv", "Latvijas", "Latvian","拉脱维亚语"), 81 | Lithuanian("lt", "Lietuvos", "Lithuanian","立陶宛语"), 82 | Macedonian("mk", "Македонски", "Macedonian","马其顿语"), 83 | Malay("ms", "Melayu", "Malay","马来语"), 84 | Maltese("mt", "Malti", "Maltese","马耳他语"), 85 | Norwegian("no", "Norsk", "Norwegian","挪威语"), 86 | Persian("fa", "فارسی", "Persian","波斯语"), 87 | Polish("pl", "Polski", "Polish","波兰语"), 88 | Portuguese("pt", "Português", "Portuguese","葡萄牙语"), 89 | Romanian("ro", "Român", "Romanian","罗马尼亚语"), 90 | Russian("ru", "Русский", "Russian","俄语"), 91 | Serbian("sr", "Српски", "Serbian","塞尔维亚语"), 92 | Slovak("sk", "Slovenčina", "Slovak","斯洛伐克语"), 93 | Slovenian("sl", "Slovenščina", "Slovenian","斯洛文尼亚语"), 94 | Spanish("es", "Español", "Spanish","西班牙语"), 95 | Swahili("sw", "Kiswahili", "Swahili","斯瓦希里语"), 96 | Swedish("sv", "Svenska", "Swedish","瑞典语"), 97 | Tamil("ta", "தமிழ்", "Tamil","泰米尔语"), 98 | Telugu("te", "తెలుగు", "Telugu","泰卢固语"), 99 | Thai("th", "ไทย", "Thai","泰文"), 100 | Turkish("tr", "Türk", "Turkish","土耳其语"), 101 | Ukrainian("uk", "Український", "Ukrainian","乌克兰语"), 102 | Urdu("ur", "اردو", "Urdu","乌尔都语"), 103 | Vietnamese("vi", "Tiếng Việt", "Vietnamese","越南语"), 104 | Welsh("cy", "Cymraeg", "Welsh","威尔士语"), 105 | Yiddish("yi", "ייִדיש", "Yiddish","意第绪语"), 106 | 107 | //http://api.fanyi.baidu.com/doc/21 108 | AUTO_BAIDU("auto","自动检测","auto check","自动检测"), 109 | Chinese_Simplified_BAIDU("zh", "简体中文", "Chinese Simplified","中文简体","zh-rCN"), 110 | English_BAIDU("en", "English", "English","英语","en"), 111 | Japanese_BAIDU("jp","日本語","Japanese","日语","ja"), 112 | Korean_BAIDU("kor","한국어","Korean","韩语","ko"), 113 | French_BAIDU("fra", "Français", "French","法语","fr"), 114 | Spanish_BAIDU("spa", "Español", "Spanish","西班牙语","es"), 115 | Thai_BAIDU("th", "ไทย", "Thai","泰语","th"), 116 | Arabic_BAIDU("ara", "العربية", "Arabic","阿拉伯语","ar"), 117 | Russian_BAIDU("ru", "Русский", "Russian","俄语","ru"), 118 | Portuguese_BAIDU("pt", "Português", "Portuguese","葡萄牙语","pt"), 119 | German_BAIDU("de", "Deutsch", "German","德语","de"), 120 | Italian_BAIDU("it", "Italiano", "Italian","意大利语","it"), 121 | Greek_BAIDU("el", "Ελληνικά", "Greek","希腊语","el"), 122 | Dutch_BAIDU("nl", "Nederlands", "Dutch","荷兰语","nl"), 123 | Polish_BAIDU("pl", "Polski", "Polish","波兰语","pl"), 124 | Bulgarian_BAIDU("bul", "Български", "Bulgarian","保加利亚语","bg"), 125 | Estonian_BAIDU("est", "Eesti", "Estonian","爱沙尼亚语","et"), 126 | Danish_BAIDU("dan", "Dansk", "Danish","丹麦语","da"), 127 | Finnish_BAIDU("fin", "Suomi", "Finnish","芬兰语","fi"), 128 | Czech_BAIDU("cs", "Čeština", "Czech","捷克语","cs"), 129 | Romanian_BAIDU("rom", "Român", "Romanian","罗马尼亚语","ro"), 130 | Slovenian_BAIDU("slo", "Slovenščina", "Slovenian","斯洛文尼亚语","sl"), 131 | Swedish_BAIDU("swe", "Svenska", "Swedish","瑞典语","sv"), 132 | Hungarian_BAIDU("hu", "Magyar", "Hungarian","匈牙利语","hu"), 133 | Chinese_Traditional_BAIDU("cht", "正體中文", "Chinese Traditional","中文繁体","zh-rTW"), 134 | Vietnamese_BAIDU("vie", "Tiếng Việt", "Vietnamese","越南语","vi"); 135 | 136 | private String languageCode; 137 | private String languageDisplayName; 138 | private String languageEnglishDisplayName; 139 | private String languageChineseDisplayName; 140 | private String realLanguageCode; 141 | 142 | SupportedLanguages(String languageCode, String languageDisplayName, String languageEnglishDisplayName,String languageChineseDisplayName) { 143 | this.languageCode = languageCode; 144 | this.languageDisplayName = languageDisplayName; 145 | this.languageEnglishDisplayName = languageEnglishDisplayName; 146 | this.languageChineseDisplayName = languageChineseDisplayName; 147 | } 148 | SupportedLanguages(String languageCode, String languageDisplayName, String languageEnglishDisplayName,String languageChineseDisplayName,String realLanguageCode) { 149 | this.languageCode = languageCode; 150 | this.languageDisplayName = languageDisplayName; 151 | this.languageEnglishDisplayName = languageEnglishDisplayName; 152 | this.languageChineseDisplayName = languageChineseDisplayName; 153 | this.realLanguageCode = realLanguageCode; 154 | } 155 | public String getLanguageCode() { 156 | return languageCode; 157 | } 158 | 159 | public String getLanguageDisplayName() { 160 | return languageDisplayName; 161 | } 162 | 163 | public String getLanguageEnglishDisplayName() { 164 | return languageEnglishDisplayName; 165 | } 166 | public String getLanguageChineseDisplayName() { 167 | return languageChineseDisplayName; 168 | } 169 | public String getRealLanguageCode() { 170 | return realLanguageCode; 171 | } 172 | 173 | public static List getAllSupportedLanguages(TranslationEngineType type) { 174 | switch (type) { 175 | case Baidu: 176 | return getBaiduLanguages(); 177 | case Bing: 178 | return getBingLanguages(); 179 | case Google: 180 | return getGoogleLanguages(); 181 | } 182 | return null; 183 | } 184 | 185 | public String toString() { 186 | return getLanguageEnglishDisplayName() + "(\"" + getLanguageCode() + "\", \"" + getLanguageDisplayName() + "\")"; 187 | } 188 | 189 | // get the right value-XX suffix 190 | public String getAndroidStringFolderNameSuffix() { 191 | if (this.name().contains("BAIDU")){ 192 | System.out.println(this.toString()); 193 | return this.getRealLanguageCode(); 194 | } 195 | if (this == Chinese_Simplified_BING || this == Chinese_Simplified) 196 | return "zh-rCN"; 197 | if (this == Chinese_Traditional_BING || this == Chinese_Traditional) 198 | return "zh-rTW"; 199 | if (this == Hebrew_BING) 200 | return Hebrew.getLanguageCode(); 201 | 202 | return this.getLanguageCode(); 203 | } 204 | // google supported language code: https://cloud.google.com/translate/v2/using_rest, language reference section 205 | private static List getBaiduLanguages() { 206 | List result = new ArrayList(); 207 | result.add(Chinese_Simplified_BAIDU); 208 | result.add(Chinese_Traditional_BAIDU); 209 | result.add(English_BAIDU); 210 | result.add(Japanese_BAIDU); 211 | result.add(Korean_BAIDU); 212 | result.add(French_BAIDU); 213 | result.add(Spanish_BAIDU); 214 | result.add(Thai_BAIDU); 215 | result.add(Arabic_BAIDU); 216 | result.add(Russian_BAIDU); 217 | result.add(Portuguese_BAIDU); 218 | result.add(German_BAIDU); 219 | result.add(Italian_BAIDU); 220 | result.add(Greek_BAIDU); 221 | result.add(Dutch_BAIDU); 222 | result.add(Polish_BAIDU); 223 | result.add(Bulgarian_BAIDU); 224 | result.add(Estonian_BAIDU); 225 | result.add(Danish_BAIDU); 226 | result.add(Finnish_BAIDU); 227 | result.add(Czech_BAIDU); 228 | result.add(Romanian_BAIDU); 229 | result.add(Slovenian_BAIDU); 230 | result.add(Swedish_BAIDU); 231 | result.add(Hungarian_BAIDU); 232 | result.add(Vietnamese_BAIDU); 233 | return result; 234 | } 235 | // google supported language code: https://cloud.google.com/translate/docs/languages, language reference section 236 | private static List getGoogleLanguages() { 237 | List result = new ArrayList(); 238 | result.add(Afrikaans); 239 | result.add(Albanian); 240 | result.add(Arabic); 241 | result.add(Azerbaijani); 242 | result.add(Basque); 243 | result.add(Bengali); 244 | result.add(Belarusian); 245 | result.add(Bulgarian); 246 | result.add(Catalan); 247 | result.add(Chinese_Simplified); 248 | result.add(Chinese_Traditional); 249 | result.add(Croatian); 250 | result.add(Czech); 251 | result.add(Danish); 252 | result.add(Dutch); 253 | result.add(English); 254 | result.add(Esperanto); 255 | result.add(Estonian); 256 | result.add(Filipino); 257 | result.add(Finnish); 258 | result.add(French); 259 | result.add(Galician); 260 | result.add(Georgian); 261 | result.add(German); 262 | result.add(Greek); 263 | result.add(Gujarati); 264 | result.add(Haitian_Creole); 265 | result.add(Hebrew); 266 | result.add(Hindi); 267 | result.add(Hungarian); 268 | result.add(Icelandic); 269 | result.add(Indonesian); 270 | result.add(Irish); 271 | result.add(Italian); 272 | result.add(Japanese); 273 | result.add(Kannada); 274 | result.add(Korean); 275 | result.add(Latin); 276 | result.add(Latvian); 277 | result.add(Macedonian); 278 | result.add(Malay); 279 | result.add(Maltese); 280 | result.add(Norwegian); 281 | result.add(Persian); 282 | result.add(Polish); 283 | result.add(Portuguese); 284 | result.add(Romanian); 285 | result.add(Russian); 286 | result.add(Serbian); 287 | result.add(Slovak); 288 | result.add(Slovenian); 289 | result.add(Spanish); 290 | result.add(Swahili); 291 | result.add(Swedish); 292 | result.add(Tamil); 293 | result.add(Telugu); 294 | result.add(Thai); 295 | result.add(Turkish); 296 | result.add(Ukrainian); 297 | result.add(Urdu); 298 | result.add(Vietnamese); 299 | result.add(Welsh); 300 | result.add(Yiddish); 301 | return result; 302 | } 303 | 304 | // bing supported language code: http://msdn.microsoft.com/en-us/library/hh456380.aspx 305 | private static List getBingLanguages() { 306 | List result = new ArrayList(); 307 | result.add(Arabic); 308 | result.add(Bulgarian); 309 | result.add(Catalan); 310 | result.add(Chinese_Simplified_BING); 311 | result.add(Chinese_Traditional_BING); 312 | result.add(Czech); 313 | result.add(Danish); 314 | result.add(Dutch); 315 | result.add(English); 316 | result.add(Estonian); 317 | result.add(Finnish); 318 | result.add(French); 319 | result.add(German); 320 | result.add(Greek); 321 | result.add(Haitian_Creole); 322 | result.add(Hebrew_BING); 323 | result.add(Hindi); 324 | result.add(Hungarian); 325 | result.add(Indonesian); 326 | result.add(Italian); 327 | result.add(Japanese); 328 | result.add(Korean); 329 | result.add(Latvian); 330 | result.add(Lithuanian); 331 | result.add(Malay); 332 | result.add(Maltese); 333 | result.add(Norwegian); 334 | result.add(Persian); 335 | result.add(Polish); 336 | result.add(Portuguese); 337 | result.add(Romanian); 338 | result.add(Russian); 339 | result.add(Slovak); 340 | result.add(Slovenian); 341 | result.add(Spanish); 342 | result.add(Swedish); 343 | result.add(Thai); 344 | result.add(Turkish); 345 | result.add(Ukrainian); 346 | result.add(Urdu); 347 | result.add(Vietnamese); 348 | result.add(Welsh); 349 | return result; 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/settings/SettingConfigurable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package settings; 18 | 19 | import com.intellij.ide.util.PropertiesComponent; 20 | import com.intellij.openapi.options.Configurable; 21 | import com.intellij.openapi.options.ConfigurationException; 22 | import com.intellij.openapi.ui.ComboBox; 23 | import com.intellij.ui.components.JBList; 24 | import com.intellij.ui.components.JBScrollPane; 25 | import data.Log; 26 | import data.SerializeUtil; 27 | import data.StorageDataKey; 28 | import language_engine.TranslationEngineType; 29 | import module.FilterRule; 30 | import org.jdesktop.swingx.VerticalLayout; 31 | import org.jdesktop.swingx.prompt.PromptSupport; 32 | import org.jetbrains.annotations.Nls; 33 | import org.jetbrains.annotations.Nullable; 34 | import ui.AddFilterRuleDialog; 35 | import ui.GoogleAlertDialog; 36 | 37 | import javax.swing.*; 38 | import java.awt.*; 39 | import java.awt.event.ActionEvent; 40 | import java.awt.event.ActionListener; 41 | import java.awt.event.MouseAdapter; 42 | import java.awt.event.MouseEvent; 43 | import java.io.IOException; 44 | import java.net.URI; 45 | import java.net.URISyntaxException; 46 | import java.util.ArrayList; 47 | 48 | /** 49 | * Created by Wesley Lin on 12/8/14. 50 | */ 51 | public class SettingConfigurable implements Configurable, ActionListener { 52 | 53 | private static final String DEFAULT_CLIENT_ID = "Default client id"; 54 | private static final String DEFAULT_CLIENT_SECRET = "Default client secret"; 55 | private static final String DEFAULT_BAIDU_APPID_PROMPT = "Please input your Baidu APP ID"; 56 | private static final String DEFAULT_BAIDU_KEY_PROMPT = "Please input your Baidu SecretKey"; 57 | 58 | private static final String DEFAULT_GOOGLE_API_KEY = "Enter API key here"; 59 | 60 | private static final String BING_HOW_TO = "How to get ClientId and ClientSecret?"; 61 | private static final String BAIDU_HOW_TO = "How to get APP ID and SecretKey?"; 62 | 63 | private MouseAdapter baiduHowTo = new MouseAdapter() { 64 | @Override 65 | public void mouseClicked(MouseEvent e) { 66 | try { 67 | Desktop.getDesktop().browse(new URI("http://api.fanyi.baidu.com/api/trans/product/index")); 68 | } catch (URISyntaxException | IOException e1) { 69 | e1.printStackTrace(); 70 | } 71 | } 72 | }; 73 | private MouseAdapter bingHowTo = new MouseAdapter() { 74 | @Override 75 | public void mouseClicked(MouseEvent e) { 76 | try { 77 | Desktop.getDesktop().browse(new URI("http://blogs.msdn.com/b/translation/p/gettingstarted1.aspx")); 78 | } catch (URISyntaxException | IOException e1) { 79 | e1.printStackTrace(); 80 | } 81 | } 82 | }; 83 | 84 | private static final String GOOGLE_HOW_TO = "How to set up Google Translation API key?"; 85 | private MouseAdapter googleHowTo = new MouseAdapter() { 86 | @Override 87 | public void mouseClicked(MouseEvent e) { 88 | try { 89 | Desktop.getDesktop().browse(new URI("https://cloud.google.com/translate/v2/getting_started#intro")); 90 | } catch (URISyntaxException | IOException e1) { 91 | e1.printStackTrace(); 92 | } 93 | } 94 | }; 95 | 96 | private JPanel settingPanel; 97 | private JComboBox languageEngineBox; 98 | private JComboBox showLanguageWhenChoose; 99 | private TranslationEngineType currentEngine; 100 | 101 | private JLabel howToLabel; 102 | private JLabel line1Text; 103 | private JTextField line1TextField; 104 | private JLabel line2Text; 105 | private JTextField line2TextField; 106 | 107 | private JBList filterList; 108 | private JButton btnAddFilter; 109 | private JButton btnDeleteFilter; 110 | 111 | private java.util.List filterRules = new ArrayList(); 112 | private boolean languageEngineChanged = false; 113 | private boolean filterRulesChanged = false; 114 | 115 | @Nls 116 | @Override 117 | public String getDisplayName() { 118 | return "AndroidTranslation"; 119 | } 120 | 121 | @Nullable 122 | @Override 123 | public String getHelpTopic() { 124 | return getDisplayName(); 125 | } 126 | 127 | @Nullable 128 | @Override 129 | public JComponent createComponent() { 130 | if (settingPanel == null) { 131 | settingPanel = new JPanel(new VerticalLayout(18)); 132 | 133 | // header UI 134 | Container container = new Container(); 135 | container.setLayout(new BorderLayout()); 136 | 137 | currentEngine = TranslationEngineType.fromName( 138 | PropertiesComponent.getInstance().getValue(StorageDataKey.SettingLanguageEngine)); 139 | TranslationEngineType[] items = TranslationEngineType.getLanguageEngineArray(); 140 | String[] showLanguage = new String[]{"English","中文"}; 141 | languageEngineBox = new ComboBox(items); 142 | showLanguageWhenChoose = new ComboBox(showLanguage); 143 | languageEngineBox.setEnabled(true); 144 | showLanguageWhenChoose.setEnabled(true); 145 | languageEngineBox.setSelectedItem(currentEngine); 146 | showLanguageWhenChoose.setSelectedItem(PropertiesComponent.getInstance().getValue(StorageDataKey.SettingLanguageShowWhenChoose,"English")); 147 | languageEngineBox.addActionListener(this); 148 | showLanguageWhenChoose.addActionListener(e -> { 149 | JComboBox comboBox = (JComboBox) e.getSource(); 150 | String type = (String) comboBox.getSelectedItem(); 151 | Log.i("selected language: " + type); 152 | PropertiesComponent.getInstance().setValue(StorageDataKey.SettingLanguageShowWhenChoose,type); 153 | 154 | }); 155 | 156 | container.add(new JLabel("Language engine: "), BorderLayout.WEST); 157 | container.add(languageEngineBox, BorderLayout.CENTER); 158 | 159 | Container container1 = new Container(); 160 | container1.setLayout(new BorderLayout()); 161 | container1.add(new JLabel("Language when choose: "), BorderLayout.WEST); 162 | container1.add(showLanguageWhenChoose, BorderLayout.CENTER); 163 | 164 | settingPanel.add(container); 165 | settingPanel.add(container1); 166 | 167 | initContentContainer(); 168 | initAndAddFilterContainer(); 169 | } 170 | return settingPanel; 171 | } 172 | 173 | @Override 174 | public boolean isModified() { 175 | if (languageEngineChanged) 176 | return true; 177 | 178 | if (filterRulesChanged) 179 | return true; 180 | 181 | PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); 182 | switch (currentEngine) { 183 | case Baidu: 184 | String baiduClientIdStored = propertiesComponent.getValue(StorageDataKey.BaiduClientIdStored); 185 | String baiduClientSecretStored = propertiesComponent.getValue(StorageDataKey.BaiduClientSecretStored); 186 | 187 | boolean baiduClientIdChanged = false; 188 | boolean baiduClientSecretChanged = false; 189 | if (baiduClientIdStored == null) { 190 | if (!line1TextField.getText().isEmpty()) 191 | baiduClientIdChanged = true; 192 | } else { 193 | if (!line1TextField.getText().equals(baiduClientIdStored) 194 | && !line1TextField.getText().trim().isEmpty()) 195 | baiduClientIdChanged = true; 196 | } 197 | 198 | if (baiduClientSecretStored == null) { 199 | if (!line2TextField.getText().isEmpty()) 200 | baiduClientSecretChanged = true; 201 | } else { 202 | if (!line2TextField.getText().equals(baiduClientSecretStored) 203 | && !line2TextField.getText().trim().isEmpty()) 204 | baiduClientSecretChanged = true; 205 | } 206 | return baiduClientIdChanged || baiduClientSecretChanged; 207 | case Bing: { 208 | String bingClientIdStored = propertiesComponent.getValue(StorageDataKey.BingClientIdStored); 209 | String bingClientSecretStored = propertiesComponent.getValue(StorageDataKey.BingClientSecretStored); 210 | 211 | boolean bingClientIdChanged = false; 212 | boolean bingClientSecretChanged = false; 213 | 214 | if (bingClientIdStored == null) { 215 | if (!line1TextField.getText().isEmpty()) 216 | bingClientIdChanged = true; 217 | } else { 218 | if (!line1TextField.getText().equals(bingClientIdStored) 219 | && !line1TextField.getText().trim().isEmpty()) 220 | bingClientIdChanged = true; 221 | } 222 | 223 | if (bingClientSecretStored == null) { 224 | if (!line2TextField.getText().isEmpty()) 225 | bingClientSecretChanged = true; 226 | } else { 227 | if (!line2TextField.getText().equals(bingClientSecretStored) 228 | && !line2TextField.getText().trim().isEmpty()) 229 | bingClientSecretChanged = true; 230 | } 231 | 232 | return bingClientIdChanged || bingClientSecretChanged; 233 | } 234 | case Google: { 235 | String googleApiKeyStored = propertiesComponent.getValue(StorageDataKey.GoogleApiKeyStored); 236 | boolean googleApiKeyStoredChanged = false; 237 | 238 | if (googleApiKeyStored == null) { 239 | if (!line1TextField.getText().isEmpty()) 240 | googleApiKeyStoredChanged = true; 241 | } else { 242 | if (!line1TextField.getText().equals(googleApiKeyStored) 243 | && !line1TextField.getText().trim().isEmpty()) 244 | googleApiKeyStoredChanged = true; 245 | } 246 | return googleApiKeyStoredChanged; 247 | } 248 | } 249 | return false; 250 | } 251 | 252 | @Override 253 | public void apply() throws ConfigurationException { 254 | Log.i("apply clicked"); 255 | if (languageEngineBox == null || filterList == null 256 | || btnAddFilter == null || btnDeleteFilter == null 257 | || line1TextField == null || line2TextField == null) 258 | return; 259 | 260 | PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); 261 | 262 | languageEngineChanged = false; 263 | propertiesComponent.setValue(StorageDataKey.SettingLanguageEngine, currentEngine.toName()); 264 | 265 | switch (currentEngine) { 266 | case Baidu: 267 | if (!line1TextField.getText().trim().isEmpty()) { 268 | propertiesComponent.setValue(StorageDataKey.BaiduClientIdStored, line1TextField.getText()); 269 | PromptSupport.setPrompt(line1TextField.getText(), line1TextField); 270 | } 271 | 272 | if (!line2TextField.getText().trim().isEmpty()) { 273 | propertiesComponent.setValue(StorageDataKey.BaiduClientSecretStored, line2TextField.getText()); 274 | PromptSupport.setPrompt(line2TextField.getText(), line2TextField); 275 | } 276 | line1TextField.setText(""); 277 | line2TextField.setText(""); 278 | break; 279 | case Bing: { 280 | if (!line1TextField.getText().trim().isEmpty()) { 281 | propertiesComponent.setValue(StorageDataKey.BingClientIdStored, line1TextField.getText()); 282 | PromptSupport.setPrompt(line1TextField.getText(), line1TextField); 283 | } 284 | 285 | if (!line2TextField.getText().trim().isEmpty()) { 286 | propertiesComponent.setValue(StorageDataKey.BingClientSecretStored, line2TextField.getText()); 287 | PromptSupport.setPrompt(line2TextField.getText(), line2TextField); 288 | } 289 | line1TextField.setText(""); 290 | line2TextField.setText(""); 291 | } 292 | break; 293 | case Google: { 294 | if (!line1TextField.getText().trim().isEmpty()) { 295 | propertiesComponent.setValue(StorageDataKey.GoogleApiKeyStored, line1TextField.getText()); 296 | PromptSupport.setPrompt(line1TextField.getText(), line1TextField); 297 | } 298 | line1TextField.setText(""); 299 | } 300 | break; 301 | } 302 | languageEngineBox.requestFocus(); 303 | 304 | filterRulesChanged = false; 305 | propertiesComponent.setValue(StorageDataKey.SettingFilterRules, 306 | SerializeUtil.serializeFilterRuleList(filterRules)); 307 | } 308 | 309 | @Override 310 | public void reset() { 311 | if (settingPanel == null || languageEngineBox == null || filterList == null 312 | || btnAddFilter == null || btnDeleteFilter == null) 313 | return; 314 | PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); 315 | 316 | currentEngine = TranslationEngineType.fromName( 317 | propertiesComponent.getValue(StorageDataKey.SettingLanguageEngine)); 318 | languageEngineBox.setSelectedItem(currentEngine); 319 | languageEngineChanged = false; 320 | initUI(currentEngine); 321 | 322 | Log.i("reset, current engine: " + currentEngine); 323 | 324 | languageEngineBox.requestFocus(); 325 | 326 | // filter rules 327 | filterRulesChanged = false; 328 | resetFilterList(); 329 | } 330 | 331 | @Override 332 | public void disposeUIResources() { 333 | 334 | } 335 | 336 | @Override 337 | public void actionPerformed(ActionEvent e) { 338 | JComboBox comboBox = (JComboBox) e.getSource(); 339 | TranslationEngineType type = (TranslationEngineType) comboBox.getSelectedItem(); 340 | if ((type == currentEngine) && (!languageEngineChanged)) 341 | return; 342 | 343 | languageEngineChanged = true; 344 | Log.i("selected type: " + type.name()); 345 | currentEngine = type; 346 | 347 | initUI(currentEngine); 348 | 349 | // default: false, if user set 'never show', set true 350 | boolean GoogleAlertMsgShownSetting = PropertiesComponent.getInstance().getBoolean(StorageDataKey.GoogleAlertMsgShownSetting, false); 351 | if (currentEngine == TranslationEngineType.Google && !GoogleAlertMsgShownSetting) { 352 | new GoogleAlertDialog(settingPanel, false).show(); 353 | } 354 | } 355 | 356 | private void initUI(TranslationEngineType engineType) { 357 | if (settingPanel == null) 358 | return; 359 | 360 | PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); 361 | switch (engineType) { 362 | case Baidu: { 363 | line1Text.setText("APP ID:"); 364 | line2Text.setText("SecretKey:"); 365 | line2Text.setVisible(true); 366 | line2TextField.setVisible(true); 367 | howToLabel.setText(BAIDU_HOW_TO); 368 | howToLabel.removeMouseMotionListener(googleHowTo); 369 | howToLabel.removeMouseMotionListener(bingHowTo); 370 | howToLabel.addMouseListener(baiduHowTo); 371 | 372 | String baiduClientIdStored = propertiesComponent.getValue(StorageDataKey.BaiduClientIdStored); 373 | String baiduClientSecretStored = propertiesComponent.getValue(StorageDataKey.BaiduClientSecretStored); 374 | 375 | if (baiduClientIdStored != null) { 376 | PromptSupport.setPrompt(baiduClientIdStored, line1TextField); 377 | } else { 378 | PromptSupport.setPrompt(DEFAULT_BAIDU_APPID_PROMPT, line1TextField); 379 | } 380 | line1TextField.setText(""); 381 | 382 | if (baiduClientSecretStored != null) { 383 | PromptSupport.setPrompt(baiduClientSecretStored, line2TextField); 384 | } else { 385 | PromptSupport.setPrompt(DEFAULT_BAIDU_KEY_PROMPT, line2TextField); 386 | } 387 | line2TextField.setText(""); 388 | } 389 | break; 390 | case Bing: { 391 | line1Text.setText("Client Id:"); 392 | line2Text.setText("Client secret:"); 393 | line2Text.setVisible(true); 394 | 395 | line2TextField.setVisible(true); 396 | 397 | howToLabel.setText(BING_HOW_TO); 398 | howToLabel.removeMouseMotionListener(googleHowTo); 399 | howToLabel.removeMouseMotionListener(baiduHowTo); 400 | howToLabel.addMouseListener(bingHowTo); 401 | 402 | String bingClientIdStored = propertiesComponent.getValue(StorageDataKey.BingClientIdStored); 403 | String bingClientSecretStored = propertiesComponent.getValue(StorageDataKey.BingClientSecretStored); 404 | 405 | if (bingClientIdStored != null) { 406 | PromptSupport.setPrompt(bingClientIdStored, line1TextField); 407 | } else { 408 | PromptSupport.setPrompt(DEFAULT_CLIENT_ID, line1TextField); 409 | } 410 | line1TextField.setText(""); 411 | 412 | if (bingClientSecretStored != null) { 413 | PromptSupport.setPrompt(bingClientSecretStored, line2TextField); 414 | } else { 415 | PromptSupport.setPrompt(DEFAULT_CLIENT_SECRET, line2TextField); 416 | } 417 | line2TextField.setText(""); 418 | } 419 | break; 420 | case Google: { 421 | line1Text.setText("API key:"); 422 | line2Text.setVisible(false); 423 | 424 | line2TextField.setVisible(false); 425 | 426 | howToLabel.setText(GOOGLE_HOW_TO); 427 | howToLabel.removeMouseMotionListener(bingHowTo); 428 | howToLabel.removeMouseMotionListener(baiduHowTo); 429 | howToLabel.addMouseListener(googleHowTo); 430 | 431 | String googleAPIKey = propertiesComponent.getValue(StorageDataKey.GoogleApiKeyStored); 432 | 433 | if (googleAPIKey != null) { 434 | PromptSupport.setPrompt(googleAPIKey, line1TextField); 435 | } else { 436 | PromptSupport.setPrompt(DEFAULT_GOOGLE_API_KEY, line1TextField); 437 | } 438 | line1TextField.setText(""); 439 | } 440 | break; 441 | } 442 | } 443 | 444 | private void initContentContainer() { 445 | line1TextField = new JTextField(); 446 | line2TextField = new JTextField(); 447 | 448 | line1Text = new JLabel("Client Id:"); 449 | line2Text = new JLabel("Client Secret:"); 450 | 451 | Container outContainer = new Container(); 452 | outContainer.setLayout(new BorderLayout(0, 5)); 453 | 454 | howToLabel = new JLabel(); 455 | howToLabel.setCursor(new Cursor(Cursor.HAND_CURSOR)); 456 | outContainer.add(howToLabel, BorderLayout.NORTH); 457 | 458 | Container contentContainer = new Container(); 459 | contentContainer.setLayout(new GridBagLayout()); 460 | ((GridBagLayout) contentContainer.getLayout()).columnWidths = new int[]{0, 0, 0}; 461 | ((GridBagLayout) contentContainer.getLayout()).rowHeights = new int[]{0, 0, 0}; 462 | ((GridBagLayout) contentContainer.getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0E-4}; 463 | ((GridBagLayout) contentContainer.getLayout()).rowWeights = new double[]{0.0, 0.0, 1.0E-4}; 464 | 465 | line1Text.setHorizontalAlignment(SwingConstants.RIGHT); 466 | contentContainer.add(line1Text, new GridBagConstraints(0, 0, 1, 1, 0.5, 0.0, 467 | GridBagConstraints.CENTER, GridBagConstraints.BOTH, 468 | new Insets(0, 0, 5, 5), 0, 0)); 469 | 470 | contentContainer.add(line1TextField, new GridBagConstraints(1, 0, 1, 1, 10.0, 0.0, 471 | GridBagConstraints.CENTER, GridBagConstraints.BOTH, 472 | new Insets(0, 0, 5, 0), 0, 0)); 473 | 474 | line2Text.setHorizontalAlignment(SwingConstants.RIGHT); 475 | contentContainer.add(line2Text, new GridBagConstraints(0, 1, 1, 1, 0.5, 0.0, 476 | GridBagConstraints.CENTER, GridBagConstraints.BOTH, 477 | new Insets(0, 0, 0, 5), 0, 0)); 478 | contentContainer.add(line2TextField, new GridBagConstraints(1, 1, 1, 1, 10.0, 0.0, 479 | GridBagConstraints.CENTER, GridBagConstraints.BOTH, 480 | new Insets(0, 0, 0, 0), 0, 0)); 481 | 482 | outContainer.add(contentContainer, BorderLayout.CENTER); 483 | settingPanel.add(outContainer); 484 | } 485 | 486 | private void initAndAddFilterContainer() { 487 | Container filterSettingContainer = new Container(); 488 | filterSettingContainer.setLayout(new BorderLayout(0, 5)); 489 | 490 | final JLabel filterLabel = new JLabel("Filter setting"); 491 | filterSettingContainer.add(filterLabel, BorderLayout.NORTH); 492 | 493 | Container listPane = new Container(); 494 | listPane.setLayout(new BorderLayout()); 495 | 496 | JBScrollPane scrollPane = new JBScrollPane(); 497 | filterList = new JBList(new String[]{"1,", "2"}); 498 | 499 | filterList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 500 | scrollPane.setViewportView(filterList); 501 | listPane.add(scrollPane, BorderLayout.NORTH); 502 | 503 | Container btnPane = new Container(); 504 | btnPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); 505 | btnAddFilter = new JButton("+"); 506 | btnDeleteFilter = new JButton("-"); 507 | btnPane.add(btnAddFilter); 508 | btnPane.add(btnDeleteFilter); 509 | 510 | filterList.addMouseListener(new MouseAdapter() { 511 | @Override 512 | public void mouseClicked(MouseEvent e) { 513 | if (SwingUtilities.isLeftMouseButton(e)) { 514 | if (filterList.getSelectedIndex() <= 0) { 515 | btnDeleteFilter.setEnabled(false); 516 | } else { 517 | btnDeleteFilter.setEnabled(true); 518 | } 519 | } 520 | } 521 | }); 522 | 523 | btnAddFilter.addActionListener(new ActionListener() { 524 | @Override 525 | public void actionPerformed(ActionEvent actionEvent) { 526 | filterRulesChanged = true; 527 | AddFilterRuleDialog dialog = new AddFilterRuleDialog(settingPanel, 528 | "Set your filter rule", false); 529 | dialog.setOnOKClickedListener(new AddFilterRuleDialog.OnOKClickedListener() { 530 | @Override 531 | public void onClick(FilterRule.FilterRuleType ruleType, String filterNameString) { 532 | filterRules.add(new FilterRule(ruleType, filterNameString)); 533 | int index = filterList.getSelectedIndex(); 534 | filterList.setListData(getFilterRulesDisplayString()); 535 | filterList.setSelectedIndex(index); 536 | } 537 | }); 538 | dialog.show(); 539 | } 540 | }); 541 | 542 | btnDeleteFilter.addActionListener(new ActionListener() { 543 | @Override 544 | public void actionPerformed(ActionEvent actionEvent) { 545 | filterRulesChanged = true; 546 | int index = filterList.getSelectedIndex(); 547 | filterRules.remove(index); 548 | filterList.setListData(getFilterRulesDisplayString()); 549 | if (index < filterRules.size()) { 550 | filterList.setSelectedIndex(index); 551 | } else { 552 | if (filterRules.size() == 1) { 553 | btnDeleteFilter.setEnabled(false); 554 | } 555 | filterList.setSelectedIndex(filterRules.size() - 1); 556 | } 557 | } 558 | }); 559 | 560 | listPane.add(btnPane, BorderLayout.CENTER); 561 | filterSettingContainer.add(listPane, BorderLayout.CENTER); 562 | 563 | settingPanel.add(filterSettingContainer); 564 | } 565 | 566 | private void resetFilterList() { 567 | btnDeleteFilter.setEnabled(false); 568 | filterRules.clear(); 569 | filterRules.addAll(FilterRule.getFilterRulesFromLocal()); 570 | 571 | filterList.setListData(getFilterRulesDisplayString()); 572 | } 573 | 574 | private String[] getFilterRulesDisplayString() { 575 | String[] displayStrings = new String[filterRules.size()]; 576 | for (int i = 0; i < filterRules.size(); i++) { 577 | displayStrings[i] = filterRules.get(i).toString(); 578 | } 579 | return displayStrings; 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /src/ui/AddFilterRuleDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ui; 18 | 19 | import com.intellij.openapi.application.ModalityState; 20 | import com.intellij.openapi.ui.ComboBox; 21 | import com.intellij.openapi.ui.DialogWrapper; 22 | import com.intellij.openapi.ui.Messages; 23 | import com.intellij.openapi.util.Computable; 24 | import com.intellij.openapi.util.SystemInfo; 25 | import com.intellij.openapi.wm.IdeFrame; 26 | import com.intellij.openapi.wm.WindowManager; 27 | import com.intellij.ui.mac.foundation.MacUtil; 28 | import com.intellij.util.Alarm; 29 | import module.FilterRule; 30 | import org.jdesktop.swingx.prompt.PromptSupport; 31 | import org.jetbrains.annotations.NotNull; 32 | import org.jetbrains.annotations.Nullable; 33 | 34 | import javax.swing.*; 35 | import java.awt.*; 36 | import java.lang.reflect.Method; 37 | import java.util.concurrent.atomic.AtomicInteger; 38 | 39 | /** 40 | * Created by Wesley Lin on 12/15/14. 41 | */ 42 | public class AddFilterRuleDialog extends DialogWrapper { 43 | 44 | public interface OnOKClickedListener { 45 | public void onClick(FilterRule.FilterRuleType ruleType, String filterNameString); 46 | } 47 | 48 | private MyBorderLayout myLayout; 49 | private ComboBox ruleType; 50 | private JTextField filterName; 51 | 52 | private OnOKClickedListener onOKClickedListener; 53 | 54 | public void setOnOKClickedListener(OnOKClickedListener onOKClickedListener) { 55 | this.onOKClickedListener = onOKClickedListener; 56 | } 57 | 58 | public AddFilterRuleDialog(@Nullable Component parent, 59 | String title, 60 | boolean canBeParent) { 61 | super(parent, canBeParent); 62 | _init(title, null); 63 | } 64 | 65 | protected void _init(String title, 66 | @Nullable DoNotAskOption doNotAskOption) { 67 | setTitle(title); 68 | if (Messages.isMacSheetEmulation()) { 69 | setUndecorated(true); 70 | } 71 | 72 | setButtonsAlignment(SwingConstants.RIGHT); 73 | setDoNotAskOption(doNotAskOption); 74 | init(); 75 | if (Messages.isMacSheetEmulation()) { 76 | MacUtil.adjustFocusTraversal(myDisposable); 77 | } 78 | } 79 | 80 | @Override 81 | protected void doOKAction() { 82 | super.doOKAction(); 83 | if (onOKClickedListener != null && ruleType != null && filterName != null) { 84 | onOKClickedListener.onClick((FilterRule.FilterRuleType) ruleType.getSelectedItem(), 85 | filterName.getText()); 86 | } 87 | } 88 | 89 | @NotNull 90 | @Override 91 | protected Action[] createActions() { 92 | Action[] actions; 93 | if (SystemInfo.isMac) { 94 | actions = new Action[]{myCancelAction, myOKAction}; 95 | } else { 96 | actions = new Action[]{myOKAction, myCancelAction}; 97 | } 98 | return actions; 99 | } 100 | 101 | @Override 102 | public void doCancelAction() { 103 | close(-1); 104 | } 105 | 106 | @Override 107 | protected JComponent createCenterPanel() { 108 | return doCreateCenterPanel(); 109 | } 110 | 111 | @NotNull 112 | protected LayoutManager createRootLayout() { 113 | return Messages.isMacSheetEmulation() ? myLayout = new MyBorderLayout() : new BorderLayout(); 114 | } 115 | 116 | @Override 117 | protected void dispose() { 118 | if (Messages.isMacSheetEmulation()) { 119 | animate(); 120 | } else { 121 | super.dispose(); 122 | } 123 | } 124 | 125 | @Override 126 | public void show() { 127 | if (Messages.isMacSheetEmulation()) { 128 | setInitialLocationCallback(new Computable() { 129 | @Override 130 | public Point compute() { 131 | JRootPane rootPane = SwingUtilities.getRootPane(getWindow().getParent()); 132 | if (rootPane == null) { 133 | rootPane = SwingUtilities.getRootPane(getWindow().getOwner()); 134 | } 135 | 136 | Point p = rootPane.getLocationOnScreen(); 137 | p.x += (rootPane.getWidth() - getWindow().getWidth()) / 2; 138 | return p; 139 | } 140 | }); 141 | animate(); 142 | if (SystemInfo.isJavaVersionAtLeast("1.7")) { 143 | try { 144 | Method method = Class.forName("java.awt.Window").getDeclaredMethod("setOpacity", float.class); 145 | if (method != null) method.invoke(getPeer().getWindow(), .8f); 146 | } catch (Exception exception) { 147 | } 148 | } 149 | setAutoAdjustable(false); 150 | setSize(getPreferredSize().width, 0);//initial state before animation, zero height 151 | } 152 | super.show(); 153 | } 154 | 155 | private void animate() { 156 | final int height = getPreferredSize().height; 157 | final int frameCount = 10; 158 | final boolean toClose = isShowing(); 159 | 160 | 161 | final AtomicInteger i = new AtomicInteger(-1); 162 | final Alarm animator = new Alarm(myDisposable); 163 | final Runnable runnable = new Runnable() { 164 | @Override 165 | public void run() { 166 | int state = i.addAndGet(1); 167 | 168 | double linearProgress = (double) state / frameCount; 169 | if (toClose) { 170 | linearProgress = 1 - linearProgress; 171 | } 172 | myLayout.myPhase = (1 - Math.cos(Math.PI * linearProgress)) / 2; 173 | Window window = getPeer().getWindow(); 174 | Rectangle bounds = window.getBounds(); 175 | bounds.height = (int) (height * myLayout.myPhase); 176 | 177 | window.setBounds(bounds); 178 | 179 | if (state == 0 && !toClose && window.getOwner() instanceof IdeFrame) { 180 | WindowManager.getInstance().requestUserAttention((IdeFrame) window.getOwner(), true); 181 | } 182 | 183 | if (state < frameCount) { 184 | animator.addRequest(this, 10); 185 | } else if (toClose) { 186 | AddFilterRuleDialog.super.dispose(); 187 | } 188 | } 189 | }; 190 | animator.addRequest(runnable, 10, ModalityState.stateForComponent(getRootPane())); 191 | } 192 | 193 | protected JComponent doCreateCenterPanel() { 194 | JPanel panel = new JPanel(new BorderLayout(5, 0)); 195 | 196 | FilterRule.FilterRuleType[] types = FilterRule.FilterRuleType.values(); 197 | 198 | ruleType = new ComboBox(types); 199 | ruleType.setEnabled(true); 200 | ruleType.setSelectedIndex(0); 201 | 202 | panel.add(ruleType, BorderLayout.WEST); 203 | 204 | filterName = new JTextField(20); 205 | PromptSupport.setPrompt("Set the string name here", filterName); 206 | panel.add(filterName, BorderLayout.CENTER); 207 | 208 | return panel; 209 | } 210 | 211 | @Override 212 | protected void doHelpAction() { 213 | // do nothing 214 | } 215 | 216 | private static class MyBorderLayout extends BorderLayout { 217 | private double myPhase = 0;//it varies from 0 (hidden state) to 1 (fully visible) 218 | 219 | private MyBorderLayout() { 220 | } 221 | 222 | @Override 223 | public void layoutContainer(Container target) { 224 | final Dimension realSize = target.getSize(); 225 | target.setSize(target.getPreferredSize()); 226 | 227 | super.layoutContainer(target); 228 | 229 | target.setSize(realSize); 230 | 231 | synchronized (target.getTreeLock()) { 232 | int yShift = (int) ((1 - myPhase) * target.getPreferredSize().height); 233 | Component[] components = target.getComponents(); 234 | for (Component component : components) { 235 | Point point = component.getLocation(); 236 | point.y -= yShift; 237 | component.setLocation(point); 238 | } 239 | } 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/ui/GoogleAlertDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ui; 18 | 19 | import com.intellij.ide.util.PropertiesComponent; 20 | import com.intellij.openapi.ui.DialogWrapper; 21 | import com.intellij.openapi.ui.Messages; 22 | import com.intellij.openapi.util.SystemInfo; 23 | import com.intellij.ui.ScrollPaneFactory; 24 | import com.intellij.ui.mac.foundation.MacUtil; 25 | import com.intellij.util.ui.UIUtil; 26 | import data.StorageDataKey; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.jetbrains.annotations.Nullable; 29 | 30 | import javax.swing.*; 31 | import java.awt.*; 32 | import java.awt.event.ActionEvent; 33 | import java.io.IOException; 34 | import java.net.URI; 35 | import java.net.URISyntaxException; 36 | 37 | /** 38 | * Created by Wesley Lin on 12/27/14. 39 | */ 40 | public class GoogleAlertDialog extends DialogWrapper { 41 | 42 | private String mMessage; 43 | 44 | public GoogleAlertDialog(@Nullable Component parent, 45 | boolean canBeParent) { 46 | super(parent, canBeParent); 47 | _init("Information", "Google Translate API is a paid service. The pricing is based on usage. Translation usage is calculated in millions of characters (M), where 1 M = 106 characters"); 48 | } 49 | 50 | protected void _init(String title, String message) { 51 | setTitle(title); 52 | mMessage = message; 53 | if (Messages.isMacSheetEmulation()) { 54 | setUndecorated(true); 55 | } 56 | 57 | setButtonsAlignment(SwingConstants.RIGHT); 58 | setDoNotAskOption(null); 59 | init(); 60 | if (Messages.isMacSheetEmulation()) { 61 | MacUtil.adjustFocusTraversal(myDisposable); 62 | } 63 | } 64 | 65 | private Action detailAction = new AbstractAction(UIUtil.replaceMnemonicAmpersand("Pricing details")) { 66 | @Override 67 | public void actionPerformed(ActionEvent e) { 68 | try { 69 | Desktop.getDesktop().browse(new URI("https://cloud.google.com/translate/v2/pricing")); 70 | } catch (URISyntaxException e1) { 71 | e1.printStackTrace(); 72 | } catch (IOException e1) { 73 | e1.printStackTrace(); 74 | } 75 | close(5, true); 76 | } 77 | }; 78 | 79 | private Action neverShowAction = new AbstractAction(UIUtil.replaceMnemonicAmpersand("Never show this again")) { 80 | @Override 81 | public void actionPerformed(ActionEvent e) { 82 | PropertiesComponent.getInstance().setValue(StorageDataKey.GoogleAlertMsgShownSetting, String.valueOf(true)); 83 | close(6, true); 84 | } 85 | }; 86 | 87 | @NotNull 88 | @Override 89 | protected Action[] createActions() { 90 | Action[] actions; 91 | if (SystemInfo.isMac) { 92 | actions = new Action[]{myCancelAction, neverShowAction, detailAction, myOKAction}; 93 | } else { 94 | actions = new Action[]{myOKAction, detailAction, neverShowAction, myCancelAction}; 95 | } 96 | return actions; 97 | } 98 | 99 | @Nullable 100 | @Override 101 | protected JComponent createCenterPanel() { 102 | JPanel panel = new JPanel(new BorderLayout(15, 0)); 103 | 104 | // icon 105 | JLabel iconLabel = new JLabel(Messages.getInformationIcon()); 106 | Container container = new Container(); 107 | container.setLayout(new BorderLayout()); 108 | container.add(iconLabel, BorderLayout.NORTH); 109 | panel.add(container, BorderLayout.WEST); 110 | 111 | if (mMessage != null) { 112 | final JTextPane messageComponent = MultiSelectDialog.createMessageComponent(mMessage); 113 | 114 | final Dimension screenSize = messageComponent.getToolkit().getScreenSize(); 115 | final Dimension textSize = messageComponent.getPreferredSize(); 116 | if (mMessage.length() > 100) { 117 | final JScrollPane pane = ScrollPaneFactory.createScrollPane(messageComponent); 118 | pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 119 | pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 120 | pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 121 | final int scrollSize = (int) new JScrollBar(Adjustable.VERTICAL).getPreferredSize().getWidth(); 122 | final Dimension preferredSize = 123 | new Dimension(Math.min(textSize.width, screenSize.width / 2) + scrollSize, 124 | Math.min(textSize.height, screenSize.height / 3) + scrollSize); 125 | pane.setPreferredSize(preferredSize); 126 | panel.add(pane, BorderLayout.CENTER); 127 | } else { 128 | panel.add(messageComponent, BorderLayout.CENTER); 129 | } 130 | } 131 | return panel; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/ui/MultiSelectDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2015 Wesley Lin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ui; 18 | 19 | import com.intellij.ide.util.PropertiesComponent; 20 | import com.intellij.openapi.application.ModalityState; 21 | import com.intellij.openapi.project.Project; 22 | import com.intellij.openapi.ui.DialogWrapper; 23 | import com.intellij.openapi.ui.Messages; 24 | import com.intellij.openapi.util.Computable; 25 | import com.intellij.openapi.util.SystemInfo; 26 | import com.intellij.openapi.wm.IdeFrame; 27 | import com.intellij.openapi.wm.WindowManager; 28 | import com.intellij.ui.BrowserHyperlinkListener; 29 | import com.intellij.ui.ScrollPaneFactory; 30 | import com.intellij.ui.mac.foundation.MacUtil; 31 | import com.intellij.util.Alarm; 32 | import com.intellij.util.ui.UIUtil; 33 | import data.StorageDataKey; 34 | import language_engine.TranslationEngineType; 35 | import module.SupportedLanguages; 36 | import org.jetbrains.annotations.NotNull; 37 | import org.jetbrains.annotations.Nullable; 38 | 39 | import javax.swing.*; 40 | import javax.swing.plaf.basic.BasicHTML; 41 | import javax.swing.text.html.HTMLEditorKit; 42 | import java.awt.*; 43 | import java.awt.event.ItemEvent; 44 | import java.awt.event.ItemListener; 45 | import java.lang.reflect.Method; 46 | import java.util.ArrayList; 47 | import java.util.List; 48 | import java.util.concurrent.atomic.AtomicInteger; 49 | 50 | /** 51 | * Created by Wesley Lin on 11/29/14. 52 | */ 53 | public class MultiSelectDialog extends DialogWrapper { 54 | 55 | public static final double GOLDEN_RATIO = 0.618; 56 | public static final double REVERSE_GOLDEN_RATIO = 1 - GOLDEN_RATIO; 57 | 58 | public interface OnOKClickedListener { 59 | public void onClick(List selectedLanguages, boolean overrideChecked); 60 | } 61 | 62 | private PropertiesComponent propertiesComponent; 63 | protected String myMessage; 64 | private MyBorderLayout myLayout; 65 | 66 | private JCheckBox myCheckBox; 67 | private String myCheckboxText; 68 | private boolean myChecked; 69 | 70 | private List data; 71 | private List selectedLanguages = new ArrayList(); 72 | private OnOKClickedListener onOKClickedListener; 73 | 74 | public void setOnOKClickedListener(OnOKClickedListener onOKClickedListener) { 75 | this.onOKClickedListener = onOKClickedListener; 76 | } 77 | 78 | public MultiSelectDialog(@Nullable Project project, 79 | String message, 80 | String title, 81 | @Nullable String checkboxText, 82 | boolean checkboxStatus, 83 | TranslationEngineType translationEngineType, 84 | boolean canBeParent) { 85 | super(project, canBeParent); 86 | data = SupportedLanguages.getAllSupportedLanguages(translationEngineType); 87 | _init(project, title, message, checkboxText, checkboxStatus, null); 88 | } 89 | 90 | protected void _init(Project project, 91 | String title, 92 | String message, 93 | @Nullable String checkboxText, 94 | boolean checkboxStatus, 95 | @Nullable DoNotAskOption doNotAskOption) { 96 | setTitle(title); 97 | if (Messages.isMacSheetEmulation()) { 98 | setUndecorated(true); 99 | } 100 | propertiesComponent = PropertiesComponent.getInstance(project); 101 | myMessage = message; 102 | myCheckboxText = checkboxText; 103 | myChecked = checkboxStatus; 104 | setButtonsAlignment(SwingConstants.RIGHT); 105 | setDoNotAskOption(doNotAskOption); 106 | init(); 107 | if (Messages.isMacSheetEmulation()) { 108 | MacUtil.adjustFocusTraversal(myDisposable); 109 | } 110 | } 111 | 112 | @Override 113 | protected void doOKAction() { 114 | super.doOKAction(); 115 | if (onOKClickedListener != null) { 116 | onOKClickedListener.onClick(selectedLanguages, myCheckBox.isSelected()); 117 | } 118 | } 119 | 120 | @NotNull 121 | @Override 122 | protected Action[] createActions() { 123 | Action[] actions; 124 | if (SystemInfo.isMac) { 125 | actions = new Action[]{myCancelAction, myOKAction}; 126 | } else { 127 | actions = new Action[]{myOKAction, myCancelAction}; 128 | } 129 | return actions; 130 | } 131 | 132 | @Override 133 | public void doCancelAction() { 134 | close(-1); 135 | } 136 | 137 | @Override 138 | protected JComponent createCenterPanel() { 139 | return doCreateCenterPanel(); 140 | } 141 | 142 | @NotNull 143 | protected LayoutManager createRootLayout() { 144 | return Messages.isMacSheetEmulation() ? myLayout = new MyBorderLayout() : new BorderLayout(); 145 | } 146 | 147 | @Override 148 | protected void dispose() { 149 | if (Messages.isMacSheetEmulation()) { 150 | animate(); 151 | } else { 152 | super.dispose(); 153 | } 154 | } 155 | 156 | @Override 157 | public void show() { 158 | if (Messages.isMacSheetEmulation()) { 159 | setInitialLocationCallback(new Computable() { 160 | @Override 161 | public Point compute() { 162 | JRootPane rootPane = SwingUtilities.getRootPane(getWindow().getParent()); 163 | if (rootPane == null) { 164 | rootPane = SwingUtilities.getRootPane(getWindow().getOwner()); 165 | } 166 | 167 | Point p = rootPane.getLocationOnScreen(); 168 | p.x += (rootPane.getWidth() - getWindow().getWidth()) / 2; 169 | return p; 170 | } 171 | }); 172 | animate(); 173 | if (SystemInfo.isJavaVersionAtLeast("1.7")) { 174 | try { 175 | Method method = Class.forName("java.awt.Window").getDeclaredMethod("setOpacity", float.class); 176 | if (method != null) method.invoke(getPeer().getWindow(), .8f); 177 | } catch (Exception exception) { 178 | } 179 | } 180 | setAutoAdjustable(false); 181 | setSize(getPreferredSize().width, 0);//initial state before animation, zero height 182 | } 183 | super.show(); 184 | } 185 | 186 | private void animate() { 187 | final int height = getPreferredSize().height; 188 | final int frameCount = 10; 189 | final boolean toClose = isShowing(); 190 | 191 | 192 | final AtomicInteger i = new AtomicInteger(-1); 193 | final Alarm animator = new Alarm(myDisposable); 194 | final Runnable runnable = new Runnable() { 195 | @Override 196 | public void run() { 197 | int state = i.addAndGet(1); 198 | 199 | double linearProgress = (double) state / frameCount; 200 | if (toClose) { 201 | linearProgress = 1 - linearProgress; 202 | } 203 | myLayout.myPhase = (1 - Math.cos(Math.PI * linearProgress)) / 2; 204 | Window window = getPeer().getWindow(); 205 | Rectangle bounds = window.getBounds(); 206 | bounds.height = (int) (height * myLayout.myPhase); 207 | 208 | window.setBounds(bounds); 209 | 210 | if (state == 0 && !toClose && window.getOwner() instanceof IdeFrame) { 211 | WindowManager.getInstance().requestUserAttention((IdeFrame) window.getOwner(), true); 212 | } 213 | 214 | if (state < frameCount) { 215 | animator.addRequest(this, 10); 216 | } else if (toClose) { 217 | MultiSelectDialog.super.dispose(); 218 | } 219 | } 220 | }; 221 | animator.addRequest(runnable, 10, ModalityState.stateForComponent(getRootPane())); 222 | } 223 | 224 | protected JComponent doCreateCenterPanel() { 225 | final JPanel panel = new JPanel(new BorderLayout(15, 0)); 226 | 227 | /*if (myMessage != null) { 228 | final JTextPane messageComponent = createMessageComponent(myMessage); 229 | 230 | final Dimension screenSize = messageComponent.getToolkit().getScreenSize(); 231 | final Dimension textSize = messageComponent.getPreferredSize(); 232 | if (myMessage.length() > 100) { 233 | final JScrollPane pane = ScrollPaneFactory.createScrollPane(messageComponent); 234 | pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 235 | pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 236 | pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 237 | final int scrollSize = (int) new JScrollBar(Adjustable.VERTICAL).getPreferredSize().getWidth() + 12; 238 | final Dimension preferredSize = 239 | new Dimension(Math.min(textSize.width, (int) (screenSize.width * REVERSE_GOLDEN_RATIO)) + scrollSize, 240 | Math.min(textSize.height, screenSize.height / 3) + scrollSize); 241 | pane.setPreferredSize(preferredSize); 242 | panel.add(pane, BorderLayout.NORTH); 243 | } else { 244 | panel.add(messageComponent, BorderLayout.NORTH); 245 | } 246 | }*/ 247 | 248 | if (!data.isEmpty()) { 249 | final Container container = new Container(); 250 | 251 | final JCheckBox checkbox_selectAll = new JCheckBox("Select All"); 252 | checkbox_selectAll.setMargin(new Insets(22, 300, 0, 0)); 253 | panel.add(checkbox_selectAll, BorderLayout.NORTH); 254 | 255 | checkbox_selectAll.addItemListener(new ItemListener() { 256 | @Override 257 | public void itemStateChanged(ItemEvent e) { 258 | if (e.getStateChange() == ItemEvent.SELECTED) { 259 | selectedLanguages.addAll(data); 260 | checkbox_selectAll.setSelected(true); 261 | 262 | for(Component component:container.getComponents()){ 263 | JCheckBox checkBox = (JCheckBox) component; 264 | checkBox.setSelected(true); 265 | } 266 | 267 | } else if (e.getStateChange() == ItemEvent.DESELECTED) { 268 | checkbox_selectAll.setSelected(false); 269 | selectedLanguages.removeAll(data); 270 | 271 | for(Component component:container.getComponents()){ 272 | JCheckBox checkBox = (JCheckBox) component; 273 | checkBox.setSelected(false); 274 | } 275 | } 276 | } 277 | }); 278 | 279 | 280 | int gridCol = 3; 281 | int gridRow = (data.size() % gridCol == 0) ? data.size() / gridCol : data.size() / gridCol + 1; 282 | container.setLayout(new GridLayout(gridRow, gridCol)); 283 | boolean showEnglish= PropertiesComponent.getInstance().getValue(StorageDataKey.SettingLanguageShowWhenChoose,"English").equals("English"); 284 | for (final SupportedLanguages language : data) { 285 | String display; 286 | if(showEnglish) 287 | display=language.getLanguageEnglishDisplayName(); 288 | else display=language.getLanguageChineseDisplayName(); 289 | JCheckBox checkbox = new JCheckBox(display 290 | + " (" + language.getLanguageDisplayName() + ") "); 291 | checkbox.addItemListener(new ItemListener() { 292 | @Override 293 | public void itemStateChanged(ItemEvent e) { 294 | if (e.getStateChange() == ItemEvent.SELECTED) { 295 | if (!selectedLanguages.contains(language)) { 296 | selectedLanguages.add(language); 297 | } 298 | } else if (e.getStateChange() == ItemEvent.DESELECTED) { 299 | if (selectedLanguages.contains(language)) { 300 | selectedLanguages.remove(language); 301 | } 302 | } 303 | } 304 | }); 305 | checkbox.setSelected( 306 | propertiesComponent.getBoolean(StorageDataKey.SupportedLanguageCheckStatusPrefix + language.getLanguageCode(), false)); 307 | container.add(checkbox); 308 | } 309 | 310 | panel.add(container, BorderLayout.CENTER); 311 | } 312 | 313 | if (myCheckboxText != null) { 314 | 315 | myCheckBox = new JCheckBox(myCheckboxText); 316 | myCheckBox.setSelected(myChecked); 317 | myCheckBox.setMargin(new Insets(2, -4, 0, 0)); 318 | 319 | panel.add(myCheckBox, BorderLayout.SOUTH); 320 | } 321 | 322 | return panel; 323 | } 324 | 325 | protected static JTextPane createMessageComponent(final String message) { 326 | final JTextPane messageComponent = new JTextPane(); 327 | return configureMessagePaneUi(messageComponent, message); 328 | } 329 | 330 | @Override 331 | protected void doHelpAction() { 332 | // do nothing 333 | } 334 | 335 | @NotNull 336 | public static JTextPane configureMessagePaneUi(JTextPane messageComponent, String message) { 337 | return configureMessagePaneUi(messageComponent, message, true); 338 | } 339 | 340 | @NotNull 341 | public static JTextPane configureMessagePaneUi(JTextPane messageComponent, 342 | String message, 343 | final boolean addBrowserHyperlinkListener) { 344 | messageComponent.setFont(UIUtil.getLabelFont()); 345 | if (BasicHTML.isHTMLString(message)) { 346 | final HTMLEditorKit editorKit = new HTMLEditorKit(); 347 | editorKit.getStyleSheet().addRule(UIUtil.displayPropertiesToCSS(UIUtil.getLabelFont(), UIUtil.getLabelForeground())); 348 | messageComponent.setEditorKit(editorKit); 349 | messageComponent.setContentType(UIUtil.HTML_MIME); 350 | if (addBrowserHyperlinkListener) { 351 | messageComponent.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); 352 | } 353 | } 354 | messageComponent.setText(message); 355 | messageComponent.setEditable(false); 356 | if (messageComponent.getCaret() != null) { 357 | messageComponent.setCaretPosition(0); 358 | } 359 | 360 | if (UIUtil.isUnderNimbusLookAndFeel()) { 361 | messageComponent.setOpaque(false); 362 | messageComponent.setBackground(UIUtil.TRANSPARENT_COLOR); 363 | } else { 364 | messageComponent.setBackground(UIUtil.getOptionPaneBackground()); 365 | } 366 | 367 | messageComponent.setForeground(UIUtil.getLabelForeground()); 368 | return messageComponent; 369 | } 370 | 371 | private static class MyBorderLayout extends BorderLayout { 372 | private double myPhase = 0;//it varies from 0 (hidden state) to 1 (fully visible) 373 | 374 | private MyBorderLayout() { 375 | } 376 | 377 | @Override 378 | public void layoutContainer(Container target) { 379 | final Dimension realSize = target.getSize(); 380 | target.setSize(target.getPreferredSize()); 381 | 382 | super.layoutContainer(target); 383 | 384 | target.setSize(realSize); 385 | 386 | synchronized (target.getTreeLock()) { 387 | int yShift = (int) ((1 - myPhase) * target.getPreferredSize().height); 388 | Component[] components = target.getComponents(); 389 | for (Component component : components) { 390 | Point point = component.getLocation(); 391 | point.y -= yShift; 392 | component.setLocation(point); 393 | } 394 | } 395 | } 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /src/util/Logger.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | 4 | import com.intellij.notification.*; 5 | 6 | /** 7 | * create on 17/12/16 8 | * @author wujs 9 | */ 10 | public class Logger { 11 | private static String NAME; 12 | private static int LEVEL = 0; 13 | 14 | public static final int DEBUG = 3; 15 | public static final int INFO = 2; 16 | public static final int WARN = 1; 17 | public static final int ERROR = 0; 18 | 19 | public static void init(String name,int level) { 20 | NAME = name; 21 | LEVEL = level; 22 | NotificationsConfiguration.getNotificationsConfiguration().register(NAME, NotificationDisplayType.NONE); 23 | } 24 | 25 | public static void debug(String text) { 26 | if (LEVEL >= DEBUG) { 27 | Notifications.Bus.notify( 28 | new Notification(NAME, NAME + " [DEBUG]", text, NotificationType.INFORMATION)); 29 | } 30 | } 31 | 32 | public static void info(String text) { 33 | if (LEVEL > INFO) { 34 | Notifications.Bus.notify( 35 | new Notification(NAME, NAME + " [INFO]", text, NotificationType.INFORMATION)); 36 | } 37 | } 38 | 39 | public static void warn(String text) { 40 | if (LEVEL > WARN) { 41 | Notifications.Bus.notify( 42 | new Notification(NAME, NAME + " [WARN]", text, NotificationType.WARNING)); 43 | } 44 | } 45 | 46 | public static void error(String text) { 47 | if (LEVEL > ERROR) { 48 | Notifications.Bus.notify( 49 | new Notification(NAME, NAME + " [ERROR]", text, NotificationType.ERROR)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/util/MyURLEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package util; 27 | 28 | import sun.security.action.GetPropertyAction; 29 | 30 | import java.io.CharArrayWriter; 31 | import java.io.UnsupportedEncodingException; 32 | import java.net.URLDecoder; 33 | import java.nio.charset.Charset; 34 | import java.nio.charset.IllegalCharsetNameException; 35 | import java.nio.charset.UnsupportedCharsetException; 36 | import java.security.AccessController; 37 | import java.util.BitSet; 38 | 39 | /** 40 | * Utility class for HTML form encoding. This class contains static methods 41 | * for converting a String to the application/x-www-form-urlencoded MIME 42 | * format. For more information about HTML form encoding, consult the HTML 43 | * specification. 44 | * 45 | *

    46 | * When encoding a String, the following rules apply: 47 | * 48 | *

      49 | *
    • The alphanumeric characters "{@code a}" through 50 | * "{@code z}", "{@code A}" through 51 | * "{@code Z}" and "{@code 0}" 52 | * through "{@code 9}" remain the same. 53 | *
    • The special characters "{@code .}", 54 | * "{@code -}", "{@code *}", and 55 | * "{@code _}" remain the same. 56 | *
    • The space character "   " is 57 | * converted into a plus sign "{@code +}". 58 | *
    • All other characters are unsafe and are first converted into 59 | * one or more bytes using some encoding scheme. Then each byte is 60 | * represented by the 3-character string 61 | * "{@code %xy}", where xy is the 62 | * two-digit hexadecimal representation of the byte. 63 | * The recommended encoding scheme to use is UTF-8. However, 64 | * for compatibility reasons, if an encoding is not specified, 65 | * then the default encoding of the platform is used. 66 | *
    67 | * 68 | *

    69 | * For example using UTF-8 as the encoding scheme the string "The 70 | * string ü@foo-bar" would get converted to 71 | * "The+string+%C3%BC%40foo-bar" because in UTF-8 the character 72 | * ü is encoded as two bytes C3 (hex) and BC (hex), and the 73 | * character @ is encoded as one byte 40 (hex). 74 | * 75 | * @author Herb Jellinek 76 | * @since JDK1.0 77 | */ 78 | public class MyURLEncoder { 79 | static BitSet dontNeedEncoding; 80 | static final int caseDiff = ('a' - 'A'); 81 | static String dfltEncName = null; 82 | 83 | static { 84 | 85 | /* The list of characters that are not encoded has been 86 | * determined as follows: 87 | * 88 | * RFC 2396 states: 89 | * ----- 90 | * Data characters that are allowed in a URI but do not have a 91 | * reserved purpose are called unreserved. These include upper 92 | * and lower case letters, decimal digits, and a limited set of 93 | * punctuation marks and symbols. 94 | * 95 | * unreserved = alphanum | mark 96 | * 97 | * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" 98 | * 99 | * Unreserved characters can be escaped without changing the 100 | * semantics of the URI, but this should not be done unless the 101 | * URI is being used in a context that does not allow the 102 | * unescaped character to appear. 103 | * ----- 104 | * 105 | * It appears that both Netscape and Internet Explorer escape 106 | * all special characters from this list with the exception 107 | * of "-", "_", ".", "*". While it is not clear why they are 108 | * escaping the other characters, perhaps it is safest to 109 | * assume that there might be contexts in which the others 110 | * are unsafe if not escaped. Therefore, we will use the same 111 | * list. It is also noteworthy that this is consistent with 112 | * O'Reilly's "HTML: The Definitive Guide" (page 164). 113 | * 114 | * As a last note, Intenet Explorer does not encode the "@" 115 | * character which is clearly not unreserved according to the 116 | * RFC. We are being consistent with the RFC in this matter, 117 | * as is Netscape. 118 | * 119 | */ 120 | 121 | dontNeedEncoding = new BitSet(256); 122 | int i; 123 | for (i = 'a'; i <= 'z'; i++) { 124 | dontNeedEncoding.set(i); 125 | } 126 | for (i = 'A'; i <= 'Z'; i++) { 127 | dontNeedEncoding.set(i); 128 | } 129 | for (i = '0'; i <= '9'; i++) { 130 | dontNeedEncoding.set(i); 131 | } 132 | dontNeedEncoding.set(' '); /* encoding a space to a + is done 133 | * in the encode() method */ 134 | dontNeedEncoding.set('-'); 135 | dontNeedEncoding.set('_'); 136 | dontNeedEncoding.set('.'); 137 | dontNeedEncoding.set('*'); 138 | 139 | dfltEncName = AccessController.doPrivileged( 140 | new GetPropertyAction("file.encoding") 141 | ); 142 | } 143 | 144 | /** 145 | * You can't call the constructor. 146 | */ 147 | private MyURLEncoder() { } 148 | 149 | /** 150 | * Translates a string into {@code x-www-form-urlencoded} 151 | * format. This method uses the platform's default encoding 152 | * as the encoding scheme to obtain the bytes for unsafe characters. 153 | * 154 | * @param s {@code String} to be translated. 155 | * @deprecated The resulting string may vary depending on the platform's 156 | * default encoding. Instead, use the encode(String,String) 157 | * method to specify the encoding. 158 | * @return the translated {@code String}. 159 | */ 160 | @Deprecated 161 | public static String encode(String s) { 162 | 163 | String str = null; 164 | 165 | try { 166 | str = encode(s, dfltEncName); 167 | } catch (UnsupportedEncodingException e) { 168 | // The system should always have the platform default 169 | } 170 | 171 | return str; 172 | } 173 | 174 | /** 175 | * Translates a string into {@code application/x-www-form-urlencoded} 176 | * format using a specific encoding scheme. This method uses the 177 | * supplied encoding scheme to obtain the bytes for unsafe 178 | * characters. 179 | *

    180 | * Note: The 182 | * World Wide Web Consortium Recommendation states that 183 | * UTF-8 should be used. Not doing so may introduce 184 | * incompatibilities. 185 | * 186 | * @param s {@code String} to be translated. 187 | * @param enc The name of a supported 188 | * character 189 | * encoding. 190 | * @return the translated {@code String}. 191 | * @exception UnsupportedEncodingException 192 | * If the named encoding is not supported 193 | * @see URLDecoder#decode(String, String) 194 | * @since 1.4 195 | */ 196 | public static String encode(String s, String enc) 197 | throws UnsupportedEncodingException { 198 | 199 | boolean needToChange = false; 200 | StringBuffer out = new StringBuffer(s.length()); 201 | Charset charset; 202 | CharArrayWriter charArrayWriter = new CharArrayWriter(); 203 | 204 | if (enc == null) 205 | throw new NullPointerException("charsetName"); 206 | 207 | try { 208 | charset = Charset.forName(enc); 209 | } catch (IllegalCharsetNameException e) { 210 | throw new UnsupportedEncodingException(enc); 211 | } catch (UnsupportedCharsetException e) { 212 | throw new UnsupportedEncodingException(enc); 213 | } 214 | 215 | for (int i = 0; i < s.length();) { 216 | int c = (int) s.charAt(i); 217 | //System.out.println("Examining character: " + c); 218 | if (dontNeedEncoding.get(c)) { 219 | if (c == ' ') { 220 | c = '+'; 221 | needToChange = true; 222 | } 223 | //System.out.println("Storing: " + c); 224 | out.append((char)c); 225 | i++; 226 | } else { 227 | // convert to external encoding before hex conversion 228 | do { 229 | charArrayWriter.write(c); 230 | /* 231 | * If this character represents the start of a Unicode 232 | * surrogate pair, then pass in two characters. It's not 233 | * clear what should be done if a bytes reserved in the 234 | * surrogate pairs range occurs outside of a legal 235 | * surrogate pair. For now, just treat it as if it were 236 | * any other character. 237 | */ 238 | if (c >= 0xD800 && c <= 0xDBFF) { 239 | /* 240 | System.out.println(Integer.toHexString(c) 241 | + " is high surrogate"); 242 | */ 243 | if ( (i+1) < s.length()) { 244 | int d = (int) s.charAt(i+1); 245 | /* 246 | System.out.println("\tExamining " 247 | + Integer.toHexString(d)); 248 | */ 249 | if (d >= 0xDC00 && d <= 0xDFFF) { 250 | /* 251 | System.out.println("\t" 252 | + Integer.toHexString(d) 253 | + " is low surrogate"); 254 | */ 255 | charArrayWriter.write(d); 256 | i++; 257 | } 258 | } 259 | } 260 | i++; 261 | } while (i < s.length() && !dontNeedEncoding.get((c = (int) s.charAt(i)))); 262 | 263 | charArrayWriter.flush(); 264 | String str = new String(charArrayWriter.toCharArray()); 265 | byte[] ba = str.getBytes(charset); 266 | for (int j = 0; j < ba.length; j++) { 267 | out.append('%'); 268 | char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16); 269 | // converting to use uppercase letter as part of 270 | // the hex value if ch is a letter. 271 | if (Character.isLetter(ch)) { 272 | ch -= caseDiff; 273 | } 274 | out.append(ch); 275 | ch = Character.forDigit(ba[j] & 0xF, 16); 276 | if (Character.isLetter(ch)) { 277 | ch -= caseDiff; 278 | } 279 | out.append(ch); 280 | } 281 | charArrayWriter.reset(); 282 | needToChange = true; 283 | } 284 | } 285 | 286 | return (needToChange? out.toString() : s); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /values-en/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Browse 5 | One 6 | M 7 | ... 8 | '' 9 | honor watch S1-c86 10 | Set goals 11 | 12 | @string/plan_step_1 13 | 我的运动计划 14 | 15 | MAF \ nhealth run 16 | 17 | Simplified Chinese 18 | English 19 | 20 | Hi, you can now wake me up by saying \% 1 $s \ "or \% 2 $s \" 21 | This operation will lock the device. You need to enter the < font color = "ා" > Settings - > device information < / font > page "< font color =" ා "> scan code to activate < / font >", then the treadmill can be used normally. 22 | 23 | 24 | -------------------------------------------------------------------------------- /values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 浏  览 6 | 7 | 1 8 | 9 | m 10 | 11 | 12 | 13 | \' 14 | 15 | honor watch S1-c86 16 | 17 | 设定目标 18 | 19 | 20 | 21 | @string/plan_step_1 22 | 我的运动计划 23 | 24 | 25 | 26 | 27 | 28 | 29 | MAF\n健康跑 30 | 31 | 32 | 33 | 34 | 中文简体 35 | English 36 | 37 | 38 | 39 | 40 | Hi,你现在可以说 \"%1$s\" 或 \"%2$s\" 来唤醒我 41 | 42 | 43 | 44 | 设置->设备信息页面“扫码激活”后,跑步机才能正常使用。 46 | ]]> 47 | 48 | 49 | 50 | 51 | 52 | --------------------------------------------------------------------------------