Repository: AlexanderWillner/deepl-alfred-workflow2 Branch: master Commit: cf0cbcd907b2 Files: 11 Total size: 57.7 KB Directory structure: gitextract_q2yra2a5/ ├── .gitignore ├── .mdlrc ├── Deepl-Translate.alfred5workflow ├── Deepl-Translate.alfredworkflow ├── Makefile ├── README.md ├── deepl-write.sh ├── deepl.bats ├── deepl.sh ├── info4.plist └── info5.plist ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store info.plist ================================================ FILE: .mdlrc ================================================ rules "~MD013" ================================================ FILE: Makefile ================================================ SHELL=bash help: @echo "Some available commands:" @echo " * workflow : create workflow" @echo " * bats : run dynamic tests" @echo " * test : test shell scripts" @echo " * style : style shell scripts" @echo " * harden : harden shell scripts" @echo " * feedback : create a GitHub issue" workflow: dyntest workflow4 workflow5 workflow4: @rm -f Deepl-Translate.alfredworkflow @cp info4.plist info.plist @zip Deepl-Translate.alfredworkflow icon.png info.plist deepl.sh @rm info.plist workflow5: @rm -f Deepl-Translate.alfred5workflow @cp info5.plist info.plist @zip Deepl-Translate.alfred5workflow icon.png info.plist deepl.sh @rm info.plist feedback: @open https://github.com/alexanderwillner/deepl-alfred-workflow2/issues bats: @echo "Running dynamic tests..." @type bats >/dev/null 2>&1 || (echo "Run 'brew install bats-core' first." >&2 ; exit 1) @bats deepl.bats dyntest: @DEEPL_KEY= ./deepl.sh -l EN "Guten Morgen." 2>&1|grep "Good morning" @./deepl.sh -l EN "Guten Morgen." 2>&1|grep "Good morning" test: @echo "Running first round of shell checks..." @type shellcheck >/dev/null 2>&1 || (echo "Run 'brew install shellcheck' first." >&2 ; exit 1) @shellcheck -x *.sh @echo "Running second round of shell checks..." @type shellharden >/dev/null 2>&1 || (echo "Run 'brew install shellharden' first." >&2 ; exit 1) @shellharden --check deepl.sh harden: @shellharden --replace deepl.sh style: @type shfmt >/dev/null 2>&1 || (echo "Run 'brew install shfmt' first." >&2 ; exit 1) @shfmt -i 2 -w -s *.sh .PHONY: workflow feedback bats test harden style ================================================ FILE: README.md ================================================ # Alfred DeepL Translation Workflow [![Codacy Badge](https://app.codacy.com/project/badge/Grade/1772be5a3aa4423a8dd122877896ce36)](https://www.codacy.com/gh/AlexanderWillner/deepl-alfred-workflow2/dashboard?utm_source=github.com&utm_medium=referral&utm_content=AlexanderWillner/deepl-alfred-workflow2&utm_campaign=Badge_Grade) [![download](https://img.shields.io/github/downloads/AlexanderWillner/deepl-alfred-workflow2/total)](https://github.com/AlexanderWillner/deepl-alfred-workflow2/releases) ## Usage To activate this workflow use the default keyword ```dl```, enter the passage you wanna get translated and end the input with ```.``` (not needed if you've an API key). The source language will be inferred automatically and the target language can be configured. ![Search](Screenshot-Search.png) After 1-2 seconds you get the translation. This is just an example. ![Result](Screenshot-Result.png) Press ```↩``` to copy the result or ```⌘ + ↩``` to show result as big screen overlay. You can also translate any selected text within macOS by pressing ```⌃ + ⌥ + ⌘ + d```. Other languages are supported as well: ![Chinese](Screenshot-Chinese.png) To quickly change the target language, you can use the ```dll``` keyword: ![Result](Screenshot-DLL.png) ## Caveats Please note that the DeepL API is designed to translate up to 600 characters per minute and per customer only. This fact and generally some longer sentences might result in the message ```Error: Too many requests.``` (see #5). However, you can also get a (free or paid) [```API key```](https://www.deepl.com/pro-api) and configure it in the settings (see screenshot below). ## Installing the Workflow 1. Simply download the [last release](https://github.com/AlexanderWillner/deepl-alfred-workflow2/releases) 2. Unzip the file on your computer 3. Install `Deepl-Translate.alfredworkflow` by double-clicking the workflow file and clicking on "Import". You'll now see the workflow listed in the left sidebar of your Workflows preferences pane. Once imported, you may want to take a quick look at the workflow settings and setup what keyword you want to use. Further, you can change the target language in the settings as shown in this screenshot: ![Config](Screenshot-Config.png) ## Command Line ```shell $ # DEEPL_TARGET="FR" $ ./deepl.sh -l DE "This is just an example." { "items": [ { "uid": null, "arg": "Dies ist nur ein Beispiel.", "valid": "yes", "autocomplete": "autocomplete", "title": "Dies ist nur ein Beispiel." }, { "uid": null, "arg": "Das ist nur ein Beispiel.", "valid": "yes", "autocomplete": "autocomplete", "title": "Das ist nur ein Beispiel." }, { "uid": null, "arg": "Dies ist nur ein Beispiel dafür.", "valid": "yes", "autocomplete": "autocomplete", "title": "Dies ist nur ein Beispiel dafür." }, { "uid": null, "arg": "Dies ist nur ein exemplarisches Beispiel.", "valid": "yes", "autocomplete": "autocomplete", "title": "Dies ist nur ein exemplarisches Beispiel." } ] } ``` ## Important configuration variables * `DEEPL_KEY`: the DeepL API key * `DEEPL_PRO`: in case you have a professional DeepL account * `DEEPL_POSTFIX`: the character to the input should end with to mitigate #5 * `DEEPL_TARGET`: the target language of the default `dl` keyword ## Builing the workflow To create a modified version of the workflow, edit the files and run ```make workflow``` to create an updated workflow. ## Disclaimer DeepL is a product from DeepL GmbH. More info: [deepl.com/publisher.html](https://www.deepl.com/publisher.html) This package has been heavily inspired by [m9dfukc's DeepL Alfred Workflow](https://github.com/m9dfukc/deepl-alfred-workflow). ================================================ FILE: deepl-write.sh ================================================ #!/bin/bash # setup ####################################################################### #set -o errexit -o pipefail -o noclobber -o nounset LANGUAGE="${DEEPL_TARGET:-EN}" LANGUAGE_SOURCE="${DEEPL_SOURCE:-auto}" LANGUAGE_PREFERRED="${DEEPL_PREFERRED:-[\"DE\",\"EN\"]}" KEY="${DEEPL_KEY:-}" PRO="${DEEPL_PRO:-}" # see https://developers.deepl.com/docs/api-reference/translate/openapi-spec-for-text-translation FORMALITY="${DEEPL_FORMALITY:-prefer_less}" POSTFIX="${DEEPL_POSTFIX:-.}" VERSION="2.1.0" PATH="$PATH:/usr/local/bin/" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" ############################################################################### # helper functions ############################################################ function printJson() { echo '{"items": [{"uid": null,"arg": "'"$1"'","valid": "yes","autocomplete": "autocomplete","title": "'"$1"'"}]}' } ############################################################################### # parameters ################################################################## POSITIONAL=() while [[ $# -gt 0 ]]; do key="$1" case "$key" in -l | --lang) LANGUAGE="$2" shift # past argument shift # past value ;; *) # unknown option POSITIONAL+=("$1") # save it in an array for later shift # past argument ;; esac done set -- "${POSITIONAL[@]:-}" # restore positional parameters ############################################################################### # help ######################################################################## if [ -z "$1" ]; then echo "Home made DeepL CLI (${VERSION}; https://github.com/AlexanderWillner/deepl-alfred-workflow2)" echo "" echo "SYNTAX : $0 [-l language] " >&2 echo "Example: $0 -l EN \"This is just an example.\"" echo "" exit 1 fi ############################################################################### # process query ############################################################### query="$1" # shellcheck disable=SC2001 query="$(echo "$query" | sed 's/\"/\\\"/g')" # shellcheck disable=SC2001 query="$(echo "$query" | sed "s/'/\\\'/g")" query="$(echo "$query" | iconv -f utf-8-mac -t utf-8 | xargs)" if [[ $KEY = "" ]] && [[ $query != *"$POSTFIX" ]]; then printJson "End query with $POSTFIX" exit 2 fi ############################################################################### # prepare query ############################################################### # shellcheck disable=SC2001 query="$(echo "$query" | sed "s/\\$POSTFIX$//")" if [ "$KEY" = "" ]; then FORM_PARAM='' else FORM_PARAM='"formality": "'"$FORMALITY"'", ' fi data='{"jsonrpc":"2.0","method": "LMT_handle_jobs","params":{"jobs":[{"kind":"default","sentences":[{"text":"'"$query"'","id":1,"prefix":""}],"raw_en_context_before":[],"raw_en_context_after":[],"preferred_num_beams":1,"write_variant_requests":["main","variants"],"style_variant":"business"}],"lang":{"target_lang":"'"${LANGUAGE:-EN}"'","preference":{"weight":{},"default":"default"},"source_lang_computed":"'"${LANGUAGE:-EN}"'"},"priority":1,"commonJobParams":{"quality":"normal","regionalVariant":"en-US","mode":"write","browserType":1,"textType":"plaintext","style_variant":"business"},"timestamp":1739745925254},"id":20270141}' HEADER=( --compressed -H 'authority: write-free.www.deepl.com' -H 'Origin: https://write-free.www.deepl.com' -H 'Referer: https://www.deepl.com/' -H 'Accept: */*' -H 'Content-Type: application/json' -H 'Accept-Language: en-us' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15' ) ############################################################################### # query ####################################################################### if [ -n "$KEY" ]; then if [ "$PRO" = "1" ]; then url="https://api.deepl.com/v2/translate" else url="https://api-free.deepl.com/v2/translate" fi if [ ! -z "$DEEPL_HOST" ]; then url="$DEEPL_HOST/v2/translate" fi echo >&2 "curl -s -X POST '$url' -H 'Authorization: DeepL-Auth-Key $KEY' --data-urlencode 'text=$query' -d 'formality=$FORMALITY' -d 'target_lang=${LANGUAGE:-EN}'" result=$(curl -s -X POST "$url" -H "Authorization: DeepL-Auth-Key $KEY" --data-urlencode "text=$query" -d "formality=$FORMALITY" -d "target_lang=${LANGUAGE:-EN}") ret=$? if [[ "x$ret" != "x0" ]] || [[ "$result" == "" ]]; then echo >&2 "$ret: $result" http_code=$(curl -s -X POST "$url" -H "Authorization: DeepL-Auth-Key $KEY" --data-urlencode "text=$query" -d "target_lang=${LANGUAGE:-EN}" -d "formality=$FORMALITY" -w %{http_code} -o /dev/null) if [[ $http_code -eq 403 ]]; then printJson "Error: Invalid API key" exit 3 fi if [[ $ret -eq 6 ]]; then printJson "Error: DNS resolution failed - no Internet connection?" exit 4 fi printJson "Error Code $ret - HTTP Code $http_code" exit 5 fi osascript -l JavaScript -e 'function run(argv) { const translations = JSON.parse(argv[0])["translations"].map(item => ({ title: item["text"], arg: item["text"] })) return JSON.stringify({ items: translations }, null, 2) }' "$result" || echo >&2 "ERROR w/ key: result '$result', query '$query'" else echo >&2 "curl -s 'https://write-free.www.deepl.com/jsonrpc' '${HEADER[@]}' --data-binary $'$data'" result=$(curl -s 'https://write-free.www.deepl.com/jsonrpc' "${HEADER[@]}" --data-binary $"$data") ret=$? if [[ "x$ret" != "x0" ]] || [[ "$result" == "" ]]; then echo >&2 "$ret: $result" http_code=$(curl -s 'https://write-free.www.deepl.com/jsonrpc' "${HEADER[@]}" --data-binary $"$data" -w %{http_code} -o /dev/null) if [[ $ret -eq 6 ]]; then printJson "Error: DNS resolution failed - no Internet connection?" exit 6 fi printJson "Error Code $ret - HTTP Code $http_code" exit 7 fi if [[ $result == *'"error":{"code":'* ]]; then message="$(osascript -l JavaScript -e 'function run(argv) { return JSON.parse(argv[0])["error"]["message"] }')" printJson "Error: $message" exit 8 else osascript -l JavaScript -e 'function run(argv) { const translations = JSON.parse(argv[0])["result"]["translations"][0]["beams"].map(item => ({ title: item["sentences"], arg: item["sentences"] })) return JSON.stringify({ items: translations }, null, 2) }' "$result" || echo >&2 "ERROR w/o key: result '$result', query '$query'" fi fi ############################################################################### ================================================ FILE: deepl.bats ================================================ #!/usr/bin/env bats teardown() { sleep 1 } @test "No parameters" { run ./deepl.sh [[ "$status" -eq 1 ]] } @test "Missing dot" { run ./deepl.sh "Vogel" [[ "$status" -eq 0 ]] [[ "$output" == *"End query with a dot"* ]] } @test "Single Word" { run ./deepl.sh -l EN "Vogel." [[ "$status" -eq 0 ]] [[ "$output" == *"bird"* ]] } @test "Trailing spaces" { run ./deepl.sh " Vogel. " [[ "$status" -eq 0 ]] [[ "$output" == *"bird"* ]] } @test "Sentence" { run ./deepl.sh -l DE "Translate from any language." [[ "$status" -eq 0 ]] [[ "$output" == *"aus jeder Sprache"* ]] } @test "Umlaut source" { run ./deepl.sh -l EN "Erdöl." [[ "$status" -eq 0 ]] [[ "$output" == *"oil"* ]] } @test "Umlaut destination" { run ./deepl.sh -l DE "Oil." [[ "$status" -eq 0 ]] [[ "$output" == *"öl"* ]] } @test "Quote source" { run ./deepl.sh -l DE "I'll." [[ "$status" -eq 0 ]] [[ "$output" == *"Ich werde"* ]] } @test "Quote destination" { run ./deepl.sh -l EN "Ich werde." [[ "$status" -eq 0 ]] [[ "$output" == *"I'll be"* ]] } @test "Double quote source" { run ./deepl.sh -l EN '"Apfel".' [[ "$status" -eq 0 ]] [[ "$output" == *'\"Apple\"'* ]] } #todo: fixme #6 #@test "Spanisch" { # run ./deepl.sh -l EN "El tiempo es una ilusión." # [[ "$status" -eq 0 ]] # [[ "$output" == *'\"time\"'* ]] #} #todo: fixme #1 #@test "Long sentences" { # run ./deepl.sh -l DE "He felt that his whole life was some kind of dream and he sometimes wondered whose it was and whether they were enjoying it." # [[ "$status" -eq 0 ]] # [[ "$output" == *'\"Leben\"'* ]] #} #todo: fixme #1 #@test "Multi sentences" { # run ./deepl.sh -l DE "This planet has - or rather had - a problem, which was this: most of the people living on it were unhappy for pretty much of the time. Many solutions were suggested for this problem, but most of these were largely concerned with the movement of small green pieces of paper, which was odd because on the whole it wasn't the small green pieces of paper that were unhappy." # [[ "$status" -eq 0 ]] # [[ "$output" == *'\"unglücklich\"'* ]] #} ================================================ FILE: deepl.sh ================================================ #!/bin/bash # setup ####################################################################### #set -o errexit -o pipefail -o noclobber -o nounset LANGUAGE="${DEEPL_TARGET:-EN}" LANGUAGE_SOURCE="${DEEPL_SOURCE:-auto}" LANGUAGE_PREFERRED="${DEEPL_PREFERRED:-[\"DE\",\"EN\"]}" KEY="${DEEPL_KEY:-}" PRO="${DEEPL_PRO:-}" # see https://developers.deepl.com/docs/api-reference/translate/openapi-spec-for-text-translation FORMALITY="${DEEPL_FORMALITY:-prefer_less}" POSTFIX="${DEEPL_POSTFIX:-.}" VERSION="2.1.0" PATH="$PATH:/usr/local/bin/" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" ############################################################################### # helper functions ############################################################ function printJson() { echo '{"items": [{"uid": null,"arg": "'"$1"'","valid": "yes","autocomplete": "autocomplete","title": "'"$1"'"}]}' } ############################################################################### # parameters ################################################################## POSITIONAL=() while [[ $# -gt 0 ]]; do key="$1" case "$key" in -l | --lang) LANGUAGE="$2" shift # past argument shift # past value ;; *) # unknown option POSITIONAL+=("$1") # save it in an array for later shift # past argument ;; esac done set -- "${POSITIONAL[@]:-}" # restore positional parameters ############################################################################### # help ######################################################################## if [ -z "$1" ]; then echo "Home made DeepL CLI (${VERSION}; https://github.com/AlexanderWillner/deepl-alfred-workflow2)" echo "" echo "SYNTAX : $0 [-l language] " >&2 echo "Example: $0 -l DE \"This is just an example.\"" echo "" exit 1 fi ############################################################################### # process query ############################################################### query="$1" # shellcheck disable=SC2001 query="$(echo "$query" | sed 's/\"/\\\"/g')" # shellcheck disable=SC2001 query="$(echo "$query" | sed "s/'/\\\'/g")" query="$(echo "$query" | iconv -f utf-8-mac -t utf-8 | xargs)" if [[ $KEY = "" ]] && [[ $query != *"$POSTFIX" ]]; then printJson "End query with $POSTFIX" exit 2 fi ############################################################################### # prepare query ############################################################### # shellcheck disable=SC2001 query="$(echo "$query" | sed "s/\\$POSTFIX$//")" if [ "$KEY" = "" ]; then FORM_PARAM='' else FORM_PARAM='"formality": "'"$FORMALITY"'", ' fi data='{"jsonrpc":"2.0","method": "LMT_handle_jobs","params":{"commonJobParams": {'$FORM_PARAM'"browserType": 1, "mode": "translate", "textType": "plaintext"}, "jobs":[{"kind":"default","raw_en_sentence":"'"$query"'","preferred_num_beams":4,"raw_en_context_before":[],"raw_en_context_after":[],"quality":"fast"}],"lang":{"user_preferred_langs":'"${LANGUAGE_PREFERRED}"',"source_lang_user_selected":"'"${LANGUAGE_SOURCE}"'","target_lang":"'"${LANGUAGE:-EN}"'"},"priority":1,"timestamp":1557063997314},"id":79120002}' HEADER=( --compressed -H 'authority: www2.deepl.com' -H 'Origin: https://www.deepl.com' -H 'Referer: https://www.deepl.com/translator' -H 'Accept: */*' -H 'Content-Type: application/json' -H 'Accept-Language: en-us' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15' ) ############################################################################### # query ####################################################################### if [ -n "$KEY" ]; then if [ "$PRO" = "1" ]; then url="https://api.deepl.com/v2/translate" else url="https://api-free.deepl.com/v2/translate" fi if [ ! -z "$DEEPL_HOST" ]; then url="$DEEPL_HOST/v2/translate" fi echo >&2 "curl -s -X POST '$url' -H 'Authorization: DeepL-Auth-Key $KEY' --data-urlencode 'text=$query' -d 'formality=$FORMALITY' -d 'target_lang=${LANGUAGE:-EN}'" result=$(curl -s -X POST "$url" -H "Authorization: DeepL-Auth-Key $KEY" --data-urlencode "text=$query" -d "formality=$FORMALITY" -d "target_lang=${LANGUAGE:-EN}") ret=$? if [[ "x$ret" != "x0" ]] || [[ "$result" == "" ]]; then echo >&2 "$ret: $result" http_code=$(curl -s -X POST "$url" -H "Authorization: DeepL-Auth-Key $KEY" --data-urlencode "text=$query" -d "target_lang=${LANGUAGE:-EN}" -d "formality=$FORMALITY" -w %{http_code} -o /dev/null) if [[ $http_code -eq 403 ]]; then printJson "Error: Invalid API key" exit 3 fi if [[ $ret -eq 6 ]]; then printJson "Error: DNS resolution failed - no Internet connection?" exit 4 fi printJson "Error Code $ret - HTTP Code $http_code" exit 5 fi osascript -l JavaScript -e 'function run(argv) { try { const parsed = JSON.parse(argv[0]); if (!parsed || !parsed.translations) { throw new Error("Invalid response structure"); } const translations = parsed.translations.map(item => ({ title: item["text"], arg: item["text"] })); return JSON.stringify({ items: translations }, null, 2); } catch(e) { return JSON.stringify({ items: [{ title: "Error parsing response: " + e.message, arg: "error", valid: false }] }, null, 2); } }' "$result" || { echo >&2 "ERROR w/ key: result '$result', query '$query'" printJson "Error: Failed to parse translation response" exit 10 } else echo >&2 "curl -s 'https://www2.deepl.com/jsonrpc' '${HEADER[@]}' --data-binary $'$data'" result=$(curl -s 'https://www2.deepl.com/jsonrpc' "${HEADER[@]}" --data-binary $"$data") ret=$? echo >&2 "DEBUG: curl exit code: $ret" echo >&2 "DEBUG: result length: ${#result}" echo >&2 "DEBUG: result first 200 chars: ${result:0:200}" if [[ "x$ret" != "x0" ]] || [[ "$result" == "" ]]; then echo >&2 "$ret: $result" http_code=$(curl -s 'https://www2.deepl.com/jsonrpc' "${HEADER[@]}" --data-binary $"$data" -w %{http_code} -o /dev/null) if [[ $ret -eq 6 ]]; then printJson "Error: DNS resolution failed - no Internet connection?" exit 6 fi printJson "Error Code $ret - HTTP Code $http_code" exit 7 fi # Validate JSON before parsing if ! echo "$result" | python3 -m json.tool > /dev/null 2>&1; then echo >&2 "ERROR: Invalid JSON response: $result" printJson "Error: Invalid JSON response from DeepL" exit 8 fi if [[ $result == *'"error":{"code":'* ]]; then message="$(osascript -l JavaScript -e 'function run(argv) { try { return JSON.parse(argv[0])["error"]["message"] } catch(e) { return "JSON parse error: " + e.message } }' "$result")" printJson "Error: $message" exit 8 else osascript -l JavaScript -e 'function run(argv) { try { const parsed = JSON.parse(argv[0]); if (!parsed || !parsed.result || !parsed.result.translations || !parsed.result.translations[0] || !parsed.result.translations[0].beams) { throw new Error("Invalid response structure"); } const translations = parsed.result.translations[0].beams.map(item => ({ title: item["postprocessed_sentence"], arg: item["postprocessed_sentence"] })); return JSON.stringify({ items: translations }, null, 2); } catch(e) { return JSON.stringify({ items: [{ title: "Error parsing response: " + e.message, arg: "error", valid: false }] }, null, 2); } }' "$result" || { echo >&2 "ERROR w/o key: result '$result', query '$query'" printJson "Error: Failed to parse translation response" exit 9 } fi fi ############################################################################### ================================================ FILE: info4.plist ================================================ bundleid ws.willner.alex.alfred.deepl category Productivity connections 5233F7C8-9221-45A4-BDE0-DE53175096E1 destinationuid A222FB0A-D703-4C45-9CB0-4EDEA3399BEF modifiers 1048576 modifiersubtext vitoclose destinationuid 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E modifiers 0 modifiersubtext vitoclose 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E destinationuid D7D47F17-02DB-4A30-8F14-C03CBD051929 modifiers 0 modifiersubtext vitoclose 84FE56AF-330C-4BB8-85E6-34859AAB73A7 destinationuid 91D16594-908E-4665-A005-0B715D96E5C2 modifiers 0 modifiersubtext vitoclose 91D16594-908E-4665-A005-0B715D96E5C2 destinationuid A222FB0A-D703-4C45-9CB0-4EDEA3399BEF modifiers 1048576 modifiersubtext vitoclose destinationuid 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E modifiers 0 modifiersubtext vitoclose AD7C89C1-0978-4469-B62F-245E595DE9BD destinationuid 91D16594-908E-4665-A005-0B715D96E5C2 modifiers 0 modifiersubtext vitoclose CAA19D9E-04B6-4AD4-A8DC-0B683215DD14 destinationuid 5233F7C8-9221-45A4-BDE0-DE53175096E1 modifiers 0 modifiersubtext vitoclose createdby Alexander Willner description Translation using deepl.com disabled name Deepl-Translate objects config alignment 0 backgroundcolor fadespeed 0 fillmode 0 font ignoredynamicplaceholders largetypetext {query} textcolor wrapat 50 type alfred.workflow.output.largetype uid A222FB0A-D703-4C45-9CB0-4EDEA3399BEF version 3 config alfredfiltersresults alfredfiltersresultsmatchmode 0 argumenttreatemptyqueryasnil argumenttrimmode 0 argumenttype 0 escaping 102 keyword dl-to-en queuedelaycustom 3 queuedelayimmediatelyinitially queuedelaymode 1 queuemode 1 runningsubtext Translating to language "EN" using DeepL ... script ./deepl.sh -l "EN" "{query}" scriptargtype 0 scriptfile deepl.sh subtext Translate to language "EN" using DeepL title DeepL Dictionary type 0 withspace type alfred.workflow.input.scriptfilter uid 5233F7C8-9221-45A4-BDE0-DE53175096E1 version 3 config action 0 argument 1 focusedappvariable focusedappvariablename hotkey 14 hotmod 1835008 hotstring E leftcursor modsmode 0 relatedAppsMode 0 type alfred.workflow.trigger.hotkey uid CAA19D9E-04B6-4AD4-A8DC-0B683215DD14 version 2 config autopaste clipboardtext ignoredynamicplaceholders transient type alfred.workflow.output.clipboard uid 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E version 3 config lastpathcomponent onlyshowifquerypopulated removeextension text {query} title Copied to clipboard: type alfred.workflow.output.notification uid D7D47F17-02DB-4A30-8F14-C03CBD051929 version 1 config action 0 argument 1 focusedappvariable focusedappvariablename hotkey 2 hotmod 1835008 hotstring D leftcursor modsmode 0 relatedAppsMode 0 type alfred.workflow.trigger.hotkey uid 84FE56AF-330C-4BB8-85E6-34859AAB73A7 version 2 config alfredfiltersresults alfredfiltersresultsmatchmode 0 argumenttreatemptyqueryasnil argumenttrimmode 0 argumenttype 0 escaping 102 keyword dl-to-de queuedelaycustom 3 queuedelayimmediatelyinitially queuedelaymode 1 queuemode 1 runningsubtext Translating to language "de" using DeepL ... script DEEPL_KEY="$DEEPL_KEY" ./deepl.sh -l "de" "{query}" scriptargtype 0 scriptfile deepl.sh subtext Translate to language "de" using DeepL title DeepL Dictionary type 0 withspace type alfred.workflow.input.scriptfilter uid 91D16594-908E-4665-A005-0B715D96E5C2 version 3 config concurrently escaping 0 script # THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH. readonly remote_info_plist='https://raw.githubusercontent.com/AlexanderWillner/deepl-alfred-workflow2/master/info.plist' readonly workflow_url='https://github.com/AlexanderWillner/deepl-alfred-workflow2/releases/latest/download/Deepl-Translate.alfredworkflow' readonly download_type='direct' readonly frequency_check='4' # FROM HERE ON, CODE SHOULD BE LEFT UNTOUCHED! function abort { echo "${1}" >&2 exit 1 } function url_exists { curl --silent --location --output /dev/null --fail --range 0-0 "${1}" } function notification { local -r notificator="$(find . -type d -name 'Notificator.app')" if [[ -n "${notificator}" ]]; then "${notificator}/Contents/Resources/Scripts/notificator" --message "${1}" --title "${alfred_workflow_name}" --subtitle 'A new version is available' return fi local -r terminal_notifier="$(find . -type f -name 'terminal-notifier')" if [[ -n "${terminal_notifier}" ]]; then "${terminal_notifier}" -title "${alfred_workflow_name}" -subtitle 'A new version is available' -message "${1}" return fi osascript -e "display notification \"${1}\" with title \"${alfred_workflow_name}\" subtitle \"A new version is available\"" } # Local sanity checks readonly local_info_plist='info.plist' readonly local_version="$(/usr/libexec/PlistBuddy -c 'print version' "${local_info_plist}")" [[ -n "${local_version}" ]] || abort 'You need to set a workflow version in the configuration sheet.' [[ "${download_type}" =~ ^(direct|page|github_release)$ ]] || abort "'download_type' (${download_type}) needs to be one of 'direct', 'page', or 'github_release'." [[ "${frequency_check}" =~ ^[0-9]+$ ]] || abort "'frequency_check' (${frequency_check}) needs to be a number." # Check for updates if [[ $(find "${local_info_plist}" -mtime +"${frequency_check}"d) ]]; then if ! url_exists "${remote_info_plist}"; then abort "'remote_info_plist' (${remote_info_plist}) appears to not be reachable."; fi # Remote sanity check readonly tmp_file="$(mktemp)" curl --silent --location --output "${tmp_file}" "${remote_info_plist}" readonly remote_version="$(/usr/libexec/PlistBuddy -c 'print version' "${tmp_file}")" if [[ "${local_version}" == "${remote_version}" ]]; then touch "${local_info_plist}" # Reset timer by touching local file exit 0 fi if [[ "${download_type}" == 'page' ]]; then notification 'Opening download page…' open "${workflow_url}" exit 0 fi download_url="$([[ "${download_type}" == 'github_release' ]] && curl --silent "https://api.github.com/repos/${workflow_url}/releases/latest" | grep 'browser_download_url' | head -1 | sed -E 's/.*browser_download_url": "(.*)"/\1/' || echo "${workflow_url}")" if url_exists "${download_url}"; then notification 'Downloading and installing…' curl --silent --location --output "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" "${download_url}" open "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" else abort "'workflow_url' (${download_url}) appears to not be reachable." fi fi scriptargtype 1 scriptfile type 0 type alfred.workflow.action.script uid 51AFB0E3-C777-4C5E-A677-3AF381488BCC version 2 config alfredfiltersresults alfredfiltersresultsmatchmode 0 argumenttreatemptyqueryasnil argumenttrimmode 0 argumenttype 0 escaping 102 keyword dl queuedelaycustom 3 queuedelayimmediatelyinitially queuedelaymode 1 queuemode 1 runningsubtext Translating to language "{var:DEEPL_TARGET}" using DeepL ... script DEEPL_KEY="$DEEPL_KEY" ./deepl.sh -l "$DEEPL_TARGET" "{query}" scriptargtype 0 scriptfile deepl.sh subtext Translate to language "{var:DEEPL_TARGET}" using DeepL title DeepL Dictionary type 0 withspace type alfred.workflow.input.scriptfilter uid AD7C89C1-0978-4469-B62F-245E595DE9BD version 3 readme Using the keyword 'dl' will translate a sentence using deepl.com. You can configure the target language in the workflow configuration. To avoid the error message 'too many requests', please configure your free (or paid) DeepL API key. uidata 51AFB0E3-C777-4C5E-A677-3AF381488BCC colorindex 12 note Automatically update workflow to latest version. xpos 400 ypos 460 5233F7C8-9221-45A4-BDE0-DE53175096E1 note Use this keyword to start the translation to an alternative hard coded language. xpos 210 ypos 30 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E note Copy translated text to the clipboard. xpos 490 ypos 270 84FE56AF-330C-4BB8-85E6-34859AAB73A7 note Send any selected text to the translator using this hotkey. xpos 40 ypos 300 91D16594-908E-4665-A005-0B715D96E5C2 note Use this keyword to start the translation to the configured language. xpos 220 ypos 300 A222FB0A-D703-4C45-9CB0-4EDEA3399BEF note Show translation as large text. xpos 480 ypos 30 AD7C89C1-0978-4469-B62F-245E595DE9BD note Use this keyword to start the translation to the configured language. xpos 40 ypos 490 CAA19D9E-04B6-4AD4-A8DC-0B683215DD14 note Send any selected text to the translator using this hotkey. xpos 30 ypos 40 D7D47F17-02DB-4A30-8F14-C03CBD051929 note Show a notifciation when text was copied to the clipboard. xpos 680 ypos 270 variables DEEPL_KEY DEEPL_POSTFIX . DEEPL_PREFERRED ["DE", "EN"] DEEPL_PRO 0 DEEPL_SOURCE auto DEEPL_TARGET en version 1.11 webaddress https://github.com/AlexanderWillner/deepl-alfred-workflow2 ================================================ FILE: info5.plist ================================================ bundleid ws.willner.alex.alfred.deepl category Productivity connections 5233F7C8-9221-45A4-BDE0-DE53175096E1 destinationuid A222FB0A-D703-4C45-9CB0-4EDEA3399BEF modifiers 1048576 modifiersubtext vitoclose destinationuid 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E modifiers 0 modifiersubtext vitoclose 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E destinationuid D7D47F17-02DB-4A30-8F14-C03CBD051929 modifiers 0 modifiersubtext vitoclose 84FE56AF-330C-4BB8-85E6-34859AAB73A7 destinationuid 91D16594-908E-4665-A005-0B715D96E5C2 modifiers 0 modifiersubtext vitoclose 91D16594-908E-4665-A005-0B715D96E5C2 destinationuid A222FB0A-D703-4C45-9CB0-4EDEA3399BEF modifiers 1048576 modifiersubtext vitoclose destinationuid 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E modifiers 0 modifiersubtext vitoclose AD7C89C1-0978-4469-B62F-245E595DE9BD destinationuid A222FB0A-D703-4C45-9CB0-4EDEA3399BEF modifiers 1048576 modifiersubtext vitoclose destinationuid 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E modifiers 0 modifiersubtext vitoclose B8B726E5-529C-4F1A-902E-A66B1589922D destinationuid DA3683E7-31BA-4DE1-9796-2EAB971CA593 modifiers 0 modifiersubtext vitoclose CAA19D9E-04B6-4AD4-A8DC-0B683215DD14 destinationuid 5233F7C8-9221-45A4-BDE0-DE53175096E1 modifiers 0 modifiersubtext vitoclose DA3683E7-31BA-4DE1-9796-2EAB971CA593 destinationuid AD7C89C1-0978-4469-B62F-245E595DE9BD modifiers 0 modifiersubtext vitoclose createdby Alexander Willner description Translation using deepl.com disabled name Deepl-Translate objects config alignment 0 backgroundcolor fadespeed 0 fillmode 0 font ignoredynamicplaceholders largetypetext {query} textcolor wrapat 50 type alfred.workflow.output.largetype uid A222FB0A-D703-4C45-9CB0-4EDEA3399BEF version 3 config alfredfiltersresults alfredfiltersresultsmatchmode 0 argumenttreatemptyqueryasnil argumenttrimmode 0 argumenttype 0 escaping 102 keyword dl-to-en queuedelaycustom 3 queuedelayimmediatelyinitially queuedelaymode 1 queuemode 1 runningsubtext Translating to language "EN" using DeepL ... script ./deepl.sh -l "EN" "{query}" scriptargtype 0 scriptfile deepl.sh subtext Translate to language "EN" using DeepL title DeepL Dictionary type 0 withspace type alfred.workflow.input.scriptfilter uid 5233F7C8-9221-45A4-BDE0-DE53175096E1 version 3 config action 0 argument 1 focusedappvariable focusedappvariablename hotkey 0 hotmod 0 leftcursor modsmode 0 relatedAppsMode 0 type alfred.workflow.trigger.hotkey uid CAA19D9E-04B6-4AD4-A8DC-0B683215DD14 version 2 config autopaste clipboardtext ignoredynamicplaceholders transient type alfred.workflow.output.clipboard uid 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E version 3 config lastpathcomponent onlyshowifquerypopulated removeextension text {query} title Copied to clipboard: type alfred.workflow.output.notification uid D7D47F17-02DB-4A30-8F14-C03CBD051929 version 1 config action 0 argument 1 focusedappvariable focusedappvariablename hotkey 0 hotmod 0 leftcursor modsmode 0 relatedAppsMode 0 type alfred.workflow.trigger.hotkey uid 84FE56AF-330C-4BB8-85E6-34859AAB73A7 version 2 config alfredfiltersresults alfredfiltersresultsmatchmode 0 argumenttreatemptyqueryasnil argumenttrimmode 0 argumenttype 0 escaping 102 keyword dl-to-de queuedelaycustom 3 queuedelayimmediatelyinitially queuedelaymode 1 queuemode 1 runningsubtext Translating to language "de" using DeepL ... script DEEPL_KEY="$DEEPL_KEY" ./deepl.sh -l "de" "{query}" scriptargtype 0 scriptfile deepl.sh subtext Translate to language "de" using DeepL title DeepL Dictionary type 0 withspace type alfred.workflow.input.scriptfilter uid 91D16594-908E-4665-A005-0B715D96E5C2 version 3 config alfredfiltersresults alfredfiltersresultsmatchmode 2 argumenttreatemptyqueryasnil argumenttrimmode 0 argumenttype 1 escaping 102 keyword dll queuedelaycustom 3 queuedelayimmediatelyinitially queuedelaymode 0 queuemode 1 runningsubtext script langs=(${(s/,/)${DEEPL_PREFERRED//[\[\]\" ]/}}) out='' for file in $langs; do out="${out:+$out,}{\"title\":\"$file\",\"arg\":\"$file\"}" done echo "{\"items\":[$out]}" scriptargtype 0 scriptfile subtext title Select Language type 11 withspace type alfred.workflow.input.scriptfilter uid B8B726E5-529C-4F1A-902E-A66B1589922D version 3 config alfredfiltersresults alfredfiltersresultsmatchmode 0 argumenttreatemptyqueryasnil argumenttrimmode 0 argumenttype 0 escaping 102 keyword dl queuedelaycustom 3 queuedelayimmediatelyinitially queuedelaymode 1 queuemode 1 runningsubtext Translating to language "{var:DEEPL_TARGET}" using DeepL ... script DEEPL_KEY="$DEEPL_KEY" ./deepl.sh -l "$DEEPL_TARGET" "{query}" scriptargtype 0 scriptfile deepl.sh subtext Translate to language "{var:DEEPL_TARGET}" using DeepL title DeepL Dictionary type 0 withspace type alfred.workflow.input.scriptfilter uid AD7C89C1-0978-4469-B62F-245E595DE9BD version 3 config argument passthroughargument variables DEEPL_TARGET {query} type alfred.workflow.utility.argument uid DA3683E7-31BA-4DE1-9796-2EAB971CA593 version 1 readme Using the keyword 'dl' will translate a sentence using deepl.com. You can configure the target language in the workflow configuration. To avoid the error message 'too many requests', please configure your free (or paid) DeepL API key. uidata 5233F7C8-9221-45A4-BDE0-DE53175096E1 note Use this keyword to start the translation to an alternative hard coded language. xpos 300 ypos 30 61BF4F8C-DC18-4BB4-8DDD-A87C45C0F79E note Copy translated text to the clipboard. xpos 580 ypos 270 84FE56AF-330C-4BB8-85E6-34859AAB73A7 note Send any selected text to the translator using this hotkey. xpos 130 ypos 300 91D16594-908E-4665-A005-0B715D96E5C2 note Use this keyword to start the translation to the configured language. xpos 310 ypos 300 A222FB0A-D703-4C45-9CB0-4EDEA3399BEF note Show translation as large text. xpos 570 ypos 30 AD7C89C1-0978-4469-B62F-245E595DE9BD note Use this keyword to start the translation to the configured language. xpos 310 ypos 500 B8B726E5-529C-4F1A-902E-A66B1589922D xpos 30 ypos 500 CAA19D9E-04B6-4AD4-A8DC-0B683215DD14 note Send any selected text to the translator using this hotkey. xpos 120 ypos 40 D7D47F17-02DB-4A30-8F14-C03CBD051929 note Show a notifciation when text was copied to the clipboard. xpos 770 ypos 270 DA3683E7-31BA-4DE1-9796-2EAB971CA593 xpos 205 ypos 530 userconfigurationconfig config default placeholder required trim description Get one at https://www.deepl.com/pro-api for higher limits. label API Key type textfield variable DEEPL_KEY config default required text You have a professional DeepL account description label DeepL Pro type checkbox variable DEEPL_PRO config default placeholder required trim description E.g., http://localhost:1188. label DeepLX Host type textfield variable DEEPL_HOST config default default pairs More Formal prefer_more Less Formal prefer_less Default default description Sets whether the translated text should lean towards formal or informal language. label Formality type popupbutton variable DEEPL_FORMALITY config default ["DE", "EN"] placeholder required trim description label Preferred Languages type textfield variable DEEPL_PREFERRED config default auto placeholder required trim description label Source Language type textfield variable DEEPL_SOURCE config default en placeholder required trim description label Target Language type textfield variable DEEPL_TARGET config default . placeholder required trim description Add this to the end of your query if you have not set an API key. label Query Postfix type textfield variable DEEPL_POSTFIX variablesdontexport version 2.2.2 webaddress https://github.com/AlexanderWillner/deepl-alfred-workflow2