Skip to content

Commit

Permalink
deepl
Browse files Browse the repository at this point in the history
  • Loading branch information
Weathercold committed Dec 15, 2023
1 parent ca69772 commit a7e87a2
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 83 deletions.
28 changes: 13 additions & 15 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
/target
/classes
/checkouts
/server
profiles.clj
pom.xml
pom.xml.asc
*.jar
*.class
/.nrepl-port
/.prepl-port
.hgignore
.hg/

.cache
.temp
/.direnv

/.idea/*
!/.idea/runConfigurations
/.direnv
/.calva
/.clj-kondo/http-kit
/.lsp
/.nrepl-port
/.prepl-port
/target
/classes
/checkouts
/server

!/.idea/clojure_compiler.xml
!/.idea/discord.xml
!/.idea/extra-logging.iml
!/.idea/jarRepositories.xml
!/.idea/misc.xml
!/.idea/modules.xml
/.lsp
/.calva
profiles.clj
1 change: 1 addition & 0 deletions .idea/extra-logging.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/runConfigurations/Desktop.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions assets/bundles/bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,8 @@ setting.extra-eventloglevel.name = Event log level
category.extra-translation = Translation
setting.extra-enabletranslation.name = Enable chat translation
setting.extra-deeplapikey.name = DeepL API key
setting.extra-deeplapikey.description = \
You need a free or pro API key to use DeepL translation. You can get one for free at\n\
[accent]https://www.deepl.com/pro-checkout/account?productId=1200&yearly=false&trial=false[]\n\
DeepL translation takes precedence over libretranslate whenever possible.
1 change: 0 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
:profiles {:dev {:plugins [[lein-kibit "0.1.8"]]}
:provided {:dependencies [[com.github.Anuken.Mindustry/core "v146"]
[com.github.Anuken.Arc/arc-core "v146"]
;; This is for task linting, you can remove this
[leiningen "2.10.0"]]}}

:main nil
Expand Down
6 changes: 3 additions & 3 deletions src/clojure/logging/core.clj
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
(ns logging.core
(:require (logging.core [events-log :as events-log]
(:require (logging.core [event-logging :as elog]
[log-handler :as log-handler]
[repl :as repl]
[settings :as settings]
[translating :as tl])
[translation :as tl])
[logging.util.lambdas :refer [consfn]]
[logging.util.log :refer [debug]])
(:import (arc Events)
Expand All @@ -15,7 +15,7 @@
(log-handler/-main)
(debug "Creating mod")
(repl/-main)
(events-log/-main)
(elog/-main)
(Events/on EventType$DisposeEvent (consfn [_] (shutdown-agents))))

(defn -init []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
(ns logging.core.events-log
(ns logging.core.event-logging
"Require as elog."
(:require [clojure.string :as str]
[logging.core.setting :refer [defsetting]]
[logging.util.lambdas :refer [consfn]]
Expand Down
23 changes: 13 additions & 10 deletions src/clojure/logging/core/settings.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
Note that although all the vars are thread-safe, this mod is largely
single-threaded like the game itself, so thread-safety is not really a
concern. (why did I use refs then)"
(:require (logging.core [events-log :as events-log]
(:require (logging.core [event-logging :as elog]
[log-handler :as log-handler]
[repl :as repl]
[setting :refer [set-setting]]
[translating :as tl])
[translation :as tl])
[logging.core.translation.deepl :as deepl]
(logging.util [lambdas :refer [cons1 s-proc]]
[log :as log]
[task-queue :as tq]))
Expand Down Expand Up @@ -56,13 +57,14 @@
(text-pref repl/port)

(add-category "extra-eventlogging")
(check-pref events-log/enable)
(slider-pref events-log/log-level 0 4
(check-pref elog/enable)
(slider-pref elog/log-level 0 4
(s-proc #(.name ^Enum (nth (Log$LogLevel/values) %))))
(area-pref events-log/listening-events)
(area-pref elog/listening-events)

(add-category "extra-translation")
(check-pref tl/enable-translation)))
(check-pref tl/enable)
(text-pref deepl/api-key)))

(defn refresh! []
(dosync
Expand All @@ -80,11 +82,12 @@
repl/address
repl/port

events-log/enable
events-log/log-level
events-log/listening-events
elog/enable
elog/log-level
elog/listening-events

tl/enable-translation]))
tl/enable
deepl/api-key]))
(tq/exec))

(defn -init []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
(ns logging.core.translating
"Frontend to the backends in logging.core.translating. There may be a DeepL
wrapper in the future.
(ns logging.core.translation
"Frontend to the backends in logging.core.translatione.translation.
Require as tl."
(:require [clojure.string :as str]
[logging.core.setting :refer [defsetting]]
[logging.core.translating.libretranslate :as libretranslate]
[logging.core.translation.deepl :as deepl]
[logging.core.translation.libretranslate :as libretranslate]
(logging.util [color :as color]
[lambdas :refer [command-runner consfn]]
[log :refer [info]]
[log :refer [debug info]]
[macros :refer [if-let' when-let']])
[logging.vars :refer [is-foo]])
(:import (arc Core Events)
Expand All @@ -19,20 +19,20 @@
(mindustry.gen Call)
(mindustry.ui.fragments ChatFragment)))

(def foo-enable-translation
(def foo-translation
"Whether chat translation of foo's client is enabled."
(delay
(and @is-foo
(try (Reflect/get (Class/forName "mindustry.client.ClientVars") "enableTranslation")
(catch Exception _ false)))))
(def backend :backends/libretranslate)
(def supported-langs (atom #{"en" "fr" "de" "ru" "zh" "ja" "ko"}))
(def target-lang (atom "en"))
(def default-backend (ref :backends/libretranslate))
(def supported-langs (ref #{"en" "fr" "de" "ru" "zh" "ja" "ko"}))
(def target-lang (ref "en"))

(defsetting enable-translation "extra-enabletranslation" true
(defsetting enable "extra-enabletranslation" true
#(and
%
(not @foo-enable-translation)
(not @foo-translation)
(not Vars/headless)))

;; Disable reflection warning, foo methods can't be statically resolved
Expand All @@ -47,56 +47,71 @@
(.addMessage (.-chatfrag Vars/ui) error "Error" Color/scarlet "" error))
(.addMessage (.-chatfrag Vars/ui) "[scarlet]Error:[] no message found.")))
(set! *warn-on-reflection* true)
(defn- send-chat [s] (Call/sendChatMessage (str s " [gray](translated)")))
(defn- send-translation [s] (Call/sendChatMessage (str s " [gray](translated)")))

(defmulti languages (constantly backend))
(defmethod languages :backends/libretranslate
([] (libretranslate/languages identity))
([callback] (libretranslate/languages callback)))
(defn languages [{:keys [backend callback]
:or {backend @default-backend, callback identity}}]
((condp = backend
:backends/libretranslate libretranslate/languages
:backends/deepl deepl/languages)
callback))

(defmulti translate (constantly backend))
(defmethod translate :backends/libretranslate
([s callback] (libretranslate/translate s "en" callback))
([s dst callback] (libretranslate/translate s dst callback)))
(defn translate [{:keys [backend s dst callback]
:or {backend @default-backend, dst "en", callback identity}}]
(debug "Translating @ to @..." s dst)
((condp = backend
:backends/libretranslate libretranslate/translate
:backends/deepl deepl/translate)
s dst callback))

(defn set-default-backend [backend]
(languages
{:backend backend
:callback (fn [langs]
(dosync
(ref-set default-backend backend)
(ref-set supported-langs langs)
(let [locale (.getLanguage (Locale/getDefault))]
(ref-set target-lang (if (langs locale) locale "en"))))
(info "Switched to @ for translation" backend))}))

(defn -init []
(when @foo-enable-translation (info "@extra-logging.footranslation"))
(when @foo-translation (info "@extra-logging.footranslation"))
(when Vars/headless (info "@extra-logging.headlesstranslation"))
(languages (fn [langs]
(reset! supported-langs langs)
(let [locale (.getLanguage (Locale/getDefault))]
(when (langs locale)
(reset! target-lang locale)))))
(deepl/usage #(set-default-backend (if (< %1 %2) :backends/deepl
:backends/libretranslate)))
(Events/on
EventType$PlayerChatEvent
(consfn [e]
(when-let' [^EventType$PlayerChatEvent
e e
_ (and @enable-translation
_ (and @enable
(not= (.-player e) Vars/player))
msg (color/remove-colors (.-message e))]
(translate msg @target-lang #(when-not (= % msg) (add-message %)))))))
(translate {:s msg
:dst @target-lang
:callback #(when (not= % msg) (add-message %))})))))

(defn -register-client-commands [^CommandHandler handler]
(.register
handler "tl" "[lang] [message...]"
(.get Core/bundle "extra-logging.command.tl.description")
(command-runner
(fn [[a b] _]
;; The command should still be usable even with enable-translation set to false.
;; The command should still be usable even when translation is disabled.
(cond
b (if (@supported-langs a)
(translate b a send-chat)
(translate (str a " " b) send-chat))
a (translate a send-chat)
(translate {:s b :dst a :callback send-translation})
(translate {:s (str a " " b) :callback send-translation}))
a (translate {:s a :callback send-translation})
;; Translate last message in chat
:else (if-let' [msg (first (Reflect/get
ChatFragment (.-chatfrag Vars/ui)
"messages"))
unformatted (if @is-foo (Reflect/get msg "unformatted")
(second (str/split msg #":\S* " 2)))]
(translate
(color/remove-colors unformatted)
@target-lang
add-message)
{:s (color/remove-colors unformatted)
:dst @target-lang
:callback add-message})
(err-msg-not-found)))))))
59 changes: 59 additions & 0 deletions src/clojure/logging/core/translation/deepl.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
(ns logging.core.translation.deepl
(:require [clojure.string :as str]
[jsonista.core :as j]
[logging.core.setting :refer [defsetting]]
[logging.util.log :refer [err log warn]]
[org.httpkit.client :as http])
(:import (arc.util Log$LogLevel)))

(defsetting api-key "extra-deeplapikey" "")

(defn- fetch [api opts callback]
(http/request
(merge {:url (str (if (str/ends-with? @api-key ":fx")
"https://api-free.deepl.com/v2"
"https://api.deepl.com/v2")
api)
:headers {"Content-Type" "application/json"
"Authorization" (str "DeepL-Auth-Key " @api-key)}
:timeout 3000}
opts)
(fn [{:keys [status body error] {:keys [url]} :opts}]
(if error
(err "Unknown client-side error." error)
(let [[lvl ret]
(condp = status
200 [Log$LogLevel/debug
(callback (j/read-value body j/keyword-keys-object-mapper))]
429 (do
(warn "Rate limit reached. Retrying...")
[Log$LogLevel/debug @(fetch api opts callback)])
(do
(err "Unknown error. Falling back on libretranslate for this session.")
;; FIXME
((resolve 'logging.core.translation/set-default-backend) :backends/libretranslate)
[Log$LogLevel/err]))]
(log lvl "Response from @:\n@" url body)
ret)))))

(defn languages [callback]
(fetch "/languages?type=target"
{} #(->> %
(map :language)
(map str/lower-case)
set
callback)))

(defn usage [callback]
(if (empty? @api-key)
(callback 0 0)
(fetch "/usage" {} #(callback (:character_count %) (:character_limit %)))))

(defn translate [s dst callback]
(fetch "/translate"
{:method :post
:body (j/write-value-as-string
{:text [s]
:target_lang (str/upper-case dst)})
:filter (http/max-body-filter 1024)}
#(callback (-> % :translations first :text))))
Loading

0 comments on commit a7e87a2

Please sign in to comment.