├── .gitignore ├── .run └── Run IDE with Plugin.run.xml ├── README.md ├── build.gradle.kts ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── media └── polyglot_screenshot_1.png ├── receipts ├── alipay_receipt.png └── wechat_receipt.png ├── settings.gradle.kts └── src └── main ├── java └── me │ └── bytebeats │ └── polyglot │ ├── dict │ ├── AbstractDictionary.kt │ ├── Dict.kt │ ├── DictConsultListener.kt │ ├── action │ │ └── ConsultDictAction.kt │ ├── http │ │ ├── DictionaryFormDataAdder.kt │ │ └── IDictionary.kt │ ├── impl │ │ ├── BaiduDictionary.kt │ │ ├── GoogleDictionary.kt │ │ └── YouDaoDictionary.kt │ └── meta │ │ ├── BaiduTranslation.kt │ │ ├── DictService.kt │ │ ├── GoogleTranslation.kt │ │ └── YouDaoTranslation.kt │ ├── dq │ ├── AbstractDailyQuoter.kt │ ├── DailyQuoter.kt │ └── impl │ │ ├── IcibaDailyQuoter.kt │ │ ├── ScallopDailyQuoter.kt │ │ └── YoudaoDailyQuoter.kt │ ├── excp │ └── NetworkException.kt │ ├── http │ ├── DailyQuoteConnectionCloser.kt │ ├── IDailyQuote.kt │ ├── IPolyglot.kt │ ├── ParamsAdder.kt │ ├── PolyglotFormDataAdder.kt │ └── TranslatorConnectionCloser.kt │ ├── lang │ └── Lang.kt │ ├── meta │ └── DailyQuote.kt │ ├── tlr │ ├── AbstractPolyglot.kt │ ├── PolyglotTranslator.kt │ └── impl │ │ ├── BaiduPolyglot.kt │ │ ├── BingPolyglot.kt │ │ ├── GooglePolyglot.kt │ │ ├── IcibaPolyglot.kt │ │ ├── OmiPolyglot.kt │ │ ├── SogouPolyglot.kt │ │ ├── TencentPolyglot.kt │ │ ├── TrycanPolyglot.kt │ │ └── YouDaoPolyglot.kt │ ├── ui │ ├── PolyglotPreferencesWindow.form │ ├── PolyglotPreferencesWindow.java │ ├── PolyglotSettingState.java │ ├── PolyglotWindow.form │ ├── PolyglotWindow.java │ └── swing │ │ └── JHintTextField.kt │ └── util │ ├── GlotJsUtils.kt │ ├── GsonUtils.java │ ├── LogUtils.kt │ ├── ParamUtils.kt │ ├── PolyglotUtils.kt │ ├── StringResUtils.kt │ └── TK.kt └── resources ├── META-INF ├── plugin.xml ├── pluginIcon.svg └── pluginIcon_dark.svg ├── arrow_vertical_switch.png └── icon_polyglot.png /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | .gradle/ 19 | .idea/ 20 | lib/ 21 | local.properties 22 | 23 | ### Eclipse ### 24 | .apt_generated 25 | .classpath 26 | .factorypath 27 | .project 28 | .settings 29 | .springBeans 30 | .sts4-cache 31 | bin/ 32 | !**/src/main/**/bin/ 33 | !**/src/test/**/bin/ 34 | 35 | ### NetBeans ### 36 | /nbproject/private/ 37 | /nbbuild/ 38 | /dist/ 39 | /nbdist/ 40 | /.nb-gradle/ 41 | 42 | ### VS Code ### 43 | .vscode/ 44 | 45 | ### Mac OS ### 46 | .DS_Store 47 | 48 | ### Local Plugin Files ### 49 | plugins/ -------------------------------------------------------------------------------- /.run/Run IDE with Plugin.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Polyglot: to translate different languages with different translators! 2 | 3 | [![GitHub latest commit](https://badgen.net/github/last-commit/bytebeats/polyglot)](https://github.com/bytebeats/polyglot/commit/) 4 | [![GitHub contributors](https://img.shields.io/github/contributors/bytebeats/polyglot.svg)](https://github.com/bytebeats/polyglot/graphs/contributors/) 5 | [![GitHub issues](https://img.shields.io/github/issues/bytebeats/polyglot.svg)](https://github.com/bytebeats/polyglot/issues/) 6 | [![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github)](https://github.com/bytebeats/polyglot/) 7 | [![JetBrains Plugins](https://img.shields.io/jetbrains/plugin/v/15036-polyglot.svg)](https://plugins.jetbrains.com/plugin/15036-polyglot-translator) 8 | [![JetBrains Plugins](https://img.shields.io/jetbrains/plugin/r/rating/15036)](https://plugins.jetbrains.com/plugin/15036-polyglot-translator) 9 | [![GitHub forks](https://img.shields.io/github/forks/bytebeats/polyglot.svg?style=social&label=Fork&maxAge=2592000)](https://github.com/bytebeats/polyglot/network/) 10 | [![GitHub stars](https://img.shields.io/github/stars/bytebeats/polyglot.svg?style=social&label=Star&maxAge=2592000)](https://github.com/bytebeats/polyglot/stargazers/) 11 | [![GitHub watchers](https://img.shields.io/github/watchers/bytebeats/polyglot.svg?style=social&label=Watch&maxAge=2592000)](https://github.com/bytebeats/polyglot/watchers/) 12 | 13 | An Intellij platform plugin project. 14 | 15 | If your project is relevant to many languages in which translation is needed, this plugin will help you a lot. 16 | 17 | A good plugin is No.1 productive force! 18 | 19 | 一个 Intellij 插件项目, 当工程需要支持多语言时, 本插件能够帮助你省去在浏览器或者翻译软件与你的项目之间来回切换的麻烦. 20 | 21 | 插件是第一生产力啊! 22 | 23 | ## Installation 24 | 25 | * Download `plugins/polyglot-x.x.x.jar` and install into your IDE 26 | * clone this project, **Build | Build project**, then you will see **build/libs/polyglot-x.x.x.jar** 27 | * **Intellij IDEA** -> **Preferences** -> **Plugins** -> Settings -> **Install from Disk**, select and load **polyglot-x.x.x.jar** or search **polyglot** from MarketPlace, then click and install **polyglot** 28 | 29 | ## Features 30 | * 支持常规的语言翻译, 诸如中文, 中文繁体, 英/法/德/俄语等 31 | * 支持非常规的语言翻译, 诸如粤语, 文言文, 伊朗语, 越南语等 32 | * 支持已经死亡的或者虚构的语言翻译, 诸如古英语, 中古法语, 梵语, 阿拉贡语, 希伯语等 33 | * 支持 `每日一句` 功能 34 | * 支持 `取词划义` 功能 35 | 36 | ## Visual Effects 37 | 38 | ![polyglot](media/polyglot_screenshot_1.png) 39 | 40 | ## To-Dos 41 | 42 | * 支持谷歌取词划义功能 43 | * 支持百度取词划义功能 44 | * 添加 Android 多语言字符串支持 45 | 46 | ## Support 47 |
Open-source is no easy job, for which I have to sacrifice my weekends and other non-working hours. 48 |
开源不易, 尤其是对于全职的开源爱好者. 需要占用不少下班以及周末时间. 49 |
If you think this plugin is meaningful, is worthy, you are welcome to support this project by following: 50 |
如果您觉着这个项目是值得做的, 是做的有意义的, 可以通过以下方式来表达支持: 51 | 52 | * [Watch/Star/Fork](https://github.com/bytebeats/polyglot) this project. [关注/标星/Fork](https://github.com/bytebeats/polyglot) 该项目. 53 | * Share/Recommend this plugin(polyglot) to your friends/work mates. 向朋友或者同事推荐该插件. 54 | * Highly rate [polyglot](https://plugins.jetbrains.com/plugin/15036-polyglot-translators) in [Marketplace](https://plugins.jetbrains.com/). 在应用市场对 [polyglot](https://plugins.jetbrains.com/plugin/15036-polyglot-translators) 进行评分 55 | * Create [PR](https://github.com/bytebeats/polyglot/pulls) s. 提交 [PR](https://github.com/bytebeats/polyglot/pulls). 56 | * Create [Issue](https://github.com/bytebeats/polyglot/issues) s. 反馈问题, 提供您的建议或者想法. 57 | * If you love this plugin, you may donate for maintain this project and this plugin. 如果你喜欢这个插件, 可以考虑捐赠, 以持续地对该项目跟插件进行维护. 58 | 59 | | Open Collective | 支付宝/Alipay | 微信/Wechat | PayPal | 60 | |--------------------------------------------------------------------------------|----------------------------------------|----------------------------------------|-----------------------------------------------------| 61 | | polyglot Collective | ![alipay](receipts/alipay_receipt.png) | ![wechat](receipts/wechat_receipt.png) | Support | 62 | 63 |
使用支付宝/微信支付捐赠后请留言或者通过邮件提供您的名字/昵称和网站,格式为: 64 |
名字/昵称 [<网站>][:留言](网站与留言为可选部分,例子:bytebeats :加油!) 65 |
您提供的名字、网站和捐赠总额将会被添加到捐赠者列表中。 66 |
邮箱地址:happychinapc@gmail.com 67 |
感谢您的支持! 68 | 69 | ## More plugins 70 | * [mns](https://github.com/bytebeats/mns) Money Never Sleeps 71 | * [kfiglet](https://github.com/bytebeats/JsonMaster) 72 | * [ascii-arts](https://github.com/bytebeats/kfiglet) Ascii Arts 73 | 74 | ## Stargazers over time 75 | [![Stargazers over time](https://starchart.cc/bytebeats/polyglot.svg)](https://starchart.cc/bytebeats/polyglot) 76 | 77 | ## Github Stars Sparklines 78 | [![Sparkline](https://stars.medv.io/bytebeats/polyglot.svg)](https://stars.medv.io/bytebeats/polyglot) 79 | 80 | ## Contributors 81 | [![Contributors over time](https://contributor-graph-api.apiseven.com/contributors-svg?chart=contributorOverTime&repo=bytebeats/polyglot)](https://www.apiseven.com/en/contributor-graph?chart=contributorOverTime&repo=bytebeats/polyglot) -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("org.jetbrains.kotlin.jvm") version "1.9.22" 4 | id("org.jetbrains.intellij") version "1.17.2" 5 | } 6 | 7 | apply(plugin = "kotlin") 8 | apply(plugin = "org.jetbrains.intellij") 9 | 10 | group = "io.github.bytebeats" 11 | version = "1.3.6" 12 | 13 | repositories { 14 | maven { 15 | url = uri("https://maven.aliyun.com/repository/google") 16 | } 17 | maven { 18 | url = uri("https://maven.aliyun.com/repository/central") 19 | } 20 | maven { 21 | url = uri("https://maven.aliyun.com/repository/jcenter") 22 | } 23 | mavenCentral() 24 | } 25 | 26 | dependencies { 27 | api(libs.bundles.jacksonCore) 28 | } 29 | 30 | // Configure Gradle IntelliJ Plugin 31 | // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html 32 | intellij { 33 | pluginName.set("Polyglot") 34 | version.set("2023.2.5") 35 | type.set("IC") // Target IDE Platform 36 | 37 | plugins.set(listOf(/* Plugin Dependencies */)) 38 | } 39 | 40 | tasks { 41 | // Set the JVM compatibility versions 42 | withType { 43 | sourceCompatibility = "17" 44 | targetCompatibility = "17" 45 | } 46 | withType { 47 | kotlinOptions.jvmTarget = "17" 48 | } 49 | 50 | patchPluginXml { 51 | sinceBuild.set("230") 52 | untilBuild.set("242.*") 53 | 54 | changeNotes.set( 55 | """ 56 | v1.3.6 project upgrade.
57 | """ 58 | ) 59 | } 60 | 61 | signPlugin { 62 | certificateChain.set(System.getenv("CERTIFICATE_CHAIN")) 63 | privateKey.set(System.getenv("PRIVATE_KEY")) 64 | password.set(System.getenv("PRIVATE_KEY_PASSWORD")) 65 | } 66 | 67 | publishPlugin { 68 | token.set(System.getenv("PUBLISH_TOKEN")) 69 | } 70 | 71 | withType { 72 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 73 | } 74 | 75 | register("MoveBuildArtifacts") { 76 | mustRunAfter("DeletePluginFiles") 77 | println("Moving Build Artifacts!") 78 | from(layout.buildDirectory.dir("distributions")) 79 | include("Polyglot-**.zip") 80 | into("plugins") 81 | } 82 | 83 | register("DeletePluginFiles") { 84 | delete(files("plugins")) 85 | } 86 | named("build") { 87 | finalizedBy("MoveBuildArtifacts") 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | jackson-core = "2.17.0" 3 | 4 | [libraries] 5 | jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson-core" } 6 | jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson-core" } 7 | jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson-core" } 8 | 9 | [bundles] 10 | jacksonCore = ["jackson-annotations", "jackson-core", "jackson-databind"] -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebeats/polyglot/e1a808a5009817dd5b1881ac9d9fd48b856f4a73/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 25 19:42:05 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /media/polyglot_screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebeats/polyglot/e1a808a5009817dd5b1881ac9d9fd48b856f4a73/media/polyglot_screenshot_1.png -------------------------------------------------------------------------------- /receipts/alipay_receipt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebeats/polyglot/e1a808a5009817dd5b1881ac9d9fd48b856f4a73/receipts/alipay_receipt.png -------------------------------------------------------------------------------- /receipts/wechat_receipt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebeats/polyglot/e1a808a5009817dd5b1881ac9d9fd48b856f4a73/receipts/wechat_receipt.png -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "polyglot" -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/AbstractDictionary.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict 2 | 3 | import me.bytebeats.polyglot.dict.http.IDictionary 4 | import me.bytebeats.polyglot.dict.http.DictionaryFormDataAdder 5 | import me.bytebeats.polyglot.dict.impl.YouDaoDictionary 6 | import me.bytebeats.polyglot.dict.meta.YouDaoTranslation 7 | import me.bytebeats.polyglot.http.TranslatorConnectionCloser 8 | import me.bytebeats.polyglot.lang.Lang 9 | import me.bytebeats.polyglot.util.StringResUtils 10 | import java.io.IOException 11 | 12 | /** 13 | * @Author bytebeats 14 | * @Email 15 | * @Github https://github.com/bytebeats 16 | * @Created on 2020/10/31 18:54 17 | * @Version 1.0 18 | * @Description AbstractDictionary 19 | */ 20 | 21 | abstract class AbstractDictionary(var listener: DictConsultListener? = null, url: String) : 22 | TranslatorConnectionCloser(url), DictionaryFormDataAdder, IDictionary { 23 | 24 | init { 25 | addSupportedLangs() 26 | } 27 | 28 | abstract fun addSupportedLangs() 29 | 30 | override fun consult(text: String) { 31 | // val dictionary = PolyglotSettingState.getInstance().dictLang 32 | val dictionary = StringResUtils.LANG_DESC_ZH 33 | addFormData(Lang.from(dictionary), text) 34 | try { 35 | val translation = parse(query()) 36 | if (translation?.isSuccessful() == true) { 37 | listener?.onSuccess(translation) 38 | } else { 39 | listener?.onFailure(translation?.getError() ?: "Unknown failure") 40 | } 41 | } catch (e: Exception) { 42 | listener?.onError(e.message ?: e.cause?.message ?: "Unknown error") 43 | } finally { 44 | close() 45 | } 46 | } 47 | 48 | /** 49 | * Parse the string to extract the content of interest. 50 | * 51 | * @param text the string form of the to-be-translated result. 52 | * @return translation results after parsing. 53 | * @throws IOException if the parsing fails. 54 | */ 55 | @Throws(IOException::class) 56 | abstract fun parse(text: String): YouDaoTranslation? 57 | 58 | /** 59 | * Execute the translation or TTS task (send a POST or GET request to the server), 60 | * receive the result of translation or speech conversion., and return the content 61 | * or save file name as string. 62 | * 63 | * @return the string form of the translated result. 64 | * @throws Exception if the request fails 65 | */ 66 | @Throws(Exception::class) 67 | abstract fun query(): String 68 | 69 | companion object Factory { 70 | 71 | fun newInstance(desc: String, listener: DictConsultListener): AbstractDictionary { 72 | return when (desc) { 73 | StringResUtils.POLYGLOT_YOUDAO, StringResUtils.QUOTOR_YOUDAO_EN -> YouDaoDictionary(listener) 74 | else -> YouDaoDictionary(listener) 75 | } 76 | } 77 | 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/Dict.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict 2 | 3 | import me.bytebeats.polyglot.util.StringResUtils 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/11/1 17:04 10 | * @Version 1.0 11 | * @Description TO-DO 12 | */ 13 | 14 | enum class Dict(val id: String, val desc: String, val descEN: String) { 15 | YouDao("youdao", StringResUtils.POLYGLOT_YOUDAO, StringResUtils.POLYGLOT_YOUDAO_EN); 16 | 17 | companion object { 18 | fun from(desc: String): Dict { 19 | for (dict in values()) { 20 | if (dict.desc == desc || dict.descEN == desc) { 21 | return dict 22 | } 23 | } 24 | return YouDao 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/DictConsultListener.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict 2 | 3 | import me.bytebeats.polyglot.dict.meta.YouDaoTranslation 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/10/31 15:53 10 | * @Version 1.0 11 | * @Description TO-DO 12 | */ 13 | 14 | interface DictConsultListener { 15 | fun onSuccess(translation: YouDaoTranslation) 16 | fun onFailure(message: String) 17 | fun onError(error: String) 18 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/action/ConsultDictAction.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.action 2 | 3 | import com.intellij.icons.AllIcons 4 | import com.intellij.openapi.actionSystem.AnAction 5 | import com.intellij.openapi.actionSystem.AnActionEvent 6 | import com.intellij.openapi.actionSystem.PlatformDataKeys 7 | import com.intellij.openapi.application.ex.ApplicationManagerEx 8 | import com.intellij.openapi.editor.Editor 9 | import com.intellij.openapi.ui.popup.Balloon 10 | import com.intellij.openapi.ui.popup.JBPopupFactory 11 | import com.intellij.openapi.util.TextRange 12 | import me.bytebeats.polyglot.dict.AbstractDictionary 13 | import me.bytebeats.polyglot.dict.DictConsultListener 14 | import me.bytebeats.polyglot.dict.meta.YouDaoTranslation 15 | import me.bytebeats.polyglot.ui.PolyglotSettingState 16 | import me.bytebeats.polyglot.util.LogUtils 17 | 18 | /** 19 | * @Author bytebeats 20 | * @Email 21 | * @Github https://github.com/bytebeats 22 | * @Created on 2020/11/1 16:09 23 | * @Version 1.0 24 | * @Description ConsultDictAction is AnAction to offer consulting-a-dictionary service 25 | */ 26 | 27 | class ConsultDictAction : 28 | AnAction("Polyglot Translate", "Offered by powerful Polyglot dictionaries", AllIcons.Actions.Annotate) { 29 | private var lastActionPerformedTime = 0L 30 | override fun actionPerformed(e: AnActionEvent) { 31 | if (isFastClick()) { 32 | return 33 | } 34 | doConsultDict(e) 35 | } 36 | 37 | private fun doConsultDict(e: AnActionEvent) { 38 | val editor = e.getData(PlatformDataKeys.EDITOR) 39 | editor?.let { 40 | val model = it.selectionModel 41 | var selectedText = model.selectedText 42 | if (selectedText.isNullOrEmpty()) { 43 | selectedText = getCurrentWords(it) 44 | if (selectedText.isNullOrEmpty()) { 45 | return 46 | } 47 | } 48 | val query = convertText2Query(selectedText) 49 | val desc = PolyglotSettingState.getInstance().dict 50 | AbstractDictionary.newInstance(desc, object : DictConsultListener { 51 | override fun onSuccess(translation: YouDaoTranslation) { 52 | popupBalloon(editor, translation.format()) 53 | } 54 | 55 | override fun onFailure(message: String) { 56 | popupBalloon(editor, message) 57 | } 58 | 59 | override fun onError(error: String) { 60 | popupBalloon(editor, error) 61 | } 62 | }).consult(query) 63 | } 64 | } 65 | 66 | private fun getCurrentWords(editor: Editor): String { 67 | val document = editor.document 68 | val caretModel = editor.caretModel 69 | val caretOffset = caretModel.offset 70 | val lineNo = document.getLineNumber(caretOffset) 71 | val startOffset = document.getLineStartOffset(lineNo) 72 | val endOffset = document.getLineEndOffset(lineNo) 73 | val lineContent = document.getText(TextRange.from(startOffset, endOffset)) 74 | var start = 0 75 | var end = 0 76 | val cursor = caretOffset - startOffset 77 | if (!lineContent[cursor].isLetter()) { 78 | LogUtils.info("Caret not in word") 79 | return "" 80 | } 81 | for (ptr in cursor downTo 0) { 82 | if (!lineContent[ptr].isLetter()) { 83 | start = ptr + 1 84 | break 85 | } 86 | } 87 | var lastLetterPos = 0 88 | for (ptr in cursor until endOffset - startOffset) { 89 | lastLetterPos = ptr 90 | if (!lineContent[ptr].isLetter()) { 91 | end = ptr 92 | break 93 | } 94 | } 95 | if (end == 0) { 96 | end = lastLetterPos + 1 97 | } 98 | return lineContent.substring(start, end - start) 99 | } 100 | 101 | private fun convertText2Query(text: String): String { 102 | if (text.isNotEmpty()) { 103 | return text.replace('_', ' ') 104 | .replace("([A-Z]+)".toRegex(), " $0") 105 | .replace("/\\*+".toRegex(), "") 106 | .replace("\\*+/".toRegex(), "") 107 | .replace("\\*".toRegex(), "") 108 | .replace("//+".toRegex(), "") 109 | .replace("\t\n".toRegex(), " ") 110 | .replace("\\s+".toRegex(), " ") 111 | } 112 | return text 113 | } 114 | 115 | private fun isFastClick(timeSpan: Long = ACTION_TIME_SPAN): Boolean { 116 | val nowInMillis = System.currentTimeMillis() 117 | if (nowInMillis - lastActionPerformedTime < timeSpan) { 118 | return true 119 | } 120 | lastActionPerformedTime = nowInMillis 121 | return false 122 | } 123 | 124 | private fun popupBalloon(editor: Editor, text: String) { 125 | ApplicationManagerEx.getApplicationEx().invokeLater { 126 | JBPopupFactory.getInstance().apply { 127 | createHtmlTextBalloonBuilder( 128 | text, 129 | AllIcons.Actions.Annotate, 130 | editor.colorsScheme.defaultBackground, 131 | null 132 | ) 133 | .createBalloon() 134 | .show(guessBestPopupLocation(editor), Balloon.Position.below) 135 | } 136 | } 137 | } 138 | 139 | companion object { 140 | const val ACTION_TIME_SPAN = 500L 141 | } 142 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/http/DictionaryFormDataAdder.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.http 2 | 3 | import me.bytebeats.polyglot.lang.Lang 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/10/31 19:03 10 | * @Version 1.0 11 | * @Description TO-DO 12 | */ 13 | 14 | interface DictionaryFormDataAdder { 15 | /** 16 | * Set the request parameters that will be sent to the server. 17 | * @param to target language 18 | * @param text the content to be translated 19 | */ 20 | fun addFormData(to: Lang, text: String) 21 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/http/IDictionary.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.http 2 | 3 | /** 4 | * @Author bytebeats 5 | * @Email 6 | * @Github https://github.com/bytebeats 7 | * @Created on 2020/10/31 19:08 8 | * @Version 1.0 9 | * @Description TO-DO 10 | */ 11 | 12 | interface IDictionary { 13 | fun consult(text: String) 14 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/impl/BaiduDictionary.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.impl 2 | 3 | import me.bytebeats.polyglot.dict.AbstractDictionary 4 | import me.bytebeats.polyglot.dict.DictConsultListener 5 | import me.bytebeats.polyglot.dict.meta.YouDaoTranslation 6 | import me.bytebeats.polyglot.lang.Lang 7 | import me.bytebeats.polyglot.util.GsonUtils 8 | import me.bytebeats.polyglot.util.ParamUtils 9 | import org.apache.http.client.entity.UrlEncodedFormEntity 10 | import org.apache.http.client.methods.HttpPost 11 | import org.apache.http.util.EntityUtils 12 | import java.util.* 13 | 14 | /** 15 | * @Author bytebeats 16 | * @Email 17 | * @Github https://github.com/bytebeats 18 | * @Created on 2020/11/7 16:25 19 | * @Version 1.0 20 | * @Description TO-DO 21 | */ 22 | 23 | class BaiduDictionary(listener: DictConsultListener) : AbstractDictionary(listener, URL) { 24 | companion object { 25 | private const val URL = "http://api.fanyi.baidu.com/api/trans/vip/translate" 26 | 27 | @JvmStatic 28 | fun main(args: Array) { 29 | println("baidu dictionary") 30 | BaiduDictionary(object : DictConsultListener { 31 | override fun onSuccess(translation: YouDaoTranslation) { 32 | println(translation.format()) 33 | } 34 | 35 | override fun onFailure(message: String) { 36 | println(message) 37 | } 38 | 39 | override fun onError(error: String) { 40 | println(error) 41 | } 42 | }).consult("remain") 43 | } 44 | } 45 | 46 | override fun addSupportedLangs() { 47 | langs[Lang.ZH] = "zh" 48 | langs[Lang.CHT] = "cht" 49 | langs[Lang.YUE] = "yue" 50 | langs[Lang.WYW] = "wyw" 51 | langs[Lang.EN] = "en" 52 | langs[Lang.JP] = "jp" 53 | langs[Lang.KOR] = "kor" 54 | langs[Lang.FRA] = "fra" 55 | langs[Lang.RU] = "ru" 56 | langs[Lang.DE] = "de" 57 | langs[Lang.SPA] = "spa" 58 | langs[Lang.PT] = "pt" 59 | langs[Lang.TR] = "tr" 60 | langs[Lang.IT] = "it" 61 | langs[Lang.ARA] = "ara" 62 | } 63 | 64 | /** 65 | * { 66 | "from": "en", 67 | "to": "zh", 68 | "trans_result": [ 69 | { 70 | "src": "apple", 71 | "dst": "苹果", 72 | "src_tts": "https:\/\/fanyiapp.cdn.bcebos.com\/api\/tts\/95e906875b87d342d7325a36a4e1ab42.mp3", 73 | "dst_tts": "https:\/\/fanyiapp.cdn.bcebos.com\/api\/tts\/62f4ff87617655bc1f65e24cf4ed4963.mp3", 74 | "dict": "{\"lang\":\"1\",\"word_result\":{\"simple_means\":{\"word_name\":\"apple\",\"from\":\"original\",\"word_means\":[\"苹果\"],\"exchange\":{\"word_pl\":[\"apples\"]},\"tags\":{\"core\":[\"高考\",\"考研\"],\"other\":[\"\"]},\"symbols\":[{\"ph_en\":\"ˈæpl\",\"ph_am\":\"ˈæpl\",\"parts\":[{\"part\":\"n.\",\"means\":[\"苹果\"]}],\"ph_other\":\"\"}]}}}" 75 | } 76 | ] 77 | } 78 | */ 79 | 80 | override fun parse(text: String): YouDaoTranslation? { 81 | val translation = GsonUtils.getInstance().from(text, YouDaoTranslation::class.java) 82 | return translation?.apply { src = formData["q"]!! } 83 | } 84 | 85 | override fun query(): String { 86 | val request = HttpPost(url) 87 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 88 | 89 | val response = httpClient.execute(request) 90 | val entity = response.entity 91 | val result = EntityUtils.toString(entity, "UTF-8") 92 | println(result) 93 | close(entity, response) 94 | return result 95 | } 96 | 97 | override fun addFormData(to: Lang, text: String) { 98 | // val appID = PolyglotSettingState.getInstance().appID 99 | // val appKey = PolyglotSettingState.getInstance().appKey 100 | val appID = "20201107000610694" 101 | val appKey = "kwjKCeUUNitS4tnwuO1K" 102 | val salt = UUID.randomUUID().toString() 103 | val sign = ParamUtils.md5("$appID$text$salt$appKey")?.lowercase() 104 | formData["from"] = langs[Lang.EN]!! 105 | formData["to"] = langs[to]!! 106 | formData["appid"] = appID 107 | formData["salt"] = salt 108 | formData["sign"] = sign!! 109 | formData["q"] = text 110 | formData["dict"] = "0" 111 | } 112 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/impl/GoogleDictionary.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.impl 2 | 3 | import me.bytebeats.polyglot.dict.AbstractDictionary 4 | import me.bytebeats.polyglot.dict.DictConsultListener 5 | import me.bytebeats.polyglot.dict.meta.YouDaoTranslation 6 | import me.bytebeats.polyglot.lang.Lang 7 | import me.bytebeats.polyglot.util.GsonUtils 8 | import me.bytebeats.polyglot.util.ParamUtils 9 | import me.bytebeats.polyglot.util.tk 10 | import org.apache.http.client.entity.UrlEncodedFormEntity 11 | import org.apache.http.client.methods.HttpPost 12 | import org.apache.http.util.EntityUtils 13 | 14 | /** 15 | * @Author bytebeats 16 | * @Email 17 | * @Github https://github.com/bytebeats 18 | * @Created on 2020/11/7 16:25 19 | * @Version 1.0 20 | * @Description TO-DO 21 | */ 22 | 23 | class GoogleDictionary(listener: DictConsultListener) : AbstractDictionary(listener, URL) { 24 | companion object { 25 | const val GOOGLE_HOST = "translate.google.com" 26 | const val GOOGLE_HOST_CN = "translate.google.cn" 27 | private const val URL = "https://%s/translate_a/single" 28 | 29 | @JvmStatic 30 | fun main(args: Array) { 31 | println("google dictionary") 32 | GoogleDictionary(object : DictConsultListener { 33 | override fun onSuccess(translation: YouDaoTranslation) { 34 | println(translation.format()) 35 | } 36 | 37 | override fun onFailure(message: String) { 38 | println(message) 39 | } 40 | 41 | override fun onError(error: String) { 42 | println(error) 43 | } 44 | }).consult("remain") 45 | } 46 | } 47 | 48 | override fun addSupportedLangs() { 49 | langs[Lang.ZH] = "zh-CN" 50 | langs[Lang.CHT] = "zh-TW" 51 | langs[Lang.EN] = "en" 52 | langs[Lang.JP] = "ja" 53 | langs[Lang.KOR] = "ko" 54 | langs[Lang.FRA] = "fr" 55 | langs[Lang.RU] = "ru" 56 | langs[Lang.DE] = "de" 57 | langs[Lang.SPA] = "es" 58 | langs[Lang.IT] = "it" 59 | langs[Lang.VIE] = "vi" 60 | langs[Lang.TH] = "th" 61 | langs[Lang.ARA] = "ar" 62 | } 63 | 64 | /** 65 | * { 66 | "from": "en", 67 | "to": "zh", 68 | "trans_result": [ 69 | { 70 | "src": "apple", 71 | "dst": "苹果", 72 | "src_tts": "https:\/\/fanyiapp.cdn.bcebos.com\/api\/tts\/95e906875b87d342d7325a36a4e1ab42.mp3", 73 | "dst_tts": "https:\/\/fanyiapp.cdn.bcebos.com\/api\/tts\/62f4ff87617655bc1f65e24cf4ed4963.mp3", 74 | "dict": "{\"lang\":\"1\",\"word_result\":{\"simple_means\":{\"word_name\":\"apple\",\"from\":\"original\",\"word_means\":[\"苹果\"],\"exchange\":{\"word_pl\":[\"apples\"]},\"tags\":{\"core\":[\"高考\",\"考研\"],\"other\":[\"\"]},\"symbols\":[{\"ph_en\":\"ˈæpl\",\"ph_am\":\"ˈæpl\",\"parts\":[{\"part\":\"n.\",\"means\":[\"苹果\"]}],\"ph_other\":\"\"}]}}}" 75 | } 76 | ] 77 | } 78 | */ 79 | 80 | override fun parse(text: String): YouDaoTranslation? { 81 | val translation = GsonUtils.getInstance().from(text, YouDaoTranslation::class.java) 82 | return translation?.apply { src = formData["q"]!! } 83 | } 84 | 85 | override fun query(): String { 86 | val request = HttpPost(url) 87 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 88 | 89 | val response = httpClient.execute(request) 90 | val entity = response.entity 91 | val result = EntityUtils.toString(entity, "UTF-8") 92 | println(result) 93 | close(entity, response) 94 | return result 95 | } 96 | 97 | override fun addFormData(to: Lang, text: String) { 98 | formData["sl"] = langs[Lang.EN]!! 99 | formData["tl"] = langs[to]!! 100 | formData["client"] = "gtx" 101 | formData["dt"] = "1" 102 | formData["t"] = "1" 103 | formData["bd"] = "1" 104 | formData["rm"] = "1" 105 | formData["qca"] = "1" 106 | formData["dj"] = "1" 107 | formData["ie"] = "UTF-8" 108 | formData["oe"] = "UTF-8" 109 | formData["hl"] = "1" 110 | formData["tk"] = text.tk() 111 | } 112 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/impl/YouDaoDictionary.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.impl 2 | 3 | import me.bytebeats.polyglot.dict.AbstractDictionary 4 | import me.bytebeats.polyglot.dict.DictConsultListener 5 | import me.bytebeats.polyglot.dict.meta.YouDaoTranslation 6 | import me.bytebeats.polyglot.lang.Lang 7 | import me.bytebeats.polyglot.ui.PolyglotSettingState 8 | import me.bytebeats.polyglot.util.GsonUtils 9 | import me.bytebeats.polyglot.util.ParamUtils 10 | import org.apache.http.client.entity.UrlEncodedFormEntity 11 | import org.apache.http.client.methods.HttpPost 12 | import org.apache.http.util.EntityUtils 13 | import java.util.* 14 | 15 | /** 16 | * @Author bytebeats 17 | * @Email 18 | * @Github https://github.com/bytebeats 19 | * @Created on 2020/10/31 19:26 20 | * @Version 1.0 21 | * @Description TO-DO 22 | */ 23 | 24 | class YouDaoDictionary(listener: DictConsultListener) : AbstractDictionary(listener, URL) { 25 | companion object { 26 | private const val URL = "https://openapi.youdao.com/api" 27 | 28 | // @JvmStatic 29 | // fun main(args: Array) { 30 | // println("youdao dictionary") 31 | // YouDaoDictionary(object : DictConsultListener { 32 | // override fun onSuccess(translation: YouDaoTranslation) { 33 | // println(translation.format()) 34 | // } 35 | // 36 | // override fun onFailure(message: String) { 37 | // println(message) 38 | // } 39 | // 40 | // override fun onError(error: String) { 41 | // println(error) 42 | // } 43 | // }).consult("remain") 44 | // } 45 | } 46 | 47 | override fun addSupportedLangs() { 48 | langs[Lang.ZH] = "zh-CHS" 49 | langs[Lang.EN] = "en" 50 | langs[Lang.JP] = "ja" 51 | langs[Lang.KOR] = "ko" 52 | langs[Lang.FRA] = "fr" 53 | langs[Lang.RU] = "ru" 54 | langs[Lang.CHT] = "zh-CHT" 55 | langs[Lang.DE] = "de" 56 | langs[Lang.SPA] = "es" 57 | langs[Lang.IT] = "it" 58 | langs[Lang.PT] = "pt" 59 | langs[Lang.VIE] = "vi" 60 | langs[Lang.ID] = "id" 61 | langs[Lang.TH] = "th" 62 | langs[Lang.ARA] = "ar" 63 | langs[Lang.NL] = "nl" 64 | } 65 | 66 | /** 67 | * { 68 | "returnPhrase": [ 69 | "dictionary" 70 | ], 71 | "query": "dictionary", 72 | "errorCode": "0", 73 | "l": "en2zh-CHS", 74 | "tSpeakUrl": "http://openapi.youdao.com/ttsapi?q\u003d%E5%AD%97%E5%85%B8\u0026langType\u003dzh-CHS\u0026sign\u003dCB3E1B4B35E08C0F9DCEF04CE16BCBBF\u0026salt\u003d1604150506142\u0026voice\u003d4\u0026format\u003dmp3\u0026appKey\u003d0de194293a5be337\u0026ttsVoiceStrict\u003dfalse", 75 | "web": [ 76 | { 77 | "value": [ 78 | "词典", 79 | "字典", 80 | "字典", 81 | "词典正文" 82 | ], 83 | "key": "dictionary" 84 | }, 85 | { 86 | "value": [ 87 | "城市词典", 88 | "都市词典", 89 | "俚语词典" 90 | ], 91 | "key": "Urban Dictionary" 92 | }, 93 | { 94 | "value": [ 95 | "数据字典", 96 | "数据词典", 97 | "资料字典", 98 | "资料辞典" 99 | ], 100 | "key": "Data Dictionary" 101 | } 102 | ], 103 | "requestId": "262d6c75-b034-4367-b0f6-a31e032c70dd", 104 | "translation": [ 105 | "字典" 106 | ], 107 | "dict": { 108 | "url": "yddict://m.youdao.com/dict?le\u003deng\u0026q\u003ddictionary" 109 | }, 110 | "webdict": { 111 | "url": "http://m.youdao.com/dict?le\u003deng\u0026q\u003ddictionary" 112 | }, 113 | "basic": { 114 | "exam_type": [ 115 | "高中", 116 | "初中", 117 | "CET6", 118 | "CET4", 119 | "考研" 120 | ], 121 | "us-phonetic": "ˈdɪkʃəneri", 122 | "phonetic": "ˈdɪkʃənri", 123 | "uk-phonetic": "ˈdɪkʃənri", 124 | "wfs": [ 125 | { 126 | "wf": { 127 | "name": "复数", 128 | "value": "dictionaries" 129 | } 130 | } 131 | ], 132 | "uk-speech": "http://openapi.youdao.com/ttsapi?q\u003ddictionary\u0026langType\u003den\u0026sign\u003d35A6F5FF28005BD4046A83CFB1610D09\u0026salt\u003d1604150506142\u0026voice\u003d5\u0026format\u003dmp3\u0026appKey\u003d0de194293a5be337\u0026ttsVoiceStrict\u003dfalse", 133 | "explains": [ 134 | "n. 字典;词典" 135 | ], 136 | "us-speech": "http://openapi.youdao.com/ttsapi?q\u003ddictionary\u0026langType\u003den\u0026sign\u003d35A6F5FF28005BD4046A83CFB1610D09\u0026salt\u003d1604150506142\u0026voice\u003d6\u0026format\u003dmp3\u0026appKey\u003d0de194293a5be337\u0026ttsVoiceStrict\u003dfalse" 137 | }, 138 | "isWord": true, 139 | "speakUrl": "http://openapi.youdao.com/ttsapi?q\u003ddictionary\u0026langType\u003den\u0026sign\u003d35A6F5FF28005BD4046A83CFB1610D09\u0026salt\u003d1604150506142\u0026voice\u003d4\u0026format\u003dmp3\u0026appKey\u003d0de194293a5be337\u0026ttsVoiceStrict\u003dfalse" 140 | } 141 | */ 142 | 143 | override fun parse(text: String): YouDaoTranslation? { 144 | val translation = GsonUtils.getInstance().from(text, YouDaoTranslation::class.java) 145 | return translation?.apply { src = formData["q"]!! } 146 | } 147 | 148 | override fun query(): String { 149 | val request = HttpPost(url) 150 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 151 | 152 | val response = httpClient.execute(request) 153 | val entity = response.entity 154 | val result = EntityUtils.toString(entity, "UTF-8") 155 | // println(result) 156 | close(entity, response) 157 | return result 158 | } 159 | 160 | override fun addFormData(to: Lang, text: String) { 161 | val appID = PolyglotSettingState.getInstance().appID 162 | val appKey = PolyglotSettingState.getInstance().appKey 163 | val curTime = (System.currentTimeMillis() / 1000).toString() 164 | val salt = UUID.randomUUID().toString() 165 | val qInSign = if (text.length <= 20) text else "${text.take(10)}${text.length}${text.takeLast(10)}" 166 | val sign = ParamUtils.sha256("$appID$qInSign$salt$curTime$appKey") 167 | formData["from"] = langs[Lang.EN]!! 168 | formData["to"] = langs[to]!! 169 | formData["appKey"] = appID 170 | formData["salt"] = salt 171 | formData["sign"] = sign!! 172 | formData["q"] = text 173 | formData["signType"] = "v3" 174 | formData["curtime"] = curTime 175 | } 176 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/meta/BaiduTranslation.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.meta 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/11/7 17:20 10 | * @Version 1.0 11 | * @Description TO-DO 12 | */ 13 | 14 | data class BaiduTranslation( 15 | @SerializedName("error_code") val code: Int = 0, 16 | @SerializedName("from") val from: String? = null, 17 | @SerializedName("to") val to: String? = null, 18 | @SerializedName("trans_result") val trans: List? = null, 19 | ) { 20 | } 21 | 22 | data class BTrans( 23 | @SerializedName("src") val src: String, 24 | @SerializedName("dst") val dst: String, 25 | @SerializedName("dict") val dict: BDict? = null 26 | ) 27 | 28 | data class BDict( 29 | @SerializedName("lang") val lang: String, 30 | @SerializedName("word_result") val wordResult: BWordResult? = null 31 | ) 32 | 33 | data class BWordResult(@SerializedName("simple_means") val simpleMeans: BSimpleMeans? = null) 34 | data class BSimpleMeans( 35 | @SerializedName("word_name") val wordName: String, 36 | @SerializedName("from") val from: String, 37 | @SerializedName("word_means") val wordMeans: List? = null, 38 | @SerializedName("exchange") val exchange: BWordPl? = null, 39 | @SerializedName("symbols") val symbols: List? = null, 40 | ) 41 | 42 | data class BWordPl(@SerializedName("word_pl") val wordPl: List? = null) 43 | data class BSymbol( 44 | @SerializedName("ph_en") val phEn: String? = null, 45 | @SerializedName("ph_am") val phAm: String? = null, 46 | @SerializedName("parts") val parts: List? = null, 47 | ) 48 | 49 | data class BPart(@SerializedName("part") val part: String, @SerializedName("means") val means: List? = null) 50 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/meta/DictService.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.meta 2 | 3 | /** 4 | * @Author bytebeats 5 | * @Email 6 | * @Github https://github.com/bytebeats 7 | * @Created on 2020/11/7 16:39 8 | * @Version 1.0 9 | * @Description TO-DO 10 | */ 11 | 12 | class DictService(val id: String, val appId: String, val appKey: String) { 13 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/meta/GoogleTranslation.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.meta 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/11/7 19:25 10 | * @Version 1.0 11 | * @Description TO-DO 12 | */ 13 | 14 | data class GoogleTranslation( 15 | val sentences: List? = null, 16 | val dict: List? = null, 17 | val spell: GSpell? = null, 18 | @SerializedName("ld_result") val ldResult: GldResult, 19 | @SerializedName("alternative_translations") val alternativeTranslations: List? = null 20 | ) { 21 | } 22 | 23 | sealed class GSentence 24 | data class GTransSentence(val orig: String, val trans: String, val backend: Int) : GSentence() 25 | data class GTranslitSentence( 26 | @SerializedName("src_translit") val srcTranslit: String?, 27 | @SerializedName("translit") val translit: String?, 28 | ) : GSentence() 29 | 30 | data class GDict(val pos: String, val terms: List? = null, val entry: List? = null) 31 | data class GDictEntry( 32 | @SerializedName("word") val word: String, 33 | @SerializedName("reverse_translation") val reverseTranslation: List?, 34 | @SerializedName("score") val score: Float, 35 | ) 36 | 37 | data class GSpell(@SerializedName("spell_res") val spell: String) 38 | data class GldResult( 39 | @SerializedName("srclangs") val srcLangs: List, 40 | @SerializedName("srclangs_confidences") val srcLangsConfidences: List 41 | ) 42 | 43 | data class GAlternativeTranslations( 44 | @SerializedName("src_phrase") val srcPhrase: String, 45 | @SerializedName("raw_src_segment") val rawSrcSegment: String, 46 | @SerializedName("alternative") val alternative: List, 47 | ) 48 | 49 | data class GAlternative( 50 | @SerializedName("word_postproc") val wordPostproc: String, 51 | @SerializedName("score") val score: Float, 52 | @SerializedName("has_preceding_space") val hasPrecedingSpace: Boolean, 53 | @SerializedName("attach_to_next_token") val attachToNextToken: Boolean, 54 | ) -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dict/meta/YouDaoTranslation.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dict.meta 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/10/31 17:32 10 | * @Version 1.0 11 | * @Description YouDaoTranslation is the data structure for response of YouDao service 12 | */ 13 | 14 | data class YouDaoTranslation( 15 | @SerializedName("query") val query: String? = null, 16 | @SerializedName("errorCode") val errorCode: Int = -1, 17 | @SerializedName("translation") val translation: List? = null, 18 | @SerializedName("basic") val basicExplain: BasicExplain? = null, 19 | @SerializedName("l") val languages: String? = null, 20 | @SerializedName("web") val webExplains: List? = null, 21 | ) { 22 | var src: String? = null 23 | fun isSuccessful() = errorCode == 0 24 | fun getError(): String = when (errorCode) { 25 | 101 -> "缺少必填参数" 26 | 102 -> "不支持的语言类型" 27 | 103 -> "翻译文本过长" 28 | 104 -> "不支持的API类型" 29 | 105 -> "不支持的签名类型" 30 | 106 -> "不支持的响应类型" 31 | 107 -> "不支持的加密传输类型" 32 | 108 -> "appKey无效, \n请在 Preferences -> Other Settings -> Polyglot Translators 输入有道智云appID和appKey" 33 | 109 -> "batchLog格式不正确" 34 | 110 -> "无相关服务的有效实例" 35 | 111 -> "开发者账号无效" 36 | 201 -> "解密失败" 37 | 202 -> "签名检验失败" 38 | 203 -> "访问IP地址不在可访问IP列表" 39 | 301 -> "辞典查询失败" 40 | 302 -> "翻译查询失败" 41 | 303 -> "服务端的其他异常" 42 | 401 -> "账户已欠费" 43 | else -> "未知返回码: $errorCode" 44 | } 45 | 46 | fun format(): String { 47 | val formatted = StringBuilder() 48 | formatted.append(src) 49 | formatted.append(" : ") 50 | if (translation?.isNotEmpty() == true) { 51 | for (index in translation.indices) { 52 | formatted.append(translation[index]) 53 | if (index != translation.lastIndex) { 54 | formatted.append(",") 55 | } else { 56 | formatted.append(";") 57 | } 58 | } 59 | } 60 | basicExplain?.apply { 61 | if (phonetic?.isNotEmpty() == true) { 62 | formatted.append("\nphonetic: [$phonetic];") 63 | } 64 | if (phoneticUS?.isNotEmpty() == true) { 65 | formatted.append(" US: [$phoneticUS];") 66 | } 67 | if (phoneticUK?.isNotEmpty() == true) { 68 | formatted.append(" UK: [$phoneticUK]") 69 | } 70 | if (explains?.isNotEmpty() == true) { 71 | for (s in explains) { 72 | formatted.append("\n$s") 73 | } 74 | } 75 | if (wordForms?.isNotEmpty() == true) { 76 | formatted.append("\n") 77 | val forms = wordForms.map { it.wordForm } 78 | for (index in forms.indices) { 79 | formatted.append("${forms[index].name}: ${forms[index].value}") 80 | if (index != wordForms.lastIndex) { 81 | formatted.append(", ") 82 | } 83 | } 84 | } 85 | if (webExplains?.isNotEmpty() == true) { 86 | formatted.append("\nInterpretation on Network:") 87 | for (e in webExplains) { 88 | formatted.append("\n${e.key} : ${e.values?.joinToString(separator = ", ")}") 89 | } 90 | } 91 | } 92 | return formatted.toString() 93 | } 94 | } 95 | 96 | data class BasicExplain( 97 | @SerializedName("phonetic") val phonetic: String? = null, 98 | @SerializedName("uk-phonetic") val phoneticUK: String? = null, 99 | @SerializedName("us-phonetic") val phoneticUS: String? = null, 100 | @SerializedName("explains") val explains: List? = null, 101 | @SerializedName("wfs") val wordForms: List? = null, 102 | ) 103 | 104 | data class WebExplain( 105 | @SerializedName("key") val key: String, 106 | @SerializedName("value") val values: List? = null, 107 | ) 108 | 109 | data class WordFormWrapper(@SerializedName("wf") val wordForm: WordForm) 110 | data class WordForm( 111 | @SerializedName("name") val name: String, 112 | @SerializedName("value") val value: String, 113 | ) -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dq/AbstractDailyQuoter.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dq 2 | 3 | import me.bytebeats.polyglot.dq.impl.IcibaDailyQuoter 4 | import me.bytebeats.polyglot.dq.impl.ScallopDailyQuoter 5 | import me.bytebeats.polyglot.dq.impl.YoudaoDailyQuoter 6 | import me.bytebeats.polyglot.http.DailyQuoteConnectionCloser 7 | import me.bytebeats.polyglot.http.IDailyQuote 8 | import me.bytebeats.polyglot.http.ParamsAdder 9 | import me.bytebeats.polyglot.meta.DailyQuote 10 | import me.bytebeats.polyglot.util.LogUtils 11 | import me.bytebeats.polyglot.util.StringResUtils 12 | import java.io.IOException 13 | import java.time.LocalDate 14 | import java.time.format.DateTimeFormatter 15 | 16 | /** 17 | * @Author bytebeats 18 | * @Email 19 | * @Github https://github.com/bytebeats 20 | * @Created on 2020/9/18 20:17 21 | * @Version 1.0 22 | * @Description AbstractDailyQuoter offers quote every day 23 | */ 24 | 25 | abstract class AbstractDailyQuoter(url: String) : DailyQuoteConnectionCloser(url = url), IDailyQuote, ParamsAdder { 26 | 27 | /** 28 | * Execute the translation or TTS task (send a POST or GET request to the server), 29 | * receive the result of translation or speech conversion., and return the content 30 | * or save file name as string. 31 | * 32 | * @return the string form of the translated result. 33 | * @throws Exception if the request fails 34 | */ 35 | @Throws(Exception::class) 36 | abstract fun query(): String 37 | 38 | /** 39 | * Parse the string to extract the {@see #DailyQuote}. 40 | * 41 | * @param text the string form of the daily quote result. 42 | * @return DailyQuote results after parsing. 43 | * @throws IOException if the parsing fails. 44 | */ 45 | @Throws(IOException::class) 46 | protected abstract fun parse(text: String?): DailyQuote? 47 | 48 | override fun quote(): DailyQuote? { 49 | var dailyQuote: DailyQuote? = null 50 | add() 51 | try { 52 | dailyQuote = parse(query()) 53 | } catch (e: Exception) { 54 | LogUtils.info(e.message) 55 | } finally { 56 | close() 57 | } 58 | return dailyQuote 59 | } 60 | 61 | fun getToday(): String = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now()) 62 | 63 | companion object Factory { 64 | fun newInstance(quoter: String): AbstractDailyQuoter = 65 | when (quoter) { 66 | StringResUtils.QUOTOR_SCALLOP, StringResUtils.QUOTOR_SCALLOP_EN -> ScallopDailyQuoter() 67 | StringResUtils.QUOTOR_ICIBA, StringResUtils.QUOTOR_ICIBA_EN -> IcibaDailyQuoter() 68 | StringResUtils.QUOTOR_YOUDAO, StringResUtils.QUOTOR_YOUDAO_EN -> YoudaoDailyQuoter() 69 | else -> YoudaoDailyQuoter() 70 | } 71 | 72 | fun newInstance(quoter: DailyQuoter): AbstractDailyQuoter = 73 | when (quoter) { 74 | DailyQuoter.SCALLOP -> ScallopDailyQuoter() 75 | DailyQuoter.ICIBA -> IcibaDailyQuoter() 76 | DailyQuoter.YOUDAO -> YoudaoDailyQuoter() 77 | } 78 | 79 | // @JvmStatic 80 | // fun main(args: Array) { 81 | // println("扇贝") 82 | // val scallop = ScallopDailyQuoter() 83 | // val scallopQuoter = scallop.quote() 84 | // println("${scallopQuoter?.date} ${scallopQuoter?.content} ${scallopQuoter?.translation}") 85 | // println("Iciba") 86 | // val iciba = IcibaDailyQuoter() 87 | // val icibaQuoter = iciba.quote() 88 | // println("${icibaQuoter?.date} ${icibaQuoter?.content} ${icibaQuoter?.translation}") 89 | // println("Youdao") 90 | // val youdao = IcibaDailyQuoter() 91 | // val youdaoQuoter = youdao.quote() 92 | // println("${youdaoQuoter?.date} ${youdaoQuoter?.content} ${youdaoQuoter?.translation}") 93 | // } 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dq/DailyQuoter.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dq 2 | 3 | import me.bytebeats.polyglot.util.StringResUtils 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/9/29 17:17 10 | * @Version 1.0 11 | * @Description AbstractDailyQuoter offers quote every day 12 | */ 13 | 14 | enum class DailyQuoter(val type: String, val desc: String, val descEn: String) { 15 | SCALLOP("scallop", StringResUtils.QUOTOR_SCALLOP, StringResUtils.QUOTOR_SCALLOP_EN), 16 | ICIBA("iciba", StringResUtils.QUOTOR_ICIBA, StringResUtils.QUOTOR_ICIBA_EN), 17 | YOUDAO("youdao", StringResUtils.QUOTOR_YOUDAO, StringResUtils.QUOTOR_YOUDAO_EN); 18 | 19 | companion object { 20 | fun fromDesc(desc: String): DailyQuoter { 21 | for (quoter in values()) { 22 | if (quoter.desc == desc || quoter.descEn == desc) { 23 | return quoter 24 | } 25 | } 26 | return SCALLOP 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dq/impl/IcibaDailyQuoter.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dq.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.dq.AbstractDailyQuoter 5 | import me.bytebeats.polyglot.meta.DailyQuote 6 | import me.bytebeats.polyglot.util.ParamUtils 7 | import org.apache.http.client.methods.HttpGet 8 | import org.apache.http.util.EntityUtils 9 | 10 | /** 11 | * @Author bytebeats 12 | * @Email 13 | * @Github https://github.com/bytebeats 14 | * @Created on 2020/9/18 20:28 15 | * @Version 1.0 16 | * @Description YoudaoDailyQuoter offer daily quote by iciba APIs. 17 | */ 18 | 19 | class IcibaDailyQuoter() : AbstractDailyQuoter(URL) { 20 | companion object { 21 | private const val URL = "http://sentence.iciba.com/index.php" 22 | } 23 | 24 | override fun query(): String { 25 | val request = HttpGet(ParamUtils.concatUrl(url, formData)) 26 | val response = httpClient.execute(request) 27 | val entity = response.entity 28 | val result = EntityUtils.toString(entity, "UTF-8") 29 | // println(result) 30 | close(entity, response) 31 | return result 32 | } 33 | 34 | override fun parse(text: String?): DailyQuote? { 35 | if (text.isNullOrEmpty()) return null 36 | val mapper = ObjectMapper() 37 | val json = mapper.readTree(text) 38 | val date = json["title"].asText() 39 | val content = json["content"].asText() 40 | val translation = json["note"].asText() 41 | return DailyQuote(date, content, translation) 42 | } 43 | 44 | override fun add() { 45 | formData["c"] = "dailysentence" 46 | formData["m"] = "getdetail" 47 | formData["title"] = getToday() 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dq/impl/ScallopDailyQuoter.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dq.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.dq.AbstractDailyQuoter 5 | import me.bytebeats.polyglot.meta.DailyQuote 6 | import me.bytebeats.polyglot.util.ParamUtils 7 | import org.apache.http.client.methods.HttpGet 8 | import org.apache.http.util.EntityUtils 9 | 10 | /** 11 | * @Author bytebeats 12 | * @Email 13 | * @Github https://github.com/bytebeats 14 | * @Created on 2020/9/18 20:28 15 | * @Version 1.0 16 | * @Description YoudaoDailyQuoter offer daily quote by scallop APIs. 17 | */ 18 | 19 | class ScallopDailyQuoter() : AbstractDailyQuoter(URL) { 20 | companion object { 21 | private const val URL = "https://apiv3.shanbay.com/weapps/dailyquote/quote/" 22 | } 23 | 24 | override fun query(): String { 25 | val request = HttpGet(ParamUtils.concatUrl(url, formData)) 26 | val response = httpClient.execute(request) 27 | val entity = response.entity 28 | val result = EntityUtils.toString(entity, "UTF-8") 29 | // println(result) 30 | close(entity, response) 31 | return result 32 | } 33 | 34 | override fun parse(text: String?): DailyQuote? { 35 | if (text.isNullOrEmpty()) return null 36 | val mapper = ObjectMapper() 37 | val json = mapper.readTree(text) 38 | val date = json["assign_date"].asText() 39 | val content = json["content"].asText() 40 | val translation = json["translation"].asText() 41 | return DailyQuote(date, content, translation) 42 | } 43 | 44 | override fun add() { 45 | formData["date"] = getToday() 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/dq/impl/YoudaoDailyQuoter.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.dq.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.dq.AbstractDailyQuoter 5 | import me.bytebeats.polyglot.meta.DailyQuote 6 | import me.bytebeats.polyglot.util.ParamUtils 7 | import org.apache.http.client.methods.HttpGet 8 | import org.apache.http.util.EntityUtils 9 | 10 | /** 11 | * @Author bytebeats 12 | * @Email 13 | * @Github https://github.com/bytebeats 14 | * @Created on 2020/9/18 20:28 15 | * @Version 1.0 16 | * @Description YoudaoDailyQuoter offer daily quote by you dao APIs. 17 | */ 18 | 19 | class YoudaoDailyQuoter() : AbstractDailyQuoter(URL) { 20 | companion object { 21 | private const val URL = "https://dict.youdao.com/infoline" 22 | } 23 | 24 | override fun query(): String { 25 | val request = HttpGet(ParamUtils.concatUrl(url, formData)) 26 | val response = httpClient.execute(request) 27 | val entity = response.entity 28 | val result = EntityUtils.toString(entity, "UTF-8") 29 | // println(result) 30 | close(entity, response) 31 | return result 32 | } 33 | 34 | override fun parse(text: String?): DailyQuote? { 35 | if (text.isNullOrEmpty()) return null 36 | val mapper = ObjectMapper() 37 | val json = mapper.readTree(text).path(getToday())[0] 38 | val date = getToday() 39 | val content = json["title"].asText() 40 | val translation = json["summary"].asText() 41 | return DailyQuote(date, content, translation) 42 | } 43 | 44 | override fun add() { 45 | formData["mode"] = "publish" 46 | formData["update"] = "auto" 47 | formData["apiversion"] = "5.0" 48 | formData["date"] = getToday() 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/excp/NetworkException.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.excp 2 | 3 | import java.io.IOException 4 | import java.net.ConnectException 5 | import java.net.SocketException 6 | import java.net.SocketTimeoutException 7 | import java.net.UnknownHostException 8 | import javax.net.ssl.SSLHandshakeException 9 | 10 | 11 | /** 12 | * @Author bytebeats 13 | * @Email 14 | * @Github https://github.com/bytebeats 15 | * @Created on 2020/11/7 20:13 16 | * @Version 1.0 17 | * @Description TO-DO 18 | */ 19 | 20 | class NetworkException(host: String, cause: IOException) : IOException("${cause.message}. host=$host", cause) { 21 | companion object { 22 | fun wrapIfIsNetworkException(throwable: Throwable, host: String): Throwable { 23 | return when (throwable) { 24 | is SocketException, 25 | is SocketTimeoutException, 26 | is SSLHandshakeException, 27 | is ConnectException, 28 | is UnknownHostException -> NetworkException(host, throwable as IOException) 29 | else -> throwable 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/http/DailyQuoteConnectionCloser.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.http 2 | 3 | import me.bytebeats.polyglot.util.LogUtils 4 | import org.apache.http.HttpEntity 5 | import org.apache.http.client.methods.CloseableHttpResponse 6 | import org.apache.http.impl.client.CloseableHttpClient 7 | import org.apache.http.impl.client.HttpClients 8 | import org.apache.http.util.EntityUtils 9 | import java.io.IOException 10 | 11 | /** 12 | * @Author bytebeats 13 | * @Email 14 | * @Github https://github.com/bytebeats 15 | * @Created on 2020/9/18 20:09 16 | * @Version 1.0 17 | * @Description DailyQuoteConnectionCloser is an abstract base class associated with HTTP 18 | */ 19 | 20 | abstract class DailyQuoteConnectionCloser( 21 | open val url: String, 22 | val formData: MutableMap = mutableMapOf(), 23 | val httpClient: CloseableHttpClient = HttpClients.createDefault() 24 | ) { 25 | 26 | /** 27 | * Release and close the resources of HTTP 28 | * @param httpEntity http entity 29 | * @param httpResponse http response 30 | */ 31 | fun close(httpEntity: HttpEntity?, httpResponse: CloseableHttpResponse?) { 32 | try { 33 | EntityUtils.consume(httpEntity) 34 | httpResponse?.close() 35 | } catch (e: IOException) { 36 | LogUtils.info(e.message) 37 | } finally { 38 | httpClient.close() 39 | } 40 | } 41 | 42 | /** 43 | * Release and close the resources of HTTP 44 | */ 45 | fun close() { 46 | try { 47 | httpClient.close() 48 | } catch (e: IOException) { 49 | LogUtils.info(e.message) 50 | } 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/http/IDailyQuote.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.http 2 | 3 | import me.bytebeats.polyglot.meta.DailyQuote 4 | 5 | /** 6 | * @Author bytebeats 7 | * @Email 8 | * @Github https://github.com/bytebeats 9 | * @Created on 2020/9/18 19:59 10 | * @Version 1.0 11 | * @Description IDailyQuote will execute a task to fetch today's quote over http. 12 | */ 13 | 14 | interface IDailyQuote { 15 | 16 | /** 17 | * fetch today's quote 18 | * @return today's quote. 19 | */ 20 | fun quote(): DailyQuote? 21 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/http/IPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.http 2 | 3 | import me.bytebeats.polyglot.lang.Lang 4 | 5 | /** 6 | * @author bytebeats 7 | * @email 8 | * @github https://github.com/bytebeats 9 | * @created on 2020/8/27 15:08 10 | * @version 1.0 11 | * @description ITranslator will execute a task to translate text over http. 12 | */ 13 | 14 | interface IPolyglot { 15 | /** 16 | * The control center includes parameter setting, sending HTTP request, receiving 17 | * and parsing text data. 18 | * 19 | * @param from source language 20 | * @param to target language 21 | * @param text the content to be translated 22 | * @return the string form of the translated result. 23 | */ 24 | fun translate(from: Lang, to: Lang, text: String): String 25 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/http/ParamsAdder.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.http 2 | 3 | /** 4 | * @Author bytebeats 5 | * @Email 6 | * @Github https://github.com/bytebeats 7 | * @Created on 2020/9/18 20:19 8 | * @Version 1.0 9 | * @Description TO-DO 10 | */ 11 | 12 | interface ParamsAdder { 13 | fun add() 14 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/http/PolyglotFormDataAdder.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.http 2 | 3 | import me.bytebeats.polyglot.lang.Lang 4 | 5 | /** 6 | * @author bytebeats 7 | * @email 8 | * @github https://github.com/bytebeats 9 | * @created on 2020/8/27 14:51 10 | * @version 1.0 11 | * @description FormDataAdder is an interface containing one function to set up 12 | * HTTP request parameters. 13 | */ 14 | 15 | interface PolyglotFormDataAdder { 16 | /** 17 | * Set the request parameters that will be sent to the server. 18 | * @param from source language 19 | * @param to target language 20 | * @param text the content to be translated 21 | */ 22 | fun addFormData(from: Lang, to: Lang, text: String) 23 | 24 | companion object { 25 | const val USER_AGENT = 26 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36" 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/http/TranslatorConnectionCloser.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.http 2 | 3 | import me.bytebeats.polyglot.util.LogUtils 4 | import me.bytebeats.polyglot.lang.Lang 5 | import org.apache.http.HttpEntity 6 | import org.apache.http.client.methods.CloseableHttpResponse 7 | import org.apache.http.impl.client.CloseableHttpClient 8 | import org.apache.http.impl.client.HttpClients 9 | import org.apache.http.util.EntityUtils 10 | import java.io.IOException 11 | 12 | /** 13 | * @author bytebeats 14 | * @email 15 | * @github https://github.com/bytebeats 16 | * @created on 2020/8/27 15:14 17 | * @version 1.0 18 | * @description TranslatorConnectionCloser is an abstract base class associated with HTTP. 19 | */ 20 | 21 | abstract class TranslatorConnectionCloser( 22 | open val url: String, 23 | val formData: MutableMap = mutableMapOf(), 24 | val langs: MutableMap = mutableMapOf(), 25 | val httpClient: CloseableHttpClient = HttpClients.createDefault() 26 | ) { 27 | 28 | /** 29 | * Release and close the resources of HTTP 30 | * @param httpEntity http entity 31 | * @param httpResponse http response 32 | */ 33 | fun close(httpEntity: HttpEntity?, httpResponse: CloseableHttpResponse?, closeClient: Boolean = true) { 34 | try { 35 | EntityUtils.consume(httpEntity) 36 | httpResponse?.close() 37 | } catch (e: IOException) { 38 | LogUtils.info(e.message) 39 | } finally { 40 | if (closeClient) { 41 | httpClient.close() 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * Release and close the resources of HTTP 48 | */ 49 | fun close() { 50 | try { 51 | httpClient.close() 52 | } catch (e: IOException) { 53 | LogUtils.info(e.message) 54 | } 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/lang/Lang.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.lang 2 | 3 | import me.bytebeats.polyglot.util.StringResUtils 4 | 5 | /** 6 | * @author bytebeats 7 | * @email 8 | * @github https://github.com/bytebeats 9 | * @created on 2020/8/27 14:39 10 | * @version 1.0 11 | * @description Lang implies languages. 12 | */ 13 | 14 | enum class Lang(val lang: String, val desc: String, val descEN: String = "") { 15 | AUTO("auto", StringResUtils.LANG_DESC_AUTO, StringResUtils.LANG_DESC_AUTO_EN), 16 | ZH("zh", StringResUtils.LANG_DESC_ZH, StringResUtils.LANG_DESC_ZH_EN), 17 | EN("en", StringResUtils.LANG_DESC_EN, StringResUtils.LANG_DESC_EN_EN), 18 | JP("jp", StringResUtils.LANG_DESC_JP, StringResUtils.LANG_DESC_JP_EN), 19 | KOR("kor", StringResUtils.LANG_DESC_KR, StringResUtils.LANG_DESC_KR_EN), 20 | FRA("fra", StringResUtils.LANG_DESC_FR, StringResUtils.LANG_DESC_FR_EN), 21 | DE("de", StringResUtils.LANG_DESC_DE, StringResUtils.LANG_DESC_DE_EN), 22 | RU("ru", StringResUtils.LANG_DESC_RU, StringResUtils.LANG_DESC_RU_EN), 23 | CHT("cht", StringResUtils.LANG_DESC_CHT, StringResUtils.LANG_DESC_CHT_EN), 24 | SPA("spa", StringResUtils.LANG_DESC_SPA, StringResUtils.LANG_DESC_SPA_EN), 25 | PT("pt", StringResUtils.LANG_DESC_PT, StringResUtils.LANG_DESC_PT_EN), 26 | ARA("ara", StringResUtils.LANG_DESC_ARA, StringResUtils.LANG_DESC_ARA_EN), 27 | TR("tr", StringResUtils.LANG_DESC_TR, StringResUtils.LANG_DESC_TR_EN), 28 | PL("pl", StringResUtils.LANG_DESC_PL, StringResUtils.LANG_DESC_PL_EN), 29 | DAN("dan", StringResUtils.LANG_DESC_DAN, StringResUtils.LANG_DESC_DAN_EN), 30 | YUE("yue", StringResUtils.LANG_DESC_YUE, StringResUtils.LANG_DESC_YUE_EN), 31 | TH("th", StringResUtils.LANG_DESC_TH, StringResUtils.LANG_DESC_TH_EN), 32 | VIE("vie", StringResUtils.LANG_DESC_VIE, StringResUtils.LANG_DESC_VIE_EN), 33 | ID("id", StringResUtils.LANG_DESC_ID, StringResUtils.LANG_DESC_ID_EN), 34 | MS("ms", StringResUtils.LANG_DESC_MS, StringResUtils.LANG_DESC_MS_EN), 35 | HI("hi", StringResUtils.LANG_DESC_HI, StringResUtils.LANG_DESC_HI_EN), 36 | IR("ir", StringResUtils.LANG_DESC_IR, StringResUtils.LANG_DESC_IR_EN), 37 | WYW("wyw", StringResUtils.LANG_DESC_WYW, StringResUtils.LANG_DESC_WYW_EN), 38 | IT("it", StringResUtils.LANG_DESC_IT, StringResUtils.LANG_DESC_IT_EN), 39 | KK("kk", StringResUtils.LANG_DESC_KK, StringResUtils.LANG_DESC_KK_EN), 40 | FIL("fil", StringResUtils.LANG_DESC_FIL, StringResUtils.LANG_DESC_FIL_EN), 41 | ICE("ice", StringResUtils.LANG_DESC_ICE, StringResUtils.LANG_DESC_ICE_EN), 42 | FIN("fin", StringResUtils.LANG_DESC_FIN, StringResUtils.LANG_DESC_FIN_EN), 43 | NL("nl", StringResUtils.LANG_DESC_NL, StringResUtils.LANG_DESC_NL_EN), 44 | TAT("tat", StringResUtils.LANG_DESC_TAT, StringResUtils.LANG_DESC_TAT_EN), 45 | KUR("kur", StringResUtils.LANG_DESC_KUR, StringResUtils.LANG_DESC_KUR_EN), 46 | 47 | /** 48 | * dead languages 49 | */ 50 | LAT("lat", StringResUtils.LANG_DESC_LAT, StringResUtils.LANG_DESC_LAT_EN), 51 | SAN("san", StringResUtils.LANG_DESC_SAN, StringResUtils.LANG_DESC_SAN_EN), 52 | ARG("arg", StringResUtils.LANG_DESC_ARG, StringResUtils.LANG_DESC_ARG_EN), 53 | GRA("gra", StringResUtils.LANG_DESC_GRA, StringResUtils.LANG_DESC_GRA_EN), 54 | KLI("kli", StringResUtils.LANG_DESC_KLI, StringResUtils.LANG_DESC_KLI_EN), 55 | HEB("heb", StringResUtils.LANG_DESC_HEB, StringResUtils.LANG_DESC_HEB_EN), 56 | ENO("eno", StringResUtils.LANG_DESC_ENO, StringResUtils.LANG_DESC_ENO_EN), 57 | FRM("frm", StringResUtils.LANG_DESC_FRM, StringResUtils.LANG_DESC_FRM_EN), 58 | PER("per", StringResUtils.LANG_DESC_PER, StringResUtils.LANG_DESC_PER_EN); 59 | 60 | companion object { 61 | fun from(desc: String): Lang { 62 | for (lang in values()) { 63 | if (lang.desc == desc || lang.descEN == desc) { 64 | return lang 65 | } 66 | } 67 | return ZH 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/meta/DailyQuote.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.meta 2 | 3 | /** 4 | * @Author bytebeats 5 | * @Email 6 | * @Github https://github.com/bytebeats 7 | * @Created on 2020/9/18 20:01 8 | * @Version 1.0 9 | * @Description DailyQuote is basic data structure 10 | */ 11 | 12 | class DailyQuote(val date: String, val content: String, val translation: String) { 13 | fun getMultilineContent(): String { 14 | val html = StringBuilder("") 15 | for (i in content.indices) {//English 16 | if (i in 1..content.lastIndex && i % (MAX_LINE_WIDTH * 2) == 0) { 17 | html.append("
") 18 | } 19 | html.append(content[i]) 20 | } 21 | html.append("") 22 | return html.toString() 23 | } 24 | 25 | fun getMultilineTranslation(): String { 26 | val html = StringBuilder("") 27 | for (i in translation.indices) {//Chinese 28 | if (i in 1..translation.lastIndex && i % MAX_LINE_WIDTH == 0) { 29 | html.append("
") 30 | } 31 | html.append(translation[i]) 32 | } 33 | html.append("") 34 | return html.toString() 35 | } 36 | 37 | companion object { 38 | private const val MAX_LINE_WIDTH = 30 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/AbstractPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr 2 | 3 | import me.bytebeats.polyglot.util.LogUtils 4 | import me.bytebeats.polyglot.http.TranslatorConnectionCloser 5 | import me.bytebeats.polyglot.http.PolyglotFormDataAdder 6 | import me.bytebeats.polyglot.http.IPolyglot 7 | import me.bytebeats.polyglot.lang.Lang 8 | import me.bytebeats.polyglot.tlr.impl.* 9 | import me.bytebeats.polyglot.util.StringResUtils 10 | import java.io.IOException 11 | 12 | /** 13 | * @author bytebeats 14 | * @email 15 | * @github https://github.com/bytebeats 16 | * @created on 2020/8/27 15:45 17 | * @version 1.0 18 | * @description AbstractPolyglot is an abstract base class for all polyglots 19 | * which includes several (abstract) functions. By setting parameters, 20 | * the request is sent to the target server, and then parse the return 21 | * result to achieve the the purpose of translation. 22 | */ 23 | 24 | abstract class AbstractPolyglot(url: String) : TranslatorConnectionCloser(url), PolyglotFormDataAdder, IPolyglot { 25 | init { 26 | addSupportedLanguages() 27 | } 28 | 29 | /** 30 | * Initialize the supported language mapping. 31 | */ 32 | abstract fun addSupportedLanguages() 33 | 34 | override fun translate(from: Lang, to: Lang, text: String): String { 35 | var result = "" 36 | addFormData(from, to, text) 37 | try { 38 | result = parse(query()) 39 | } catch (e: Exception) { 40 | LogUtils.info(e.message) 41 | } finally { 42 | close() 43 | } 44 | return result 45 | } 46 | 47 | /** 48 | * Parse the string to extract the content of interest. 49 | * 50 | * @param text the string form of the to-be-translated result. 51 | * @return translation results after parsing. 52 | * @throws IOException if the parsing fails. 53 | */ 54 | @Throws(IOException::class) 55 | abstract fun parse(text: String): String 56 | 57 | /** 58 | * Execute the translation or TTS task (send a POST or GET request to the server), 59 | * receive the result of translation or speech conversion., and return the content 60 | * or save file name as string. 61 | * 62 | * @return the string form of the translated result. 63 | * @throws Exception if the request fails 64 | */ 65 | @Throws(Exception::class) 66 | abstract fun query(): String 67 | 68 | companion object Factory { 69 | fun newInstance(polyglot: String): AbstractPolyglot = 70 | when (polyglot) { 71 | StringResUtils.POLYGLOT_BING -> BingPolyglot() 72 | StringResUtils.POLYGLOT_GOOGLE -> GooglePolyglot() 73 | StringResUtils.POLYGLOT_OMI -> OmiPolyglot() 74 | StringResUtils.POLYGLOT_SOGOU -> SogouPolyglot() 75 | StringResUtils.POLYGLOT_TENCENT -> TencentPolyglot() 76 | StringResUtils.POLYGLOT_YOUDAO -> YouDaoPolyglot() 77 | else -> BaiduPolyglot() 78 | } 79 | 80 | // @JvmStatic 81 | // fun main(args: Array) { 82 | // println("Baidu----------------") 83 | // val baidu = BaiduPolyglot() 84 | // println(baidu.translate(Lang.EN, Lang.ZH, "God, are you testing me?")) 85 | // println("Tencent----------------") 86 | // val tencent = TencentPolyglot() 87 | // println(tencent.translate(Lang.ZH, Lang.FRA, "德意志")) 88 | // println("YouDao----------------") 89 | // val youdao = YouDaoPolyglot() 90 | // println(youdao.translate(Lang.ZH, Lang.EN, "忧郁的小乌龟")) 91 | // println("Google----------------") 92 | // val google = GooglePolyglot() 93 | // println(google.translate(Lang.ZH, Lang.CHT, "台湾")) 94 | // println("Bing----------------") 95 | // val bing = BingPolyglot() 96 | // println(bing.translate(Lang.ZH, Lang.FRA, "忧郁的小乌龟")) 97 | // println("Sogou----------------") 98 | // val sogou = SogouPolyglot() 99 | // println(sogou.translate(Lang.ZH, Lang.CHT, "忧郁小乌龟")) 100 | //// println("trycan----------------") 101 | //// val trycan = TrycanPolyglot() 102 | //// println(trycan.execute(Lang.ZH, Lang.CHT, "忧郁的小乌龟")) 103 | // println("Omi----------------") 104 | // val omi = OmiPolyglot() 105 | // println(omi.translate(Lang.EN, Lang.ZH, "Blue turtle")) 106 | //// println("iciba----------------") 107 | //// val iciba = IcibaPolyglot() 108 | //// println(iciba.execute(Lang.ZH, Lang.EN, "好的")) 109 | // } 110 | } 111 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/PolyglotTranslator.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr 2 | 3 | import me.bytebeats.polyglot.util.StringResUtils 4 | 5 | /** 6 | * @author bytebeats 7 | * @email 8 | * @github https://github.com/bytebeats 9 | * @created on 2020/9/6 18:03 10 | * @version 1.0 11 | * @description TO-DO 12 | */ 13 | 14 | enum class PolyglotTranslator(val idx: String, val desc: String, val descEN: String) { 15 | Baidu("baidu", StringResUtils.POLYGLOT_BAIDU, StringResUtils.POLYGLOT_BAIDU_EN), 16 | Bing("bing", StringResUtils.POLYGLOT_BING, StringResUtils.POLYGLOT_BING_EN), 17 | Google("google", StringResUtils.POLYGLOT_GOOGLE, StringResUtils.POLYGLOT_GOOGLE_EN), 18 | Omi("omi", StringResUtils.POLYGLOT_OMI, StringResUtils.POLYGLOT_OMI_EN), 19 | Sogou("sogou", StringResUtils.POLYGLOT_SOGOU, StringResUtils.POLYGLOT_SOGOU_EN), 20 | Tencent("tencent", StringResUtils.POLYGLOT_TENCENT, StringResUtils.POLYGLOT_TENCENT_EN), 21 | Youdao("youdao", StringResUtils.POLYGLOT_YOUDAO, StringResUtils.POLYGLOT_YOUDAO_EN); 22 | 23 | companion object { 24 | fun from(desc: String): PolyglotTranslator { 25 | for (t in values()) { 26 | if (t.desc == desc || t.descEN == desc) return t 27 | } 28 | return Baidu 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/BaiduPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.http.PolyglotFormDataAdder 5 | import me.bytebeats.polyglot.lang.Lang 6 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 7 | import me.bytebeats.polyglot.util.GlotJsUtils 8 | import me.bytebeats.polyglot.util.LogUtils 9 | import me.bytebeats.polyglot.util.ParamUtils 10 | import org.apache.http.client.entity.UrlEncodedFormEntity 11 | import org.apache.http.client.methods.HttpPost 12 | import org.apache.http.util.EntityUtils 13 | import javax.script.Invocable 14 | import javax.script.ScriptEngineManager 15 | import javax.script.ScriptException 16 | 17 | /** 18 | * @author bytebeats 19 | * @email 20 | * @github https://github.com/bytebeats 21 | * @created on 2020/8/27 15:56 22 | * @version 1.0 23 | * @description BaiduPolyglot depends on baidu to offer translation service. 24 | */ 25 | 26 | class BaiduPolyglot() : AbstractPolyglot(URL) { 27 | companion object { 28 | private const val URL = "https://fanyi.baidu.com/v2transapi" 29 | private const val AUTO_DETECT_URL = "https://fanyi.baidu.com/langdetect" 30 | private const val COOKIE_VALUE = 31 | "BIDUPSID=FAAF6477F3E560AC94B0AADD45FB51E8; PSTM=1596595912; BAIDUID=FAAF6477F3E560ACC2CF11072FF242AC:FG=1; BDUSS=XhybkNqSjlUbnZmTTlxdGRVazZDdkVqMmpUeFlESWJTTmp6eUlpeEtIQU9yRkZmRVFBQUFBJCQAAAAAAAAAAAEAAADi0kwgcGNMaXR0bGVQYW4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4fKl8OHypfN; BDUSS_BFESS=XhybkNqSjlUbnZmTTlxdGRVazZDdkVqMmpUeFlESWJTTmp6eUlpeEtIQU9yRkZmRVFBQUFBJCQAAAAAAAAAAAEAAADi0kwgcGNMaXR0bGVQYW4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4fKl8OHypfN; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; MCITY=-131%3A; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0; delPer=0; PSINO=2; H_PS_PSSID=32662_1464_32533_32328_31254_32046_32679_32117_32092_32582; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1597741056,1598237654,1598348583,1598517252; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1598517252; __yjsv5_shitong=1.0_7_41feb6601e1a40dca04ed5516fdd1e3b4647_300_1598517252370_123.125.8.130_b60c7695; yjs_js_security_passport=6b0b4132f6ffb40f2bcbc632f9e8a52c94f473b4_1598517253_js" 32 | } 33 | 34 | private var isAutoDetected = false 35 | 36 | override fun addSupportedLanguages() { 37 | langs[Lang.AUTO] = "auto" 38 | langs[Lang.ZH] = "zh" 39 | langs[Lang.CHT] = "cht" 40 | langs[Lang.YUE] = "yue" 41 | langs[Lang.WYW] = "wyw" 42 | langs[Lang.EN] = "en" 43 | langs[Lang.JP] = "jp" 44 | langs[Lang.KOR] = "kor" 45 | langs[Lang.FRA] = "fra" 46 | langs[Lang.RU] = "ru" 47 | langs[Lang.DE] = "de" 48 | langs[Lang.SPA] = "spa" 49 | langs[Lang.PT] = "pt" 50 | langs[Lang.TR] = "tr" 51 | langs[Lang.IT] = "it" 52 | langs[Lang.ARA] = "ara" 53 | //dead languages or virtual languages 54 | langs[Lang.SAN] = "san" 55 | langs[Lang.ARG] = "arg" 56 | langs[Lang.GRA] = "gra" 57 | langs[Lang.KLI] = "kli" 58 | langs[Lang.HEB] = "heb" 59 | langs[Lang.ENO] = "eno" 60 | langs[Lang.FRM] = "frm" 61 | langs[Lang.PER] = "per" 62 | langs[Lang.LAT] = "lat" 63 | langs[Lang.DAN] = "dan" 64 | langs[Lang.PL] = "pl" 65 | langs[Lang.KK] = "kaz" 66 | langs[Lang.ICE] = "ice" 67 | langs[Lang.FIN] = "fin" 68 | langs[Lang.FIL] = "fil" 69 | langs[Lang.TAT] = "tat" 70 | langs[Lang.KUR] = "kur" 71 | } 72 | 73 | /** 74 | * {"trans_result":{"data":[{"dst":"\u4e0a\u5e1d\uff0c\u4f60\u5728\u8003\u9a8c\u6211\u5417\uff1f","prefixWrap":0,"result":[[0,"\u4e0a\u5e1d\uff0c\u4f60\u5728\u8003\u9a8c\u6211\u5417\uff1f",["0|24"],[],["0|24"],["0|30"]]],"src":"God, are you testing me?"}],"from":"en","status":0,"to":"zh","type":2,"phonetic":[{"src_str":"\u4e0a","trg_str":"sh\u00e0ng"},{"src_str":"\u5e1d","trg_str":"d\u00ec"},{"src_str":"\uff0c","trg_str":" "},{"src_str":"\u4f60","trg_str":"n\u01d0"},{"src_str":"\u5728","trg_str":"z\u00e0i"},{"src_str":"\u8003","trg_str":"k\u01ceo"},{"src_str":"\u9a8c","trg_str":"y\u00e0n"},{"src_str":"\u6211","trg_str":"w\u01d2"},{"src_str":"\u5417","trg_str":"ma"},{"src_str":"\uff1f","trg_str":" "}],"keywords":[{"means":["\u795e","\u4e0a\u5e1d","\u5929\u4e3b"],"word":"God"},{"means":["\u8bd5\u9a8c","\u6d4b\u8bd5","\u68c0\u67e5","\u68d8\u624b\u7684","\u4f24\u8111\u7b4b\u7684","\u96be\u5e94\u4ed8\u7684","\u6d4b\u9a8c","\u8003\u67e5","\u5316\u9a8c","\u68c0\u9a8c","test\u7684\u73b0\u5728\u5206\u8bcd"],"word":"testing"}]}, 75 | * "liju_result":{"double":"","single":""}, 76 | * "logid":2220363170} 77 | */ 78 | override fun parse(text: String): String { 79 | val mapper = ObjectMapper() 80 | val result = mapper.readTree(text).path("trans_result").findValuesAsText("dst") 81 | if (result != null) { 82 | val des = StringBuilder() 83 | for (s in result) { 84 | if (des.isNotEmpty()) { 85 | des.append("\n") 86 | } 87 | des.append(s) 88 | } 89 | return des.toString() 90 | } 91 | return "" 92 | } 93 | 94 | override fun query(): String { 95 | if (isAutoDetected) {//自动检测源语言 96 | val request = HttpPost(AUTO_DETECT_URL) 97 | val form = mutableMapOf() 98 | form["query"] = formData["query"]!! 99 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(form), "UTF-8") 100 | request.addHeader("Cookie", COOKIE_VALUE) 101 | request.addHeader("User-Agent", PolyglotFormDataAdder.USER_AGENT) 102 | val response = httpClient.execute(request) 103 | val entity = response.entity 104 | val entityStr = EntityUtils.toString(entity, "UTF-8") 105 | // println(entityStr) 106 | close(entity, response, false) 107 | val mapper = ObjectMapper() 108 | formData["from"] = mapper.readTree(entityStr).path("lan").asText() 109 | } 110 | val request = HttpPost(url) 111 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 112 | request.addHeader("Cookie", COOKIE_VALUE) 113 | request.addHeader("User-Agent", PolyglotFormDataAdder.USER_AGENT) 114 | val response = httpClient.execute(request) 115 | val entity = response.entity 116 | val result = EntityUtils.toString(entity, "UTF-8") 117 | // println(result) 118 | close(entity, response) 119 | return result 120 | } 121 | 122 | override fun addFormData(from: Lang, to: Lang, text: String) { 123 | if (from == Lang.AUTO) { 124 | isAutoDetected = true 125 | } else { 126 | isAutoDetected = false 127 | formData["from"] = langs[from]!! 128 | } 129 | formData["to"] = langs[to]!! 130 | formData["query"] = text 131 | formData["transtype"] = "translang" 132 | formData["simple_means_flag"] = "3" 133 | formData["sign"] = token(text, "320305.131321201") 134 | formData["token"] = "72f97fdf8a444e118884a466c86703d8" 135 | } 136 | 137 | private fun token(text: String, gtk: String): String { 138 | try { 139 | val engine = ScriptEngineManager().getEngineByName("js") 140 | val reader = GlotJsUtils.getReader(GlotJsUtils.JS_BAIDU) 141 | engine.eval(reader) 142 | if (engine is Invocable) { 143 | return engine.invokeFunction("token", text, gtk).toString() 144 | } 145 | } catch (e: ScriptException) { 146 | LogUtils.info(e.message) 147 | } catch (e: NoSuchMethodException) { 148 | LogUtils.info(e.message) 149 | } 150 | return "" 151 | } 152 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/BingPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.lang.Lang 5 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 6 | import me.bytebeats.polyglot.util.ParamUtils 7 | import org.apache.http.client.entity.UrlEncodedFormEntity 8 | import org.apache.http.client.methods.HttpPost 9 | import org.apache.http.client.utils.URIBuilder 10 | import org.apache.http.util.EntityUtils 11 | 12 | /** 13 | * @author bytebeats 14 | * @email 15 | * @github https://github.com/bytebeats 16 | * @created on 2020/8/30 14:48 17 | * @version 1.0 18 | * @description BingPolyglot depends on Bing-cn to offer translation service 19 | */ 20 | 21 | class BingPolyglot() : AbstractPolyglot(URL) { 22 | companion object { 23 | private const val URL = 24 | "https://cn.bing.com/ttranslatev3" 25 | } 26 | 27 | override fun addSupportedLanguages() { 28 | langs[Lang.AUTO] = "auto-detect" 29 | langs[Lang.ZH] = "zh-Hans" 30 | langs[Lang.CHT] = "zh-Hant" 31 | langs[Lang.EN] = "en" 32 | langs[Lang.JP] = "ja" 33 | langs[Lang.KOR] = "ko" 34 | langs[Lang.FRA] = "fr" 35 | langs[Lang.RU] = "ru" 36 | langs[Lang.DE] = "de" 37 | langs[Lang.SPA] = "es" 38 | langs[Lang.IT] = "it" 39 | langs[Lang.VIE] = "vi" 40 | langs[Lang.TH] = "th" 41 | langs[Lang.ARA] = "ar" 42 | } 43 | 44 | /** 45 | * [{"detectedLanguage":{"language":"zh-Hans","score":1.0},"translations":[{"text":"台湾。","to":"ja","sentLen":{"srcSentLen":[2],"transSentLen":[3]}}]}] 46 | */ 47 | override fun parse(text: String): String { 48 | val mapper = ObjectMapper() 49 | val translations = mapper.readTree(text)[0].path("translations")[0] 50 | val result = StringBuilder() 51 | for (i in 0 until translations.size()) { 52 | result.append(translations[i].path("text").asText()) 53 | if (i != translations.size() - 1) { 54 | result.append(";") 55 | } 56 | } 57 | return result.toString() 58 | } 59 | 60 | override fun query(): String { 61 | val uri = URIBuilder(url) 62 | uri.addParameter("isVertical", "1") 63 | uri.addParameter("IG", "1D91F775B11C43EC843BB93AC6D98BBC") 64 | uri.addParameter("IID", "translator.5028.10") 65 | 66 | val request = HttpPost(uri.toString()) 67 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 68 | val response = httpClient.execute(request) 69 | val entity = response.entity 70 | val result = EntityUtils.toString(entity, "UTF-8") 71 | close(entity, response) 72 | return result 73 | } 74 | 75 | override fun addFormData(from: Lang, to: Lang, text: String) { 76 | formData["fromLang"] = langs[from]!! 77 | formData["to"] = langs[to]!! 78 | formData["text"] = text 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/GooglePolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.lang.Lang 5 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 6 | import me.bytebeats.polyglot.ui.PolyglotSettingState 7 | import me.bytebeats.polyglot.util.GlotJsUtils 8 | import me.bytebeats.polyglot.util.LogUtils 9 | import org.apache.http.client.methods.HttpGet 10 | import org.apache.http.client.utils.URIBuilder 11 | import org.apache.http.util.EntityUtils 12 | import javax.script.Invocable 13 | import javax.script.ScriptEngineManager 14 | 15 | /** 16 | * @author bytebeats 17 | * @email 18 | * @github https://github.com/bytebeats 19 | * @created on 2020/8/30 00:18 20 | * @version 1.0 21 | * @description GooglePolyglot depends on Google-translate-cn to offer translation service 22 | */ 23 | 24 | class GooglePolyglot() : AbstractPolyglot(URL_CN) { 25 | companion object { 26 | private const val URL_CN = "https://translate.google.cn/translate_a/single" 27 | private const val URL = "https://translate.google.com/translate_a/single" 28 | } 29 | 30 | override fun addSupportedLanguages() { 31 | langs[Lang.AUTO] = "auto" 32 | langs[Lang.ZH] = "zh-CN" 33 | langs[Lang.CHT] = "zh-TW" 34 | langs[Lang.EN] = "en" 35 | langs[Lang.JP] = "ja" 36 | langs[Lang.KOR] = "ko" 37 | langs[Lang.FRA] = "fr" 38 | langs[Lang.RU] = "ru" 39 | langs[Lang.DE] = "de" 40 | langs[Lang.SPA] = "es" 41 | langs[Lang.IT] = "it" 42 | langs[Lang.VIE] = "vi" 43 | langs[Lang.TH] = "th" 44 | langs[Lang.ARA] = "ar" 45 | } 46 | 47 | override fun parse(text: String): String { 48 | val mapper = ObjectMapper() 49 | val parent = mapper.readTree(text)[0] 50 | val dst = StringBuilder() 51 | for (i in 0 until parent.size()) { 52 | dst.append(parent[i][0].asText()) 53 | if (i != parent.size() - 1) { 54 | dst.append(";") 55 | } 56 | } 57 | return dst.toString() 58 | } 59 | 60 | override val url: String 61 | get() = if (PolyglotSettingState.getInstance().isCnPreferred) { 62 | URL_CN 63 | } else { 64 | URL 65 | } 66 | 67 | override fun query(): String { 68 | val uri = URIBuilder(url) 69 | for ((k, v) in formData) { 70 | uri.addParameter(k, v) 71 | } 72 | 73 | val request = HttpGet(uri.toString()) 74 | val response = httpClient.execute(request) 75 | val entity = response.entity 76 | val result = EntityUtils.toString(entity, "UTF-8") 77 | close(entity, response) 78 | return result 79 | } 80 | 81 | override fun addFormData(from: Lang, to: Lang, text: String) { 82 | formData["client"] = "t" 83 | formData["sl"] = langs[from]!! 84 | formData["tl"] = langs[to]!! 85 | formData["q"] = text 86 | formData["tk"] = token(text) 87 | formData["hl"] = "zh-CN" 88 | formData["dt"] = "at" 89 | formData["dt"] = "bd" 90 | formData["dt"] = "ld" 91 | formData["dt"] = "md" 92 | formData["dt"] = "qca" 93 | formData["dt"] = "rw" 94 | formData["dt"] = "rm" 95 | formData["dt"] = "ss" 96 | formData["dt"] = "t" 97 | formData["ie"] = "UTF-8" 98 | formData["oe"] = "UTF-8" 99 | formData["source"] = "btn" 100 | formData["sssel"] = "0" 101 | formData["tssel"] = "0" 102 | formData["kc"] = "0" 103 | } 104 | 105 | private fun token(text: String): String { 106 | try { 107 | val engine = ScriptEngineManager().getEngineByName("js") 108 | val reader = GlotJsUtils.getReader(GlotJsUtils.JS_GOOGLE) 109 | engine.eval(reader) 110 | if (engine is Invocable) { 111 | return engine.invokeFunction("token", text).toString() 112 | } 113 | } catch (e: Exception) { 114 | LogUtils.info(e.message) 115 | } 116 | return "" 117 | } 118 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/IcibaPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.http.PolyglotFormDataAdder 5 | import me.bytebeats.polyglot.lang.Lang 6 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 7 | import me.bytebeats.polyglot.util.ParamUtils 8 | import org.apache.http.client.entity.UrlEncodedFormEntity 9 | import org.apache.http.client.methods.HttpPost 10 | import org.apache.http.util.EntityUtils 11 | 12 | /** 13 | * @author bytebeats 14 | * @email 15 | * @github https://github.com/bytebeats 16 | * @created on 2020/9/3 18:23 17 | * @version 1.0 18 | * @description IcibaPolyglot depends on iciba to offer translation service. 金山词霸 19 | */ 20 | 21 | @Deprecated( 22 | message = "sign的算法没有破解成功", 23 | replaceWith = ReplaceWith("IcibaPolyglot", "null"), 24 | level = DeprecationLevel.HIDDEN 25 | ) 26 | class IcibaPolyglot() : AbstractPolyglot(URL) { 27 | companion object { 28 | // private const val URL = "http://www.iciba.com/fy" 29 | private const val URL = "https://ifanyi.iciba.com/index.php" 30 | } 31 | 32 | private val queries = mutableMapOf() 33 | 34 | override fun addSupportedLanguages() { 35 | langs[Lang.ZH] = "zh" 36 | langs[Lang.EN] = "en" 37 | langs[Lang.JP] = "ja" 38 | langs[Lang.KOR] = "ko" 39 | langs[Lang.FRA] = "fr" 40 | langs[Lang.DE] = "de" 41 | langs[Lang.CHT] = "cht" 42 | langs[Lang.RU] = "ru" 43 | langs[Lang.DE] = "de" 44 | langs[Lang.SPA] = "es" 45 | langs[Lang.PT] = "pt" 46 | langs[Lang.TR] = "tr" 47 | langs[Lang.IT] = "it" 48 | } 49 | 50 | /** 51 | * { 52 | "status": 1, 53 | "content": { 54 | "from": "zh", 55 | "to": "es", 56 | "vendor": "ciba", 57 | "out": "Traducción", 58 | "ciba_use": "来自机器翻译。", 59 | "ciba_out": "", 60 | "err_no": 0 61 | } 62 | } 63 | * 64 | */ 65 | override fun parse(text: String): String { 66 | val mapper = ObjectMapper() 67 | return mapper.readTree(text).path("content").path("out").asText() 68 | } 69 | 70 | override fun query(): String { 71 | // println(ParamUtils.concatUrl(url, queries)) 72 | val request = HttpPost(ParamUtils.concatUrl(url, queries)) 73 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 74 | request.setHeader("Content-Type", "application/x-www-form-urlencoded") 75 | request.setHeader("Host", "ifanyi.iciba.com") 76 | request.setHeader("Origin", "http://www.iciba.com") 77 | request.addHeader("Referer", "http://www.iciba.com/fy") 78 | request.addHeader("User-Agent", PolyglotFormDataAdder.USER_AGENT) 79 | val response = httpClient.execute(request) 80 | val entity = response.entity 81 | val result = EntityUtils.toString(entity, "utf-8") 82 | // println(response) 83 | close(entity, response) 84 | return result 85 | } 86 | 87 | override fun addFormData(from: Lang, to: Lang, text: String) { 88 | formData["from"] = langs[from]!! 89 | formData["to"] = langs[to]!! 90 | formData["q"] = text.trim() 91 | queries["c"] = "trans" 92 | queries["m"] = "fy" 93 | queries["clint"] = "6" 94 | queries["auth_user"] = "key_ciba" 95 | queries["sign"] = ParamUtils.md5( 96 | "6key_cibaifanyicjbysdlove1${ 97 | formData.map { 98 | "${ParamUtils.encodeUtf8(it.key)}=${ 99 | ParamUtils.encodeUtf8(it.value) 100 | }" 101 | }.joinToString("&") 102 | }" 103 | )!!.substring(0, 16) 104 | } 105 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/OmiPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.lang.Lang 5 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 6 | import me.bytebeats.polyglot.util.ParamUtils 7 | import org.apache.http.client.entity.UrlEncodedFormEntity 8 | import org.apache.http.client.methods.HttpPost 9 | import org.apache.http.util.EntityUtils 10 | 11 | /** 12 | * @author bytebeats 13 | * @email 14 | * @github https://github.com/bytebeats 15 | * @created on 2020/9/3 18:09 16 | * @version 1.0 17 | * @description OmiPolyglot depends on Omi to offer translation service 18 | */ 19 | 20 | class OmiPolyglot() : AbstractPolyglot(URL) { 21 | companion object { 22 | private const val URL = "https://www.omifanyi.com/transsents.do" 23 | } 24 | 25 | override fun addSupportedLanguages() { 26 | langs[Lang.AUTO] = "undef" 27 | langs[Lang.ZH] = "c" 28 | langs[Lang.EN] = "e" 29 | } 30 | 31 | /** 32 | * 33 | * { 34 | "languageType": "c2e", 35 | "sentsToTrans": "好\n的\n吧", 36 | "wordToTrans": "", 37 | "srcLength": 5, 38 | "srcTransLength": 5, 39 | "unTransStartChar": "", 40 | "resType": "1", 41 | "sentsResults": [ 42 | [ 43 | "好", 44 | "\n", 45 | "的", 46 | "\n", 47 | "吧" 48 | ], 49 | [ 50 | "All right", 51 | "", 52 | "Of", 53 | "", 54 | "Come on" 55 | ], 56 | [ 57 | "TEXTCONTENT", 58 | "SPECIALSYMBOL", 59 | "TEXTCONTENT", 60 | "SPECIALSYMBOL", 61 | "TEXTCONTENT" 62 | ] 63 | ], 64 | "sentExample": [], 65 | "doYouMean": "", 66 | "userDbName": "", 67 | "success": true, 68 | "errorInfo": "", 69 | "servletWrapper": {} 70 | } 71 | * 72 | */ 73 | override fun parse(text: String): String { 74 | val mapper = ObjectMapper() 75 | val sentsResults = mapper.readTree(text).path("sentsResults") 76 | val dst = StringBuilder() 77 | val srcs = sentsResults[0] 78 | val tgts = sentsResults[1] 79 | val types = sentsResults[2] 80 | for (i in 0 until types.size()) { 81 | if (types[i].asText() == "TEXTCONTENT") { 82 | dst.append(tgts[i].asText()) 83 | } else { 84 | dst.append(srcs[i].asText()) 85 | } 86 | } 87 | return dst.toString() 88 | } 89 | 90 | override fun query(): String { 91 | val request = HttpPost(url) 92 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 93 | val response = httpClient.execute(request) 94 | val entity = response.entity 95 | val result = EntityUtils.toString(entity, "utf-8") 96 | close(entity, response) 97 | return result 98 | } 99 | 100 | override fun addFormData(from: Lang, to: Lang, text: String) { 101 | if (from == Lang.AUTO) { 102 | formData["languageType"] = "${langs[from]}" 103 | } else { 104 | formData["languageType"] = "${langs[from]}2${langs[to]}" 105 | } 106 | formData["sentsToTrans"] = text.trim() 107 | } 108 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/SogouPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.http.PolyglotFormDataAdder 5 | import me.bytebeats.polyglot.lang.Lang 6 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 7 | import me.bytebeats.polyglot.util.GlotJsUtils 8 | import me.bytebeats.polyglot.util.LogUtils 9 | import me.bytebeats.polyglot.util.ParamUtils 10 | import org.apache.http.client.entity.UrlEncodedFormEntity 11 | import org.apache.http.client.methods.HttpPost 12 | import org.apache.http.util.EntityUtils 13 | import javax.script.Invocable 14 | import javax.script.ScriptEngineManager 15 | 16 | /** 17 | * @author bytebeats 18 | * @email 19 | * @github https://github.com/bytebeats 20 | * @created on 2020/8/30 17:30 21 | * @version 1.0 22 | * @description TO-DO 23 | */ 24 | 25 | class SogouPolyglot() : AbstractPolyglot(URL) { 26 | companion object { 27 | private const val URL = "https://fanyi.sogou.com/reventondc/translateV2" 28 | } 29 | 30 | override fun addSupportedLanguages() { 31 | langs[Lang.AUTO] = "auto" 32 | langs[Lang.ZH] = "zh-CHS" 33 | langs[Lang.CHT] = "zh-CHT" 34 | langs[Lang.EN] = "en" 35 | langs[Lang.JP] = "ja" 36 | langs[Lang.KOR] = "ko" 37 | langs[Lang.FRA] = "fr" 38 | langs[Lang.RU] = "ru" 39 | langs[Lang.DE] = "de" 40 | langs[Lang.SPA] = "es" 41 | langs[Lang.IT] = "it" 42 | langs[Lang.VIE] = "vi" 43 | langs[Lang.TH] = "th" 44 | langs[Lang.ARA] = "ar" 45 | } 46 | 47 | override fun parse(text: String): String { 48 | val mapper = ObjectMapper() 49 | val result = mapper.readTree(text).path("data").path("translate").path("dit").asText() 50 | return result 51 | } 52 | 53 | /** 54 | * { 55 | "data": { 56 | "detect": { 57 | "zly": "zly", 58 | "detect": "zh-CHS", 59 | "errorCode": "0", 60 | "language": "中文", 61 | "id": "a0761c8f8d35a43b130976bb5718504c", 62 | "text": "忧郁的小乌龟" 63 | }, 64 | "translate": { 65 | "qc_type": "1", 66 | "zly": "zly", 67 | "errorCode": "10", 68 | "index": "content0", 69 | "from": "zh-CHS", 70 | "source": "sogou", 71 | "text": "忧郁的小乌龟", 72 | "to": "en", 73 | "id": "a0761c8f8d35a43b130976bb5718504c", 74 | "dit": "憂鬱的小烏龜", 75 | "orig_text": "忧郁的小乌龟", 76 | "md5": "" 77 | } 78 | }, 79 | "status": "0", 80 | "info": "success", 81 | "zly": "zly" 82 | } 83 | */ 84 | override fun query(): String { 85 | val request = HttpPost(url) 86 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 87 | // val request = HttpPost(ParamUtils.concatUrl(url, formData)) 88 | request.setHeader("Host", "fanyi.sogou.com") 89 | request.setHeader("Origin", "https://fanyi.sogou.com") 90 | //Referer really mattered. It's very important 91 | request.setHeader( 92 | "Referer", 93 | "https://fanyi.sogou.com/?keyword=${formData["text"]}&transfrom=${formData["from"]}&transto=${formData["to"]}" 94 | ) 95 | //It's very important 96 | request.setHeader("User-Agent", PolyglotFormDataAdder.USER_AGENT) 97 | //It's very important 98 | request.setHeader( 99 | "Cookie", 100 | "ssuid=3013214756; sw_uuid=6726719957; SUID=82087D7B6B20A00A000000005F44DC76; ABTEST=0|1598779621|v17; SUV=1598779628713; SNUID=296DBEA4DFDA71CB3BE2153BDF9B2F65; SGINPUT_UPSCREEN=1598779644157; usid=0-sIGlnrPalPfwS2; IPLOC=CN3201" 101 | ) 102 | val response = httpClient.execute(request) 103 | val entity = response.entity 104 | val result = EntityUtils.toString(entity, "UTF-8") 105 | // println(result) 106 | close(entity, response) 107 | return result 108 | } 109 | 110 | override fun addFormData(from: Lang, to: Lang, text: String) { 111 | formData["from"] = langs[from]!! 112 | formData["to"] = langs[to]!! 113 | formData["text"] = text.trim() 114 | formData["client"] = "pc" 115 | formData["fr"] = "browser_pc" 116 | formData["useDetect"] = "on" 117 | // formData["useDetectResult"] = if (from == Lang.AUTO) "on" else "off" 118 | formData["needQc"] = "1" 119 | formData["uuid"] = token() 120 | formData["oxford"] = "on" 121 | formData["word_group"] = "true" 122 | formData["second_query"] = "true" 123 | formData["dict"] = "true" 124 | formData["pid"] = "sogou-dict-vr" 125 | // formData["isReturnSugg"] = "on" 126 | formData["s"] = ParamUtils.md5("${langs[from]!!}${langs[to]!!}${text.trim()}8511813095152")!! 127 | } 128 | 129 | private fun token(): String { 130 | try { 131 | val engine = ScriptEngineManager().getEngineByName("js") 132 | val reader = GlotJsUtils.getReader(GlotJsUtils.JS_SOGOU) 133 | engine.eval(reader) 134 | if (engine is Invocable) { 135 | return engine.invokeFunction("token").toString() 136 | } 137 | } catch (e: Exception) { 138 | LogUtils.info(e.message) 139 | } 140 | return "" 141 | } 142 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/TencentPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.lang.Lang 5 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 6 | import me.bytebeats.polyglot.util.ParamUtils 7 | import org.apache.http.client.entity.UrlEncodedFormEntity 8 | import org.apache.http.client.methods.HttpPost 9 | import org.apache.http.util.EntityUtils 10 | 11 | /** 12 | * @author bytebeats 13 | * @email 14 | * @github https://github.com/bytebeats 15 | * @created on 2020/8/29 22:39 16 | * @version 1.0 17 | * @description TencentPolyglot depends on Tencent/QQ to offer translation service 18 | */ 19 | 20 | class TencentPolyglot() : AbstractPolyglot(URL) { 21 | companion object { 22 | private const val URL = "https://fanyi.qq.com/api/translate" 23 | } 24 | 25 | override fun addSupportedLanguages() { 26 | langs[Lang.AUTO] = "auto" 27 | langs[Lang.ZH] = "zh" 28 | langs[Lang.EN] = "en" 29 | langs[Lang.JP] = "jp" 30 | langs[Lang.KOR] = "kr" 31 | langs[Lang.FRA] = "fr" 32 | langs[Lang.RU] = "ru" 33 | langs[Lang.DE] = "de" 34 | langs[Lang.SPA] = "es" 35 | langs[Lang.IT] = "it" 36 | langs[Lang.TR] = "tr" 37 | langs[Lang.PT] = "pt" 38 | langs[Lang.VIE] = "vi" 39 | langs[Lang.ID] = "id" 40 | langs[Lang.TH] = "th" 41 | langs[Lang.MS] = "ms" 42 | langs[Lang.HI] = "hi" 43 | } 44 | 45 | /** 46 | * 47 | * { 48 | "sessionUuid": "translate_uuid1599536278882", 49 | "translate": { 50 | "errCode": 0, 51 | "errMsg": "", 52 | "sessionUuid": "translate_uuid1599536278882", 53 | "source": "zh", 54 | "target": "en", 55 | "records": [ 56 | { 57 | "sourceText": "好!", 58 | "targetText": "good! ", 59 | "traceId": "5d972ecc338f4f4c9a73e9a9118deb65" 60 | }, 61 | { 62 | "sourceText": "\n", 63 | "targetText": "\n ", 64 | "traceId": "5d972ecc338f4f4c9a73e9a9118deb65" 65 | }, 66 | { 67 | "sourceText": "的?", 68 | "targetText": "What? ", 69 | "traceId": "5d972ecc338f4f4c9a73e9a9118deb65" 70 | }, 71 | { 72 | "sourceText": "\n", 73 | "targetText": "\n ", 74 | "traceId": "5d972ecc338f4f4c9a73e9a9118deb65" 75 | }, 76 | { 77 | "sourceText": "吧%", 78 | "targetText": "Bar%", 79 | "traceId": "5d972ecc338f4f4c9a73e9a9118deb65" 80 | } 81 | ], 82 | "full": true, 83 | "options": {} 84 | }, 85 | "dict": { 86 | "data": [ 87 | { 88 | "en_hash": "44d35c0d5b0bb0252821f55f432a3c59", 89 | "word": "good" 90 | }, 91 | { 92 | "en_hash": "b08b8364c97fc0e1e27612765140d8d3", 93 | "word": "What" 94 | }, 95 | { 96 | "word": "Bar%" 97 | } 98 | ], 99 | "errCode": 0, 100 | "errMsg": "", 101 | "type": "important words", 102 | "map": { 103 | "good": { 104 | "detailId": "44d35c0d5b0bb0252821f55f432a3c59" 105 | }, 106 | "What": { 107 | "detailId": "b08b8364c97fc0e1e27612765140d8d3" 108 | }, 109 | "Bar%": {} 110 | } 111 | }, 112 | "errCode": 0, 113 | "errMsg": "ok" 114 | } 115 | * 116 | */ 117 | override fun parse(text: String): String { 118 | val mapper = ObjectMapper() 119 | val records = mapper.readTree(text).path("translate").path("records") 120 | if (records.isEmpty) { 121 | return "" 122 | } 123 | val dst = StringBuilder() 124 | for (i in 0 until records.size()) { 125 | dst.append(records[0].path("targetText").asText()) 126 | if (i != records.size() - 1) { 127 | dst.append(";") 128 | } 129 | } 130 | return dst.toString() 131 | } 132 | 133 | override fun query(): String { 134 | val request = HttpPost(url) 135 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 136 | 137 | request.setHeader( 138 | "Cookie", 139 | "pgv_pvi=1705084928; ptui_loginuin=894072484; RK=mAZRrIPzYv; ptcz=8b45af582e9364916ec12acb830387b73c2d4c9bb6272bdf710ead1d36582fb0; pgv_pvid=298308139; tvfe_boss_uuid=130f8de49cadf041; fy_guid=5034187a-2473-4826-b642-7f98189285f5; ADHOC_MEMBERSHIP_CLIENT_ID1.0=a3427cb4-7fcc-4780-d795-19367eb07ba8; qtv=1a2fdfe6b95fe470; qtk=q7HYYVBOT2nCyJYCAl7PMDfA7OREbOjwX/+sS0uPR15/1WL70JPZzjoaY01WFs2JcTvQBYgafngxsIp7pYsTCM8HfpEFVaEh/4CfYWCFIJk/PQOEngkKMyWEj+XVUrCEVsb67qFT85YD6/OWh71w2w==; openCount=1; gr_user_id=ca0d5edc-7bc0-443b-87b6-65c86398475a; 8507d3409e6fad23_gr_session_id=627af602-9933-494c-9bf5-82aeb077142e; grwng_uid=ec18381f-451e-4b36-acc2-f17ae5c9869e; 8c66aca9f0d1ff2e_gr_session_id=755231cc-f017-44af-bb8e-d605457e4cbb; 8c66aca9f0d1ff2e_gr_session_id_755231cc-f017-44af-bb8e-d605457e4cbb=true" 140 | ) 141 | request.setHeader("Origin", "https://fanyi.qq.com") 142 | 143 | val response = httpClient.execute(request) 144 | val entity = response.entity 145 | val result = EntityUtils.toString(entity, "UTF-8") 146 | // println(result) 147 | close(entity, response) 148 | return result 149 | } 150 | 151 | override fun addFormData(from: Lang, to: Lang, text: String) { 152 | formData["source"] = langs[from]!! 153 | formData["target"] = langs[to]!! 154 | formData["sourceText"] = text 155 | formData["sessionUuid"] = "translate_uuid${System.currentTimeMillis()}" 156 | } 157 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/TrycanPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.lang.Lang 5 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 6 | import me.bytebeats.polyglot.util.ParamUtils 7 | import org.apache.http.client.entity.UrlEncodedFormEntity 8 | import org.apache.http.client.methods.HttpPost 9 | import org.apache.http.util.EntityUtils 10 | 11 | /** 12 | * @author bytebeats 13 | * @email 14 | * @github https://github.com/bytebeats 15 | * @created on 2020/8/30 19:09 16 | * @version 1.0 17 | * @description TrycanPolyglot depends on trycan to offer translation service 18 | */ 19 | 20 | @Deprecated( 21 | message = "Trycan url is deprecated and never responds", 22 | replaceWith = ReplaceWith("TrycanPolyglot", "null"), 23 | level = DeprecationLevel.HIDDEN 24 | ) 25 | class TrycanPolyglot() : AbstractPolyglot(URL) { 26 | companion object { 27 | private const val URL = "http://fanyi.trycan.com/Transfer.do" 28 | } 29 | 30 | override fun addSupportedLanguages() { 31 | langs[Lang.ZH] = "zh" 32 | langs[Lang.CHT] = "cht" 33 | langs[Lang.EN] = "en" 34 | } 35 | 36 | override fun parse(text: String): String { 37 | val mapper = ObjectMapper() 38 | val result = mapper.readTree(text).path("data").findValuesAsText("dst") 39 | if (result.isNullOrEmpty()) { 40 | return "" 41 | } 42 | val dst = StringBuilder() 43 | for (s in result) { 44 | if (dst.isNotEmpty()) { 45 | dst.append("\n") 46 | } 47 | dst.append(s) 48 | } 49 | return dst.toString() 50 | } 51 | 52 | override fun query(): String { 53 | val request = HttpPost(url) 54 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 55 | // val request = HttpPost(ParamUtils.concatUrl(url, formData)) 56 | val response = httpClient.execute(request) 57 | val entity = response.entity 58 | val result = EntityUtils.toString(entity, "UTF-8") 59 | // println(result) 60 | close(entity, response) 61 | return result 62 | } 63 | 64 | override fun addFormData(from: Lang, to: Lang, text: String) { 65 | formData["word_from"] = langs[from]!! 66 | formData["word_to"] = langs[to]!! 67 | formData["word_src"] = text 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/tlr/impl/YouDaoPolyglot.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.tlr.impl 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import me.bytebeats.polyglot.http.PolyglotFormDataAdder 5 | import me.bytebeats.polyglot.lang.Lang 6 | import me.bytebeats.polyglot.tlr.AbstractPolyglot 7 | import me.bytebeats.polyglot.util.LogUtils 8 | import me.bytebeats.polyglot.util.ParamUtils 9 | import org.apache.http.client.entity.UrlEncodedFormEntity 10 | import org.apache.http.client.methods.HttpPost 11 | import org.apache.http.util.EntityUtils 12 | 13 | /** 14 | * @author bytebeats 15 | * @email 16 | * @github https://github.com/bytebeats 17 | * @created on 2020/8/29 18:14 18 | * @version 1.0 19 | * @description YouDaoPolyglot depends on YouDao to offer translation service. 20 | */ 21 | 22 | class YouDaoPolyglot() : AbstractPolyglot(URL) { 23 | companion object { 24 | private const val URL = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule" 25 | } 26 | 27 | override fun addSupportedLanguages() { 28 | langs[Lang.AUTO] = "AUTO" 29 | langs[Lang.ZH] = "zh-CHS" 30 | langs[Lang.EN] = "en" 31 | langs[Lang.JP] = "ja" 32 | langs[Lang.KOR] = "ko" 33 | langs[Lang.FRA] = "fr" 34 | langs[Lang.RU] = "ru" 35 | langs[Lang.CHT] = "zh-CHT" 36 | langs[Lang.DE] = "de" 37 | langs[Lang.SPA] = "es" 38 | langs[Lang.IT] = "it" 39 | langs[Lang.PT] = "pt" 40 | langs[Lang.VIE] = "vi" 41 | langs[Lang.ID] = "id" 42 | langs[Lang.TH] = "th" 43 | langs[Lang.ARA] = "ar" 44 | langs[Lang.NL] = "nl" 45 | } 46 | 47 | /** 48 | * 49 | * { 50 | "translateResult": [ 51 | [ 52 | { 53 | "tgt": "Good!", 54 | "src": "好!" 55 | } 56 | ], 57 | [ 58 | { 59 | "tgt": "The #", 60 | "src": "的#" 61 | } 62 | ], 63 | [ 64 | { 65 | "tgt": "?", 66 | "src": "吧?" 67 | } 68 | ] 69 | ], 70 | "errorCode": 0, 71 | "type": "zh-CHS2en" 72 | } 73 | * 74 | */ 75 | override fun parse(text: String): String { 76 | val mapper = ObjectMapper() 77 | val tree = mapper.readTree(text) 78 | val translations = tree.path("translateResult") 79 | val dst = StringBuilder() 80 | if (!translations.isEmpty) { 81 | for (i in 0 until translations.size()) { 82 | val node = translations[i] 83 | if (node.isEmpty) continue 84 | if (dst.isNotEmpty()) { 85 | dst.append("\n") 86 | } 87 | dst.append(node[0].path("tgt").asText()) 88 | } 89 | } 90 | val entries = tree.path("smartResult").path("entries") 91 | if (!entries.isEmpty) { 92 | dst.append("\nMore:\n") 93 | for (node in entries) { 94 | if (!node.asText().isNullOrEmpty()) { 95 | dst.append(node.asText()) 96 | } 97 | } 98 | } 99 | return dst.toString() 100 | } 101 | 102 | override fun query(): String { 103 | val request = HttpPost(url) 104 | request.entity = UrlEncodedFormEntity(ParamUtils.map2List(formData), "UTF-8") 105 | request.addHeader( 106 | "Cookie", 107 | "OUTFOX_SEARCH_USER_ID=1541101350@10.108.160.105; JSESSIONID=aaaRkwXJWDDOnaNJOQ5qx; OUTFOX_SEARCH_USER_ID_NCOO=1678950924.2441375; ___rl__test__cookies=${System.currentTimeMillis()}" 108 | ) 109 | request.setHeader("Host", "fanyi.youdao.com") 110 | request.setHeader("Origin", "http://fanyi.youdao.com") 111 | request.addHeader("Referer", "http://fanyi.youdao.com/") 112 | request.addHeader("User-Agent", PolyglotFormDataAdder.USER_AGENT) 113 | 114 | val response = httpClient.execute(request) 115 | val entity = response.entity 116 | val result = EntityUtils.toString(entity, "UTF-8") 117 | // println(result) 118 | LogUtils.info(result) 119 | close(entity, response) 120 | return result 121 | } 122 | 123 | override fun addFormData(from: Lang, to: Lang, text: String) { 124 | val ts = System.currentTimeMillis().toString()//timestamp 125 | val salt = "$ts${(10 * Math.random()).toInt()}" 126 | val client = "fanyideskweb" 127 | formData["i"] = text 128 | formData["from"] = langs[from]!! 129 | formData["to"] = langs[to]!! 130 | formData["client"] = client 131 | formData["salt"] = salt//salt 132 | formData["sign"] = ParamUtils.md5("${client}${text}${salt}]BjuETDhU)zqSxf-=B#7m") ?: ""//sign 133 | formData["lts"] = ts//ts 134 | formData["bv"] = 135 | ParamUtils.md5(PolyglotFormDataAdder.USER_AGENT.substring(PolyglotFormDataAdder.USER_AGENT.indexOf('/') + 1))!!//bv 136 | formData["doctype"] = "json" 137 | formData["version"] = "2.1" 138 | formData["keyfrom"] = "fanyi.web" 139 | formData["action"] = "FY_BY_CLICKBUTTON" 140 | formData["smartresult"] = "dict" 141 | formData["typoResult"] = "false" 142 | } 143 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/ui/PolyglotPreferencesWindow.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |
147 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/ui/PolyglotPreferencesWindow.java: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.ui; 2 | 3 | import com.intellij.openapi.options.Configurable; 4 | import com.intellij.openapi.options.ConfigurationException; 5 | import com.intellij.openapi.util.NlsContexts; 6 | import me.bytebeats.polyglot.dq.DailyQuoter; 7 | import me.bytebeats.polyglot.lang.Lang; 8 | import me.bytebeats.polyglot.ui.swing.JHintTextField; 9 | import me.bytebeats.polyglot.util.PolyglotUtils; 10 | import me.bytebeats.polyglot.util.StringResUtils; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import javax.swing.*; 14 | import java.awt.event.ItemEvent; 15 | 16 | /** 17 | * @Author bytebeats 18 | * @Email 19 | * @Github https://github.com/bytebeats 20 | * @Created on 2020/10/31 16:23 21 | * @Version 1.0 22 | * @Description Setting screen 23 | */ 24 | 25 | public class PolyglotPreferencesWindow implements Configurable { 26 | private JPanel polyglot_pref_panel; 27 | private JCheckBox daily_quote_switch; 28 | private JComboBox daily_quote_providers; 29 | private JComboBox preferred_lang_provider; 30 | private JRadioButton dict_you_dao_rb; 31 | private JTextField dict_app_id; 32 | private JTextField dict_app_key; 33 | private JLabel preferred_lang; 34 | private JCheckBox consult_dict_cb; 35 | private JLabel dict_app_id_label; 36 | private JLabel dict_app_key_label; 37 | private JComboBox dict_target_cb; 38 | private JLabel dict_target_lang; 39 | 40 | @Override 41 | public @NlsContexts.ConfigurableName String getDisplayName() { 42 | return polyglot_pref_panel.getToolTipText(); 43 | } 44 | 45 | @Override 46 | public @Nullable 47 | JComponent createComponent() { 48 | inflateDailyQuoterProviders(); 49 | daily_quote_providers.addItemListener(e -> { 50 | if (e.getStateChange() == ItemEvent.SELECTED) { 51 | PolyglotSettingState.getInstance().setDailyQuoter(daily_quote_providers.getSelectedItem().toString()); 52 | } 53 | }); 54 | inflateDictionaryTargetLangs(); 55 | dict_target_cb.addItemListener(e -> { 56 | if (e.getStateChange() == ItemEvent.SELECTED) { 57 | PolyglotSettingState.getInstance().setDictLang(dict_target_cb.getSelectedItem().toString()); 58 | } 59 | }); 60 | preferred_lang_provider.addItem(StringResUtils.PREFERRED_LANG_EN); 61 | preferred_lang_provider.addItem(StringResUtils.PREFERRED_LANG_CN); 62 | preferred_lang_provider.setSelectedItem(PolyglotSettingState.getInstance().getPreferredLang()); 63 | preferred_lang_provider.addItemListener(e -> { 64 | if (e.getStateChange() == ItemEvent.SELECTED) { 65 | PolyglotSettingState.getInstance().setPreferredLang(preferred_lang_provider.getSelectedItem().toString()); 66 | inflateDailyQuoterProviders(); 67 | } 68 | }); 69 | daily_quote_switch.addItemListener(e -> { 70 | boolean on = e.getStateChange() == ItemEvent.SELECTED; 71 | daily_quote_providers.setEnabled(on); 72 | PolyglotSettingState.getInstance().setDailyQuoterOn(on); 73 | }); 74 | dict_you_dao_rb.addItemListener(e -> { 75 | if (e.getStateChange() == ItemEvent.SELECTED) { 76 | PolyglotSettingState.getInstance().setDict(StringResUtils.POLYGLOT_YOUDAO); 77 | } 78 | }); 79 | consult_dict_cb.addItemListener(e -> { 80 | boolean on = e.getStateChange() == ItemEvent.SELECTED; 81 | dict_you_dao_rb.setEnabled(on); 82 | PolyglotSettingState.getInstance().setSelectWordToConsultOn(on); 83 | }); 84 | if ("".equals(PolyglotSettingState.getInstance().getAppID())) { 85 | dict_app_id.setText(dict_app_id.getToolTipText()); 86 | } else { 87 | dict_app_id.setText(PolyglotSettingState.getInstance().getAppID()); 88 | } 89 | if ("".equals(PolyglotSettingState.getInstance().getAppKey())) { 90 | dict_app_key.setText(dict_app_key.getToolTipText()); 91 | } else { 92 | dict_app_key.setText(PolyglotSettingState.getInstance().getAppKey()); 93 | } 94 | return polyglot_pref_panel; 95 | } 96 | 97 | private void inflateDailyQuoterProviders() { 98 | daily_quote_providers.removeAllItems(); 99 | int idx = 0; 100 | String dailyQuoterDesc = PolyglotSettingState.getInstance().getDailyQuoter(); 101 | for (int i = 0; i < DailyQuoter.values().length; i++) { 102 | DailyQuoter quoter = DailyQuoter.values()[i]; 103 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 104 | daily_quote_providers.addItem(quoter.getDesc()); 105 | } else { 106 | daily_quote_providers.addItem(quoter.getDescEn()); 107 | } 108 | if (dailyQuoterDesc.equals(quoter.getDesc()) || dailyQuoterDesc.equals(quoter.getDescEn())) { 109 | idx = i; 110 | } 111 | } 112 | daily_quote_providers.setSelectedItem(idx); 113 | } 114 | 115 | private void inflateDictionaryTargetLangs() { 116 | dict_target_cb.removeAllItems(); 117 | int idx = 0; 118 | String dictDesc = PolyglotSettingState.getInstance().getDictLang(); 119 | for (int i = 0; i < PolyglotUtils.Companion.getDictionaryTargetLangs().size(); i++) { 120 | Lang lang = PolyglotUtils.Companion.getDictionaryTargetLangs().get(i); 121 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 122 | dict_target_cb.addItem(lang.getDesc()); 123 | } else { 124 | dict_target_cb.addItem(lang.getDescEN()); 125 | } 126 | if (dictDesc.equals(lang.getDesc()) || dictDesc.equals(lang.getDescEN())) { 127 | idx = i; 128 | } 129 | } 130 | dict_target_cb.setSelectedIndex(idx); 131 | } 132 | 133 | @Override 134 | public boolean isModified() { 135 | return true; 136 | } 137 | 138 | @Override 139 | public void apply() throws ConfigurationException { 140 | PolyglotSettingState state = PolyglotSettingState.getInstance(); 141 | state.setDailyQuoter(daily_quote_providers.getSelectedItem().toString()); 142 | state.setDailyQuoterOn(daily_quote_switch.isSelected()); 143 | state.setSelectWordToConsultOn(consult_dict_cb.isSelected()); 144 | state.setAppID(dict_app_id.getText()); 145 | state.setAppKey(dict_app_key.getText()); 146 | } 147 | 148 | @Override 149 | public void reset() { 150 | dict_you_dao_rb.setSelected(true); 151 | daily_quote_switch.setSelected(PolyglotSettingState.getInstance().isDailyQuoterOn()); 152 | consult_dict_cb.setSelected(PolyglotSettingState.getInstance().isSelectWordToConsultOn()); 153 | daily_quote_providers.setSelectedIndex(0); 154 | dict_target_cb.setSelectedIndex(0); 155 | dict_app_id.setText(PolyglotSettingState.getInstance().getAppID()); 156 | dict_app_key.setText(PolyglotSettingState.getInstance().getAppKey()); 157 | } 158 | 159 | @Override 160 | public void disposeUIResources() { 161 | polyglot_pref_panel = null; 162 | } 163 | 164 | @Override 165 | public JComponent getPreferredFocusedComponent() { 166 | return polyglot_pref_panel; 167 | } 168 | 169 | private void createUIComponents() { 170 | dict_app_id = new JHintTextField("Enter App ID here"); 171 | dict_app_key = new JHintTextField("Enter App Key here"); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/ui/PolyglotSettingState.java: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.ui; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.components.PersistentStateComponent; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.components.Storage; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import me.bytebeats.polyglot.lang.Lang; 9 | import me.bytebeats.polyglot.util.StringResUtils; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | /** 14 | * @author bytebeats 15 | * @version 1.0 16 | * @email 17 | * @github https://github.com/bytebeats 18 | * @created on 2020/9/7 11:41 19 | * @description PolyglotSettingState offers settings for Polyglot 20 | */ 21 | 22 | @State(name = "me.bytebeats.polyglot.ui.PolyglotSettingState", storages = {@Storage("polyglot_setting.xml")}) 23 | public class PolyglotSettingState implements PersistentStateComponent { 24 | private String from = StringResUtils.LANG_DESC_ZH; 25 | private String to = StringResUtils.LANG_DESC_EN; 26 | private String dailyQuoter = StringResUtils.QUOTOR_SCALLOP; 27 | private String preferredLang = StringResUtils.PREFERRED_LANG_EN; 28 | private boolean isDailyQuoterOn = true; 29 | 30 | private String appID = ""; 31 | private String appKey = ""; 32 | private boolean isSelectWordToConsultOn = true; 33 | private String dict = StringResUtils.POLYGLOT_YOUDAO;//字典, 有道, 谷歌, 百度或者必应 34 | private String dictLang = Lang.ZH.getDesc();//字典中要查询的目标语言, 比如英语, 法语, 日语等 35 | 36 | public static PolyglotSettingState getInstance() { 37 | return ApplicationManager.getApplication().getService(PolyglotSettingState.class); 38 | } 39 | 40 | @Nullable 41 | @Override 42 | public PolyglotSettingState getState() { 43 | return this; 44 | } 45 | 46 | @Override 47 | public void loadState(@NotNull PolyglotSettingState polyglotSettingState) { 48 | XmlSerializerUtil.copyBean(polyglotSettingState, this); 49 | } 50 | 51 | public void reset() { 52 | from = StringResUtils.LANG_DESC_ZH; 53 | to = StringResUtils.LANG_DESC_EN; 54 | dailyQuoter = StringResUtils.QUOTOR_SCALLOP; 55 | isDailyQuoterOn = true; 56 | isSelectWordToConsultOn = true; 57 | appID = ""; 58 | appKey = ""; 59 | dictLang = Lang.ZH.getDesc(); 60 | } 61 | 62 | public String getFrom() { 63 | return from; 64 | } 65 | 66 | public void setFrom(String from) { 67 | this.from = from; 68 | } 69 | 70 | public String getTo() { 71 | return to; 72 | } 73 | 74 | public void setTo(String to) { 75 | this.to = to; 76 | } 77 | 78 | public String getDailyQuoter() { 79 | return dailyQuoter; 80 | } 81 | 82 | public void setDailyQuoter(String dailyQuoter) { 83 | this.dailyQuoter = dailyQuoter; 84 | } 85 | 86 | public boolean isDailyQuoterOn() { 87 | return isDailyQuoterOn; 88 | } 89 | 90 | public void setDailyQuoterOn(boolean dailyQuoterOn) { 91 | isDailyQuoterOn = dailyQuoterOn; 92 | } 93 | 94 | public String getPreferredLang() { 95 | return preferredLang; 96 | } 97 | 98 | public void setPreferredLang(String preferredLang) { 99 | this.preferredLang = preferredLang; 100 | } 101 | 102 | public boolean isCnPreferred() { 103 | return StringResUtils.PREFERRED_LANG_CN.equals(getPreferredLang()); 104 | } 105 | 106 | public String getAppID() { 107 | return appID; 108 | } 109 | 110 | public void setAppID(String appID) { 111 | this.appID = appID; 112 | } 113 | 114 | public String getAppKey() { 115 | return appKey; 116 | } 117 | 118 | public void setAppKey(String appKey) { 119 | this.appKey = appKey; 120 | } 121 | 122 | public boolean isSelectWordToConsultOn() { 123 | return isSelectWordToConsultOn; 124 | } 125 | 126 | public void setSelectWordToConsultOn(boolean selectWordToConsultOn) { 127 | isSelectWordToConsultOn = selectWordToConsultOn; 128 | } 129 | 130 | public String getDict() { 131 | return dict; 132 | } 133 | 134 | public void setDict(String dict) { 135 | this.dict = dict; 136 | } 137 | 138 | public String getDictLang() { 139 | return dictLang; 140 | } 141 | 142 | public void setDictLang(String dictLang) { 143 | this.dictLang = dictLang; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/ui/PolyglotWindow.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 |
266 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/ui/PolyglotWindow.java: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.ui; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.wm.ToolWindow; 5 | import com.intellij.openapi.wm.ToolWindowFactory; 6 | import com.intellij.ui.content.Content; 7 | import com.intellij.ui.content.ContentFactory; 8 | import me.bytebeats.polyglot.dq.AbstractDailyQuoter; 9 | import me.bytebeats.polyglot.lang.Lang; 10 | import me.bytebeats.polyglot.meta.DailyQuote; 11 | import me.bytebeats.polyglot.tlr.AbstractPolyglot; 12 | import me.bytebeats.polyglot.tlr.PolyglotTranslator; 13 | import me.bytebeats.polyglot.util.LogUtils; 14 | import me.bytebeats.polyglot.util.PolyglotUtils; 15 | import me.bytebeats.polyglot.util.StringResUtils; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import javax.swing.*; 19 | import java.awt.*; 20 | import java.awt.datatransfer.Clipboard; 21 | import java.awt.datatransfer.StringSelection; 22 | import java.awt.event.*; 23 | import java.util.List; 24 | 25 | /** 26 | * @author bytebeats 27 | * @version 1.0 28 | * @email 29 | * @github bytebeats 30 | * @created on 2020/9/5 22:09 31 | * @description PolyglotWindow displays window where translation is executed. 32 | */ 33 | 34 | public class PolyglotWindow implements ToolWindowFactory { 35 | private JPanel polyglot_panel; 36 | private JPanel plgt_source_panel; 37 | private JComboBox plgt_source_lang_cb; 38 | private JLabel plgt_source_label; 39 | private JLabel plgt_translator_label; 40 | private JComboBox plgt_translator_cb; 41 | private JPanel plgt_source_input_panel; 42 | private JLabel plgt_langs_switch; 43 | private JTextArea plgt_source_lang_input; 44 | private JScrollPane plgt_source_scroll_pane; 45 | private JComboBox plgt_target_langs_cb; 46 | private JLabel plgt_target_lang_label; 47 | private JButton plgt_translate_btn; 48 | private JTextArea plgt_target_lang_output; 49 | private JScrollPane plgt_target_lang_scroll_panel; 50 | private JButton plgt_target_lang_copy; 51 | private JPanel plgt_target_panel; 52 | private JPanel plgt_target_ouput_panel; 53 | private JPanel plgt_daily_quote_panel; 54 | private JLabel daily_quote_label; 55 | private JLabel daily_quote_content; 56 | private JLabel daily_quote_translation; 57 | private JLabel lang_pref_label; 58 | private JComboBox preferred_langs_cb; 59 | 60 | private String from = PolyglotSettingState.getInstance().getFrom(); 61 | private String to = PolyglotSettingState.getInstance().getTo(); 62 | 63 | private final List fromLangs = PolyglotUtils.Companion.getDEFAULT_FROM_LANGS(); 64 | private final List toLangs = PolyglotUtils.Companion.getDEFAULT_TO_LANGS(); 65 | 66 | @Override 67 | public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { 68 | ContentFactory contentFactory = ContentFactory.getInstance(); 69 | Content polyglotContent = contentFactory.createContent(polyglot_panel, "", true); 70 | toolWindow.getContentManager().addContent(polyglotContent); 71 | plgt_translate_btn.addActionListener(e -> { 72 | if (from != null && from.equals(to)) { 73 | plgt_target_lang_output.setText(plgt_source_lang_input.getText()); 74 | return; 75 | } 76 | String desc = (String) plgt_translator_cb.getSelectedItem(); 77 | assert desc != null; 78 | AbstractPolyglot polyglot = AbstractPolyglot.Factory.newInstance(desc); 79 | String result = polyglot.translate(Lang.Companion.from(from), Lang.Companion.from(to), plgt_source_lang_input.getText()); 80 | plgt_target_lang_output.setText(result); 81 | }); 82 | plgt_target_lang_copy.addActionListener(e -> { 83 | String content = plgt_target_lang_output.getText(); 84 | StringSelection selection = new StringSelection(content); 85 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 86 | clipboard.setContents(selection, null); 87 | LogUtils.INSTANCE.info("Copy succeeded"); 88 | }); 89 | plgt_langs_switch.addMouseListener(new MouseListener() { 90 | @Override 91 | public void mouseClicked(MouseEvent e) { 92 | if (from.equals(to) || from.equals(Lang.AUTO.getDesc()) || from.equals(Lang.AUTO.getDescEN())) { 93 | return; 94 | } 95 | String lang = from; 96 | from = to; 97 | to = lang; 98 | plgt_source_lang_cb.setSelectedIndex(fromLangs.indexOf(Lang.Companion.from(from))); 99 | plgt_target_langs_cb.setSelectedIndex(toLangs.indexOf(Lang.Companion.from(to))); 100 | String text = plgt_source_lang_input.getText(); 101 | plgt_source_lang_input.setText(plgt_target_lang_output.getText()); 102 | plgt_target_lang_output.setText(text); 103 | } 104 | 105 | @Override 106 | public void mousePressed(MouseEvent e) { 107 | 108 | } 109 | 110 | @Override 111 | public void mouseReleased(MouseEvent e) { 112 | 113 | } 114 | 115 | @Override 116 | public void mouseEntered(MouseEvent e) { 117 | 118 | } 119 | 120 | @Override 121 | public void mouseExited(MouseEvent e) { 122 | 123 | } 124 | }); 125 | } 126 | 127 | @Override 128 | public void init(@NotNull ToolWindow toolWindow) { 129 | plgt_source_lang_cb.removeAllItems(); 130 | plgt_target_langs_cb.removeAllItems(); 131 | for (Lang lang : fromLangs) { 132 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 133 | plgt_source_lang_cb.addItem(lang.getDesc()); 134 | } else { 135 | plgt_source_lang_cb.addItem(lang.getDescEN()); 136 | } 137 | } 138 | for (Lang lang : toLangs) { 139 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 140 | plgt_target_langs_cb.addItem(lang.getDesc()); 141 | } else { 142 | plgt_target_langs_cb.addItem(lang.getDescEN()); 143 | } 144 | } 145 | plgt_source_lang_cb.addItemListener(e -> { 146 | if (e.getStateChange() == ItemEvent.SELECTED) { 147 | from = (String) plgt_source_lang_cb.getSelectedItem(); 148 | PolyglotSettingState.getInstance().setFrom(from); 149 | updateTranslators(); 150 | } 151 | }); 152 | plgt_target_langs_cb.addItemListener(e -> { 153 | if (e.getStateChange() == ItemEvent.SELECTED) { 154 | to = (String) plgt_target_langs_cb.getSelectedItem(); 155 | PolyglotSettingState.getInstance().setTo(to); 156 | updateTranslators(); 157 | } 158 | }); 159 | plgt_source_lang_cb.setSelectedIndex(fromLangs.indexOf(Lang.Companion.from(from))); 160 | plgt_target_langs_cb.setSelectedIndex(toLangs.indexOf(Lang.Companion.from(to))); 161 | updateTranslators(); 162 | requestDailyQuote(); 163 | preferred_langs_cb.removeAllItems(); 164 | preferred_langs_cb.addItem(StringResUtils.PREFERRED_LANG_CN); 165 | preferred_langs_cb.addItem(StringResUtils.PREFERRED_LANG_EN); 166 | preferred_langs_cb.addItemListener(e -> { 167 | if (e.getStateChange() == ItemEvent.SELECTED) { 168 | String preferred = (String) preferred_langs_cb.getSelectedItem(); 169 | PolyglotSettingState.getInstance().setPreferredLang(preferred); 170 | updateWidgetLiterals(); 171 | plgt_source_lang_cb.removeAllItems(); 172 | plgt_target_langs_cb.removeAllItems(); 173 | for (Lang lang : fromLangs) { 174 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 175 | plgt_source_lang_cb.addItem(lang.getDesc()); 176 | } else { 177 | plgt_source_lang_cb.addItem(lang.getDescEN()); 178 | } 179 | } 180 | for (Lang lang : toLangs) { 181 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 182 | plgt_target_langs_cb.addItem(lang.getDesc()); 183 | } else { 184 | plgt_target_langs_cb.addItem(lang.getDescEN()); 185 | } 186 | } 187 | plgt_source_lang_cb.setSelectedIndex(fromLangs.indexOf(Lang.Companion.from(from))); 188 | plgt_target_langs_cb.setSelectedIndex(toLangs.indexOf(Lang.Companion.from(to))); 189 | requestDailyQuote(); 190 | } 191 | }); 192 | preferred_langs_cb.setSelectedItem(PolyglotSettingState.getInstance().getPreferredLang()); 193 | updateWidgetLiterals(); 194 | } 195 | 196 | private void updateWidgetLiterals() { 197 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 198 | plgt_source_label.setText(StringResUtils.TEXT_SRC_LANG); 199 | plgt_target_lang_label.setText(StringResUtils.TEXT_TRGT_LANG); 200 | plgt_translator_label.setText(StringResUtils.TEXT_TRANSLATORS); 201 | } else { 202 | plgt_source_label.setText(StringResUtils.TEXT_SRC_LANG_EN); 203 | plgt_target_lang_label.setText(StringResUtils.TEXT_TRGT_LANG_EN); 204 | plgt_translator_label.setText(StringResUtils.TEXT_TRANSLATORS_EN); 205 | } 206 | } 207 | 208 | private void requestDailyQuote() { 209 | if (PolyglotSettingState.getInstance().isDailyQuoterOn()) { 210 | DailyQuote quote = AbstractDailyQuoter.Factory.newInstance(PolyglotSettingState.getInstance().getDailyQuoter()).quote(); 211 | if (quote != null) { 212 | plgt_daily_quote_panel.setVisible(true); 213 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 214 | daily_quote_label.setText(String.format("每日一句: %s", quote.getDate())); 215 | } else { 216 | daily_quote_label.setText(String.format("Daily Quote: %s", quote.getDate())); 217 | } 218 | daily_quote_content.setText(quote.getMultilineContent()); 219 | daily_quote_translation.setText(quote.getMultilineTranslation()); 220 | } else { 221 | plgt_daily_quote_panel.setVisible(false); 222 | } 223 | } else { 224 | plgt_daily_quote_panel.setVisible(false); 225 | } 226 | } 227 | 228 | private void updateTranslators() {// different translators may support different languages. 229 | plgt_translator_cb.removeAllItems(); 230 | List polyglots = PolyglotUtils.Companion.getSupportedPolyglot(Lang.Companion.from(from), Lang.Companion.from(to), PolyglotSettingState.getInstance().isCnPreferred()); 231 | for (PolyglotTranslator translator : polyglots) { 232 | if (PolyglotSettingState.getInstance().isCnPreferred()) { 233 | plgt_translator_cb.addItem(translator.getDesc()); 234 | } else { 235 | plgt_translator_cb.addItem(translator.getDescEN()); 236 | } 237 | } 238 | plgt_translator_cb.setSelectedIndex(0); 239 | } 240 | 241 | @Override 242 | public boolean shouldBeAvailable(@NotNull Project project) { 243 | return true;//is window visible when ide starts up. 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/ui/swing/JHintTextField.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.ui.swing 2 | 3 | import com.intellij.ui.JBColor 4 | import java.awt.Font 5 | import java.awt.event.FocusEvent 6 | import java.awt.event.FocusListener 7 | import javax.swing.JTextField 8 | 9 | /** 10 | * @Author bytebeats 11 | * @Email 12 | * @Github https://github.com/bytebeats 13 | * @Created on 2021/9/16 20:18 14 | * @Version 1.0 15 | * @Description JTextField with a hint 16 | */ 17 | 18 | class JHintTextField(private val hint: String?) : JTextField(), FocusListener { 19 | private var gainFont: Font? = null 20 | private var lostFont: Font? = null 21 | 22 | init { 23 | val font = font 24 | gainFont = Font(font.fontName, font.style, font.size) 25 | lostFont = Font(font.fontName, Font.ITALIC, font.size) 26 | text = hint 27 | setFont(lostFont) 28 | foreground = JBColor.GRAY 29 | super.addFocusListener(this) 30 | } 31 | 32 | override fun focusGained(e: FocusEvent?) { 33 | if (super.getText() == hint) { 34 | text = "" 35 | font = gainFont 36 | } else { 37 | text = super.getText() 38 | font = gainFont 39 | } 40 | } 41 | 42 | override fun focusLost(e: FocusEvent?) { 43 | if (super.getText() == hint || super.getText().isEmpty()) { 44 | super.setText(hint) 45 | font = lostFont 46 | foreground = JBColor.GRAY 47 | } else { 48 | text = super.getText() 49 | font = gainFont 50 | foreground = JBColor.BLACK 51 | } 52 | } 53 | 54 | override fun getText(): String? { 55 | return if (super.getText() == hint) { 56 | "" 57 | } else { 58 | super.getText() 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/util/GlotJsUtils.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.util 2 | 3 | import java.io.Reader 4 | import java.io.StringReader 5 | 6 | /** 7 | * @author bytebeats 8 | * @email 9 | * @github https://github.com/bytebeats 10 | * @created on 2020/8/27 16:19 11 | * @version 1.0 12 | * @description GlotJsUtils offers some js functions. 13 | */ 14 | 15 | class GlotJsUtils { 16 | companion object { 17 | const val JS_BAIDU = """function a(r, o) { 18 | for (var t = 0; t < o.length - 2; t += 3) { 19 | var a = o.charAt(t + 2); 20 | a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a), 21 | a = "+" === o.charAt(t + 1) ? r >>> a: r << a, 22 | r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a 23 | } 24 | return r 25 | } 26 | var C = null; 27 | var token = function(r, _gtk) { 28 | var o = r.length; 29 | o > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(o / 2) - 5, 10) + r.substring(r.length, r.length - 10)); 30 | var t = void 0, 31 | t = null !== C ? C: (C = _gtk || "") || ""; 32 | for (var e = t.split("."), h = Number(e[0]) || 0, i = Number(e[1]) || 0, d = [], f = 0, g = 0; g < r.length; g++) { 33 | var m = r.charCodeAt(g); 34 | 128 > m ? d[f++] = m: (2048 > m ? d[f++] = m >> 6 | 192 : (55296 === (64512 & m) && g + 1 < r.length && 56320 === (64512 & r.charCodeAt(g + 1)) ? (m = 65536 + ((1023 & m) << 10) + (1023 & r.charCodeAt(++g)), d[f++] = m >> 18 | 240, d[f++] = m >> 12 & 63 | 128) : d[f++] = m >> 12 | 224, d[f++] = m >> 6 & 63 | 128), d[f++] = 63 & m | 128) 35 | } 36 | for (var S = h, 37 | u = "+-a^+6", 38 | l = "+-3^+b+-f", 39 | s = 0; s < d.length; s++) S += d[s], 40 | S = a(S, u); 41 | 42 | return S = a(S, l), 43 | S ^= i, 44 | 0 > S && (S = (2147483647 & S) + 2147483648), 45 | S %= 1e6, 46 | S.toString() + "." + (S ^ h) 47 | }""" 48 | 49 | const val JS_GOOGLE = """function token(a) { 50 | var k = ""; 51 | var b = 406644; 52 | var b1 = 3293161072; 53 | 54 | var jd = "."; 55 | var sb = "+-a^+6"; 56 | var Zb = "+-3^+b+-f"; 57 | 58 | for (var e = [], f = 0, g = 0; g < a.length; g++) { 59 | var m = a.charCodeAt(g); 60 | 128 > m ? e[f++] = m: (2048 > m ? e[f++] = m >> 6 | 192 : (55296 == (m & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (m = 65536 + ((m & 1023) << 10) + (a.charCodeAt(++g) & 1023), e[f++] = m >> 18 | 240, e[f++] = m >> 12 & 63 | 128) : e[f++] = m >> 12 | 224, e[f++] = m >> 6 & 63 | 128), e[f++] = m & 63 | 128) 61 | } 62 | a = b; 63 | for (f = 0; f < e.length; f++) a += e[f], 64 | a = RL(a, sb); 65 | a = RL(a, Zb); 66 | a ^= b1 || 0; 67 | 0 > a && (a = (a & 2147483647) + 2147483648); 68 | a %= 1E6; 69 | return a.toString() + jd + (a ^ b) 70 | }; 71 | 72 | function RL(a, b) { 73 | var t = "a"; 74 | var Yb = "+"; 75 | for (var c = 0; c < b.length - 2; c += 3) { 76 | var d = b.charAt(c + 2), 77 | d = d >= t ? d.charCodeAt(0) - 87 : Number(d), 78 | d = b.charAt(c + 1) == Yb ? a >>> d: a << d; 79 | a = b.charAt(c) == Yb ? a + d & 4294967295 : a ^ d 80 | } 81 | return a 82 | }""" 83 | const val JS_SOGOU = """function token() { 84 | return tk = t() + t() + t() + t() + t() + t() + t() + t() 85 | }; 86 | function t() { 87 | return Math.floor(65536 * (1 + Math.random())).toString(16).substring(1) 88 | }""" 89 | 90 | fun getReader(jsText: String): Reader { 91 | return StringReader(jsText) 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/util/GsonUtils.java: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.util; 2 | 3 | import com.google.gson.Gson; 4 | 5 | import java.lang.reflect.Type; 6 | 7 | /** 8 | * @Author bytebeats 9 | * @Email 10 | * @Github https://github.com/bytebeats 11 | * @Created on 2020/11/1 19:49 12 | * @Version 1.0 13 | * @Description TO-DO 14 | */ 15 | 16 | public final class GsonUtils { 17 | 18 | /** 19 | * 工具类对象
20 | * (单例) 21 | */ 22 | private static final GsonUtils gsonUtils = new GsonUtils(); 23 | 24 | /** 25 | * Gson对象 26 | */ 27 | private static final Gson gson = new Gson(); 28 | 29 | /** 30 | * 私有构造 31 | */ 32 | private GsonUtils() { 33 | } 34 | 35 | /** 36 | * 对外提供静态公有的方法 37 | * 38 | * @return 本类对象 39 | */ 40 | public static GsonUtils getInstance() { 41 | return gsonUtils; 42 | } 43 | 44 | /** 45 | * 1,该方法主要功能是将json字符串转换成Java类对象 46 | * 47 | * @param json json字符串 48 | * @param cls Java类的字节码对象 49 | * @return 解析后的Java类对象 50 | * @throws Exception 如果解析中出了问题,或者是json不完整都会抛出这个异常信息 51 | */ 52 | public T from(String json, Class cls) throws Exception { 53 | return gson.fromJson(json, cls); 54 | } 55 | 56 | /** 57 | * 2,该方法主要功能是将Java类对象转换成json字符串 58 | * 59 | * @param obj Java对象 60 | * @return json字符串 61 | */ 62 | public String toJson(Object obj) { 63 | return gson.toJson(obj); 64 | } 65 | 66 | /** 67 | * 3,该方法主要功能是将json字符串转换成指定类型的对象 68 | * 69 | * @param json json字符串 70 | * @param typeOfT 指定类型 71 | * @return 指定类型的对象 72 | */ 73 | public T from(String json, Type typeOfT) { 74 | return gson.fromJson(json, typeOfT); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/util/LogUtils.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.util 2 | 3 | import com.intellij.notification.NotificationGroupManager 4 | import com.intellij.notification.NotificationType 5 | 6 | 7 | /** 8 | * @author bytebeats 9 | * @email 10 | * @github https://github.com/bytebeats 11 | * @created on 2020/8/27 15:32 12 | * @version 1.0 13 | * @description LogUtils notifies users on EventLog window 14 | */ 15 | 16 | object LogUtils { 17 | private const val NOTIFICATION_GROUP_ID = "Polyglot" 18 | fun info(message: String?) { 19 | NotificationGroupManager.getInstance() 20 | .getNotificationGroup(NOTIFICATION_GROUP_ID) 21 | .createNotification(message ?: "", NotificationType.INFORMATION) 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/util/ParamUtils.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.util 2 | 3 | import org.apache.http.NameValuePair 4 | import org.apache.http.message.BasicNameValuePair 5 | import java.io.UnsupportedEncodingException 6 | import java.net.URLEncoder 7 | import java.security.MessageDigest 8 | import java.security.NoSuchAlgorithmException 9 | 10 | /** 11 | * @author bytebeats 12 | * @email 13 | * @github https://github.com/bytebeats 14 | * @created on 2020/8/27 16:43 15 | * @version 1.0 16 | * @description TO-DO 17 | */ 18 | 19 | class ParamUtils { 20 | companion object { 21 | private val hexDigits = 22 | charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') 23 | 24 | fun map2List(params: Map): List { 25 | val pairs = mutableListOf() 26 | for ((k, v) in params) { 27 | pairs.add(BasicNameValuePair(k, v)) 28 | } 29 | return pairs 30 | } 31 | 32 | fun concatUrl(url: String, params: Map): String { 33 | if (params.isEmpty()) return url 34 | val target = StringBuilder(url) 35 | if (!target.contains("?")) { 36 | target.append("?") 37 | } 38 | for ((k, v) in params) { 39 | if (!target.endsWith("?")) { 40 | target.append("&") 41 | } 42 | target.append(k) 43 | target.append("=") 44 | target.append(encodeUtf8(v)) 45 | } 46 | return target.toString() 47 | } 48 | 49 | fun encodeUtf8(input: String?): String { 50 | if (input.isNullOrEmpty()) return "" 51 | try { 52 | return URLEncoder.encode(input, "utf-8") 53 | } catch (e: UnsupportedEncodingException) { 54 | LogUtils.info(e.message) 55 | } 56 | return "" 57 | } 58 | 59 | fun md5(input: String?): String? { 60 | if (input == null) return null 61 | try { 62 | val msgDigest = MessageDigest.getInstance("MD5") 63 | val inputBytes = input.toByteArray(Charsets.UTF_8) 64 | msgDigest.update(inputBytes) 65 | val md5Bytes = msgDigest.digest() 66 | return bytes2Hex(md5Bytes) 67 | } catch (e: NoSuchAlgorithmException) { 68 | LogUtils.info(e.message) 69 | } catch (e: UnsupportedEncodingException) { 70 | LogUtils.info(e.message) 71 | } 72 | return "" 73 | } 74 | 75 | private fun bytes2Hex(bytes: ByteArray): String { 76 | val chars = CharArray(bytes.size * 2) 77 | var idx = 0 78 | for (b in bytes) { 79 | chars[idx++] = hexDigits[(b.toInt() shr 4) and 0xf] 80 | chars[idx++] = hexDigits[b.toInt() and 0xf] 81 | } 82 | return String(chars) 83 | } 84 | 85 | fun sha256(input: String?): String? { 86 | if (input == null) return null 87 | try { 88 | val msgDigest = MessageDigest.getInstance("SHA-256") 89 | val inputBytes = input.toByteArray(Charsets.UTF_8) 90 | msgDigest.update(inputBytes) 91 | val md5Bytes = msgDigest.digest() 92 | return bytes2Hex(md5Bytes) 93 | } catch (e: NoSuchAlgorithmException) { 94 | LogUtils.info(e.message) 95 | } catch (e: UnsupportedEncodingException) { 96 | LogUtils.info(e.message) 97 | } 98 | return "" 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/util/PolyglotUtils.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.util 2 | 3 | import me.bytebeats.polyglot.lang.Lang 4 | import me.bytebeats.polyglot.tlr.PolyglotTranslator 5 | 6 | /** 7 | * @author bytebeats 8 | * @email 9 | * @github https://github.com/bytebeats 10 | * @created on 2020/9/5 18:01 11 | * @version 1.0 12 | * @description PolyglotUtils supports languages. 13 | */ 14 | 15 | class PolyglotUtils { 16 | companion object { 17 | val DEFAULT_FROM_LANGS = listOf( 18 | Lang.AUTO, Lang.ZH, Lang.CHT, Lang.EN, Lang.FRA, Lang.DE, Lang.JP, Lang.KOR, Lang.RU, Lang.SPA, Lang.IT, 19 | Lang.VIE, Lang.TH, Lang.ARA, Lang.YUE, Lang.WYW, Lang.LAT, Lang.SAN, Lang.ARG, Lang.GRA, Lang.KLI, Lang.HEB, 20 | Lang.ENO, Lang.FRM, Lang.PER 21 | ) 22 | val DEFAULT_TO_LANGS = listOf( 23 | Lang.ZH, Lang.CHT, Lang.EN, Lang.FRA, Lang.DE, Lang.JP, Lang.KOR, Lang.RU, Lang.SPA, Lang.IT, Lang.VIE, 24 | Lang.TH, Lang.ARA, Lang.YUE, Lang.WYW, Lang.LAT, Lang.SAN, Lang.ARG, Lang.GRA, Lang.KLI, Lang.HEB, Lang.ENO, 25 | Lang.FRM, Lang.PER 26 | ) 27 | 28 | private val LANGS_BAIDU = listOf( 29 | Lang.ZH, Lang.CHT, Lang.YUE, Lang.WYW, Lang.EN, Lang.JP, Lang.KOR, Lang.FRA, Lang.RU, Lang.DE, Lang.SPA, 30 | Lang.PT, Lang.TR, Lang.IT, Lang.ARA, Lang.LAT, Lang.SAN, Lang.ARG, Lang.GRA, Lang.KLI, Lang.HEB, Lang.ENO, 31 | Lang.FRM, Lang.PER, Lang.AUTO 32 | ) 33 | 34 | private val LANGS_BING = listOf( 35 | Lang.ZH, Lang.CHT, Lang.EN, Lang.JP, Lang.KOR, Lang.FRA, Lang.RU, Lang.DE, Lang.SPA, Lang.IT, Lang.ARA, 36 | Lang.TH, Lang.VIE, Lang.AUTO 37 | ) 38 | private val LANGS_GOOGLE = listOf( 39 | Lang.ZH, Lang.CHT, Lang.EN, Lang.JP, Lang.KOR, Lang.FRA, Lang.RU, Lang.DE, Lang.SPA, Lang.IT, Lang.VIE, 40 | Lang.TH, Lang.ARA, Lang.AUTO 41 | ) 42 | private val LANGS_OMI = listOf(Lang.ZH, Lang.EN, Lang.AUTO) 43 | private val LANGS_SOGOU = listOf( 44 | Lang.AUTO, Lang.ZH, Lang.CHT, Lang.EN, Lang.JP, Lang.KOR, Lang.FRA, Lang.RU, Lang.DE, Lang.SPA, Lang.IT, 45 | Lang.VIE, Lang.TH, Lang.ARA 46 | ) 47 | private val LANGS_TENCENT = listOf( 48 | Lang.ZH, Lang.EN, Lang.JP, Lang.KOR, Lang.FRA, Lang.RU, Lang.DE, Lang.SPA, Lang.IT, Lang.TR, Lang.PT, 49 | Lang.VIE, Lang.ID, Lang.TH, Lang.MS, Lang.HI, Lang.AUTO 50 | ) 51 | private val LANGS_YOUDAO = listOf( 52 | Lang.ZH, Lang.EN, Lang.JP, Lang.KOR, Lang.FRA, Lang.RU, Lang.CHT, Lang.DE, Lang.SPA, Lang.IT, Lang.PT, 53 | Lang.VIE, Lang.ID, Lang.TH, Lang.ARA, Lang.NL, Lang.AUTO 54 | ) 55 | 56 | @JvmStatic 57 | fun getSupportedPolyglot(from: Lang, to: Lang, isCnPreferred: Boolean = true): List { 58 | val polyglots = mutableListOf() 59 | if (isCnPreferred) { 60 | if (LANGS_BAIDU.contains(from) && LANGS_BAIDU.contains(to)) { 61 | polyglots.add(PolyglotTranslator.Baidu) 62 | } 63 | if (LANGS_GOOGLE.contains(from) && LANGS_GOOGLE.contains(to)) { 64 | polyglots.add(PolyglotTranslator.Google) 65 | } 66 | if (LANGS_BING.contains(from) && LANGS_BING.contains(to)) { 67 | polyglots.add(PolyglotTranslator.Bing) 68 | } 69 | } else { 70 | if (LANGS_GOOGLE.contains(from) && LANGS_GOOGLE.contains(to)) { 71 | polyglots.add(PolyglotTranslator.Google) 72 | } 73 | if (LANGS_BING.contains(from) && LANGS_BING.contains(to)) { 74 | polyglots.add(PolyglotTranslator.Bing) 75 | } 76 | if (LANGS_BAIDU.contains(from) && LANGS_BAIDU.contains(to)) { 77 | polyglots.add(PolyglotTranslator.Baidu) 78 | } 79 | } 80 | if (LANGS_YOUDAO.contains(from) && LANGS_YOUDAO.contains(to)) { 81 | polyglots.add(PolyglotTranslator.Youdao) 82 | } 83 | if (LANGS_SOGOU.contains(from) && LANGS_SOGOU.contains(to)) { 84 | polyglots.add(PolyglotTranslator.Sogou) 85 | } 86 | if (LANGS_OMI.contains(from) && LANGS_OMI.contains(to)) { 87 | polyglots.add(PolyglotTranslator.Omi) 88 | } 89 | if (LANGS_TENCENT.contains(from) && LANGS_TENCENT.contains(to)) { 90 | polyglots.add(PolyglotTranslator.Tencent) 91 | } 92 | return polyglots 93 | } 94 | 95 | val dictionaryTargetLangs = listOf( 96 | Lang.ZH, Lang.CHT, Lang.EN, Lang.JP, Lang.KOR, Lang.FRA, Lang.RU, Lang.DE, Lang.SPA, Lang.IT, 97 | Lang.VIE, Lang.TH, Lang.ARA 98 | ) 99 | } 100 | } -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/util/StringResUtils.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.util 2 | 3 | /** 4 | * @author bytebeats 5 | * @email 6 | * @github https://github.com/bytebeats 7 | * @created on 2020/9/5 17:13 8 | * @version 1.0 9 | * @description TO-DO 10 | */ 11 | 12 | class StringResUtils { 13 | companion object { 14 | /** 15 | * Plugins 16 | */ 17 | const val APP_NAME = "Polyglot" 18 | const val APP_NAME_DESC = "Translator" 19 | 20 | /** 21 | * Languages 22 | */ 23 | const val LANG_DESC_AUTO = "自动检测" 24 | const val LANG_DESC_ZH = "中文(简体)" 25 | const val LANG_DESC_CHT = "中文(繁体)" 26 | const val LANG_DESC_EN = "英语" 27 | const val LANG_DESC_JP = "日语" 28 | const val LANG_DESC_FR = "法语" 29 | const val LANG_DESC_SPA = "西班牙语" 30 | const val LANG_DESC_PT = "葡萄牙语" 31 | const val LANG_DESC_KR = "韩语" 32 | const val LANG_DESC_DE = "德语" 33 | const val LANG_DESC_RU = "俄语" 34 | const val LANG_DESC_ARA = "阿拉伯语" 35 | const val LANG_DESC_TR = "土耳其语" 36 | const val LANG_DESC_PL = "波兰语" 37 | const val LANG_DESC_DAN = "丹麦语" 38 | const val LANG_DESC_YUE = "粤语" 39 | const val LANG_DESC_TH = "泰语" 40 | const val LANG_DESC_VIE = "越南语" 41 | const val LANG_DESC_ID = "印尼语" 42 | const val LANG_DESC_MS = "马来语" 43 | const val LANG_DESC_HI = "印地语" 44 | const val LANG_DESC_IR = "伊朗语" 45 | const val LANG_DESC_WYW = "文言文" 46 | const val LANG_DESC_IT = "意大利语" 47 | const val LANG_DESC_KK = "哈萨克语" 48 | const val LANG_DESC_FIL = "菲律宾语" 49 | const val LANG_DESC_ICE = "冰岛语" 50 | const val LANG_DESC_FIN = "芬兰语" 51 | const val LANG_DESC_NL = "荷兰语" 52 | const val LANG_DESC_TAT = "鞑靼语" 53 | const val LANG_DESC_KUR = "库尔德语" 54 | 55 | /** 56 | * Ancient languages 57 | */ 58 | const val LANG_DESC_LAT = "拉丁语" 59 | const val LANG_DESC_SAN = "梵语" 60 | const val LANG_DESC_ARG = "阿拉贡语" 61 | const val LANG_DESC_GRA = "古希腊语" 62 | const val LANG_DESC_KLI = "克林贡语" 63 | const val LANG_DESC_HEB = "希伯来语" 64 | const val LANG_DESC_ENO = "古英语" 65 | const val LANG_DESC_FRM = "中古法语" 66 | const val LANG_DESC_PER = "波斯语" 67 | 68 | /** 69 | * Polyglots 70 | */ 71 | const val POLYGLOT_BAIDU = "百度" 72 | const val POLYGLOT_BING = "必应" 73 | const val POLYGLOT_GOOGLE = "谷歌" 74 | const val POLYGLOT_OMI = "欧米" 75 | const val POLYGLOT_SOGOU = "搜狗" 76 | const val POLYGLOT_TENCENT = "腾讯" 77 | const val POLYGLOT_YOUDAO = "有道" 78 | 79 | /** 80 | * Daily quoters 81 | */ 82 | const val QUOTOR_SCALLOP = "扇贝" 83 | const val QUOTOR_ICIBA = "金山词霸" 84 | const val QUOTOR_YOUDAO = "有道" 85 | 86 | /** 87 | * Languages 88 | */ 89 | const val LANG_DESC_AUTO_EN = "Auto-detect" 90 | const val LANG_DESC_ZH_EN = "Simplified CHS" 91 | const val LANG_DESC_CHT_EN = "Traditional CHS" 92 | const val LANG_DESC_EN_EN = "English" 93 | const val LANG_DESC_JP_EN = "Japanese" 94 | const val LANG_DESC_FR_EN = "French" 95 | const val LANG_DESC_SPA_EN = "Spanish" 96 | const val LANG_DESC_PT_EN = "Portuguese" 97 | const val LANG_DESC_KR_EN = "Korean" 98 | const val LANG_DESC_DE_EN = "German" 99 | const val LANG_DESC_RU_EN = "Russian" 100 | const val LANG_DESC_ARA_EN = "Arabic" 101 | const val LANG_DESC_TR_EN = "Turkish" 102 | const val LANG_DESC_PL_EN = "Polish" 103 | const val LANG_DESC_DAN_EN = "Danish" 104 | const val LANG_DESC_YUE_EN = "Cantonese" 105 | const val LANG_DESC_TH_EN = "Thai" 106 | const val LANG_DESC_VIE_EN = "Vietnamese" 107 | const val LANG_DESC_ID_EN = "Indonesian" 108 | const val LANG_DESC_MS_EN = "Malay" 109 | const val LANG_DESC_HI_EN = "Hindi" 110 | const val LANG_DESC_IR_EN = "Iranian" 111 | const val LANG_DESC_WYW_EN = "Classical CHS" 112 | const val LANG_DESC_IT_EN = "Italian" 113 | const val LANG_DESC_KK_EN = "Kazakh" 114 | const val LANG_DESC_FIL_EN = "Filipino" 115 | const val LANG_DESC_ICE_EN = "Icelandic" 116 | const val LANG_DESC_FIN_EN = "Finnish" 117 | const val LANG_DESC_NL_EN = "Dutch" 118 | const val LANG_DESC_TAT_EN = "Tatar" 119 | const val LANG_DESC_KUR_EN = "Kurdish" 120 | 121 | /** 122 | * Ancient languages 123 | */ 124 | const val LANG_DESC_LAT_EN = "Latin" 125 | const val LANG_DESC_SAN_EN = "Sanskrit" 126 | const val LANG_DESC_ARG_EN = "Aragonese" 127 | const val LANG_DESC_GRA_EN = "Ancient Greek" 128 | const val LANG_DESC_KLI_EN = "Klingon" 129 | const val LANG_DESC_HEB_EN = "Hebrew" 130 | const val LANG_DESC_ENO_EN = "Old English" 131 | const val LANG_DESC_FRM_EN = "Middle French" 132 | const val LANG_DESC_PER_EN = "Persian" 133 | 134 | /** 135 | * Polyglots 136 | */ 137 | const val POLYGLOT_BAIDU_EN = "Baidu" 138 | const val POLYGLOT_BING_EN = "Bing" 139 | const val POLYGLOT_GOOGLE_EN = "Google" 140 | const val POLYGLOT_OMI_EN = "Omi" 141 | const val POLYGLOT_SOGOU_EN = "Sogou" 142 | const val POLYGLOT_TENCENT_EN = "Tencent" 143 | const val POLYGLOT_YOUDAO_EN = "Youdao" 144 | 145 | /** 146 | * Daily quoters 147 | */ 148 | const val QUOTOR_SCALLOP_EN = "Shanbay" 149 | const val QUOTOR_ICIBA_EN = "Iciba" 150 | const val QUOTOR_YOUDAO_EN = "Youdao" 151 | 152 | /** 153 | * Preferred languages 154 | */ 155 | const val PREFERRED_LANG_CN = "中文" 156 | const val PREFERRED_LANG_EN = "English" 157 | 158 | /** 159 | * literals 160 | */ 161 | const val TEXT_SRC_LANG = "源语言" 162 | const val TEXT_TRGT_LANG = "目标语言" 163 | const val TEXT_TRANSLATORS = "翻译器" 164 | 165 | const val TEXT_SRC_LANG_EN = "Source Langs" 166 | const val TEXT_TRGT_LANG_EN = "Target Langs" 167 | const val TEXT_TRANSLATORS_EN = "Translators" 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/me/bytebeats/polyglot/util/TK.kt: -------------------------------------------------------------------------------- 1 | package me.bytebeats.polyglot.util 2 | 3 | import com.intellij.notification.Notification 4 | import com.intellij.notification.NotificationType 5 | import com.intellij.notification.Notifications 6 | import com.intellij.util.io.HttpRequests 7 | import me.bytebeats.polyglot.dict.impl.GoogleDictionary.Companion.GOOGLE_HOST 8 | import me.bytebeats.polyglot.dict.impl.GoogleDictionary.Companion.GOOGLE_HOST_CN 9 | import me.bytebeats.polyglot.excp.NetworkException 10 | import me.bytebeats.polyglot.http.PolyglotFormDataAdder 11 | import me.bytebeats.polyglot.ui.PolyglotSettingState 12 | import java.lang.Math.abs 13 | import java.util.* 14 | import java.util.regex.Pattern 15 | 16 | /** 17 | * @Author bytebeats 18 | * @Email 19 | * @Github https://github.com/bytebeats 20 | * @Created on 2020/11/7 20:05 21 | * @Version 1.0 22 | * @Description TO-DO 23 | */ 24 | 25 | private fun `fun`(a: Long, b: String): Long { 26 | var g = a 27 | for (c in 0..b.length - 2 step 3) { 28 | val d = b[c + 2] 29 | val e = if ('a' <= d) d.code - 87 else d.toString().toInt() 30 | val f = if ('+' == b[c + 1]) g.ushr(e) else g shl e 31 | g = if ('+' == b[c]) g + f and (Int.MAX_VALUE.toLong() * 2 + 1) else g xor f 32 | } 33 | 34 | return g 35 | } 36 | 37 | /** 38 | * 计算谷歌翻译的tkk值. 39 | */ 40 | object TKK { 41 | private const val MIM = 60 * 60 * 1000 42 | private const val ELEMENT_URL_FORMAT = "https://%s/translate_a/element.js" 43 | private const val NOTIFICATION_DISPLAY_ID = "TKK Update Failed" 44 | 45 | private val generator = Random() 46 | 47 | private val tkkPattern = Pattern.compile("tkk='(\\d+).(-?\\d+)'") 48 | 49 | private var innerValue: Pair = 0L to 0L 50 | 51 | private var needUpdate: Boolean = true 52 | 53 | val value get() = update() 54 | 55 | @Synchronized 56 | fun update(): Pair { 57 | val now = System.currentTimeMillis() / MIM 58 | val (curVal) = innerValue 59 | if (!needUpdate && now == curVal) { 60 | return innerValue 61 | } 62 | 63 | val newTKK = updateFromGoogle() 64 | needUpdate = newTKK == null 65 | innerValue = newTKK ?: now to (abs(generator.nextInt().toLong()) + generator.nextInt().toLong()) 66 | return innerValue 67 | } 68 | 69 | private fun updateFromGoogle(): Pair? { 70 | val updateUrl = ELEMENT_URL_FORMAT.format(googleHost) 71 | 72 | return try { 73 | val elementJS = HttpRequests.request(updateUrl) 74 | .userAgent(PolyglotFormDataAdder.USER_AGENT).apply { 75 | tuner { 76 | val googleReferer = if (PolyglotSettingState.getInstance().isCnPreferred) { 77 | "https://translate.google.com/" 78 | } else { 79 | "https://translate.google.cn/" 80 | } 81 | it.setRequestProperty("Referer", googleReferer) 82 | } 83 | } 84 | .readString(null) 85 | val matcher = tkkPattern.matcher(elementJS) 86 | if (matcher.find()) { 87 | val value1 = matcher.group(1).toLong() 88 | val value2 = matcher.group(2).toLong() 89 | 90 | LogUtils.info("TKK Updated: $value1.$value2") 91 | 92 | value1 to value2 93 | } else { 94 | LogUtils.info("TKK update failed: TKK not found.") 95 | Notifications.Bus.notify( 96 | Notification( 97 | NOTIFICATION_DISPLAY_ID, 98 | "TKK", 99 | "TKK update failed: TKK not found.", 100 | NotificationType.ERROR 101 | ) 102 | ) 103 | 104 | null 105 | } 106 | } catch (e: Throwable) { 107 | val error = NetworkException.wrapIfIsNetworkException(e, googleHost) 108 | 109 | LogUtils.info("TKK update failed $error") 110 | Notifications.Bus.notify( 111 | Notification( 112 | NOTIFICATION_DISPLAY_ID, 113 | "TKK", 114 | "notification.ttk.update.failed $error", 115 | NotificationType.ERROR 116 | ) 117 | ) 118 | 119 | null 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * 计算tk值. 126 | */ 127 | fun String.tk(tkk: Pair = TKK.value): String { 128 | val a = mutableListOf() 129 | var b = 0 130 | while (b < length) { 131 | var c = this[b].code 132 | if (128 > c) { 133 | a += c.toLong() 134 | } else { 135 | if (2048 > c) { 136 | a += (c shr 6 or 192).toLong() 137 | } else { 138 | if (55296 == (c and 64512) && b + 1 < length && 56320 == (this[b + 1].code and 64512)) { 139 | c = 65536 + ((c and 1023) shl 10) + (this[++b].code and 1023) 140 | a += (c shr 18 or 240).toLong() 141 | a += (c shr 12 and 63 or 128).toLong() 142 | } else { 143 | a += (c shr 12 or 224).toLong() 144 | } 145 | a += (c shr 6 and 63 or 128).toLong() 146 | } 147 | a += (c and 63 or 128).toLong() 148 | } 149 | 150 | b++ 151 | } 152 | 153 | val (d, e) = tkk 154 | var f = d 155 | for (h in a) { 156 | f += h 157 | f = `fun`(f, "+-a^+6") 158 | } 159 | f = `fun`(f, "+-3^+b+-f") 160 | f = f xor e 161 | if (0 > f) { 162 | f = (f and Int.MAX_VALUE.toLong()) + Int.MAX_VALUE.toLong() + 1 163 | } 164 | f = (f % 1E6).toLong() 165 | 166 | return "$f.${f xor d}" 167 | } 168 | 169 | val googleHost: String 170 | get() = if (PolyglotSettingState.getInstance().isCnPreferred) { 171 | GOOGLE_HOST_CN 172 | } else { 173 | GOOGLE_HOST 174 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | me.bytebeats.polyglot 3 | Polyglot Translator 4 | 1.3.6 5 | bytebeats 6 | 7 | 8 | 10 | An Intellij platform plugin project.
11 | If your project is relevant to many languages in which translation is needed, this plugin will help you a lot.
12 | 一个 Intellij 插件项目, 当工程需要支持多语言时, 本插件能够帮助你省去在浏览器或者翻译软件与你的项目之间来回切换的麻烦.
13 | A good plugin is No.1 productive force. 插件是第一生产力啊!
14 | Introduction: https://github.com/bytebeats/polyglot
15 | ]]>
16 | 17 | 19 | 提供了百度, 必应, 谷歌, 搜狗, 有道, 欧米, 腾讯等多种翻译器来提供翻译服务.
20 | v1.0.1 updated configure files.
21 | 更新了相关配置.
22 | v1.0.2 supported switching source and target languages.
23 | v1.1.0 offers daily quote service for English lover.
24 | v1.1.1 ui bugs.
25 | v1.1.2 preferred language.
26 | v1.2.0 automatically detect source language.
27 | v1.3.0 consult a dictionary to translate the selected words.
28 | v1.3.1 optimized translation result.
29 | v1.3.2, v1.3.3 bugs fixed.
30 | v1.3.4 update configurations and refactor codes.
31 | v1.3.6 project upgrade.
32 | ]]> 33 |
34 | 35 | 37 | com.intellij.modules.platform 38 | 39 | 40 | 42 | 43 | 47 | 48 | 52 | 53 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
-------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon_dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/arrow_vertical_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebeats/polyglot/e1a808a5009817dd5b1881ac9d9fd48b856f4a73/src/main/resources/arrow_vertical_switch.png -------------------------------------------------------------------------------- /src/main/resources/icon_polyglot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebeats/polyglot/e1a808a5009817dd5b1881ac9d9fd48b856f4a73/src/main/resources/icon_polyglot.png --------------------------------------------------------------------------------