feat: gpt translate

pull/12555/head
feng 2024-01-15 17:14:38 +08:00 committed by 老广
parent fdaec3c959
commit f0dfff0625
9 changed files with 303 additions and 16 deletions

View File

@ -0,0 +1,3 @@
import os
LOCALE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

View File

@ -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())

View File

@ -0,0 +1,2 @@
from .core import *
from .other import *

View File

@ -0,0 +1,52 @@
import asyncio
import os
class BaseTranslateManager:
bulk_size = 30
SEPARATOR = "<SEP>"
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

View File

@ -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)

View File

@ -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)

View File

@ -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()

80
poetry.lock generated
View File

@ -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"

View File

@ -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]