Skip to content

Commit 9a85da4

Browse files
feat: add JSON example
1 parent f4acbea commit 9a85da4

File tree

4 files changed

+254
-0
lines changed

4 files changed

+254
-0
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77

8+
## [Unreleased]
9+
### Added
10+
* Add [example](examples/json) to translate JSON inputs.
11+
12+
813
## [1.13.0] - 2023-01-26
914
### Added
1015
* Add [example script](examples/mustache) to translate Mustache templates.
@@ -234,6 +239,7 @@ Version increased to avoid conflicts with old packages on PyPI.
234239
Initial version.
235240

236241

242+
[Unreleased]: https://github.com/DeepLcom/deepl-python/compare/v1.13.0...HEAD
237243
[1.13.0]: https://github.com/DeepLcom/deepl-python/compare/v1.12.0...v1.13.0
238244
[1.12.0]: https://github.com/DeepLcom/deepl-python/compare/v1.11.0...v1.12.0
239245
[1.11.0]: https://github.com/DeepLcom/deepl-python/compare/v1.10.0...v1.11.0

examples/json/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Example: Translation of JSON values
2+
3+
An example showing how to translate strings within JSON objects.
4+
5+
The script translates strings, but leaves numbers, booleans, null values and
6+
object keys untranslated. It also recurses into arrays and objects.
7+
8+
For example:
9+
10+
```
11+
{
12+
"greeting": "Good morning!",
13+
"messages": [
14+
"This is a message.",
15+
{
16+
"text": "Here is an embedded text.",
17+
"sent": false
18+
},
19+
null
20+
],
21+
"active": true,
22+
"balance": 100.0
23+
}
24+
```
25+
26+
could be translated into German as:
27+
28+
```
29+
{
30+
"greeting": "Guten Morgen!",
31+
"messages": [
32+
"Dies ist eine Nachricht.",
33+
{
34+
"text": "Hier ist ein Text eingebettet.",
35+
"sent": false
36+
},
37+
null
38+
],
39+
"active": true,
40+
"balance": 100.0
41+
}
42+
```
43+
44+
## Usage
45+
46+
Install the [`deepl` Python library](../../README.md).
47+
48+
Define your DeepL auth key as an environment variable `DEEPL_AUTH_KEY`.
49+
50+
```
51+
export DEEPL_AUTH_KEY=f63c02c5-f056-...
52+
```
53+
54+
Run the JSON translator by running Python on this directory:
55+
56+
```
57+
python examples/json --to de '{"greeting": "Hello!"}'
58+
```
59+
60+
For an explanation of the command line arguments, provide the `--help` option:
61+
62+
```
63+
python examples/json --help
64+
```

examples/json/__main__.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Copyright 2023 DeepL SE (https://www.deepl.com)
2+
# Use of this source code is governed by an MIT
3+
# license that can be found in the LICENSE file.
4+
5+
import argparse
6+
import deepl
7+
import os
8+
9+
from translate_json import translate_json
10+
11+
12+
env_auth_key = "DEEPL_AUTH_KEY"
13+
env_server_url = "DEEPL_SERVER_URL"
14+
15+
16+
def get_parser(prog_name):
17+
"""Constructs and returns the argument parser."""
18+
parser = argparse.ArgumentParser(
19+
prog=prog_name,
20+
description="Translate strings in JSON using the DeepL API "
21+
"(https://www.deepl.com/docs-api).",
22+
epilog="If you encounter issues while using this example, please "
23+
"report them at https://github.com/DeepLcom/deepl-python/issues",
24+
)
25+
26+
parser.add_argument(
27+
"--auth-key",
28+
default=None,
29+
help="authentication key as given in your DeepL account; the "
30+
f"{env_auth_key} environment variable is used as secondary fallback",
31+
)
32+
parser.add_argument(
33+
"--server-url",
34+
default=None,
35+
metavar="URL",
36+
help=f"alternative server URL for testing; the {env_server_url} "
37+
f"environment variable may be used as secondary fallback",
38+
)
39+
parser.add_argument(
40+
"--to",
41+
"--target-lang",
42+
dest="target_lang",
43+
required=True,
44+
help="language into which the JSON strings should be translated",
45+
)
46+
parser.add_argument(
47+
"--from",
48+
"--source-lang",
49+
dest="source_lang",
50+
help="language of the JSON strings to be translated",
51+
)
52+
parser.add_argument(
53+
"json",
54+
nargs="+",
55+
type=str,
56+
help="JSON to be translated. Wrap JSON in quotes to prevent "
57+
"the shell from splitting on whitespace.",
58+
)
59+
60+
return parser
61+
62+
63+
def main():
64+
# Create a parser, reusing most of the arguments from the main CLI
65+
parser = get_parser(prog_name=None)
66+
args = parser.parse_args()
67+
auth_key = args.auth_key or os.getenv(env_auth_key)
68+
server_url = args.server_url or os.getenv(env_server_url)
69+
if auth_key is None:
70+
raise Exception(
71+
f"Please provide authentication key via the {env_auth_key} "
72+
"environment variable or --auth_key argument"
73+
)
74+
75+
# Create a Translator object, and call get_usage() to validate connection
76+
translator = deepl.Translator(auth_key, server_url=server_url)
77+
translator.get_usage()
78+
79+
for json in args.json:
80+
output = translate_json(
81+
json,
82+
translator=translator,
83+
source_lang=args.source_lang,
84+
target_lang=args.target_lang,
85+
)
86+
print(output)
87+
88+
89+
if __name__ == "__main__":
90+
main()

examples/json/translate_json.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Copyright 2023 DeepL SE (https://www.deepl.com)
2+
# Use of this source code is governed by an MIT
3+
# license that can be found in the LICENSE file.
4+
5+
import deepl
6+
import json
7+
import logging
8+
from typing import Any, Callable, List, Tuple
9+
10+
11+
def batch_translate(
12+
input_handler_pairs: List[Tuple[str, Callable[[deepl.TextResult], None]]],
13+
target_lang: str,
14+
translator: deepl.Translator,
15+
**kwargs,
16+
) -> None:
17+
"""
18+
Takes a list of translation inputs and result handlers, translates all
19+
inputs and calls each handler with the translation results.
20+
"""
21+
inputs = [input_str for input_str, _ in input_handler_pairs]
22+
handlers = [handler for _, handler in input_handler_pairs]
23+
results = translator.translate_text(
24+
inputs, target_lang=target_lang, **kwargs
25+
)
26+
for result, handler in zip(results, handlers):
27+
handler(result)
28+
29+
30+
def parse_json_for_translation(
31+
obj: Any,
32+
translation_candidates: List[
33+
Tuple[str, Callable[[deepl.TextResult], None]]
34+
],
35+
):
36+
"""
37+
Steps into the given JSON object and adds all strings to
38+
translation candidates list
39+
"""
40+
41+
if type(obj) is dict:
42+
keys = obj.keys()
43+
elif type(obj) is list:
44+
keys = range(len(obj))
45+
else:
46+
return
47+
48+
for key in keys:
49+
if type(obj[key]) is str:
50+
current_obj = obj
51+
current_key = key
52+
assign = lambda val: current_obj.__setitem__(current_key, val.text)
53+
translation_candidates.append((obj[key], assign))
54+
else:
55+
parse_json_for_translation(obj[key], translation_candidates)
56+
57+
58+
def translate_json(
59+
json_input: str,
60+
target_lang: str,
61+
translator: deepl.Translator,
62+
**kwargs,
63+
) -> str:
64+
"""
65+
Translates given JSON input using DeepL Translator.
66+
67+
Most of the arguments of the translate_text function are supported,
68+
source_lang, target_lang, glossary_id, formality, etc.
69+
70+
:param json_input: JSON input to be translated.
71+
:param target_lang: language code to translate template into, for example
72+
"DE", "EN-US", "FR".
73+
:param translator: deepl.Translator to use for translation.
74+
:return: Translated JSON.
75+
"""
76+
77+
logger = logging.getLogger("deepl")
78+
obj = json.loads(json_input)
79+
# Wrap the JSON object in an array, in case the input is a string
80+
obj = [obj]
81+
82+
# Find all text in the JSON that is to be translated
83+
translation_candidates = []
84+
parse_json_for_translation(obj, translation_candidates)
85+
logger.info(
86+
f"Found {len(translation_candidates)} strings to be translated"
87+
)
88+
89+
# Translate all texts
90+
batch_translate(translation_candidates, target_lang, translator, **kwargs)
91+
logger.info("Translation complete")
92+
93+
# Unwrap the dummy array and convert to JSON
94+
return json.dumps(obj[0])

0 commit comments

Comments
 (0)