Skip to content

Commit 8e46471

Browse files
feat: Add support for style_id and get all style rules endpoint
1 parent 366c4eb commit 8e46471

File tree

6 files changed

+374
-4
lines changed

6 files changed

+374
-4
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ arguments are:
173173
model that minimizes response time, at the cost of translation quality.
174174
- `tag_handling`: type of tags to parse before translation, options are `'html'`
175175
and `'xml'`.
176+
- `style_rule`: specifies a style rule to use with translation, either as a string
177+
containing the ID of the style rule, or a `StyleRuleInfo` object.
176178
- `extra_body_parameter`: Dictionary of extra parameters to pass in the body of
177179
the HTTP request. Mostly used by DeepL employees to test functionality, or for
178180
beta programs.
@@ -627,6 +629,46 @@ Note that glossaries work for all target regional-variants: a glossary for the
627629
target language English (`"EN"`) supports translations to both American English
628630
(`"EN-US"`) and British English (`"EN-GB"`).
629631

632+
633+
### Style Rules
634+
635+
Style rules allow you to customize your translations using a managed, shared list
636+
of rules for style, formatting, and more. Multiple style rules can be stored with
637+
your account, each with a user-specified name and a uniquely-assigned ID.
638+
639+
#### Creating and managing style rules
640+
641+
Currently style rules must be created and managed in the DeepL UI via
642+
https://www.deepl.com/en/custom-rules. Full CRUD functionality via the APIs will
643+
come shortly.
644+
645+
#### Listing all style rules
646+
647+
`get_all_style_rules()` returns a list of `StyleRuleInfo` objects
648+
corresponding to all of your stored style rules. The method accepts optional
649+
parameters: `page` (page number for pagination, 0-indexed), `page_size` (number
650+
of items per page), and `detailed` (whether to include detailed configuration
651+
rules in the `configured_rules` property).
652+
653+
```python
654+
# Get all style rules
655+
style_rules = deepl_client.get_all_style_rules()
656+
for rule in style_rules:
657+
print(f"{rule.name} ({rule.style_id})")
658+
659+
# Get style rules with detailed configuration
660+
style_rules = deepl_client.get_all_style_rules(detailed=True)
661+
for rule in style_rules:
662+
if rule.configured_rules:
663+
print(f" Number formatting: {rule.configured_rules.numbers}")
664+
```
665+
666+
Style rules can also be used with the command line interface for text translation:
667+
668+
```bash
669+
python3 -m deepl --auth-key=YOUR_AUTH_KEY text --to=DE --style-id=YOUR_STYLE_ID "Text to translate"
670+
```
671+
630672
### Writing a Plugin
631673

632674
If you use this library in an application, please identify the application with

deepl/__main__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,12 @@ def add_common_arguments(subparser: argparse.ArgumentParser):
379379
default=None,
380380
help="control model used for translation, see API for information",
381381
)
382+
parser_text.add_argument(
383+
"--style-id",
384+
dest="style_rule",
385+
type=str,
386+
help="ID of style rule to use for translation",
387+
)
382388
parser_text.add_argument(
383389
"text",
384390
nargs="+",

deepl/api_data.py

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,3 +676,233 @@ class WritingTone(Enum):
676676

677677
def __str__(self):
678678
return self.value
679+
680+
681+
class ConfiguredRules:
682+
"""Configuration rules for a style rule list.
683+
684+
:param dates_and_times: Date and time formatting rules.
685+
:param formatting: Text formatting rules.
686+
:param numbers: Number formatting rules.
687+
:param punctuation: Punctuation rules.
688+
:param spelling_and_grammar: Spelling and grammar rules.
689+
:param style_and_tone: Style and tone rules.
690+
:param vocabulary: Vocabulary rules.
691+
"""
692+
693+
def __init__(
694+
self,
695+
dates_and_times: Optional[Dict[str, str]] = None,
696+
formatting: Optional[Dict[str, str]] = None,
697+
numbers: Optional[Dict[str, str]] = None,
698+
punctuation: Optional[Dict[str, str]] = None,
699+
spelling_and_grammar: Optional[Dict[str, str]] = None,
700+
style_and_tone: Optional[Dict[str, str]] = None,
701+
vocabulary: Optional[Dict[str, str]] = None,
702+
):
703+
self._dates_and_times = dates_and_times or {}
704+
self._formatting = formatting or {}
705+
self._numbers = numbers or {}
706+
self._punctuation = punctuation or {}
707+
self._spelling_and_grammar = spelling_and_grammar or {}
708+
self._style_and_tone = style_and_tone or {}
709+
self._vocabulary = vocabulary or {}
710+
711+
@staticmethod
712+
def from_json(json) -> "ConfiguredRules":
713+
"""Create ConfiguredRules from the given API JSON object."""
714+
if not json:
715+
return ConfiguredRules()
716+
717+
return ConfiguredRules(
718+
dates_and_times=json.get("dates_and_times"),
719+
formatting=json.get("formatting"),
720+
numbers=json.get("numbers"),
721+
punctuation=json.get("punctuation"),
722+
spelling_and_grammar=json.get("spelling_and_grammar"),
723+
style_and_tone=json.get("style_and_tone"),
724+
vocabulary=json.get("vocabulary"),
725+
)
726+
727+
@property
728+
def dates_and_times(self) -> Dict[str, str]:
729+
"""Returns date and time formatting rules."""
730+
return self._dates_and_times
731+
732+
@property
733+
def formatting(self) -> Dict[str, str]:
734+
"""Returns text formatting rules."""
735+
return self._formatting
736+
737+
@property
738+
def numbers(self) -> Dict[str, str]:
739+
"""Returns number formatting rules."""
740+
return self._numbers
741+
742+
@property
743+
def punctuation(self) -> Dict[str, str]:
744+
"""Returns punctuation rules."""
745+
return self._punctuation
746+
747+
@property
748+
def spelling_and_grammar(self) -> Dict[str, str]:
749+
"""Returns spelling and grammar rules."""
750+
return self._spelling_and_grammar
751+
752+
@property
753+
def style_and_tone(self) -> Dict[str, str]:
754+
"""Returns style and tone rules."""
755+
return self._style_and_tone
756+
757+
@property
758+
def vocabulary(self) -> Dict[str, str]:
759+
"""Returns vocabulary rules."""
760+
return self._vocabulary
761+
762+
763+
class CustomInstruction:
764+
"""Custom instruction for a style rule.
765+
766+
:param label: Label for the custom instruction.
767+
:param prompt: Prompt text for the custom instruction.
768+
:param source_language: Optional source language code for the custom
769+
instruction.
770+
"""
771+
772+
def __init__(
773+
self,
774+
label: str,
775+
prompt: str,
776+
source_language: Optional[str] = None,
777+
):
778+
self._label = label
779+
self._prompt = prompt
780+
self._source_language = source_language
781+
782+
@staticmethod
783+
def from_json(json) -> "CustomInstruction":
784+
"""Create CustomInstruction from the given API JSON object."""
785+
return CustomInstruction(
786+
label=json["label"],
787+
prompt=json["prompt"],
788+
source_language=json.get("source_language"),
789+
)
790+
791+
@property
792+
def label(self) -> str:
793+
"""Returns the label of the custom instruction."""
794+
return self._label
795+
796+
@property
797+
def prompt(self) -> str:
798+
"""Returns the prompt text of the custom instruction."""
799+
return self._prompt
800+
801+
@property
802+
def source_language(self) -> Optional[str]:
803+
"""Returns the source language code, if specified."""
804+
return self._source_language
805+
806+
807+
class StyleRuleInfo:
808+
"""Information about a style rule list.
809+
810+
:param style_id: Unique ID assigned to the style rule list.
811+
:param name: User-defined name assigned to the style rule list.
812+
:param creation_time: Timestamp when the style rule list was created.
813+
:param updated_time: Timestamp when the style rule list was last updated.
814+
:param language: Language code for the style rule list.
815+
:param version: Version number of the style rule list.
816+
:param configured_rules: The predefined rules that have been enabled.
817+
:param custom_instructions: Optional list of custom instructions.
818+
"""
819+
820+
def __init__(
821+
self,
822+
style_id: str,
823+
name: str,
824+
creation_time: datetime.datetime,
825+
updated_time: datetime.datetime,
826+
language: str,
827+
version: int,
828+
configured_rules: Optional[ConfiguredRules] = None,
829+
custom_instructions: Optional[List[CustomInstruction]] = None,
830+
):
831+
self._style_id = style_id
832+
self._name = name
833+
self._creation_time = creation_time
834+
self._updated_time = updated_time
835+
self._language = language
836+
self._version = version
837+
self._configured_rules = configured_rules
838+
self._custom_instructions = custom_instructions
839+
840+
def __str__(self) -> str:
841+
return f'StyleRule "{self.name}" ({self.style_id})'
842+
843+
@staticmethod
844+
def from_json(json) -> "StyleRuleInfo":
845+
"""Create StyleRuleInfo from the given API JSON object."""
846+
configured_rules_data = json.get("configured_rules")
847+
configured_rules = None
848+
if configured_rules_data:
849+
configured_rules = ConfiguredRules.from_json(configured_rules_data)
850+
851+
custom_instructions_data = json.get("custom_instructions")
852+
custom_instructions = None
853+
if custom_instructions_data:
854+
custom_instructions = [
855+
CustomInstruction.from_json(instruction)
856+
for instruction in custom_instructions_data
857+
]
858+
859+
return StyleRuleInfo(
860+
style_id=json["style_id"],
861+
name=json["name"],
862+
creation_time=parse_timestamp(json["creation_time"]),
863+
updated_time=parse_timestamp(json["updated_time"]),
864+
language=json["language"],
865+
version=json["version"],
866+
configured_rules=configured_rules,
867+
custom_instructions=custom_instructions,
868+
)
869+
870+
@property
871+
def style_id(self) -> str:
872+
"""Returns the unique ID of the style rule set."""
873+
return self._style_id
874+
875+
@property
876+
def name(self) -> str:
877+
"""Returns the name of the style rule set."""
878+
return self._name
879+
880+
@property
881+
def creation_time(self) -> datetime.datetime:
882+
"""Returns the creation timestamp."""
883+
return self._creation_time
884+
885+
@property
886+
def updated_time(self) -> datetime.datetime:
887+
"""Returns the last update timestamp."""
888+
return self._updated_time
889+
890+
@property
891+
def language(self) -> str:
892+
"""Returns the language code."""
893+
return self._language
894+
895+
@property
896+
def version(self) -> int:
897+
"""Returns the version number."""
898+
return self._version
899+
900+
@property
901+
def configured_rules(self) -> Optional[ConfiguredRules]:
902+
"""Returns the detailed configuration rules."""
903+
return self._configured_rules
904+
905+
@property
906+
def custom_instructions(self) -> List[CustomInstruction]:
907+
"""Returns the list of custom instructions."""
908+
return self._custom_instructions or []

deepl/deepl_client.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
MultilingualGlossaryInfo,
1010
Language,
1111
WriteResult,
12+
StyleRuleInfo,
1213
)
1314
from deepl.translator import Translator
1415
from deepl import util
@@ -621,3 +622,44 @@ def delete_multilingual_glossary_dictionary(
621622
method="DELETE",
622623
)
623624
self._raise_for_status(status, content, json, glossary=True)
625+
626+
def get_all_style_rules(
627+
self,
628+
page: Optional[int] = None,
629+
page_size: Optional[int] = None,
630+
detailed: Optional[bool] = None,
631+
) -> List[StyleRuleInfo]:
632+
"""Retrieves a list of StyleRuleInfo for all available style rules.
633+
634+
:param page: Page number for pagination, 0-indexed (optional).
635+
:param page_size: Number of items per page (optional).
636+
:param detailed: Whether to include detailed configuration rules
637+
(optional).
638+
:return: List of StyleRuleInfo objects for all available style rules.
639+
"""
640+
params = {}
641+
if page is not None:
642+
params["page"] = str(page)
643+
if page_size is not None:
644+
params["page_size"] = str(page_size)
645+
if detailed is not None:
646+
params["detailed"] = str(detailed).lower()
647+
648+
query_string = "&".join(
649+
[f"{key}={value}" for key, value in params.items()]
650+
)
651+
endpoint = "v3/style_rules"
652+
if query_string:
653+
endpoint += f"?{query_string}"
654+
655+
status, content, json = self._api_call(endpoint, method="GET")
656+
self._raise_for_status(status, content, json)
657+
658+
style_rules = (
659+
json.get("style_rules", [])
660+
if (json and isinstance(json, dict))
661+
else []
662+
)
663+
return [
664+
StyleRuleInfo.from_json(style_rule) for style_rule in style_rules
665+
]

0 commit comments

Comments
 (0)