perf: 添加进度条

pull/12554/head
feng 2024-01-16 21:15:39 +08:00 committed by 老广
parent f0dfff0625
commit 1cec27ed70
11 changed files with 2258 additions and 541 deletions

File diff suppressed because it is too large Load Diff

1846
apps/locale/lina/ja.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,199 +1,201 @@
{
"ACL reject login asset": "アクセス制御ポリシーの制限により、このログインは拒否されました",
"ACL reject login asset": "このログインはアクセス制御ポリシーの制限により拒否されました",
"Account info": "アカウント情報",
"Account not found": "アカウントが見つかりません",
"Account: ": "アカウント: {{value}}",
"Action: ": "アクション: ",
"Advanced option": "上級オプション",
"All sessions": "すべてのセッション",
"Applet": "アプレット",
"Applet connect method": "アプレット接続方法",
"Are you sure to reconnect it?(RDP not support)": "本当に再接続しますか? (RDPは一時的にサポートしていない)",
"Asset disabled": "この資産はすでに無効になっている場合、管理者に連絡してください",
"Asset not found or You have no permission to access it, please refresh asset tree": "アセットが見つからないか、アクセスする権限がありません。アセット ツリーを更新してください",
"Asset tree loading method": "資産ツリーのロード方法の設定",
"Asset: ": "アセット: {{value}}",
"Assignees": "受信者",
"Automatic login next": "次回の自動登録 (右クリック資産接続は再選択できます)",
"Backspace as Ctrl+H": "文字端末backspacas Ctrl H",
"Batch actions": "バッチ操作",
"Batch connect": "バッチ接続",
"Belgian French keyboard layout": "Belgian French (Azerty)",
"Action: ": "操作:",
"Advanced option": "高度なオプション",
"All sessions": "セッション",
"Applet": "リモートアプリケーション",
"Applet connect method": "リモートアプリケーションの接続方法",
"Are you sure to reconnect it?(RDP not support)": "再接続しますかRDPは暫定的にサポートしていません",
"Asset disabled": "この資産は無効化されています、管理者に連絡してください",
"Asset not found or You have no permission to access it, please refresh asset tree": "資産が見つからないか、あなたがアクセスする権限がない、資産ツリーをリフレッシュしてください",
"Asset tree loading method": "資産ツリーの読み込み方法を設定",
"Asset: ": "資産: {{value}}",
"Assignees": "受付人",
"Automatic login next": "次回は自動ログイン(アセットリンクを右クリックすると再選択できます)",
"Backspace as Ctrl+H": "バックスペースキーをCtrl+Hとして使用",
"Batch actions": "一括操作",
"Batch connect": "一括接続",
"Belgian French keyboard layout": "Belgian FrenchAzerty",
"CLI": "コマンドライン",
"CLI font size": "文字端末のフォントサイズ",
"CLI font size": "キャラクターターミナルのフォントサイズ",
"Cancel": "キャンセル",
"Charset": "文字セット",
"Checkbox": "複数選択",
"Choose a User": "ユーザーを選択します",
"Click to copy": "クリックしてコピーする",
"Client": "お客様",
"Clone Connect": "コピーウィンドウ",
"Close": "閉",
"Close All Tabs": "すべてを閉じる",
"Close Current Tab": "現在を閉じる",
"Checkbox": "複数選択 ",
"Choose a User": "ユーザーを選択",
"Click to copy": "クリックしてコピー",
"Client": "クライアント",
"Clone Connect": "ウィンドウをコピー",
"Close": "閉じる",
"Close All Tabs": "全て閉じる",
"Close Current Tab": "現在のウィンドウを閉じる",
"Close Left Tabs": "左側を閉じる",
"Close Other Tabs": "その他を閉じる",
"Close Other Tabs": "それ以外を閉じる",
"Close Right Tabs": "右側を閉じる",
"Close split connect": "分割表示を閉じる",
"Close split connect": "分割画面を閉じる",
"Command Line": "コマンドライン",
"Command line": "命令行",
"Command line": "コマンドラインに接続",
"Confirm": "確認",
"Connect": "接続",
"Connect Method": "接続方法",
"Connect checked": "接続済み",
"Connect command line": "接続コマンドライン",
"Copied": "複製済み",
"Copy link": "リンクをコピーする",
"Connect checked": "選択した接続",
"Connect command line": "コマンドラインへの接続",
"Copied": "コピー済み",
"Copy link": "リンクをコピー",
"Current online": "現在オンライン",
"Current session": "現在のセッション",
"Database": "データベース",
"Database disabled": "このリンク方式をサポートしていない、管理者に連絡してください",
"Database info": "データベース接続情報",
"Database token help text": " クライアントが接続するデータベースの種類のトークンは、コンポーネントによって5分間キャッシュされます。つまり、トークンは使用後すぐに無効になるわけではありませんが、クライアントが切断されてから5分後に無効になります",
"Database connect info": "データベース接続情報",
"Database disabled": "この種類の接続はサポートされていません, 管理者に連絡してください",
"Database info": "データベース情報",
"Database token help text": "データベース型のトークンは5分間キャッシュされます。つまり、トークンを使用してから、すぐには無効にならず、クライアントが切断されてから5分後に、このトークンは完全に無効になります。",
"Databases": "データベース",
"Directly": "直接接続",
"Disable auto completion": "自動補完の無効化",
"Disconnect": "リンクを切断",
"Disfavor": "コレクションをキャンセル",
"Directly": "ユーザー名は、指定された資産とアカウントに接続します",
"Disable auto completion": "オートコンプリートを無効にする",
"Disconnect": "接続を切断",
"Disfavor": "お気に入り解除",
"Do not close this page": "このページを閉じないでください",
"Document": "ドキュメント",
"Don't prompt again": "次回は提示しない",
"Document": "文書",
"Don't prompt again": "次回から表示しない",
"Download": "ダウンロード",
"Download the latest client": "最新クライアントのダウンロード",
"Driver redirect": "ディスクマウント",
"Download the latest client": "最新のクライアントをダウンロード",
"Driver redirect": "ディスクマウント",
"Expand": "展開",
"Expand all": "すべて展開",
"Expand all asset": "現在のノードの下にあるすべてのアセットを展開",
"Expire time": "期限切れ",
"Failed to open address": "アドレスを開くことができませんでした",
"Favorite": "コレクション",
"Expand all": "て展開",
"Expand all asset": "全ての資産を展開",
"Expire time": "有効期限",
"Failed to open address": "アドレスの開封に失敗",
"Favorite": "お気に入り",
"File Manager": "ファイル管理",
"Fold": "折りたた",
"Fold all": "すべて折りたたみ",
"Force refresh": "強制更新",
"Fold": "折りたた",
"Fold all": "全て折りたたむ",
"Force refresh": "強制的に更新",
"Found": "発見",
"French keyboard layout": "French (Azerty)",
"French keyboard layout": "フレンチ(アゼルティ)",
"Full Screen": "全画面表示",
"Full screen": "全画面",
"GUI": "グラフィカル",
"General": "基本構成",
"Full screen": "フルスクリーン",
"GUI": "視覚化",
"General": "基本設定",
"Help": "ヘルプ",
"Help or download": "ヘルプ → ダウンロード",
"Help text": "します",
"Hide left manager": "左サイドバーを非表示",
"Help or download": "メニューヘルプ → ダウンロード",
"Help text": "説明",
"Hide left manager": "左のサイドバーを隠す",
"Host": "ホスト",
"Info": "ヒント",
"InstallClientMsg": "JumpServerクライアントがインストールされていない、今ダウンロードしてインストールしますか?",
"Japanese keyboard layout": "Japanese (Qwerty)",
"InstallClientMsg": "JumpServerクライアントがインストールされていません、今すぐダウンロードしてインストールしますか?",
"Japanese keyboard layout": "日本語 (Qwerty)",
"Keyboard keys": "Option + Left / Option + Right",
"Keyboard layout": "キーボードレイアウト",
"Keyboard switch session": "セッションの切り替え → ショートカットキー",
"Keyboard switch session": "会話を切り替え → ショートカット",
"Kubernetes": "Kubernetes",
"Language": "言語",
"Last login": "前回ログイン",
"Launch Program": "起動プログラム",
"LeftInfo": "コマンドログをクリックして迅速に定位できるビデオ",
"Load tree async": "アセットツリーの非同期ロード",
"Loading": "ロード中",
"Log out": "ログインを終了",
"Last login": "前回ログイン",
"Launch Program": "プログラムを開始",
"LeftInfo": "コマンドレコードをクリックすると、録画を素早く見つけられます",
"Load tree async": "非同期でアセットツリーを読み込む",
"Loading": "読み込み中",
"Log out": "ログアウト",
"Login reminder": "ログインリマインダー",
"Login review approved": "ログイン監査に合格し、アセットを接続しています...",
"LoginExpireMsg": "ログインが期限切れになりました。ログインし直してください",
"Login review approved": "ログイン審査が通過しました, 資産へ接続中...",
"LoginExpireMsg": "ログインが期限切れです。再度ログインしてください。",
"Manual accounts": "手動アカウント",
"Module": "ユニット",
"Multi Screen": "マルチスクリーン",
"My applications": "私の応用",
"My assets": "マイ資産",
"Name": "めいしょう",
"Module": "モジュール",
"Multi Screen": "マルチスクリーン表示",
"My applications": "マイアプリ",
"My assets": "私の資産",
"Name": "名前",
"Native": "クライアント",
"Need review for login asset": "このログインは手動で確認する必要があります。続行しますか?",
"Need to use": "使用する必要がある",
"No": "いいえ",
"No account available": "アカウントがありません",
"No available connect method": "接続方法がありません",
"No matching found": "マッチがありません",
"Need review for login asset": "今回のログインは人間によるオーディットが必要です、続行しますか?",
"Need to use": "使用が必要",
"No": "",
"No account available": "使用可能なアカウントがありません",
"No available connect method": "利用可能な接続方法がありません",
"No matching found": "該当する項目はありません",
"No permission": "権限がありません",
"No protocol available": "利用可能なプロトコルがありません",
"Normal accounts": "通常のログインアカウント",
"Not quick command": "非高速コマンド",
"Open in new window": "新しいウィンドウが開きます",
"Not quick command": "ショートカットコマンドはありません",
"Open in new window": "新しいウィンドウで開く",
"Password": "パスワード",
"Password is token password on the table": "パスワードは、テーブルのトークンパスワードです",
"Password is your password login to system": "パスワードは、システムにログインするためのパスワードです",
"Pause": "タスクを一時停止",
"Pause task has been send": "一時停止タスク送信されました",
"Password is token password on the table": "パスワードはのトークンパスワードです",
"Password is your password login to system": "パスワードはあなたのログインシステムのパスワードです",
"Pause": "一時停止",
"Pause task has been send": "一時停止タスク送信されました",
"Please choose an account": "ユーザーを選択してください",
"Please input password": "パスワードを入力してください",
"Port": "ポート",
"Protocol": "プロトコル",
"Protocol": "規約",
"Protocol: ": "プロトコル: {{value}}",
"RDP Client": "RDPクライアント",
"RDP File": "RDPファイル",
"RDP client options": "RDPクライアントオプション",
"RDP color quality": "RDP 色品質",
"RDP resolution": "RDP 解像度",
"RDP smart size": "RDP スマートサイズ",
"Re-use for a long time after opening": "開いた後、長い間再利用する",
"RDP color quality": "RDP色品質",
"RDP resolution": "RDP解像度",
"RDP smart size": "RDPスマートサイズ",
"Re-use for a long time after opening": "有効にした後、この接続情報を長期間、何度でも使用できます",
"Reconnect": "再接続",
"Refresh": "リフレッシュ",
"Remember password": "パスワードを覚える",
"Remember select": "選択を覚える",
"Refresh": "更新",
"Remember password": "パスワードを保持",
"Remember select": "選択を記憶",
"Remote apps": "リモートアプリケーション",
"Reselect connection method": "資産の接続方法の再選択",
"Resume": "タスクを再開",
"Resume task has been send": "タスクが再開されました",
"Right click asset": "右クリック資産",
"Right click node": "右クリックノード",
"Right mouse quick paste": "右クイック貼り付け",
"Run it by client": "クライアントで実行する",
"Reselect connection method": "接続方法を再選択可能",
"Resume": "回復",
"Resume task has been send": "復旧タスクが送信されました",
"Right click asset": "資産を右クリック → 接続",
"Right click node": "ノードを右クリック→全て展開",
"Right mouse quick paste": "右クリックで速やかにペースト",
"Run it by client": "クライアントで実行",
"SQL Client": "SQLクライアント",
"Save command": "保存コマンド",
"Save success": "保存に成功",
"Save command": "コマンドを保存",
"Save success": "保存が成功しました",
"Search": "検索",
"Select account": "システムユーザーの選択",
"Select account": "アカウントを選択",
"Send command": "コマンドを送信",
"Send text to all ssh terminals": "すべてのssh端末にテキストを送信します",
"Set reusable": "再利用可能な",
"Send text to all ssh terminals": "全てのssh端末にテキストを送信",
"Set reusable": "再利用を開始",
"Setting": "設定",
"Settings or basic settings": "設定 → 基本設定",
"Show left manager": "左サイドバーを表示",
"Settings or basic settings": "メニュー設定 → 基本設定",
"Show left manager": "左側のバーを表示",
"Skip": "スキップ",
"Skip manual password": "手動パスワードウィンドウをスキップします",
"Special account": "特別なアカウント",
"Special accounts": "特別ログインアカウント",
"Speed": "スピード",
"Split connect": "スクリーン接続",
"Split connect number": "1つのセッションで最大3つの画面分割接続をサポート",
"Split vertically": "垂直分割",
"Start Time: ": "開始時間: {{value}}",
"Stop": "Stop",
"Skip manual password": "手動パスワードウィンドウをスキップ",
"Special accounts": "特別なアカウント",
"Speed": "速度",
"Split connect": "スプリット画面で接続",
"Split connect number": "1セッションに最大3つの分割接続が可能",
"Split vertically": "垂直分割スクリーン",
"Start Time: ": "開始時間:{{value}}",
"Stop": "停止",
"Support": "サポート",
"Swiss French keyboard layout": "Swiss French (Qwertz)",
"Switch to input command": "入力コマンドに切り替え",
"Switch to quick command": "クイックコマンドに切り替え",
"Swiss French keyboard layout": "スイスフレンチQwertz",
"Switch to input command": "コマンド入力に切り替え",
"Switch to quick command": "ショートカットコマンドに切り替え",
"Tab List": "ウィンドウリスト",
"The connection method is invalid, please refresh the page": "接続方法が無効です。ページを更新してください",
"Ticket review closed for login asset": "このログイン レビューは終了しており、アセットを接続できません",
"Ticket review pending for login asset": "ログイン アプリケーションが送信され、承認者が確認するのを待っています。リンクをコピーして承認者に送信することもできます。",
"Ticket review rejected for login asset": "このログイン レビューは拒否されたため、アセットを接続できません",
"The connection method is invalid, please refresh the page": "接続方法が無効になりました、ページを更新してください",
"Ticket review approved for login asset": "このログイン審査は通過しました, 資産に接続しますか?",
"Ticket review closed for login asset": "このログイン監査は閉じられ、リソースに接続できません",
"Ticket review pending for login asset": "ログイン申請が提出され、審査者の審査を待っています。リンクをコピーして彼に送ることもできます",
"Ticket review rejected for login asset": "今回のログイン審査は拒否され、資産に接続できません",
"Tips": "ヒント",
"Token expired": "トークンの有効期限が切れました",
"Tool download": "ツールダウンロード",
"Turkey keyboard layout": "Turkish-Q (Qwerty)",
"Token expired": "トークンの有効期限が切れました、再接続してください",
"Tool download": "ツールダウンロード",
"Turkey keyboard layout": "Turkish-QQwerty",
"Type tree": "タイプツリー",
"UK English keyboard layout": "UK English (Qwerty)",
"UK English keyboard layout": "イギリス英語 (Qwerty)",
"US English keyboard layout": "US English (Qwerty)",
"User": "ユーザー",
"User: ": "ユーザー: {{value}}",
"Username": "ユーザー名",
"Username@Domain": "ユーザ @ ADドメイン",
"Username@Domain": "ユーザ名@ADドメイン",
"Users": "ユーザー",
"Using token": "トークンを使用する",
"Using token": "トークンを使用",
"View": "ビュー",
"VirtualApp": "仮想アプリケーション",
"Web Terminal": "Web端末",
"Website": "公式サイト",
"Website": "公式ウェブサイト",
"With secret accounts": "ホストアカウント",
"Yes": "はい",
"asset": "資産",
"asset": "アセット",
"cols": "列数",
"confirm": "確認",
"connect info": "接続情報",

View File

@ -1,3 +1,12 @@
import os
LOCALE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
RED = "\033[91m"
BLUE = "\033[94m"
CYAN = "\033[96m"
RESET = "\033[0m"
GREEN = "\033[92m"
WHITE = "\033[97m"
YELLOW = "\033[93m"
MAGENTA = "\033[95m"

View File

@ -1,7 +1,7 @@
import asyncio
import os
from apps.locale.translate import LOCALE_DIR
from apps.locale.translate import LOCALE_DIR, RED
from apps.locale.translate.manager import OtherTranslateManager, CoreTranslateManager
from apps.locale.translate.utils import OpenAITranslate
@ -25,7 +25,7 @@ class Translate:
_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.')
print(f'{RED}File: {zh_file} not exists.{RED}')
return
await CoreTranslateManager(_dir, self.oai_trans).run()
@ -34,7 +34,7 @@ class Translate:
_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.')
print(f'{RED}File: {zh_file} not exists.{RED}\n')
return
await OtherTranslateManager(_dir, self.oai_trans).run()

View File

@ -1,6 +1,10 @@
import asyncio
import os
from tqdm import tqdm
from apps.locale.translate import RED, GREEN, RESET
class BaseTranslateManager:
bulk_size = 30
@ -12,6 +16,7 @@ class BaseTranslateManager:
def __init__(self, dir_path, oai_trans_instance):
self.oai_trans = oai_trans_instance
self._dir = dir_path
self.dir_name = os.path.basename(self._dir)
if not os.path.exists(self._dir):
os.makedirs(self._dir)
@ -37,16 +42,25 @@ class BaseTranslateManager:
translated_texts = translated_text.split(self.SEPARATOR)
return dict(zip(keys, translated_texts))
except Exception as e:
print(f"Error during translation task: {e}")
print(f"{RED}Error during translation task: {e}{RED}")
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)
number_of_tasks = len(tasks)
translated_dict = {}
for result in translated_results:
translated_dict.update(result)
bar_format = "{l_bar}%s{bar}%s{r_bar}" % (GREEN, RESET)
desc = f"{target_lang} translate"
with tqdm(
total=number_of_tasks, ncols=100,
desc=desc, bar_format=bar_format
) as pbar:
for task in asyncio.as_completed(tasks):
pbar.set_description_str(f"{GREEN}{desc}{RESET}")
result = await task
translated_dict.update(result)
pbar.update(1)
return translated_dict

View File

@ -2,6 +2,7 @@ import os
import polib
from apps.locale.translate import RED, MAGENTA, GREEN
from .base import BaseTranslateManager
@ -21,14 +22,14 @@ class CoreTranslateManager(BaseTranslateManager):
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.')
print(f'{MAGENTA}msgid: {entry.msgid} not in data.{MAGENTA}')
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}')
print(f'{RED}File save error: {e}{RED}')
async def run(self):
po_file_path = os.path.join(self._dir, 'zh', 'LC_MESSAGES', 'django.po')
@ -39,9 +40,9 @@ class CoreTranslateManager(BaseTranslateManager):
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)}')
print(f'{GREEN}Translate: {self.dir_name} {file_prefix} '
f'django.po need to translate {len(need_trans_dict)}{GREEN}\n')
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

@ -1,6 +1,7 @@
import json
import os
from apps.locale.translate import RED, GREEN
from .base import BaseTranslateManager
@ -20,7 +21,7 @@ class OtherTranslateManager(BaseTranslateManager):
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}')
print(f'{RED}File: {file_path} load error: {e}{RED}')
return {}
def save_dict_as_json(self, data, file_prefix='ja'):
@ -29,7 +30,7 @@ class OtherTranslateManager(BaseTranslateManager):
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}')
print(f'{RED}File: {file_path} save error: {e}{RED}')
async def run(self):
zh_dict = self.load_json_as_dict()
@ -37,9 +38,9 @@ class OtherTranslateManager(BaseTranslateManager):
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)}')
print(f'{GREEN}Translate: {self.dir_name} {file_prefix} need to translate '
f'{len(need_trans_dict)}{GREEN}\n')
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)

View File

@ -2,8 +2,8 @@ from openai import AsyncOpenAI
class OpenAITranslate:
def __init__(self, key: str | None = None):
self.client = AsyncOpenAI(api_key=key)
def __init__(self, key: str | None = None, base_url: str | None = None):
self.client = AsyncOpenAI(api_key=key, base_url=base_url)
async def translate_text(self, text, target_lang="English") -> str | None:
try:

2
poetry.lock generated
View File

@ -7827,4 +7827,4 @@ reference = "tsinghua"
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "86fd825091e6032ad4a48f595a627555822618f8a1a0ed1f314f024e54d190d0"
content-hash = "5474eb8b2e55c714fc812ea637bf83fe40c5acaadebccc10072e30101ab9ba13"

View File

@ -147,6 +147,7 @@ mistune = "2.0.3"
openai = "^1.3.7"
xlsxwriter = "^3.1.9"
polib = "^1.2.0"
tqdm = "^4.66.1"
[tool.poetry.group.xpack.dependencies]