Skip to content

Commit

Permalink
feat!: Support for XML/HTML tags on the API calls and a myriad of oth…
Browse files Browse the repository at this point in the history
…er things (#20)

* 📝 Add badge to README.md

* 📝 change usage text

* 💪 Delete `stdin` flag

* 📝 Update README

* ⤴ Update version of library

* 🥷 divide package

* 🐛 fix test and ci

* 💪 Add test

* 🐛 Fix default config directory permission to 0755

To avoid "permission denied"

* Add renovate.json (#11)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore: use conventional commit (#15)

* Update module github.com/mattn/go-isatty to v0.0.19 (#12)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update module github.com/urfave/cli/v2 to v2.25.7 (#13)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update actions/checkout action to v4 (#16)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update actions/setup-go action to v4 (#17)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update goreleaser/goreleaser-action action to v5 (#18)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* build: update golang version

* ci: update ci

* ci: fix go-version

* fix(deps): update module github.com/mattn/go-isatty to v0.0.20 (#19)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore: update .goreleaser

* build: update go.sum

* Chore: small fixes
Mostly English spelling and getting rid of deprecated Go functions.

* chore: refactor code for returning error message
The idea now is to rely more on `net/http` and less on our own internal table.

* chore: major code refactoring
This essentially separates the actual API call from the translator bits, so we can now work on the extra nifty features we need.

* feat: add simple function to return usage

* feat: adding usage call

* chore: refactor code to avoid object ambiguities
New code requires passing structs representing JSON objects, instead of relying on loose interface conversions which may fail. Stricter is better!

* chore: add help for languages

* fix: add = to flag `type` usage line

* feat: adding autocomplete files

* doc: mentioned the autocompletion feature

* fix: correctly display versions and build dates
Note: I don’t know where the “builtBy” parameter comes from; currently, it needs to be force-pushed at bildtime with a -X tag to the linker.

* chore: refactor more code, add option for glossary
This was mostly meant as an experiment which can later be copied & pasted for other very similar options. apiCall() gained a new parameter, the method (because some things in the API stupidly use GET and not POST)

* docs: better organise the explanatons, add links

* chore: refactoring code — moving setup to “Before”

* feat: add more flag support, upgrade dependencies

* fix: revert changes: init must be done in main()
I’ve attempted to do all initialisation chores under the “Before:” for the main cli.App loop. However, this wasn’t retrieving the data properly. Moving everything  back to where it was in main().

* feat: major refactoring, we’ll get rid of settings
In essence, we can use and reuse the DeepLClient type/object as the ‘de facto’ settings structure, we just need to find a way to read/save settings (possibly with cli-altsrv)

* chore: adding ChatGPT-generated texts for testing

* feat: adding support for (simple) debugging

* test: test data in XML

* docs: update README with latest changes

* add readline package

* fix: interactive prompt now works with readline

* Update README.md

Correction submitted by @coderabbitai

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: upgrade to latest versions yadda yadda

* Bug: fix expected error text for DEEPL_TOKEN
I had changed the text in main.g0, but forgot to update it in main_test.go

* Docs: add backticks on comments

* Docs: changes suggested by @coderabbitai

* Docs: comments ending with period

* Fix: match correct error text (changed on main.go)

* Bug: possible scoping issues with deeplToken (?)
Not confirmed. But… this way we can be sure that it gets properly initialised and not scooped up into the “wrong” place…

* Chore: bump year to 2024

* Fix: add timeout as per @coderabbitai suggestion

* Fix: add try-catch as per @coderabbitai suggestion

* Chore: add test for Exists; err.Error() is redundant
… at least, when called with the text formatting functions derived from the `fmt` package.

* Docs: add comment

* Fix: check for edge case of empty string
As suggested by @coderabbitai
Also: return nil and not []string{}; we’re supposed to check for the `err` code, and nil is returned avoiding memory allocation of something that will never be used…

* Chore: use http.StatusXXX instead of numbers
It’s more idiomatic that way, even if not necessarily easier to read (everybody knows their HTTP error codes by heart, right? no? well, then perhaps it’s better to follow the usual practice of Go’s core developers…)

* Bug: missing `)`

* Bug: fix a testing bug
The reason for it was that a potential JSON error was not being correctly caught; this was flagged by the test suite, and therefore I sort of fixed it. Now it correctly passes all tests it’s supposed to pass :)

* Doc: fix stupid typo

* Docs: add comment made by @coderabbitai
Future TODO — have `Languages()` optionally reply in structured formats.

* Fix: error checking for writing configuration file
Caught by @coderabbitai

---------

Co-authored-by: mochi-MizLab <mochice.mls.ntl@gmail.com>
Co-authored-by: Osamu Takiya <takiya@toran.sakura.ne.jp>
Co-authored-by: Omochice <44566328+Omochice@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
6 people committed Apr 15, 2024
1 parent a3ad1ee commit 8c84d94
Show file tree
Hide file tree
Showing 19 changed files with 1,097 additions and 240 deletions.
23 changes: 23 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
charset = utf-8
indent_style = tab
indent_size = tab
tab_width = 4
trim_trailing_whitespace = true

# The property below is not yet universally supported
[*.md]
max_line_length = 108
word_wrap = true
# Markdown sometimes uses two spaces at the end to
# mark soft line breaks
trim_trailing_whitespace = false

[*.css]
indent_style = space
indent_size = 2
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ jobs:
- name: Dependencies
run: go get -v -t -d ./...
- name: Go test
run: go test -v ./...
run: go test -v -timeout 30m ./...
58 changes: 58 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,59 @@
~*
# Stupid macOS temporary files

# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


Icon?

# Thumbnails
._*
nohup.out

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# Stuff from the Nova editor
.nova
node_modules
package.json
package-lock.json
.eslintrc.yml
.prettierrc.json
.env

#
logs
testdata

profile.out
coverage.html
coverage.txt
delay.txt

*.log

# executables
deepl-translate-cli

# originally by @Omochice
dist/
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 Omochice
Copyright (c) 2021,2024 Omochice

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
106 changes: 82 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,125 @@
[![go-test](https://github.com/Omochice/deepl-translate-cli/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Omochice/deepl-translate-cli/actions/workflows/ci.yml)
[![goreleaser](https://github.com/Omochice/deepl-translate-cli/actions/workflows/autorelease.yml/badge.svg)](https://github.com/Omochice/deepl-translate-cli/actions/workflows/autorelease.yml)

# ️Deepl translate cli
# [DeepL](https://www.deepl.com) Translate CLI (Unofficial)

![sampleMovie](https://i.gyazo.com/09a4801d44e85980f83666dceda0166e.gif)


## Installation

### By [github release page](https://github.com/Omochice/deepl-translate-cli/releases)
### Via the [GitHub release page](https://github.com/Omochice/deepl-translate-cli/releases)

1. Download zipped file from [Releases](https://github.com/Omochice/deepl-translate-cli/releases).

2. Unzip downloaded file.

3. Move executable file into directory in PATH. (like `$HOME/.local/bin/`)
3. Move the executable file into a directory in your `PATH` (e.g., `$HOME/.local/bin/`).

### By `go install`
```sh

```console
go install github.com/Omochice/deepl-translate-cli@latest
```

## Usage
## Basic usage

1. Get deepl access token. See [here](https://www.deepl.com/docs-api).
1. First, [get a DeepL access token](https://www.deepl.com/docs-api). It looks like a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) with the characters `:fx` appended to it.

2. Set access token as `DEEPL_TOKEN`
2. Assign the access token to the `DEEPL_TOKEN` environment variable.

ex. in `Bash`.
e.g., in `bash`:

```bash
export DEEPL_TOKEN <YOUR TOKEN>
```console
export DEEPL_TOKEN=<YOUR DEEPL API TOKEN>
```

3. On the first run, if `<user home directory>/.config/deepl-translate-cli/setting.json` does not exist, make it automatically.
3. On the first run, if `$HOME/.config/deepl-translate-cli/setting.json` does not exist, it gets automatically created.

The format of the settings file is as shown below:

The format of setting file is below.
```json
{
"source_lang": "FILLIN",
"target_lang": "FILLIN"
"source_lang": "FILLIN",
"target_lang": "FILLIN"
}
```
For write setting file, see [this page](https://www.deepl.com/docs-api/translating-text/request/).

For all existing languages that can be translated, as well as their identifying tags, see [this page](https://www.deepl.com/docs-api/translating-text/request/). You can also query the server directly:

```console
deepl-translate-cli languages

4. If file path is not specified, load text from STDIN.
```

Currentry, only one path can be specified as argument.
4. If the filename path is not specified, text is read from `STDIN`.

Currently, only one file path can be specified as an argument.

- If you want to select `source_lang`/`target_lang` _without_ using the settings file, you can use the command-line parameters `--source_lang (-s)` and `target_lang (-t)` instead.

- If you want to use `source_lang`/`target_lang` without using setting file, try to use `--source_lang (-s)` or `target_lang (-t)` argument.

```console
cat <text.txt> | deepl-translate-cli --source_lang ES --target_lang DE
```

- If you use Pro plan, use `--pro` flag to switch endpoint URL.
_this feature is not tested because I use free plan._
- If you are a Pro plan user, switch to the correct endpoint URL with the `--pro` flag.

_**Note**: This feature has not been tested, because the developers only have a free plan._

```console
cat <text.txt> | deepl-translate-cli --pro

```

- Note that it's also possible to run `deepl-translate-cli` in interactive mode, when the input comes from a TTY and not a pipe. In this case, only the first sentence typed (terminated by pressing **ENTER**) will be sent via the API for translation. The before-mentioned flags will also be available in this mode.

## More advanced usage

`deepl-translate-cli` now includes more commands, namely,

- `deepl-translate-cli usage` which will query DeepL to return the number of characters still available for translations.
- `deepl-translate-cli languages` will show the languages currently supported by DeepL. By default, only the _source_ languages are listed; with the `--type target` flag, it will also show those languages (and variants) that are available as translation targets.
- `deepl-translate-cli glossary-language-pairs` retrieves the list of language pairs supported by the glossary feature. Right now, it only does that — you cannot use glossaries yet.

DeepL is also able to translate structured text, i.e. text inside HTML or XML tags. This requires using a few more parameters; see `./deepl-translate-cli translate --help` for a list of all the options. While all are supported and sent to DeepL for processing, there are many possible combinations (some of which make no sense) which haven't been thoroughly tested.

## Shell autocompletion (⚠️ experimental)

Under the `autocomplete` folder are three scripts to enable auto-completion (for `bash`, `zsh`, and PowerShell). To use these, do the following (the example is for `bash`):

```console
PROG=deepl-translate-cli source autocomplete/bash_autocomplete
```

## ⚠️ Warning! ⚠️

If you run the tests, these may actually use your API Token, and consume some of your monthly credits!

Make sure you call `deepl-translate-cli usage` every now and then, to be sure you're well within your limits (half a million characters per month for free accounts; however, unlike other services, Unicode characters just count as one character each!).

## TODO

- Support uploading documents for translation (the API allows that as well)
- Better configuration/settings support (the system, as it is now, offers too few choices)
- Make calls purely in JSON (as opposed to using `application/x-www-form-urlencoded` to post data, while retrieving the results in JSON)
- Write tests!
- Add more glossary-related options

## Known bugs 🪳

- When trying to run help _without_ a valid authentication token (which will be the case), the error message is confusing
- Help formatting is quite a bit off on many of the (larger) entries
- Wrong orders of parameters/commands give unexpected errors
- You can only give _one_ filename as input (to do more, you'll have to use shell scripting to browse through all files and feed them to `deepl-translate-cli`)
- The interactive command has some annoing quirks and just translates one single (non-structured) sentence; additionally, it has a _huge_ overhead (but it sort of works)

## Building

If you wish to embed the build's author in the executable binary (to distinguish _your_ build from someone else's), you can build this with

```console
go build -ldflags "-X main.TheBuilder=<YOUR NAME HERE>"
```

## Disclaimer

None of the developers are affiliated with [DeepL](https://www.deepl.com/) and this code should not be considered to represent an endorsement by DeepL or any of its affiliates, partners or subsidiaries. It is released in the hope that it might be helpful to the Go programming community (which lacks official support by DeepL at the time of writing), without any warranty whatsoever (see [LICENSE](./LICENSE) for more information).
35 changes: 35 additions & 0 deletions autocomplete/bash_autocomplete
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#! /bin/bash

: ${PROG:=$(basename ${BASH_SOURCE})}

# Macs have bash3 for which the bash-completion package doesn't include
# _init_completion. This is a minimal version of that function.
_cli_init_completion() {
COMPREPLY=()
_get_comp_words_by_ref "$@" cur prev words cword
}

_cli_bash_autocomplete() {
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
local cur opts base words
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if declare -F _init_completion >/dev/null 2>&1; then
_init_completion -n "=:" || return
else
_cli_init_completion -n "=:" || return
fi
words=("${words[@]:0:$cword}")
if [[ "$cur" == "-"* ]]; then
requestComp="${words[*]} ${cur} --generate-shell-completion"
else
requestComp="${words[*]} --generate-shell-completion"
fi
opts=$(eval "${requestComp}" 2>/dev/null)
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
return 0
fi
}

complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG
unset PROG
13 changes: 13 additions & 0 deletions autocomplete/powershell_autocomplete.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
$fn = $($MyInvocation.MyCommand.Name)
$name = $fn -replace "(.*)\.ps1$", '$1'
Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock {
param($commandName, $wordToComplete, $cursorPosition)
$other = "$wordToComplete --generate-shell-completion"
Try {
Invoke-Expression $other | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
} Catch {
Write-Error "Error generating completions: $_"
}
}
20 changes: 20 additions & 0 deletions autocomplete/zsh_autocomplete
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#compdef $PROG

_cli_zsh_autocomplete() {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-shell-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-shell-completion)}")
fi

if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
}

compdef _cli_zsh_autocomplete $PROG
Loading

0 comments on commit 8c84d94

Please sign in to comment.