From f8108a22dff3b3ef954469355a617e3353c5dda2 Mon Sep 17 00:00:00 2001 From: Loren Eteval Date: Mon, 17 Aug 2026 22:06:37 +0800 Subject: [PATCH] Refine translatable editor inputs Signed-off-by: Loren Eteval --- Furious/Backends/ExternalCore/Editor.py | 55 ++++++++++++++++++------- Furious/Backends/Xray/RoutingWindow.py | 6 +-- Furious/Externals/GenTranslation.py | 36 +++++++++++++--- Furious/Qt/EditorWidgets.py | 2 +- Furious/Window/NetworkTestDialog.py | 4 +- Furious/Window/ProxyBypassDialog.py | 2 +- Furious/Window/SettingsPage.py | 2 +- Furious/Window/SubscriptionPage.py | 8 ++-- 8 files changed, 83 insertions(+), 32 deletions(-) diff --git a/Furious/Backends/ExternalCore/Editor.py b/Furious/Backends/ExternalCore/Editor.py index 37001c1..db29be2 100644 --- a/Furious/Backends/ExternalCore/Editor.py +++ b/Furious/Backends/ExternalCore/Editor.py @@ -24,6 +24,7 @@ from Furious.Interface import EditorWidgetBinding from Furious.Models import ConfigFactory, ServerProfile from Furious.Qt import ( AppQLabel, + AppQLineEdit, AppQMessageBox, AppQPushButton, GuiEditorItemBasicRemark, @@ -39,10 +40,11 @@ from Furious.Qt import gettext as _ from PySide6 import QtCore from PySide6.QtWidgets import ( QFileDialog, + QFormLayout, QHBoxLayout, - QLineEdit, QPlainTextEdit, QSizePolicy, + QVBoxLayout, QWidget, ) @@ -76,7 +78,7 @@ class ExternalCorePathInput(EditorWidgetBinding): self._key = key self._directory = directory self._title = AppQLabel(_(title), translatable=True) - self._input = QLineEdit() + self._input = AppQLineEdit() self._input.setPlaceholderText(_(placeholder) if placeholder else '') self._browse = AppQPushButton(_('Browse...')) self._browse.clicked.connect(self.browse) @@ -155,10 +157,8 @@ class ExternalCoreArgumentsInput(EditorWidgetBinding): super().__init__() self._title = AppQLabel(_('Arguments'), translatable=True) - self._input = QLineEdit() - self._input.setPlaceholderText( - _('Separate arguments with spaces; quote values containing spaces.') - ) + self._input = AppQLineEdit() + self._input.setPlaceholderText(_('Space-separated arguments')) def widgets(self): """Return the form-row label and line editor.""" @@ -295,7 +295,7 @@ class ExternalCoreApplicationTun2socksInput(GuiEditorItemTextSwitch): def __init__(self): """Initialize the application tun2socks opt-in control.""" - super().__init__(title=_('Use Application tun2socks'), translatable=True) + super().__init__(title=_('Use Application Tun2socks'), translatable=True) def connectToggled(self, callback): """Connect a same-lifetime field-state callback to this control.""" @@ -320,7 +320,7 @@ class ExternalCoreTunRemoteAddressInput(EditorWidgetBinding): super().__init__() self._title = AppQLabel(_('TUN Remote Address'), translatable=True) - self._input = QLineEdit() + self._input = AppQLineEdit() self._input.setMaximumWidth(self.MaximumWidth) self._input.setPlaceholderText(_('Hostname, IPv4, or IPv6 address')) @@ -378,23 +378,50 @@ class ExternalCoreConfigurationGroup(GuiEditorWidgetQGroupBox): socksProxyInput = GuiEditorItemProxySocks(title=_('SOCKS Proxy')) socksProxyInput.widgets()[1].setMaximumWidth(self.ProxyEndpointWidth) - return [ + self._processContainers = ( remarkInput, - ExternalCorePathInput('Executable', 'executable'), + ExternalCorePathInput(_('Executable'), 'executable'), ExternalCorePathInput( - 'Working Directory', + _('Working Directory'), 'workingDirectory', directory=True, - placeholder='Defaults to the executable directory', + placeholder=_('Defaults to executable folder'), ), self._argumentsInput, self._environmentInput, + ) + self._runtimeContainers = ( ExternalCoreShutdownTimeoutInput(), httpProxyInput, socksProxyInput, self._applicationTun2socksInput, self._tunRemoteAddressInput, - ] + ) + + return [*self._processContainers, *self._runtimeContainers] + + @staticmethod + def _formLayout(containers): + """Align one related section independently from the other section.""" + layout = QFormLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.setFormAlignment(QtCore.Qt.AlignmentFlag.AlignLeft) + layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow) + + for container in containers: + layout.addRow(*container.widgets()) + + return layout + + def setupPageLayout(self): + """Keep process fields independent from runtime-network label widths.""" + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(18) + layout.addLayout(self._formLayout(self._processContainers)) + layout.addLayout(self._formLayout(self._runtimeContainers)) + + return layout class ExternalCoreEditor(GuiEditorWidgetQDialog): @@ -415,7 +442,7 @@ class ExternalCoreEditor(GuiEditorWidgetQDialog): super().__init__(*args, **kwargs) - dialogWidth = 600 + dialogWidth = 540 self.setFixedSize(dialogWidth, int(dialogWidth * GOLDEN_RATIO)) self.setTabText(_('External Core')) diff --git a/Furious/Backends/Xray/RoutingWindow.py b/Furious/Backends/Xray/RoutingWindow.py index 9212596..d3c6bd8 100644 --- a/Furious/Backends/Xray/RoutingWindow.py +++ b/Furious/Backends/Xray/RoutingWindow.py @@ -365,7 +365,7 @@ class RoutingRuleEditDialog(AppQTransientDialog): @staticmethod def lineEdit(text=''): """Return the line edit value used by the routing rule edit dialog.""" - widget = QLineEdit(text) + widget = AppQLineEdit(text) widget.setFixedHeight(RoutingRuleEditDialog.MatchInputHeight) return widget @@ -587,7 +587,7 @@ class RoutingRemarkEditDialog(AppQTransientDialog): self.setWindowTitle(_('Edit Routing Remark')) self.setWindowModality(QtCore.Qt.WindowModality.WindowModal) - self.remarkEdit = QLineEdit(remark) + self.remarkEdit = AppQLineEdit(remark) self.dialogBtns = AppQDialogButtonBox(QtCore.Qt.Orientation.Horizontal) self.dialogBtns.addButton(_('OK'), AppQDialogButtonBox.ButtonRole.AcceptRole) @@ -623,7 +623,7 @@ class RoutingProfileEditDialog(AppQTransientDialog): self.setWindowTitle(_('Add Routing')) self.setWindowModality(QtCore.Qt.WindowModality.WindowModal) - self.remarkEdit = QLineEdit(_('New Routing')) + self.remarkEdit = AppQLineEdit(_('New Routing')) self.domainStrategyCombo = AppQComboBox(translatable=False) self.domainStrategyCombo.addItems(DOMAIN_STRATEGIES) diff --git a/Furious/Externals/GenTranslation.py b/Furious/Externals/GenTranslation.py index 494784b..8b17037 100644 --- a/Furious/Externals/GenTranslation.py +++ b/Furious/Externals/GenTranslation.py @@ -2886,6 +2886,22 @@ TRANSLATION = { "ZH": "添加外部Core...", "isReviewed": "True" }, + "Executable": { + "source": [ + "Furious.Backends.ExternalCore.Editor" + ], + "RU": "Исполняемый файл", + "ZH": "可执行文件", + "isReviewed": "True" + }, + "Working Directory": { + "source": [ + "Furious.Backends.ExternalCore.Editor" + ], + "RU": "Рабочий каталог", + "ZH": "工作目录", + "isReviewed": "True" + }, "External Core": { "source": [ "Furious.Backends.ExternalCore.Editor" @@ -2934,12 +2950,12 @@ TRANSLATION = { "ZH": "SOCKS代理", "isReviewed": "True" }, - "Use Application tun2socks": { + "Use Application Tun2socks": { "source": [ "Furious.Backends.ExternalCore.Editor" ], - "RU": "Использовать tun2socks приложения", - "ZH": "使用应用程序的tun2socks", + "RU": "Использовать Tun2socks приложения", + "ZH": "使用应用程序的Tun2socks", "isReviewed": "True" }, "TUN Remote Address": { @@ -2990,12 +3006,20 @@ TRANSLATION = { "ZH": "关闭超时(秒)", "isReviewed": "True" }, - "Separate arguments with spaces; quote values containing spaces.": { + "Space-separated arguments": { "source": [ "Furious.Backends.ExternalCore.Editor" ], - "RU": "Разделяйте аргументы пробелами; значения с пробелами заключайте в кавычки.", - "ZH": "使用空格分隔参数;包含空格的值请使用引号。", + "RU": "Аргументы через пробел", + "ZH": "参数以空格分隔", + "isReviewed": "True" + }, + "Defaults to executable folder": { + "source": [ + "Furious.Backends.ExternalCore.Editor" + ], + "RU": "Папка исполняемого файла", + "ZH": "默认使用可执行文件目录", "isReviewed": "True" }, "Arguments contain invalid quoting.": { diff --git a/Furious/Qt/EditorWidgets.py b/Furious/Qt/EditorWidgets.py index e2f99f0..bfffd3b 100644 --- a/Furious/Qt/EditorWidgets.py +++ b/Furious/Qt/EditorWidgets.py @@ -59,7 +59,7 @@ class GuiEditorItemTextInput(EditorWidgetBinding): title = _(title) self._title = AppQLabel(title, translatable=translatable, parent=parent) - self._input = QLineEdit(parent=parent) + self._input = AppQLineEdit(parent=parent) def text(self) -> str: """Return the text value.""" diff --git a/Furious/Window/NetworkTestDialog.py b/Furious/Window/NetworkTestDialog.py index 3cabd84..c16a580 100644 --- a/Furious/Window/NetworkTestDialog.py +++ b/Furious/Window/NetworkTestDialog.py @@ -47,7 +47,7 @@ class NetworkTestDialog(AppQDialog): self.setWindowModality(QtCore.Qt.WindowModality.WindowModal) self.speedTestURLText = AppQLabel(_('Enter network speed test URL:')) - self.speedTestURLEdit = QLineEdit() + self.speedTestURLEdit = AppQLineEdit() speedTestURLSettings = AppSettings.get('CustomNetworkSpeedTestURL') @@ -57,7 +57,7 @@ class NetworkTestDialog(AppQDialog): self.speedTestURLEdit.setText(NETWORK_SPEED_TEST_URL) self.connectivityText = AppQLabel(_('Enter network connectivity test URL:')) - self.connectivityEdit = QLineEdit() + self.connectivityEdit = AppQLineEdit() connectivitySettings = AppSettings.get('CustomNetworkConnectivityTestURL') diff --git a/Furious/Window/ProxyBypassDialog.py b/Furious/Window/ProxyBypassDialog.py index 53fac9f..0216252 100644 --- a/Furious/Window/ProxyBypassDialog.py +++ b/Furious/Window/ProxyBypassDialog.py @@ -46,7 +46,7 @@ class ProxyBypassDialog(AppQDialog): self.proxyBypassText = AppQLabel( _('Enter system proxy bypass address (separated by semicolons):') ) - self.proxyBypassEdit = QLineEdit() + self.proxyBypassEdit = AppQLineEdit() settings = AppSettings.get('CustomProxyBypass') diff --git a/Furious/Window/SettingsPage.py b/Furious/Window/SettingsPage.py index b4c80a3..d2dadcc 100644 --- a/Furious/Window/SettingsPage.py +++ b/Furious/Window/SettingsPage.py @@ -303,7 +303,7 @@ class _LineEditSettingsCard(_SettingsCard): self.lineEdit.setText(str(AppSettings.get(settingName) or '')) if secret: - self.lineEdit.setEchoMode(QLineEdit.EchoMode.Password) + self.lineEdit.setEchoMode(AppQLineEdit.EchoMode.Password) self.lineEdit.editingFinished.connect(self.persist) diff --git a/Furious/Window/SubscriptionPage.py b/Furious/Window/SubscriptionPage.py index b551146..cba64f7 100644 --- a/Furious/Window/SubscriptionPage.py +++ b/Furious/Window/SubscriptionPage.py @@ -63,14 +63,14 @@ class _SubscriptionEditorDialog(AppQTransientDialog): subscription = dict(subscription or {}) - self.remarkEdit = QLineEdit(subscription.get('remark', '')) - self.urlEdit = QLineEdit(subscription.get('webURL', '')) + self.remarkEdit = AppQLineEdit(subscription.get('remark', '')) + self.urlEdit = AppQLineEdit(subscription.get('webURL', '')) self.enabledSwitch = AppQSwitch() self.enabledSwitch.syncChecked(subscription.get('enabled', True)) self.autoUpdateComboBox = AppQComboBox() self.proxyComboBox = AppQComboBox() - self.userAgentEdit = QLineEdit(subscription.get('userAgent', '')) - self.filterEdit = QLineEdit(subscription.get('filter', '')) + self.userAgentEdit = AppQLineEdit(subscription.get('userAgent', '')) + self.filterEdit = AppQLineEdit(subscription.get('filter', '')) self.remarkEdit.setMinimumWidth(240) self.remarkEdit.setMaximumWidth(360)