[A-Z]
(a space, followed by any upper-case letter).
51 |
52 | #### No Suffix
53 |
54 | | pattern | |substitution |
55 | | -------- | ----------- |
56 | | Künstler | |artist |
57 |
58 | Like exact patterns, but do not insert a trailing space.
59 |
60 | #### Post term
61 |
62 | | pattern | ~substitution |
63 | | ------- | ------------- |
64 | | paper | ~Arbeit |
65 |
66 | Like exact matches, but are simply performed last. This can be useful to avoid potential collisions with other terms that should be processed first.
67 |
68 | ## Supported File Formats
69 |
70 | DeepQt uses [pyexcel](http://docs.pyexcel.org/en/latest/) to read glossaries. It supports a plethora of formats, including:
71 | .ods, .xlsx, .html, .csv, .csvz, .tsv, .tsvz, .rst, .json ... and many more.
72 | You can see the full list [here](https://github.com/pyexcel/pyexcel#feature-highlights).
--------------------------------------------------------------------------------
/flatpak/io.github.voxelcubes.deepqt.appdata.xml:
--------------------------------------------------------------------------------
1 |
2 | DeepQt is a user-friendly application that harnesses the power of the DeepL API for translation purposes. 12 | With a clean interface and support for batch processing of plain text files and EPUB books, 13 | DeepQt makes it easy to translate texts efficiently and effectively.
14 | 15 |DeepQt also features higher-level glossaries allowing you to make replacements before translation, 16 | to ensure consistnecy in terms. These can even be applied without translating anything!
17 | 18 |To use DeepQt's translation features, you will need a DeepL API license, either free or pro.
19 |Hi Mom!
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/mock_files/mime_types/word.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/mime_types/word.docx -------------------------------------------------------------------------------- /tests/mock_files/mime_types/writer.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/mime_types/writer.odt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/__init__.py -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/generate.py: -------------------------------------------------------------------------------- 1 | # Import necessary modules 2 | import codecs 3 | 4 | 5 | # Define messages tailored for each encoding 6 | messages = { 7 | "utf-8": "Hello, 世界! Привет, мир! مرحبا بالعالم!", 8 | "utf-16": "Hello, 世界! Привет, мир! مرحبا بالعالم!", 9 | "iso-8859-1": "Hello, world! Bonjour, le monde! ¡Hola, mundo!", 10 | "ascii": "Hello, world! Greetings, Earth!", 11 | "shift_jis": "こんにちは、世界!これはShift JISエンコーディングのテキストサンプルです。日本語の文字が正しく表示されることを確認してください。", 12 | "gb2312": "你好,世界! Hello, 世界!", 13 | "euc_kr": "안녕하세요, 세계! Hello, world!", 14 | "windows-1251": "Hello, мир! Привет, world!", 15 | "iso-8859-5": "Привет, мир! Hello, world!", 16 | "big5": "你好,世界! Hello, 世界!", 17 | } 18 | 19 | # Dictionary to map encoding names to file suffixes 20 | suffixes = { 21 | "utf-8": "utf8", 22 | "utf-16": "utf16", 23 | "iso-8859-1": "iso8859-1", 24 | "ascii": "ascii", 25 | "shift_jis": "shiftjis", 26 | "gb2312": "gb2312", 27 | "euc_kr": "euckr", 28 | "windows-1251": "windows1251", 29 | "iso-8859-5": "iso8859-5", 30 | "big5": "big5", 31 | } 32 | 33 | # Write the message to a file in each encoding 34 | for encoding, message in messages.items(): 35 | # Create a file name based on the encoding 36 | file_name = f"message_{suffixes[encoding]}.txt" 37 | 38 | # Open the file with the specified encoding and write the message 39 | with codecs.open(file_name, "w", encoding) as file: 40 | try: 41 | file.write(message) 42 | print(f"File created: {file_name}") 43 | except Exception as e: 44 | print(f"Failed to write to {file_name} with encoding {encoding}: {e}") 45 | 46 | print("Files created for each encoding with the custom message.") 47 | -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_ascii.txt: -------------------------------------------------------------------------------- 1 | Hello, world! Greetings, Earth! -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_big5.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_big5.txt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_euckr.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_euckr.txt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_gb2312.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_gb2312.txt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_iso8859-1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_iso8859-1.txt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_iso8859-5.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_iso8859-5.txt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_shiftjis.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_shiftjis.txt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_utf16.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_utf16.txt -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_utf8.txt: -------------------------------------------------------------------------------- 1 | Hello, 世界! Привет, мир! مرحبا بالعالم! -------------------------------------------------------------------------------- /tests/mock_files/various_encodings/message_windows1251.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VoxelCubes/DeepQt/cdf01e791ef44fe283d466c65744201bb38c4413/tests/mock_files/various_encodings/message_windows1251.txt -------------------------------------------------------------------------------- /tests/test_backend_interface.py: -------------------------------------------------------------------------------- 1 | from typing import get_type_hints 2 | import deepqt.backends.backend_interface as bi 3 | import deepqt.backends.mock_backend as mb 4 | import deepqt.backends.deepl_backend as db 5 | 6 | 7 | def check_attribute_metadata(some_thing: bi.BackendConfig | bi.BackendStatus): 8 | """ 9 | Test some config class that inherits from BackendConfig or BackendStatus. 10 | (Not a test on its own, but a helper function for other tests.) 11 | """ 12 | meta = some_thing.attribute_metadata() 13 | 14 | # Get attributes of the class and the base class. 15 | attributes = get_type_hints(some_thing) 16 | parent = some_thing.__class__.__bases__[0] 17 | attributes.update(get_type_hints(parent)) 18 | 19 | # Check if all attributes are covered in the metadata. 20 | # for attr in attributes: 21 | # if attr not in meta: 22 | # raise ValueError(f"Attribute {attr} not covered in metadata") 23 | attr_names = list(attributes.keys()) 24 | meta_names = list(meta.keys()) 25 | 26 | # Check for duplicates. 27 | for name in attr_names: 28 | if attr_names.count(name) > 1: 29 | raise ValueError(f"Duplicate attribute name: {name}") 30 | for name in meta_names: 31 | if meta_names.count(name) > 1: 32 | raise ValueError(f"Duplicate metadata name: {name}") 33 | 34 | # Check for missing attributes. 35 | attr_names = set(attr_names) 36 | meta_names = set(meta_names) 37 | in_attr_not_in_meta = attr_names - meta_names 38 | in_meta_not_in_attr = meta_names - attr_names 39 | if in_attr_not_in_meta: 40 | raise ValueError(f"Attributes not covered in metadata: {in_attr_not_in_meta}") 41 | if in_meta_not_in_attr: 42 | raise ValueError(f"Metadata not covered in attributes: {in_meta_not_in_attr}") 43 | 44 | # Check the meta type matches the attribute type. 45 | for name in attr_names & meta_names: 46 | if meta[name].type != attributes[name]: 47 | raise ValueError( 48 | f"Type mismatch for attribute {name}: meta {meta[name].type} != actual {attributes[name]}" 49 | ) 50 | 51 | 52 | def test_mock(): 53 | """ 54 | Test the MockConfig. 55 | """ 56 | check_attribute_metadata(mb.MockConfig()) 57 | 58 | 59 | def test_deepl(): 60 | """ 61 | Test the DeepLConfig. 62 | """ 63 | check_attribute_metadata(db.DeepLConfig()) 64 | -------------------------------------------------------------------------------- /tests/test_encoding_recognition.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import deepqt.utils as ut 3 | from tests.helpers import mock_file_path 4 | from tests.mock_files import various_encodings 5 | 6 | # Define expected messages tailored for each encoding 7 | expected_messages = { 8 | "message_utf8.txt": "Hello, 世界! Привет, мир! مرحبا بالعالم!", 9 | "message_utf16.txt": "Hello, 世界! Привет, мир! مرحبا بالعالم!", 10 | "message_iso8859-1.txt": "Hello, world! Bonjour, le monde! ¡Hola, mundo!", 11 | "message_ascii.txt": "Hello, world! Greetings, Earth!", 12 | "message_shiftjis.txt": "こんにちは、世界!これはShift JISエンコーディングのテキストサンプルです。日本語の文字が正しく表示されることを確認してください。", 13 | "message_gb2312.txt": "你好,世界! Hello, 世界!", 14 | "message_euckr.txt": "안녕하세요, 세계! Hello, world!", 15 | "message_windows1251.txt": "Hello, мир! Привет, world!", 16 | "message_iso8859-5.txt": "Привет, мир! Hello, world!", 17 | "message_big5.txt": "你好,世界! Hello, 世界!", 18 | } 19 | 20 | 21 | @pytest.mark.parametrize("file_name, expected_message", expected_messages.items()) 22 | def test_read_text_encoding(file_name, expected_message): 23 | # Get the file path using mock_file_path 24 | path = mock_file_path(file_name, module=various_encodings) 25 | 26 | # Guess the encoding using the read_text_encoding function 27 | guessed_encoding = ut.detect_encoding(path) 28 | 29 | # Read the file with the guessed encoding 30 | with path.open(encoding=guessed_encoding) as f: 31 | recovered_message = f.read() 32 | 33 | # Assert that the recovered message matches the expected message 34 | actual_encoding = file_name.split("_")[1].split(".")[0] 35 | 36 | def simplify(encoding): 37 | return encoding.lower().replace("-", "").replace("_", "") 38 | 39 | assert simplify(actual_encoding) == simplify( 40 | guessed_encoding 41 | ), f"Failed for encoding in {file_name}: Guessed {guessed_encoding}, got {actual_encoding}" 42 | assert ( 43 | recovered_message == expected_message 44 | ), f"Failed for encoding in {file_name}: Guessed {guessed_encoding}, got {recovered_message}" 45 | 46 | # Running the wrapper version that combines both tests. 47 | with ut.read_autodetect_encoding(path) as f: 48 | recovered_message = f.read() 49 | 50 | assert ( 51 | recovered_message == expected_message 52 | ), f"Failed for encoding in {file_name}: Got {recovered_message}" 53 | -------------------------------------------------------------------------------- /tests/test_log_parser.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import patch 2 | import deepqt.log_parser as lp 3 | from tests.helpers import read_mock_file 4 | import tests.mock_files.logs as log_files 5 | 6 | 7 | def test_parse_good(): 8 | # Test with good_pcleaner.log 9 | logfile = read_mock_file("good.log", module=log_files) 10 | # mock the get_username function to return "testvm" 11 | with patch("deepqt.log_parser.get_username", return_value="testvm"): 12 | sessions = lp.parse_log_file(logfile) 13 | 14 | assert len(sessions) == lp.MAX_SESSIONS 15 | assert sessions[19].criticals == 1 16 | assert sessions[19].errors == 3 17 | assert sessions[1].criticals == 0 18 | assert sessions[1].errors == 1 19 | assert sessions[1].corrupted is False 20 | assert sessions[5].criticals == 0 21 | assert sessions[5].errors == 0 22 | -------------------------------------------------------------------------------- /tests/test_mime_recognition.py: -------------------------------------------------------------------------------- 1 | from tests.helpers import mock_file_path 2 | from pathlib import Path 3 | 4 | import deepqt.utils as ut 5 | import deepqt.constants as ct 6 | from tests.mock_files import mime_types 7 | 8 | 9 | def test_mime_recognition(): 10 | path = mock_file_path("text.txt", module=mime_types) 11 | assert ut.read_mime_type(path) == ct.Formats.TEXT, "Text file not recognized." 12 | 13 | path = mock_file_path("book.epub", module=mime_types) 14 | assert ut.read_mime_type(path) == ct.Formats.EPUB, "EPUB file not recognized." 15 | 16 | path = mock_file_path("document.pdf", module=mime_types) 17 | assert ut.read_mime_type(path) == ct.Formats.PDF, "PDF file not recognized." 18 | 19 | path = mock_file_path("writer.odt", module=mime_types) 20 | assert ut.read_mime_type(path) == ct.Formats.ODT, "ODT file not recognized." 21 | 22 | path = mock_file_path("word.docx", module=mime_types) 23 | assert ut.read_mime_type(path) == ct.Formats.DOCX, "DOCX file not recognized." 24 | 25 | path = mock_file_path("impress.odp", module=mime_types) 26 | assert ut.read_mime_type(path) == ct.Formats.ODP, "ODP file not recognized." 27 | 28 | path = mock_file_path("powerpoint.pptx", module=mime_types) 29 | assert ut.read_mime_type(path) == ct.Formats.PPTX, "PPTX file not recognized." 30 | 31 | path = mock_file_path("calc.ods", module=mime_types) 32 | assert ut.read_mime_type(path) == ct.Formats.ODS, "ODS file not recognized." 33 | 34 | path = mock_file_path("excel.xlsx", module=mime_types) 35 | assert ut.read_mime_type(path) == ct.Formats.XLSX, "XLSX file not recognized." 36 | 37 | path = mock_file_path("website.html", module=mime_types) 38 | assert ut.read_mime_type(path) == ct.Formats.HTML, "HTML file not recognized." 39 | 40 | path = mock_file_path("translation.xliff", module=mime_types) 41 | assert ut.read_mime_type(path) == ct.Formats.XLF, "XLF file not recognized." 42 | 43 | path = mock_file_path("archive.zip", module=mime_types) 44 | assert ut.read_mime_type(path) == ct.Formats.UNKNOWN, "ZIP file recognized as something." 45 | 46 | path = mock_file_path("blank_file") 47 | assert ut.read_mime_type(path) == ct.Formats.UNKNOWN, "Blank file not recognized." 48 | -------------------------------------------------------------------------------- /tests/test_theming.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | from pathlib import Path, PosixPath 3 | from importlib import resources 4 | 5 | import deepqt.utils as ut 6 | import deepqt.ui_generated_files 7 | import deepqt.data.theme_icons as theme_icons_module 8 | 9 | 10 | def test_get_available_themes(): 11 | """ 12 | Test the get_available_themes function. 13 | """ 14 | themes = ut.get_available_themes() 15 | 16 | assert themes 17 | assert themes == [("breeze", "Breeze Light"), ("breeze-dark", "Breeze Dark")] 18 | 19 | 20 | def test_theme_icon_presence(): 21 | """ 22 | Test that all icons are present in the resources. 23 | """ 24 | # Read the icon list from the yaml file located at DeepQt/icons/theme_list.yaml 25 | yaml_path = Path(__file__).parent.parent / "icons" / "theme_list.yaml" 26 | with resources.files(theme_icons_module) as theme_icons_path: 27 | theme_icons_root = Path(theme_icons_path) 28 | 29 | with yaml_path.open() as file: 30 | data = yaml.safe_load(file) 31 | 32 | theme_directories = data["Theme directories"] 33 | theme_icons = data["Files"] 34 | 35 | # Perform a depth-first search for each icon in a theme, which means 36 | # checking for leaf nodes, which are the icon file names. 37 | # When coming across a directory, the name of that directory is added to the path, 38 | # then scanned recursively. 39 | for theme_path in theme_directories: 40 | # Only use the last directory name of the theme path. 41 | theme_directory_name = PosixPath(theme_path).name 42 | for category, sizes in theme_icons.items(): 43 | for size, icons in sizes.items(): 44 | for icon in icons: 45 | expected_relative_path = ( 46 | PosixPath(theme_directory_name) / category / str(size) / icon 47 | ) 48 | 49 | for suffix in ["svg", "png"]: 50 | full_path = ( 51 | theme_icons_root 52 | / expected_relative_path.with_suffix(f".{suffix}").as_posix() 53 | ) 54 | if full_path.exists(): 55 | break 56 | else: 57 | assert ( 58 | False 59 | ), f"Icon {expected_relative_path} not found among the theme_icons data files." 60 | 61 | 62 | def test_theme_icon_app_presence(): 63 | # Load the source code and inspect it for xdg icon names. 64 | with resources.files(deepqt.ui_generated_files) as source_dir: 65 | source_dir = Path(source_dir) 66 | 67 | # This only works for the generated code from ui files. 68 | # General python code would be undecidable. 69 | expected_icons: set[str] = set() 70 | for source_file in source_dir.rglob("*.py"): 71 | with source_file.open() as file: 72 | for line in file: 73 | if 'iconThemeName = u"' in line: 74 | theme_name = line.split('iconThemeName = u"')[1].split('"')[0] 75 | expected_icons.add(theme_name) 76 | 77 | # Ensure they are listed in the yaml icon list file. 78 | yaml_path = Path(__file__).parent.parent / "icons" / "theme_list.yaml" 79 | with yaml_path.open() as file: 80 | data = yaml.safe_load(file) 81 | 82 | theme_icons = data["Files"] 83 | icon_set = set() 84 | for category, sizes in theme_icons.items(): 85 | for size, icons in sizes.items(): 86 | for icon in icons: 87 | icon_set.add(icon) 88 | 89 | missing = expected_icons - icon_set 90 | assert not missing 91 | -------------------------------------------------------------------------------- /tests/test_writing_config.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from loguru import logger 4 | 5 | from tests.helpers import mock_file_path 6 | from tests import mock_files 7 | import tests.mock_files.config as config_files 8 | 9 | import deepqt.config as cfg 10 | import deepqt.constants as ct 11 | 12 | # Suppress the loguru logger. 13 | logger.remove() 14 | 15 | 16 | def test_un_structuring(): 17 | # We shouldn't find any attributes that have the "no_save" metadata flag set. 18 | # All other attributes should be present. 19 | 20 | config: cfg.Config = cfg.Config() 21 | 22 | dump: dict = config.dump() 23 | 24 | # First check the top level attributes. 25 | assert dump["gui_theme"] == config.gui_theme 26 | assert dump["lang_from"] == config.lang_from 27 | assert dump["lang_to"] == config.lang_to 28 | assert dump["current_backend"] == config.current_backend 29 | 30 | # Check all backend configs. 31 | for backend, bconf in config.backend_configs.items(): 32 | backend_dump = dump["backend_configs"][backend] 33 | metadata = bconf.attribute_metadata() 34 | 35 | for attr, meta in metadata.items(): 36 | if meta.no_save: 37 | assert attr not in backend_dump 38 | else: 39 | assert backend_dump[attr] == getattr(bconf, attr) 40 | -------------------------------------------------------------------------------- /ui_files/IssueReporter.ui: -------------------------------------------------------------------------------- 1 | 2 |