├── .cross_compile.sh
├── .github
├── FUNDING.yaml
└── workflows
│ ├── ci.yaml
│ ├── docker.yaml
│ └── release.yaml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── compose.yaml
├── deeplx.service
├── go.mod
├── go.sum
├── img
├── 6a48ba28621f2465028f0.png
└── c5c19dd89df6fae1a256d.png
├── install.sh
├── main.go
├── me.missuo.deeplx.plist
├── service
├── config.go
└── service.go
└── translate
├── translate.go
├── types.go
└── utils.go
/.cross_compile.sh:
--------------------------------------------------------------------------------
1 |
2 | ###
3 | # @Author: Vincent Young
4 | # @Date: 2022-10-20 02:19:06
5 | # @LastEditors: Vincent Yang
6 | # @LastEditTime: 2024-03-20 16:52:40
7 | # @FilePath: /DeepLX/.cross_compile.sh
8 | # @Telegram: https://t.me/missuo
9 | #
10 | # Copyright © 2022 by Vincent, All Rights Reserved.
11 | ###
12 | set -e
13 |
14 | DIST_PREFIX="deeplx"
15 | DEBUG_MODE=${2}
16 | TARGET_DIR="dist"
17 | PLATFORMS="darwin/amd64 darwin/arm64 linux/386 linux/amd64 linux/arm64 linux/mips openbsd/amd64 openbsd/arm64 freebsd/amd64 freebsd/arm64 windows/386 windows/amd64"
18 |
19 | rm -rf ${TARGET_DIR}
20 | mkdir ${TARGET_DIR}
21 |
22 | for pl in ${PLATFORMS}; do
23 | export GOOS=$(echo ${pl} | cut -d'/' -f1)
24 | export GOARCH=$(echo ${pl} | cut -d'/' -f2)
25 | export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH}
26 | if [ "${GOOS}" == "windows" ]; then
27 | export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH}.exe
28 | fi
29 |
30 | echo "build => ${TARGET}"
31 | if [ "${DEBUG_MODE}" == "debug" ]; then
32 | CGO_ENABLED=0 go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \
33 | -ldflags "-w -s" .
34 | else
35 | CGO_ENABLED=0 go build -trimpath -o ${TARGET} \
36 | -ldflags "-w -s" .
37 | fi
38 | done
39 |
--------------------------------------------------------------------------------
/.github/FUNDING.yaml:
--------------------------------------------------------------------------------
1 | github: [missuo]
2 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | pull_request:
4 |
5 | name: CI
6 |
7 | jobs:
8 | build:
9 | name: Build
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout Code
13 | uses: actions/checkout@v3
14 |
15 | - name: Set up Go
16 | uses: actions/setup-go@v4
17 | with:
18 | go-version: '1.24.2'
19 |
20 | - name: Install golint
21 | run: go install golang.org/x/lint/golint@latest
22 |
23 | - name: Build
24 | run: go build ./...
25 |
26 | - name: Test
27 | run: go test ./...
28 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yaml:
--------------------------------------------------------------------------------
1 | name: Docker Image CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - dev
7 | tags:
8 | - 'v*'
9 |
10 | env:
11 | DOCKER_IMAGE_NAME: missuo/deeplx
12 | GHCR_IMAGE_NAME: ${{ github.repository }}
13 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
14 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
15 | GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 | GHCR_USERNAME: ${{ github.repository_owner }}
17 |
18 | jobs:
19 | docker_build:
20 | runs-on: ubuntu-latest
21 |
22 | steps:
23 | - name: Checkout
24 | uses: actions/checkout@v3
25 | with:
26 | fetch-depth: 0
27 |
28 | - name: Set up QEMU
29 | uses: docker/setup-qemu-action@v2
30 | with:
31 | platforms: all
32 |
33 | - name: Set up docker buildx
34 | id: buildx
35 | uses: docker/setup-buildx-action@v2
36 | with:
37 | version: latest
38 |
39 | - name: Login to DockerHub
40 | uses: docker/login-action@v2
41 | with:
42 | registry: docker.io
43 | username: ${{ env.DOCKER_USERNAME }}
44 | password: ${{ env.DOCKER_PASSWORD }}
45 |
46 | - name: Login to GHCR
47 | uses: docker/login-action@v2
48 | with:
49 | registry: ghcr.io
50 | username: ${{ env.GHCR_USERNAME }}
51 | password: ${{ env.GHCR_TOKEN }}
52 |
53 | - name: Docker meta
54 | id: meta
55 | uses: docker/metadata-action@v4
56 | with:
57 | # list of Docker images to use as base name for tags
58 | images: |
59 | docker.io/${{ env.DOCKER_IMAGE_NAME }}
60 | ghcr.io/${{ env.GHCR_IMAGE_NAME }}
61 | # generate Docker tags based on the following events/attributes
62 | tags: |
63 | type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
64 | type=pep440,pattern={{raw}},enable=${{ startsWith(github.ref, 'refs/tags/') }}
65 | type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}
66 |
67 | - name: Build and push
68 | uses: docker/build-push-action@v3
69 | with:
70 | context: .
71 | platforms: linux/amd64,linux/arm64
72 | push: true
73 | tags: ${{ steps.meta.outputs.tags }}
74 | labels: ${{ steps.meta.outputs.labels }}
75 | cache-from: type=gha
76 | cache-to: type=gha,mode=max
77 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - 'v*'
5 | pull_request:
6 |
7 | name: Release
8 | jobs:
9 |
10 | Build:
11 | if: startsWith(github.ref, 'refs/tags/v')
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - uses: actions/setup-go@v4
17 | with:
18 | go-version: "1.24.2"
19 |
20 | - run: bash .cross_compile.sh
21 |
22 | - name: Release
23 | uses: softprops/action-gh-release@v1
24 | with:
25 | draft: false
26 | generate_release_notes: true
27 | files: |
28 | dist/*
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | DeepLX
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.24.2 AS builder
2 | WORKDIR /go/src/github.com/OwO-Network/DeepLX
3 | COPY . .
4 | RUN go get -d -v ./
5 | RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o deeplx .
6 |
7 | FROM alpine:latest
8 | WORKDIR /app
9 | COPY --from=builder /go/src/github.com/OwO-Network/DeepLX/deeplx /app/deeplx
10 | CMD ["/app/deeplx"]
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OwO Network Limited
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
11 |
12 | [![GitHub Workflow][1]](https://github.com/OwO-Network/DeepLX/actions)
13 | [![Go Version][2]](https://github.com/OwO-Network/DeepLX/blob/main/go.mod)
14 | [![Go Report][3]](https://goreportcard.com/badge/github.com/OwO-Network/DeepLX)
15 | [![GitHub License][4]](https://github.com/OwO-Network/DeepLX/blob/main/LICENSE)
16 | [![Docker Pulls][5]](https://hub.docker.com/r/missuo/deeplx)
17 | [![Releases][6]](https://github.com/OwO-Network/DeepLX/releases)
18 |
19 | [1]: https://img.shields.io/github/actions/workflow/status/OwO-Network/DeepLX/release.yaml?logo=github
20 | [2]: https://img.shields.io/github/go-mod/go-version/OwO-Network/DeepLX?logo=go
21 | [3]: https://goreportcard.com/badge/github.com/OwO-Network/DeepLX
22 | [4]: https://img.shields.io/github/license/OwO-Network/DeepLX
23 | [5]: https://img.shields.io/docker/pulls/missuo/deeplx?logo=docker
24 | [6]: https://img.shields.io/github/v/release/OwO-Network/DeepLX?logo=smartthings
25 |
26 | ## How to use
27 |
28 | > \[!TIP]
29 | >
30 | > Learn more about [📘 Using DeepLX](https://deeplx.owo.network) by checking it out.
31 |
32 | ## Discussion Group
33 | [Telegram Group](https://t.me/+8KDGHKJCxEVkNzll)
34 |
35 | ## Acknowledgements
36 |
37 | ### Contributors
38 |
39 |
40 |
41 |
42 |
43 | ## Activity
44 | 
45 |
46 | ## License
47 | [](https://app.fossa.com/projects/git%2Bgithub.com%2FOwO-Network%2FDeepLX?ref=badge_large)
48 |
--------------------------------------------------------------------------------
/compose.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | deeplx:
3 | image: ghcr.io/owo-network/deeplx:latest
4 | restart: always
5 | ports:
6 | - "1188:1188"
7 | # environment:
8 | # - TOKEN=helloworld
9 | # - DL_SESSION=xxxxxx
10 |
--------------------------------------------------------------------------------
/deeplx.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=DeepLX Service
3 | After=network.target
4 |
5 | [Service]
6 | Type=simple
7 | Restart=always
8 | WorkingDirectory=/usr/bin/
9 | ExecStart=/usr/bin/deeplx
10 |
11 | [Install]
12 | WantedBy=multi-user.target
13 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/OwO-Network/DeepLX
2 |
3 | go 1.24.0
4 |
5 | toolchain go1.24.2
6 |
7 | require (
8 | github.com/abadojack/whatlanggo v1.0.1
9 | github.com/andybalholm/brotli v1.1.0
10 | github.com/gin-contrib/cors v1.6.0
11 | github.com/gin-gonic/gin v1.9.1
12 | github.com/imroc/req/v3 v3.48.0
13 | github.com/tidwall/gjson v1.14.3
14 | )
15 |
16 | require (
17 | github.com/bytedance/sonic v1.11.2 // indirect
18 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
19 | github.com/chenzhuoyu/iasm v0.9.1 // indirect
20 | github.com/cloudflare/circl v1.5.0 // indirect
21 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect
22 | github.com/gin-contrib/sse v0.1.0 // indirect
23 | github.com/go-playground/locales v0.14.1 // indirect
24 | github.com/go-playground/universal-translator v0.18.1 // indirect
25 | github.com/go-playground/validator/v10 v10.19.0 // indirect
26 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
27 | github.com/goccy/go-json v0.10.2 // indirect
28 | github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
29 | github.com/hashicorp/errwrap v1.1.0 // indirect
30 | github.com/hashicorp/go-multierror v1.1.1 // indirect
31 | github.com/json-iterator/go v1.1.12 // indirect
32 | github.com/klauspost/compress v1.17.9 // indirect
33 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect
34 | github.com/kr/text v0.2.0 // indirect
35 | github.com/leodido/go-urn v1.4.0 // indirect
36 | github.com/mattn/go-isatty v0.0.20 // indirect
37 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
38 | github.com/modern-go/reflect2 v1.0.2 // indirect
39 | github.com/onsi/ginkgo/v2 v2.20.2 // indirect
40 | github.com/pelletier/go-toml/v2 v2.1.1 // indirect
41 | github.com/quic-go/qpack v0.5.1 // indirect
42 | github.com/quic-go/quic-go v0.48.2 // indirect
43 | github.com/refraction-networking/utls v1.7.0 // indirect
44 | github.com/tidwall/match v1.1.1 // indirect
45 | github.com/tidwall/pretty v1.2.0 // indirect
46 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
47 | github.com/ugorji/go/codec v1.2.12 // indirect
48 | go.uber.org/mock v0.4.0 // indirect
49 | golang.org/x/arch v0.7.0 // indirect
50 | golang.org/x/crypto v0.36.0 // indirect
51 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
52 | golang.org/x/mod v0.21.0 // indirect
53 | golang.org/x/net v0.38.0 // indirect
54 | golang.org/x/sync v0.12.0 // indirect
55 | golang.org/x/sys v0.31.0 // indirect
56 | golang.org/x/text v0.23.0 // indirect
57 | golang.org/x/tools v0.25.0 // indirect
58 | google.golang.org/protobuf v1.34.1 // indirect
59 | gopkg.in/yaml.v3 v3.0.1 // indirect
60 | )
61 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/abadojack/whatlanggo v1.0.1 h1:19N6YogDnf71CTHm3Mp2qhYfkRdyvbgwWdd2EPxJRG4=
2 | github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH9B/K8tQB2uoc=
3 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
4 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
5 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
6 | github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
7 | github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A=
8 | github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
9 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
10 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
11 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
12 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
13 | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
14 | github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
15 | github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
16 | github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
17 | github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
18 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
19 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
20 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
21 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
22 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
23 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
24 | github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg=
25 | github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro=
26 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
27 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
28 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
29 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
30 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
31 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
32 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
33 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
34 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
35 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
36 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
37 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
38 | github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
39 | github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
40 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
41 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
42 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
43 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
44 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
45 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
46 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
47 | github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ=
48 | github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
49 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
50 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
51 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
52 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
53 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
54 | github.com/imroc/req/v3 v3.48.0 h1:IYuMGetuwLzOOTzDCquDqs912WNwpsPK0TBXWPIvoqg=
55 | github.com/imroc/req/v3 v3.48.0/go.mod h1:weam9gmyb00QnOtu6HXSnk44dNFkIUQb5QdMx13FeUU=
56 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
57 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
58 | github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
59 | github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
60 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
61 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
62 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
63 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
64 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
65 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
66 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
67 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
68 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
69 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
70 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
71 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
72 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
73 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
74 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
75 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
76 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
77 | github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
78 | github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
79 | github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
80 | github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
81 | github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
82 | github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
83 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
84 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
85 | github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
86 | github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
87 | github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
88 | github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
89 | github.com/refraction-networking/utls v1.7.0 h1:9JTnze/Md74uS3ZWiRAabityY0un69rOLXsBf8LGgTs=
90 | github.com/refraction-networking/utls v1.7.0/go.mod h1:lV0Gwc1/Fi+HYH8hOtgFRdHfKo4FKSn6+FdyOz9hRms=
91 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
92 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
93 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
94 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
95 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
96 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
97 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
98 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
99 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
100 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
101 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
102 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
103 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
104 | github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
105 | github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
106 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
107 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
108 | github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
109 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
110 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
111 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
112 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
113 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
114 | go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
115 | go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
116 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
117 | golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
118 | golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
119 | golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
120 | golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
121 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
122 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
123 | golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
124 | golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
125 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
126 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
127 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
128 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
129 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
130 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
131 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
132 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
133 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
134 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
135 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
136 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
137 | golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
138 | golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
139 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
140 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
141 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
142 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
143 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
144 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
145 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
146 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
147 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
148 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
149 |
--------------------------------------------------------------------------------
/img/6a48ba28621f2465028f0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OwO-Network/DeepLX/56637adc3c82c83bf26bcaf0cceb286ffc32c109/img/6a48ba28621f2465028f0.png
--------------------------------------------------------------------------------
/img/c5c19dd89df6fae1a256d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OwO-Network/DeepLX/56637adc3c82c83bf26bcaf0cceb286ffc32c109/img/c5c19dd89df6fae1a256d.png
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | ###
2 | # @Author: Vincent Young
3 | # @Date: 2023-02-12 09:53:21
4 | # @LastEditors: Vincent Young
5 | # @LastEditTime: 2023-02-12 10:01:57
6 | # @FilePath: /DeepLX/install.sh
7 | # @Telegram: https://t.me/missuo
8 | #
9 | # Copyright © 2023 by Vincent, All Rights Reserved.
10 | ###
11 |
12 | install_deeplx(){
13 | last_version=$(curl -Ls "https://api.github.com/repos/OwO-Network/DeepLX/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
14 | if [[ ! -n "$last_version" ]]; then
15 | echo -e "${red}Failed to detect DeepLX version, probably due to exceeding Github API limitations.${plain}"
16 | exit 1
17 | fi
18 | echo -e "DeepLX latest version: ${last_version}, Start install..."
19 | wget -q -N --no-check-certificate -O /usr/bin/deeplx https://github.com/OwO-Network/DeepLX/releases/download/${last_version}/deeplx_linux_amd64
20 |
21 | chmod +x /usr/bin/deeplx
22 | wget -q -N --no-check-certificate -O /etc/systemd/system/deeplx.service https://raw.githubusercontent.com/OwO-Network/DeepLX/main/deeplx.service
23 | systemctl daemon-reload
24 | systemctl enable deeplx
25 | systemctl start deeplx
26 | echo -e "Installed successfully, listening at 0.0.0.0:1188"
27 | }
28 | install_deeplx
29 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Vincent Yang
3 | * @Date: 2023-07-01 21:45:34
4 | * @LastEditors: Jason Lyu
5 | * @LastEditTime: 2025-04-08 13:45:00
6 | * @FilePath: /DeepLX/main.go
7 | * @Telegram: https://t.me/missuo
8 | * @GitHub: https://github.com/missuo
9 | *
10 | * Copyright © 2024 by Vincent, All Rights Reserved.
11 | */
12 |
13 | package main
14 |
15 | import (
16 | "fmt"
17 |
18 | "github.com/gin-gonic/gin"
19 |
20 | "github.com/OwO-Network/DeepLX/service"
21 | )
22 |
23 | func main() {
24 | cfg := service.InitConfig()
25 |
26 | fmt.Printf("DeepL X has been successfully launched! Listening on %v:%v\n", cfg.IP, cfg.Port)
27 | fmt.Println("Developed by sjlleo and missuo .")
28 |
29 | // Setting the application to release mode
30 | gin.SetMode(gin.ReleaseMode)
31 |
32 | app := service.Router(cfg)
33 | app.Run(fmt.Sprintf("%v:%v", cfg.IP, cfg.Port))
34 | }
35 |
--------------------------------------------------------------------------------
/me.missuo.deeplx.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | me.missuo.deeplx
7 | KeepAlive
8 |
9 | ProgramArguments
10 |
11 | /usr/local/bin/deeplx
12 |
13 | RunAtLoad
14 |
15 | OnDemand
16 |
17 | LaunchOnlyOnce
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/service/config.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Vincent Yang
3 | * @Date: 2024-04-23 00:39:03
4 | * @LastEditors: Jason Lyu
5 | * @LastEditTime: 2025-04-08 13:45:00
6 | * @FilePath: /DeepLX/config.go
7 | * @Telegram: https://t.me/missuo
8 | * @GitHub: https://github.com/missuo
9 | *
10 | * Copyright © 2024 by Vincent, All Rights Reserved.
11 | */
12 |
13 | package service
14 |
15 | import (
16 | "flag"
17 | "fmt"
18 | "os"
19 | )
20 |
21 | type Config struct {
22 | IP string
23 | Port int
24 | Token string
25 | DlSession string
26 | Proxy string
27 | }
28 |
29 | func InitConfig() *Config {
30 | cfg := &Config{
31 | IP: "0.0.0.0",
32 | Port: 1188,
33 | }
34 |
35 | // IP flag
36 | if ip, ok := os.LookupEnv("IP"); ok && ip != "" {
37 | cfg.IP = ip
38 | }
39 | flag.StringVar(&cfg.IP, "ip", cfg.IP, "set up the IP address to bind to")
40 | flag.StringVar(&cfg.IP, "i", cfg.IP, "set up the IP address to bind to")
41 |
42 | // Port flag
43 | if port, ok := os.LookupEnv("PORT"); ok && port != "" {
44 | fmt.Sscanf(port, "%d", &cfg.Port)
45 | }
46 | flag.IntVar(&cfg.Port, "port", cfg.Port, "set up the port to listen on")
47 | flag.IntVar(&cfg.Port, "p", cfg.Port, "set up the port to listen on")
48 |
49 | // DL Session flag
50 | flag.StringVar(&cfg.DlSession, "s", "", "set the dl-session for /v1/translate endpoint")
51 | if cfg.DlSession == "" {
52 | if dlSession, ok := os.LookupEnv("DL_SESSION"); ok {
53 | cfg.DlSession = dlSession
54 | }
55 | }
56 |
57 | // Access token flag
58 | flag.StringVar(&cfg.Token, "token", "", "set the access token for /translate endpoint")
59 | if cfg.Token == "" {
60 | if token, ok := os.LookupEnv("TOKEN"); ok {
61 | cfg.Token = token
62 | }
63 | }
64 |
65 | // HTTP Proxy flag
66 | flag.StringVar(&cfg.Proxy, "proxy", "", "set the proxy URL for HTTP requests")
67 | if cfg.Proxy == "" {
68 | if proxy, ok := os.LookupEnv("PROXY"); ok {
69 | cfg.Proxy = proxy
70 | }
71 | }
72 |
73 | flag.Parse()
74 | return cfg
75 | }
76 |
--------------------------------------------------------------------------------
/service/service.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Vincent Yang
3 | * @Date: 2023-07-01 21:45:34
4 | * @LastEditors: Jason Lyu
5 | * @LastEditTime: 2025-04-08 13:45:00
6 | * @FilePath: /DeepLX/main.go
7 | * @Telegram: https://t.me/missuo
8 | * @GitHub: https://github.com/missuo
9 | *
10 | * Copyright © 2024 by Vincent, All Rights Reserved.
11 | */
12 |
13 | package service
14 |
15 | import (
16 | "fmt"
17 | "log"
18 | "net/http"
19 | "net/url"
20 | "os"
21 | "strings"
22 |
23 | "github.com/gin-contrib/cors"
24 | "github.com/gin-gonic/gin"
25 |
26 | "github.com/OwO-Network/DeepLX/translate"
27 | )
28 |
29 | func authMiddleware(cfg *Config) gin.HandlerFunc {
30 | return func(c *gin.Context) {
31 | if cfg.Token != "" {
32 | providedTokenInQuery := c.Query("token")
33 | providedTokenInHeader := c.GetHeader("Authorization")
34 |
35 | // Compatability with the Bearer token format
36 | if providedTokenInHeader != "" {
37 | parts := strings.Split(providedTokenInHeader, " ")
38 | if len(parts) == 2 {
39 | if parts[0] == "Bearer" || parts[0] == "DeepL-Auth-Key" {
40 | providedTokenInHeader = parts[1]
41 | } else {
42 | providedTokenInHeader = ""
43 | }
44 | } else {
45 | providedTokenInHeader = ""
46 | }
47 | }
48 |
49 | if providedTokenInHeader != cfg.Token && providedTokenInQuery != cfg.Token {
50 | c.JSON(http.StatusUnauthorized, gin.H{
51 | "code": http.StatusUnauthorized,
52 | "message": "Invalid access token",
53 | })
54 | c.Abort()
55 | return
56 | }
57 | }
58 |
59 | c.Next()
60 | }
61 | }
62 |
63 | type PayloadFree struct {
64 | TransText string `json:"text"`
65 | SourceLang string `json:"source_lang"`
66 | TargetLang string `json:"target_lang"`
67 | TagHandling string `json:"tag_handling"`
68 | }
69 |
70 | type PayloadAPI struct {
71 | Text []string `json:"text"`
72 | TargetLang string `json:"target_lang"`
73 | SourceLang string `json:"source_lang"`
74 | TagHandling string `json:"tag_handling"`
75 | }
76 |
77 | func Router(cfg *Config) *gin.Engine {
78 | // Set Proxy
79 | proxyURL := os.Getenv("PROXY")
80 | if proxyURL == "" {
81 | proxyURL = cfg.Proxy
82 | }
83 | if proxyURL != "" {
84 | proxy, err := url.Parse(proxyURL)
85 | if err != nil {
86 | log.Fatalf("Failed to parse proxy URL: %v", err)
87 | }
88 | http.DefaultTransport = &http.Transport{
89 | Proxy: http.ProxyURL(proxy),
90 | }
91 | }
92 |
93 | if cfg.Token != "" {
94 | fmt.Println("Access token is set.")
95 | }
96 |
97 | r := gin.Default()
98 | r.Use(cors.Default())
99 |
100 | // Defining the root endpoint which returns the project details
101 | r.GET("/", func(c *gin.Context) {
102 | c.JSON(http.StatusOK, gin.H{
103 | "code": http.StatusOK,
104 | "message": "DeepL Free API, Developed by sjlleo and missuo. Go to /translate with POST. http://github.com/OwO-Network/DeepLX",
105 | })
106 | })
107 |
108 | // Free API endpoint, No Pro Account required
109 | r.POST("/translate", authMiddleware(cfg), func(c *gin.Context) {
110 | req := PayloadFree{}
111 | c.BindJSON(&req)
112 |
113 | sourceLang := req.SourceLang
114 | targetLang := req.TargetLang
115 | translateText := req.TransText
116 | tagHandling := req.TagHandling
117 |
118 | proxyURL := cfg.Proxy
119 |
120 | if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" {
121 | c.JSON(http.StatusBadRequest, gin.H{
122 | "code": http.StatusBadRequest,
123 | "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.",
124 | })
125 | return
126 | }
127 |
128 | result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, "")
129 | if err != nil {
130 | log.Fatalf("Translation failed: %s", err)
131 | }
132 |
133 | if result.Code == http.StatusOK {
134 | c.JSON(http.StatusOK, gin.H{
135 | "code": http.StatusOK,
136 | "id": result.ID,
137 | "data": result.Data,
138 | "alternatives": result.Alternatives,
139 | "source_lang": result.SourceLang,
140 | "target_lang": result.TargetLang,
141 | "method": result.Method,
142 | })
143 | } else {
144 | c.JSON(result.Code, gin.H{
145 | "code": result.Code,
146 | "message": result.Message,
147 | })
148 |
149 | }
150 | })
151 |
152 | // Pro API endpoint, Pro Account required
153 | r.POST("/v1/translate", authMiddleware(cfg), func(c *gin.Context) {
154 | req := PayloadFree{}
155 | c.BindJSON(&req)
156 |
157 | sourceLang := req.SourceLang
158 | targetLang := req.TargetLang
159 | translateText := req.TransText
160 | tagHandling := req.TagHandling
161 | proxyURL := cfg.Proxy
162 |
163 | dlSession := cfg.DlSession
164 |
165 | if tagHandling != "" && tagHandling != "html" && tagHandling != "xml" {
166 | c.JSON(http.StatusBadRequest, gin.H{
167 | "code": http.StatusBadRequest,
168 | "message": "Invalid tag_handling value. Allowed values are 'html' and 'xml'.",
169 | })
170 | return
171 | }
172 |
173 | cookie := c.GetHeader("Cookie")
174 | if cookie != "" {
175 | dlSession = strings.Replace(cookie, "dl_session=", "", -1)
176 | }
177 |
178 | if dlSession == "" {
179 | c.JSON(http.StatusUnauthorized, gin.H{
180 | "code": http.StatusUnauthorized,
181 | "message": "No dl_session Found",
182 | })
183 | return
184 | } else if strings.Contains(dlSession, ".") {
185 | c.JSON(http.StatusUnauthorized, gin.H{
186 | "code": http.StatusUnauthorized,
187 | "message": "Your account is not a Pro account. Please upgrade your account or switch to a different account.",
188 | })
189 | return
190 | }
191 |
192 | result, err := translate.TranslateByDeepLX(sourceLang, targetLang, translateText, tagHandling, proxyURL, dlSession)
193 | if err != nil {
194 | log.Fatalf("Translation failed: %s", err)
195 | }
196 |
197 | if result.Code == http.StatusOK {
198 | c.JSON(http.StatusOK, gin.H{
199 | "code": http.StatusOK,
200 | "id": result.ID,
201 | "data": result.Data,
202 | "alternatives": result.Alternatives,
203 | "source_lang": result.SourceLang,
204 | "target_lang": result.TargetLang,
205 | "method": result.Method,
206 | })
207 | } else {
208 | c.JSON(result.Code, gin.H{
209 | "code": result.Code,
210 | "message": result.Message,
211 | })
212 |
213 | }
214 | })
215 |
216 | // Free API endpoint, Consistent with the official API format
217 | r.POST("/v2/translate", authMiddleware(cfg), func(c *gin.Context) {
218 | proxyURL := cfg.Proxy
219 |
220 | var translateText string
221 | var targetLang string
222 |
223 | translateText = c.PostForm("text")
224 | targetLang = c.PostForm("target_lang")
225 |
226 | if translateText == "" || targetLang == "" {
227 | var jsonData struct {
228 | Text []string `json:"text"`
229 | TargetLang string `json:"target_lang"`
230 | }
231 |
232 | if err := c.BindJSON(&jsonData); err != nil {
233 | c.JSON(http.StatusBadRequest, gin.H{
234 | "code": http.StatusBadRequest,
235 | "message": "Invalid request payload",
236 | })
237 | return
238 | }
239 |
240 | translateText = strings.Join(jsonData.Text, "\n")
241 | targetLang = jsonData.TargetLang
242 | }
243 |
244 | result, err := translate.TranslateByDeepLX("", targetLang, translateText, "", proxyURL, "")
245 | if err != nil {
246 | log.Fatalf("Translation failed: %s", err)
247 | }
248 |
249 | if result.Code == http.StatusOK {
250 | c.JSON(http.StatusOK, gin.H{
251 | "translations": []map[string]interface{}{
252 | {
253 | "detected_source_language": result.SourceLang,
254 | "text": result.Data,
255 | },
256 | },
257 | })
258 | } else {
259 | c.JSON(result.Code, gin.H{
260 | "code": result.Code,
261 | "message": result.Message,
262 | })
263 | }
264 | })
265 |
266 | // Catch-all route to handle undefined paths
267 | r.NoRoute(func(c *gin.Context) {
268 | c.JSON(http.StatusNotFound, gin.H{
269 | "code": http.StatusNotFound,
270 | "message": "Path not found",
271 | })
272 | })
273 |
274 | return r
275 | }
276 |
--------------------------------------------------------------------------------
/translate/translate.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Vincent Young
3 | * @Date: 2024-09-16 11:59:24
4 | * @LastEditors: Vincent Yang
5 | * @LastEditTime: 2025-04-08 14:26:33
6 | * @FilePath: /DeepLX/translate/translate.go
7 | * @Telegram: https://t.me/missuo
8 | * @GitHub: https://github.com/missuo
9 | *
10 | * Copyright © 2024 by Vincent, All Rights Reserved.
11 | */
12 |
13 | package translate
14 |
15 | import (
16 | "bytes"
17 | "compress/flate"
18 | "compress/gzip"
19 | "fmt"
20 | "io"
21 | "net/http"
22 | "net/url"
23 | "strings"
24 |
25 | "github.com/abadojack/whatlanggo"
26 | "github.com/imroc/req/v3"
27 |
28 | "github.com/andybalholm/brotli"
29 | "github.com/tidwall/gjson"
30 | )
31 |
32 | // makeRequest makes an HTTP request to DeepL API
33 | func makeRequest(postData *PostData, proxyURL string, dlSession string) (gjson.Result, error) {
34 | urlFull := "https://www2.deepl.com/jsonrpc"
35 | postStr := formatPostString(postData)
36 |
37 | // Create a new req client
38 | client := req.C().SetTLSFingerprintRandomized()
39 |
40 | // Set headers
41 | headers := http.Header{
42 | "Content-Type": []string{"application/json"},
43 | "User-Agent": []string{"DeepL/1627620 CFNetwork/3826.500.62.2.1 Darwin/24.4.0"},
44 | "Accept": []string{"*/*"},
45 | "X-App-Os-Name": []string{"iOS"},
46 | "X-App-Os-Version": []string{"18.4.0"},
47 | "Accept-Language": []string{"en-US,en;q=0.9"},
48 | "Accept-Encoding": []string{"gzip, deflate, br"}, // Keep this!
49 | "X-App-Device": []string{"iPhone16,2"},
50 | "Referer": []string{"https://www.deepl.com/"},
51 | "X-Product": []string{"translator"},
52 | "X-App-Build": []string{"1627620"},
53 | "X-App-Version": []string{"25.1"},
54 | }
55 |
56 | if dlSession != "" {
57 | headers.Set("Cookie", "dl_session="+dlSession)
58 | }
59 |
60 | // Set proxy if provided
61 | if proxyURL != "" {
62 | proxy, err := url.Parse(proxyURL)
63 | if err != nil {
64 | return gjson.Result{}, err
65 | }
66 | client.SetProxyURL(proxy.String())
67 | }
68 |
69 | // Make the request
70 | r := client.R()
71 | r.Headers = headers
72 | resp, err := r.
73 | SetBody(bytes.NewReader([]byte(postStr))).
74 | Post(urlFull)
75 |
76 | if err != nil {
77 | return gjson.Result{}, err
78 | }
79 |
80 | var bodyReader io.Reader
81 | contentEncoding := resp.Header.Get("Content-Encoding")
82 | switch contentEncoding {
83 | case "br":
84 | bodyReader = brotli.NewReader(resp.Body)
85 | case "gzip":
86 | bodyReader, err = gzip.NewReader(resp.Body) // Use gzip.NewReader
87 | if err != nil {
88 | return gjson.Result{}, fmt.Errorf("failed to create gzip reader: %w", err)
89 | }
90 | case "deflate": // Less common, but good to handle
91 | bodyReader = flate.NewReader(resp.Body)
92 | default:
93 | bodyReader = resp.Body
94 | }
95 |
96 | body, err := io.ReadAll(bodyReader)
97 | if err != nil {
98 | return gjson.Result{}, fmt.Errorf("failed to read response body: %w", err)
99 | }
100 | return gjson.ParseBytes(body), nil
101 | }
102 |
103 | // TranslateByDeepLX performs translation using DeepL API
104 | func TranslateByDeepLX(sourceLang, targetLang, text string, tagHandling string, proxyURL string, dlSession string) (DeepLXTranslationResult, error) {
105 | if text == "" {
106 | return DeepLXTranslationResult{
107 | Code: http.StatusNotFound,
108 | Message: "No text to translate",
109 | }, nil
110 | }
111 |
112 | if tagHandling == "" {
113 | tagHandling = "plaintext"
114 | }
115 |
116 | // Split text by newlines and store them for later reconstruction
117 | textParts := strings.Split(text, "\n")
118 | var translatedParts []string
119 | var allAlternatives [][]string // Store alternatives for each part
120 |
121 | for _, part := range textParts {
122 | if strings.TrimSpace(part) == "" {
123 | translatedParts = append(translatedParts, "")
124 | allAlternatives = append(allAlternatives, []string{""})
125 | continue
126 | }
127 |
128 | // Get detected language if source language is auto
129 | if sourceLang == "auto" || sourceLang == "" {
130 | sourceLang = strings.ToUpper(whatlanggo.DetectLang(part).Iso6391())
131 | }
132 |
133 | // Prepare jobs from split result
134 | var jobs []Job
135 |
136 | jobs = append(jobs, Job{
137 | Kind: "default",
138 | PreferredNumBeams: 4,
139 | RawEnContextBefore: []string{},
140 | RawEnContextAfter: []string{},
141 | Sentences: []Sentence{{
142 | Prefix: "",
143 | Text: text,
144 | ID: 0,
145 | }},
146 | })
147 |
148 | hasRegionalVariant := false
149 | targetLangCode := targetLang
150 | targetLangParts := strings.Split(targetLang, "-")
151 | if len(targetLangParts) > 1 {
152 | targetLangCode = targetLangParts[0]
153 | hasRegionalVariant = true
154 | }
155 |
156 | // Prepare translation request
157 | id := getRandomNumber()
158 |
159 | postData := &PostData{
160 | Jsonrpc: "2.0",
161 | Method: "LMT_handle_jobs",
162 | ID: id,
163 | Params: Params{
164 | CommonJobParams: CommonJobParams{
165 | Mode: "translate",
166 | Formality: "undefined",
167 | TranscribeAs: "romanize",
168 | AdvancedMode: false,
169 | TextType: tagHandling,
170 | WasSpoken: false,
171 | },
172 | Lang: Lang{
173 | SourceLangUserSelected: "auto",
174 | TargetLang: strings.ToUpper(targetLangCode),
175 | SourceLangComputed: strings.ToUpper(sourceLang),
176 | },
177 | Jobs: jobs,
178 | Timestamp: getTimeStamp(getICount(part)),
179 | },
180 | }
181 |
182 | if hasRegionalVariant {
183 | postData = &PostData{
184 | Jsonrpc: "2.0",
185 | Method: "LMT_handle_jobs",
186 | ID: id,
187 | Params: Params{
188 | CommonJobParams: CommonJobParams{
189 | Mode: "translate",
190 | Formality: "undefined",
191 | TranscribeAs: "romanize",
192 | AdvancedMode: false,
193 | TextType: tagHandling,
194 | WasSpoken: false,
195 | RegionalVariant: targetLang,
196 | },
197 | Lang: Lang{
198 | SourceLangUserSelected: "auto",
199 | TargetLang: strings.ToUpper(targetLangCode),
200 | SourceLangComputed: strings.ToUpper(sourceLang),
201 | },
202 | Jobs: jobs,
203 | Timestamp: getTimeStamp(getICount(part)),
204 | },
205 | }
206 | }
207 |
208 | // Make translation request
209 | result, err := makeRequest(postData, proxyURL, dlSession)
210 | if err != nil {
211 | return DeepLXTranslationResult{
212 | Code: http.StatusServiceUnavailable,
213 | Message: err.Error(),
214 | }, nil
215 | }
216 |
217 | // Process translation results
218 | var partTranslation string
219 | var partAlternatives []string
220 |
221 | translations := result.Get("result.translations").Array()
222 | if len(translations) > 0 {
223 | // Process main translation
224 | for _, translation := range translations {
225 | partTranslation += translation.Get("beams.0.sentences.0.text").String() + " "
226 | }
227 | partTranslation = strings.TrimSpace(partTranslation)
228 |
229 | // Process alternatives
230 | numBeams := len(translations[0].Get("beams").Array())
231 | for i := 1; i < numBeams; i++ { // Start from 1 since 0 is the main translation
232 | var altText string
233 | for _, translation := range translations {
234 | beams := translation.Get("beams").Array()
235 | if i < len(beams) {
236 | altText += beams[i].Get("sentences.0.text").String() + " "
237 | }
238 | }
239 | if altText != "" {
240 | partAlternatives = append(partAlternatives, strings.TrimSpace(altText))
241 | }
242 | }
243 | }
244 |
245 | if partTranslation == "" {
246 | return DeepLXTranslationResult{
247 | Code: http.StatusServiceUnavailable,
248 | Message: "Translation failed",
249 | }, nil
250 | }
251 |
252 | translatedParts = append(translatedParts, partTranslation)
253 | allAlternatives = append(allAlternatives, partAlternatives)
254 | }
255 |
256 | // Join all translated parts with newlines
257 | translatedText := strings.Join(translatedParts, "\n")
258 |
259 | // Combine alternatives with proper newline handling
260 | var combinedAlternatives []string
261 | maxAlts := 0
262 | for _, alts := range allAlternatives {
263 | if len(alts) > maxAlts {
264 | maxAlts = len(alts)
265 | }
266 | }
267 |
268 | // Create combined alternatives preserving line structure
269 | for i := 0; i < maxAlts; i++ {
270 | var altParts []string
271 | for j, alts := range allAlternatives {
272 | if i < len(alts) {
273 | altParts = append(altParts, alts[i])
274 | } else if len(translatedParts[j]) == 0 {
275 | altParts = append(altParts, "") // Keep empty lines
276 | } else {
277 | altParts = append(altParts, translatedParts[j]) // Use main translation if no alternative
278 | }
279 | }
280 | combinedAlternatives = append(combinedAlternatives, strings.Join(altParts, "\n"))
281 | }
282 |
283 | return DeepLXTranslationResult{
284 | Code: http.StatusOK,
285 | ID: getRandomNumber(), // Using new ID for the complete translation
286 | Data: translatedText,
287 | Alternatives: combinedAlternatives,
288 | SourceLang: sourceLang,
289 | TargetLang: targetLang,
290 | Method: map[bool]string{true: "Pro", false: "Free"}[dlSession != ""],
291 | }, nil
292 | }
293 |
--------------------------------------------------------------------------------
/translate/types.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Vincent Young
3 | * @Date: 2024-09-16 11:59:24
4 | * @LastEditors: Vincent Yang
5 | * @LastEditTime: 2025-03-01 04:16:07
6 | * @FilePath: /DeepLX/translate/types.go
7 | * @Telegram: https://t.me/missuo
8 | * @GitHub: https://github.com/missuo
9 | *
10 | * Copyright © 2024 by Vincent, All Rights Reserved.
11 | */
12 |
13 | package translate
14 |
15 | // Lang represents the language settings for translation
16 | type Lang struct {
17 | SourceLangUserSelected string `json:"source_lang_user_selected"` // Can be "auto"
18 | TargetLang string `json:"target_lang"`
19 | SourceLangComputed string `json:"source_lang_computed,omitempty"`
20 | }
21 |
22 | // CommonJobParams represents common parameters for translation jobs
23 | type CommonJobParams struct {
24 | Formality string `json:"formality"` // Can be "undefined"
25 | TranscribeAs string `json:"transcribe_as"`
26 | Mode string `json:"mode"`
27 | WasSpoken bool `json:"wasSpoken"`
28 | AdvancedMode bool `json:"advancedMode"`
29 | TextType string `json:"textType"`
30 | RegionalVariant string `json:"regionalVariant,omitempty"`
31 | }
32 |
33 | // Sentence represents a sentence in the translation request
34 | type Sentence struct {
35 | Prefix string `json:"prefix"`
36 | Text string `json:"text"`
37 | ID int `json:"id"`
38 | }
39 |
40 | // Job represents a translation job
41 | type Job struct {
42 | Kind string `json:"kind"`
43 | PreferredNumBeams int `json:"preferred_num_beams"`
44 | RawEnContextBefore []string `json:"raw_en_context_before"`
45 | RawEnContextAfter []string `json:"raw_en_context_after"`
46 | Sentences []Sentence `json:"sentences"`
47 | }
48 |
49 | // Params represents parameters for translation requests
50 | type Params struct {
51 | CommonJobParams CommonJobParams `json:"commonJobParams"`
52 | Lang Lang `json:"lang"`
53 | Jobs []Job `json:"jobs"`
54 | Timestamp int64 `json:"timestamp"`
55 | }
56 |
57 | // PostData represents the complete translation request
58 | type PostData struct {
59 | Jsonrpc string `json:"jsonrpc"`
60 | Method string `json:"method"`
61 | ID int64 `json:"id"`
62 | Params Params `json:"params"`
63 | }
64 |
65 | // TranslationResponse represents the response from translation
66 | type TranslationResponse struct {
67 | Jsonrpc string `json:"jsonrpc"`
68 | ID int64 `json:"id"`
69 | Result struct {
70 | Translations []struct {
71 | Beams []struct {
72 | Sentences []SentenceResponse `json:"sentences"`
73 | NumSymbols int `json:"num_symbols"`
74 | RephraseVariant struct { // Added rephrase_variant
75 | Name string `json:"name"`
76 | } `json:"rephrase_variant"`
77 | } `json:"beams"`
78 | Quality string `json:"quality"` // Added quality
79 | } `json:"translations"`
80 | TargetLang string `json:"target_lang"`
81 | SourceLang string `json:"source_lang"`
82 | SourceLangIsConfident bool `json:"source_lang_is_confident"`
83 | DetectedLanguages map[string]interface{} `json:"detectedLanguages"` // Use interface{} for now
84 | } `json:"result"`
85 | }
86 |
87 | // SentenceResponse is a helper struct for the response sentences
88 | type SentenceResponse struct {
89 | Text string `json:"text"`
90 | IDS []int `json:"ids"` // Added IDS
91 | }
92 |
93 | // DeepLXTranslationResult represents the final translation result
94 | type DeepLXTranslationResult struct {
95 | Code int `json:"code"`
96 | ID int64 `json:"id"`
97 | Message string `json:"message,omitempty"`
98 | Data string `json:"data"` // The primary translated text
99 | Alternatives []string `json:"alternatives"` // Other possible translations
100 | SourceLang string `json:"source_lang"`
101 | TargetLang string `json:"target_lang"`
102 | Method string `json:"method"`
103 | }
104 |
--------------------------------------------------------------------------------
/translate/utils.go:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Vincent Young
3 | * @Date: 2024-09-16 11:59:24
4 | * @LastEditors: Vincent Yang
5 | * @LastEditTime: 2025-04-08 14:27:21
6 | * @FilePath: /DeepLX/translate/utils.go
7 | * @Telegram: https://t.me/missuo
8 | * @GitHub: https://github.com/missuo
9 | *
10 | * Copyright © 2024 by Vincent, All Rights Reserved.
11 | */
12 |
13 | package translate
14 |
15 | import (
16 | "encoding/json"
17 | "math/rand"
18 | "strings"
19 | "time"
20 | )
21 |
22 | // getICount returns the number of 'i' characters in the text
23 | func getICount(translateText string) int64 {
24 | return int64(strings.Count(translateText, "i"))
25 | }
26 |
27 | // getRandomNumber generates a random number for request ID
28 | func getRandomNumber() int64 {
29 | src := rand.NewSource(time.Now().UnixNano())
30 | rng := rand.New(src)
31 | num := rng.Int63n(99999) + 8300000
32 | return num * 1000
33 | }
34 |
35 | // getTimeStamp generates timestamp for request based on i count
36 | func getTimeStamp(iCount int64) int64 {
37 | ts := time.Now().UnixMilli()
38 | if iCount != 0 {
39 | iCount = iCount + 1
40 | return ts - ts%iCount + iCount
41 | }
42 | return ts
43 | }
44 |
45 | // formatPostString formats the request JSON string with specific spacing rules
46 | func formatPostString(postData *PostData) string {
47 | postBytes, _ := json.Marshal(postData)
48 | postStr := string(postBytes)
49 |
50 | if (postData.ID+5)%29 == 0 || (postData.ID+3)%13 == 0 {
51 | postStr = strings.Replace(postStr, `"method":"`, `"method" : "`, 1)
52 | } else {
53 | postStr = strings.Replace(postStr, `"method":"`, `"method": "`, 1)
54 | }
55 |
56 | return postStr
57 | }
58 |
--------------------------------------------------------------------------------