From f0dfff0625638cb614278d56d3eed9a03075b16b Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Mon, 15 Jan 2024 17:14:38 +0800 Subject: [PATCH] feat: gpt translate --- apps/locale/translate/__init__.py | 3 + apps/locale/translate/main.py | 57 ++++++++++++++++ apps/locale/translate/manager/__init__.py | 2 + apps/locale/translate/manager/base.py | 52 +++++++++++++++ apps/locale/translate/manager/core.py | 47 +++++++++++++ apps/locale/translate/manager/other.py | 46 +++++++++++++ apps/locale/translate/utils.py | 31 +++++++++ poetry.lock | 80 ++++++++++++++++++----- pyproject.toml | 1 + 9 files changed, 303 insertions(+), 16 deletions(-) create mode 100644 apps/locale/translate/__init__.py create mode 100644 apps/locale/translate/main.py create mode 100644 apps/locale/translate/manager/__init__.py create mode 100644 apps/locale/translate/manager/base.py create mode 100644 apps/locale/translate/manager/core.py create mode 100644 apps/locale/translate/manager/other.py create mode 100644 apps/locale/translate/utils.py diff --git a/apps/locale/translate/__init__.py b/apps/locale/translate/__init__.py new file mode 100644 index 000000000..cf7e61a1d --- /dev/null +++ b/apps/locale/translate/__init__.py @@ -0,0 +1,3 @@ +import os + +LOCALE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) diff --git a/apps/locale/translate/main.py b/apps/locale/translate/main.py new file mode 100644 index 000000000..8217afdab --- /dev/null +++ b/apps/locale/translate/main.py @@ -0,0 +1,57 @@ +import asyncio +import os + +from apps.locale.translate import LOCALE_DIR +from apps.locale.translate.manager import OtherTranslateManager, CoreTranslateManager +from apps.locale.translate.utils import OpenAITranslate + + +class Translate: + IGNORE_TRANSLATE_DIRS = ('translate',) + + def __init__(self, oai_trans_instance): + self.oai_trans = oai_trans_instance + + def get_dir_names(self): + dir_names = [] + for name in os.listdir(LOCALE_DIR): + _path = os.path.join(LOCALE_DIR, name) + if not os.path.isdir(_path) or name in self.IGNORE_TRANSLATE_DIRS: + continue + dir_names.append(name) + return dir_names + + async def core_trans(self, dir_name): + _dir = os.path.join(LOCALE_DIR, dir_name) + zh_file = os.path.join(_dir, 'zh', 'LC_MESSAGES', 'django.po') + if not os.path.exists(zh_file): + print(f'File: {zh_file} not exists.') + return + + await CoreTranslateManager(_dir, self.oai_trans).run() + + async def other_trans(self, dir_name): + _dir = os.path.join(LOCALE_DIR, dir_name) + zh_file = os.path.join(_dir, 'zh.json') + if not os.path.exists(zh_file): + print(f'File: {zh_file} not exists.') + return + + await OtherTranslateManager(_dir, self.oai_trans).run() + + async def run(self): + dir_names = self.get_dir_names() + if not dir_names: + return + + for dir_name in dir_names: + if hasattr(self, f'{dir_name}_trans'): + await getattr(self, f'{dir_name}_trans')(dir_name) + else: + await self.other_trans(dir_name) + + +if __name__ == '__main__': + oai_trans = OpenAITranslate() + manager = Translate(oai_trans) + asyncio.run(manager.run()) diff --git a/apps/locale/translate/manager/__init__.py b/apps/locale/translate/manager/__init__.py new file mode 100644 index 000000000..a355c4831 --- /dev/null +++ b/apps/locale/translate/manager/__init__.py @@ -0,0 +1,2 @@ +from .core import * +from .other import * diff --git a/apps/locale/translate/manager/base.py b/apps/locale/translate/manager/base.py new file mode 100644 index 000000000..4d3f715d6 --- /dev/null +++ b/apps/locale/translate/manager/base.py @@ -0,0 +1,52 @@ +import asyncio +import os + + +class BaseTranslateManager: + bulk_size = 30 + SEPARATOR = "" + LANG_MAPPER = { + 'ja': 'Japanese', + } + + def __init__(self, dir_path, oai_trans_instance): + self.oai_trans = oai_trans_instance + self._dir = dir_path + if not os.path.exists(self._dir): + os.makedirs(self._dir) + + @staticmethod + def split_dict_into_chunks(input_dict, chunk_size=20): + temp = {} + result = [] + + for i, (k, v) in enumerate(input_dict.items()): + temp[k] = v + if (i + 1) % chunk_size == 0 or i == len(input_dict) - 1: + result.append(temp) + temp = {} + + return result + + async def create_translate_task(self, data, target_lang): + try: + keys = list(data.keys()) + values = list(data.values()) + combined_text = self.SEPARATOR.join(values) + translated_text = await self.oai_trans.translate_text(combined_text, target_lang) + translated_texts = translated_text.split(self.SEPARATOR) + return dict(zip(keys, translated_texts)) + except Exception as e: + print(f"Error during translation task: {e}") + return {} + + async def bulk_translate(self, need_trans_dict, target_lang): + split_data = self.split_dict_into_chunks(need_trans_dict, self.bulk_size) + + tasks = [self.create_translate_task(batch, target_lang) for batch in split_data] + translated_results = await asyncio.gather(*tasks) + translated_dict = {} + for result in translated_results: + translated_dict.update(result) + + return translated_dict diff --git a/apps/locale/translate/manager/core.py b/apps/locale/translate/manager/core.py new file mode 100644 index 000000000..266c5f2bc --- /dev/null +++ b/apps/locale/translate/manager/core.py @@ -0,0 +1,47 @@ +import os + +import polib + +from .base import BaseTranslateManager + + +class CoreTranslateManager(BaseTranslateManager): + + @staticmethod + def get_need_trans_dict(zh_dict, trans_po): + need_trans_dict = { + entry.msgid: zh_dict[entry.msgid] + for entry in trans_po.untranslated_entries() + trans_po.fuzzy_entries() + if entry.msgid in zh_dict + } + return need_trans_dict + + @staticmethod + def save_translations_to_po(data, trans_po): + try: + for entry in trans_po.untranslated_entries() + trans_po.fuzzy_entries(): + if entry.msgid not in data: + print(f'msgid: {entry.msgid} not in data.') + continue + entry.flags = [] + entry.previous_msgid = None + entry.msgstr = data[entry.msgid] + trans_po.save() + except Exception as e: + print(f'File save error: {e}') + + async def run(self): + po_file_path = os.path.join(self._dir, 'zh', 'LC_MESSAGES', 'django.po') + po = polib.pofile(po_file_path) + zh_dict = {entry.msgid: entry.msgstr for entry in po.translated_entries()} + + for file_prefix, target_lang in self.LANG_MAPPER.items(): + po_file_path = os.path.join(self._dir, file_prefix, 'LC_MESSAGES', 'django.po') + trans_po = polib.pofile(po_file_path) + need_trans_dict = self.get_need_trans_dict(zh_dict, trans_po) + print(f'File: {file_prefix}.json need to translate: {len(need_trans_dict)}') + if not need_trans_dict: + print(f'File: {file_prefix}.json is already translated.') + continue + translated_dict = await self.bulk_translate(need_trans_dict, target_lang) + self.save_translations_to_po(translated_dict, trans_po) diff --git a/apps/locale/translate/manager/other.py b/apps/locale/translate/manager/other.py new file mode 100644 index 000000000..4a498dcda --- /dev/null +++ b/apps/locale/translate/manager/other.py @@ -0,0 +1,46 @@ +import json +import os + +from .base import BaseTranslateManager + + +class OtherTranslateManager(BaseTranslateManager): + + @staticmethod + def get_need_trans_dict(zh_dict, other_dict): + diff_keys = set(zh_dict.keys()) - set(other_dict.keys()) + need_trans_dict = {k: zh_dict[k] for k in diff_keys if k} + return need_trans_dict + + def load_json_as_dict(self, file_prefix='zh'): + file_path = os.path.join(self._dir, f'{file_prefix}.json') + if not os.path.exists(file_path): + return {} + try: + with open(file_path, 'r', encoding='utf-8') as f: + return json.load(f) + except Exception as e: + print(f'File: {file_path} load error: {e}') + return {} + + def save_dict_as_json(self, data, file_prefix='ja'): + file_path = os.path.join(self._dir, f'{file_prefix}.json') + try: + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, sort_keys=True, indent=4) + except Exception as e: + print(f'File: {file_path} save error: {e}') + + async def run(self): + zh_dict = self.load_json_as_dict() + + for file_prefix, target_lang in self.LANG_MAPPER.items(): + other_dict = self.load_json_as_dict(file_prefix) + need_trans_dict = self.get_need_trans_dict(zh_dict, other_dict) + print(f'File: {file_prefix}.json need to translate: {len(need_trans_dict)}') + if not need_trans_dict: + print(f'File: {file_prefix}.json is already translated.') + continue + translated_dict = await self.bulk_translate(need_trans_dict, target_lang) + other_dict.update(translated_dict) + self.save_dict_as_json(other_dict, file_prefix) diff --git a/apps/locale/translate/utils.py b/apps/locale/translate/utils.py new file mode 100644 index 000000000..adefe19ba --- /dev/null +++ b/apps/locale/translate/utils.py @@ -0,0 +1,31 @@ +from openai import AsyncOpenAI + + +class OpenAITranslate: + def __init__(self, key: str | None = None): + self.client = AsyncOpenAI(api_key=key) + + async def translate_text(self, text, target_lang="English") -> str | None: + try: + response = await self.client.chat.completions.create( + messages=[ + { + "role": "system", + "content": f"Now I ask you to be the translator. " + f"Your goal is to understand the Chinese " + f"I provided you and translate it into {target_lang}. " + f"Please do not use a translation accent when translating, " + f"but translate naturally, smoothly and authentically, " + f"using beautiful and elegant words. way of expression.", + }, + { + "role": "user", + "content": text, + }, + ], + model="gpt-4", + ) + except Exception as e: + print("Open AI Error: ", e) + return + return response.choices[0].message.content.strip() diff --git a/poetry.lock b/poetry.lock index d11c14093..f3378db0e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "adal" @@ -2424,17 +2424,6 @@ files = [ {file = "ephem-4.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8f9b27117e7a82f7f70db9cb23b5cc36d37b166a2f73c55e14d7225d0ab95afa"}, {file = "ephem-4.1.4-cp311-cp311-win32.whl", hash = "sha256:9bb21c0b117c9122c0141b0a71ee6fbbb087ed2aab4a7ab60f009e95e9f4a521"}, {file = "ephem-4.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:55d7fb5c34b2e453e01fa4ca7ee375b19b438c9401ae8c4099ae4a3a37656972"}, - {file = "ephem-4.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f9e24aeea560dfcece3c2e313eb94e6be3e84888091455e541fa88f3a44da584"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:653d99386932e5f78bb9cfc4495030ad9f3345eb4c2b32dca55547da8f1f0332"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53786461a6d5799d5fffe76622ad51444b264d1c7263b92a6dfcac640c3da93a"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:268f57f8768ccb0abbdf4cefb4781c7db812950019868f687b407b428513ee53"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d630aa287255ea9fba6962f351e4e0729bb620570684d52fbfcc31b11527f09e"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5f229bbf62ecb4cd6bb3374b15d0f8ff7b3d970c2936fccd89bdf9d693907a2"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d60d56f182de54bd84fadd6ea2dd8e8ef6fdef6a698c7cafd404ecb6eeefa598"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:404500c8d0030d75ec15bb6b98eee78ad163fd5252102c962ae6fb39c9488198"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fb020d6cc5ab1ad1cd9d3da4a6e2506beebb41d1b337d79cc20cc0a17f550f1"}, - {file = "ephem-4.1.4-cp312-cp312-win32.whl", hash = "sha256:29e71636ee4719419d03184abc85085f76989c79a61844f5e60acbf2513d2b42"}, - {file = "ephem-4.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:549654f63d88e0ab6248ae25ac2939131474ab9f3a91bee6b68ca6f214747c2a"}, {file = "ephem-4.1.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:40067fc050c946c8d4c2d779805b61f063471a091e6124cbabcf61ac538011b2"}, {file = "ephem-4.1.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e2abe97aa2b091090012768b4d94793213cc01f0bf040dcc311a380ab08df69"}, {file = "ephem-4.1.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2677d3a5b42aedc578de10b0eecdba6a50731f159cb28f7ad38c5f62143494"}, @@ -2777,8 +2766,14 @@ files = [ [package.dependencies] google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" -grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio = [ + {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = [ + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -3781,7 +3776,41 @@ files = [ {file = "lxml-5.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7188495c1bf71bfda87d78ed50601e72d252119ce11710d6e71ff36e35fea5a0"}, {file = "lxml-5.0.0-cp37-cp37m-win32.whl", hash = "sha256:6a2de85deabf939b0af89e2e1ea46bfb1239545e2da6f8ac96522755a388025f"}, {file = "lxml-5.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ea56825c1e23c9c8ea385a191dac75f9160477057285b88c88736d9305e6118f"}, - {file = "lxml-5.0.0.tar.gz", hash = "sha256:9165c82bcccf0249feff82cd1fba202e4ce26c25dc69040a0d2c2d0e49cbeba3"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:3f908afd0477cace17f941d1b9cfa10b769fe1464770abe4cfb3d9f35378d0f8"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52a9ab31853d3808e7cf0183b3a5f7e8ffd622ea4aee1deb5252dbeaefd5b40d"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c7fe19abb3d3c55a9e65d289b12ad73b3a31a3f0bda3c539a890329ae9973bd6"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:1ef0793e1e2dd221fce7c142177008725680f7b9e4a184ab108d90d5d3ab69b7"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:581a78f299a9f5448b2c3aea904bfcd17c59bf83016d221d7f93f83633bb2ab2"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:affdd833f82334fdb10fc9a1c7b35cdb5a86d0b672b4e14dd542e1fe7bcea894"}, + {file = "lxml-5.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6bba06d8982be0f0f6432d289a8d104417a0ab9ed04114446c4ceb6d4a40c65d"}, + {file = "lxml-5.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:80209b31dd3908bc5b014f540fd192c97ea52ab179713a730456c5baf7ce80c1"}, + {file = "lxml-5.0.0-cp38-cp38-win32.whl", hash = "sha256:dac2733fe4e159b0aae0439db6813b7b1d23ff96d0b34c0107b87faf79208c4e"}, + {file = "lxml-5.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:ee60f33456ff34b2dd1d048a740a2572798356208e4c494301c931de3a0ab3a2"}, + {file = "lxml-5.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:5eff173f0ff408bfa578cbdafd35a7e0ca94d1a9ffe09a8a48e0572d0904d486"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:78d6d8e5b54ed89dc0f0901eaaa579c384ad8d59fa43cc7fb06e9bb89115f8f4"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:71a7cee869578bc17b18050532bb2f0bc682a7b97dda77041741a1bd2febe6c7"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7df433d08d4587dc3932f7fcfc3194519a6824824104854e76441fd3bc000d29"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:793be9b4945c2dfd69828fb5948d7d9569b78e0599e4a2e88d92affeb0ff3aa3"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7cfb6af73602c8d288581df8a225989d7e9d5aab0a174be0e19fcfa800b6797"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bfdc4668ac56687a89ca3eca44231144a2e9d02ba3b877558db74ba20e2bd9fa"}, + {file = "lxml-5.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2992591e2294bb07faf7f5f6d5cb60710c046404f4bfce09fb488b85d2a8f58f"}, + {file = "lxml-5.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4786b0af7511ea614fd86407a52a7bc161aa5772d311d97df2591ed2351de768"}, + {file = "lxml-5.0.0-cp39-cp39-win32.whl", hash = "sha256:016de3b29a262655fc3d2075dc1b2611f84f4c3d97a71d579c883d45e201eee4"}, + {file = "lxml-5.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:52c0acc2f29b0a204efc11a5ed911a74f50a25eb7d7d5069c2b1fd3b3346ce11"}, + {file = "lxml-5.0.0-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:96095bfc0c02072fc89afa67626013a253596ea5118b8a7f4daaae049dafa096"}, + {file = "lxml-5.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:992029258ed719f130d5a9c443d142c32843046f1263f2c492862b2a853be570"}, + {file = "lxml-5.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:db40e85cffd22f7d65dcce30e85af565a66401a6ed22fc0c56ed342cfa4ffc43"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:cfa8a4cdc3765574b7fd0c7cfa5fbd1e2108014c9dfd299c679e5152bea9a55e"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:049fef98d02513c34f5babd07569fc1cf1ed14c0f2fbff18fe72597f977ef3c2"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a85136d0ee18a41c91cc3e2844c683be0e72e6dda4cb58da9e15fcaef3726af7"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:766868f729f3ab84125350f1a0ea2594d8b1628a608a574542a5aff7355b9941"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:99cad5c912f359e59e921689c04e54662cdd80835d80eeaa931e22612f515df7"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:c90c593aa8dd57d5dab0ef6d7d64af894008971d98e6a41b320fdd75258fbc6e"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8134d5441d1ed6a682e3de3d7a98717a328dce619ee9c4c8b3b91f0cb0eb3e28"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f298ac9149037d6a3d5c74991bded39ac46292520b9c7c182cb102486cc87677"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:894c5f71186b410679aaab5774543fcb9cbabe8893f0b31d11cf28a0740e80be"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9cd3d6c2c67d4fdcd795e4945e2ba5434909c96640b4cc09453bd0dc7e8e1bac"}, + {file = "lxml-5.0.0.zip", hash = "sha256:2219cbf790e701acf9a21a31ead75f983e73daf0eceb9da6990212e4d20ebefe"}, ] [package.extras] @@ -4813,6 +4842,22 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "polib" +version = "1.2.0" +description = "A library to manipulate gettext files (po and mo files)." +optional = false +python-versions = "*" +files = [ + {file = "polib-1.2.0-py2.py3-none-any.whl", hash = "sha256:1c77ee1b81feb31df9bca258cbc58db1bbb32d10214b173882452c73af06d62d"}, + {file = "polib-1.2.0.tar.gz", hash = "sha256:f3ef94aefed6e183e342a8a269ae1fc4742ba193186ad76f175938621dbfc26b"}, +] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "portalocker" version = "2.8.2" @@ -5490,6 +5535,8 @@ files = [ {file = "pyfreerdp-0.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:881003ce2853d9a290c47a04c165941a5a0addd7ad360d033f275dec3eead192"}, {file = "pyfreerdp-0.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:a54ae7c62b3c92e4dba082e328e0bb190f83f45eb84cf59eabb894dfe24a07f7"}, {file = "pyfreerdp-0.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:33c3e14664e306c31c2a792cb8dfdd6a381e35f79583a271dd20be25b14ad73d"}, + {file = "pyfreerdp-0.0.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c7df63da48b67a31d76b62ee70f5f0ffc964b79b34c4e6c610f67a24fba2aee1"}, + {file = "pyfreerdp-0.0.2-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:df62184ef7fd8bc76f9efd172387e6e7cef9f3777a57bb14f3551b2972f55ef3"}, {file = "pyfreerdp-0.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfd97b8da87b1439407b40e7eda998f0df44332211353b0f4217e91d233bc53"}, {file = "pyfreerdp-0.0.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9ce1a906e861335864bb8c976612a5b1d0595955b0cde35e3697eed7812c8b6"}, {file = "pyfreerdp-0.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b630f604300323b3e1fe6b27d442636f9b0d247eba3cc4340a39721c6d85273"}, @@ -5513,6 +5560,7 @@ files = [ {file = "pyfreerdp-0.0.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb31b620013859a7142a391d9ce57c195f26c3c9991883f87bc5ef1703cf806f"}, {file = "pyfreerdp-0.0.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c839d3ed9c0cc4021129e26c06d3bc45982bdaba8a2a115051ad43c5f6108d11"}, {file = "pyfreerdp-0.0.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c2a23db4a56903dfff9ac5e8b2b7aa713af694dce5a4f3905ef7703b4f9dcd"}, + {file = "pyfreerdp-0.0.2.tar.gz", hash = "sha256:caf4b422eb32a327095b548bb1a22a22b926b6e15819c9a83b6461050b11870d"}, ] [package.source] @@ -7779,4 +7827,4 @@ reference = "tsinghua" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "f3a3ef297a045ff896b54897f8ac3593e0f0894637339c7de139453991278139" +content-hash = "86fd825091e6032ad4a48f595a627555822618f8a1a0ed1f314f024e54d190d0" diff --git a/pyproject.toml b/pyproject.toml index e46f10eb5..ee7a8b12c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -146,6 +146,7 @@ django-cors-headers = "^4.3.0" mistune = "2.0.3" openai = "^1.3.7" xlsxwriter = "^3.1.9" +polib = "^1.2.0" [tool.poetry.group.xpack.dependencies]