├── .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 |
6 |
7 |
8 |
9 |
12 |
17 |
18 |
19 | true
20 | true
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Polyglot: to translate different languages with different translators!
2 |
3 | [](https://github.com/bytebeats/polyglot/commit/)
4 | [](https://github.com/bytebeats/polyglot/graphs/contributors/)
5 | [](https://github.com/bytebeats/polyglot/issues/)
6 | [](https://github.com/bytebeats/polyglot/)
7 | [](https://plugins.jetbrains.com/plugin/15036-polyglot-translator)
8 | [](https://plugins.jetbrains.com/plugin/15036-polyglot-translator)
9 | [](https://github.com/bytebeats/polyglot/network/)
10 | [](https://github.com/bytebeats/polyglot/stargazers/)
11 | [](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 | 
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 |  |  | 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 | [](https://starchart.cc/bytebeats/polyglot)
76 |
77 | ## Github Stars Sparklines
78 | [](https://stars.medv.io/bytebeats/polyglot)
79 |
80 | ## Contributors
81 | [](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 |
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 |
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
--------------------------------------------------------------------------------