style(settings): fold the Happ settings into four tabs

Seven tabs, several holding one or two settings, made the page hard to
scan. The encrypted-links switch moves above the tabs under
auto-detection, the colour profile joins the banners under Appearance &
Theme, and Android per-app proxy joins Network & TUN Engine. The QR
modal's settings link no longer needs a happTab selector, and the three
orphaned tab-label keys are dropped from every locale.
This commit is contained in:
MHSanaei
2026-09-27 16:06:42 +02:00
parent a33b2341e9
commit 8f47b53879
18 changed files with 103 additions and 209 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ type QrVariant = 'standard' | 'happ';
type HappError = 'too_long' | 'unavailable' | null; type HappError = 'too_long' | 'unavailable' | null;
const HAPP_CRYPT5_PREFIX = 'happ://crypt5/'; const HAPP_CRYPT5_PREFIX = 'happ://crypt5/';
const HAPP_SETTINGS_PATH = '/settings?subscriptionTab=happ&happTab=links#subscription'; const HAPP_SETTINGS_PATH = '/settings?subscriptionTab=happ#subscription';
// QrPanel encodes at error level L; QR version 40 holds 2953 UTF-8 bytes at that level. // QrPanel encodes at error level L; QR version 40 holds 2953 UTF-8 bytes at that level.
const HAPP_QR_MAX_BYTES = 2953; const HAPP_QR_MAX_BYTES = 2953;
const UTF8_ENCODER = new TextEncoder(); const UTF8_ENCODER = new TextEncoder();
@@ -6,9 +6,6 @@ import {
BuildOutlined, BuildOutlined,
CloudSyncOutlined, CloudSyncOutlined,
DesktopOutlined, DesktopOutlined,
LinkOutlined,
MobileOutlined,
NotificationOutlined,
ThunderboltOutlined, ThunderboltOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import type { AllSetting } from '@/models/setting'; import type { AllSetting } from '@/models/setting';
@@ -22,7 +19,6 @@ interface HappSettingsContentProps {
updateSetting: (patch: Partial<AllSetting>) => void; updateSetting: (patch: Partial<AllSetting>) => void;
isMobile: boolean; isMobile: boolean;
remoteSourceBadge: (val: string) => React.ReactNode; remoteSourceBadge: (val: string) => React.ReactNode;
defaultActiveTab?: 'routing' | 'links';
} }
export default function HappSettingsContent({ export default function HappSettingsContent({
@@ -30,7 +26,6 @@ export default function HappSettingsContent({
updateSetting, updateSetting,
isMobile, isMobile,
remoteSourceBadge, remoteSourceBadge,
defaultActiveTab = 'routing',
}: HappSettingsContentProps) { }: HappSettingsContentProps) {
const { t } = useTranslation(); const { t } = useTranslation();
// Generator choices stay local until Apply updates the draft; page Save persists it. // Generator choices stay local until Apply updates the draft; page Save persists it.
@@ -65,17 +60,27 @@ export default function HappSettingsContent({
/> />
</SettingListItem> </SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.happLinkEnable')}
description={t('pages.settings.happLinkEnableDesc')}
>
<Switch
checked={allSetting.happLinkEnable}
onChange={(v) => updateSetting({ happLinkEnable: v })}
/>
</SettingListItem>
<Tabs <Tabs
type="card" type="card"
size="small" size="small"
defaultActiveKey={defaultActiveTab}
items={[ items={[
{ {
key: 'routing', key: 'routing',
label: ( label: catTabLabel(
<span> <BranchesOutlined />,
<BranchesOutlined /> {!isMobile && t('pages.settings.subHappGroupRouting')} t('pages.settings.subHappGroupRouting'),
</span> isMobile,
), ),
children: ( children: (
<> <>
@@ -173,27 +178,11 @@ export default function HappSettingsContent({
), ),
}, },
{ {
key: 'links', key: 'appearance',
label: catTabLabel(<LinkOutlined />, t('pages.settings.subHappGroupLinks'), isMobile), label: catTabLabel(
children: ( <DesktopOutlined />,
<SettingListItem t('pages.settings.subHappGroupThemes'),
paddings="small" isMobile,
title={t('pages.settings.happLinkEnable')}
description={t('pages.settings.happLinkEnableDesc')}
>
<Switch
checked={allSetting.happLinkEnable}
onChange={(v) => updateSetting({ happLinkEnable: v })}
/>
</SettingListItem>
),
},
{
key: 'banners',
label: (
<span>
<NotificationOutlined /> {!isMobile && t('pages.settings.subHappGroupBanners')}
</span>
), ),
children: ( children: (
<> <>
@@ -285,15 +274,59 @@ export default function HappSettingsContent({
onChange={(v) => updateSetting({ subHappNotificationExpire: v })} onChange={(v) => updateSetting({ subHappNotificationExpire: v })}
/> />
</SettingListItem> </SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappColorProfile')}
description={t('pages.settings.subHappColorProfileDesc')}
>
<Space orientation="vertical" style={{ width: '100%' }}>
<Input
value={allSetting.subHappColorProfile}
placeholder='{"serverRowBackgroundColor":"#21003D67"} or resetcolors'
onChange={(e) => updateSetting({ subHappColorProfile: e.target.value })}
/>
<Space wrap size="small">
<Button
size="small"
onClick={() => updateSetting({ subHappColorProfile: 'resetcolors' })}
>
{t('reset')}
</Button>
<Button
size="small"
onClick={() =>
updateSetting({
subHappColorProfile:
'{"serverRowBackgroundColor":"#21003D67","cardBackgroundColor":"#120023B3"}',
})
}
>
Violet
</Button>
<Button
size="small"
onClick={() =>
updateSetting({
subHappColorProfile:
'{"serverRowBackgroundColor":"#002B3667","cardBackgroundColor":"#001F27B3"}',
})
}
>
Turquoise
</Button>
</Space>
</Space>
</SettingListItem>
</> </>
), ),
}, },
{ {
key: 'network', key: 'network',
label: ( label: catTabLabel(
<span> <ThunderboltOutlined />,
<ThunderboltOutlined /> {!isMobile && t('pages.settings.subHappGroupNetwork')} t('pages.settings.subHappGroupNetwork'),
</span> isMobile,
), ),
children: ( children: (
<> <>
@@ -422,70 +455,45 @@ export default function HappSettingsContent({
]} ]}
/> />
</SettingListItem> </SettingListItem>
</>
),
},
{
key: 'themes',
label: (
<span>
<DesktopOutlined /> {!isMobile && t('pages.settings.subHappGroupThemes')}
</span>
),
children: (
<>
<SettingListItem <SettingListItem
paddings="small" paddings="small"
title={t('pages.settings.subHappColorProfile')} title={t('pages.settings.subHappPerAppMode')}
description={t('pages.settings.subHappColorProfileDesc')} description={t('pages.settings.subHappPerAppModeDesc')}
> >
<Space orientation="vertical" style={{ width: '100%' }}> <Select
<Input value={allSetting.subHappPerAppMode || 'off'}
value={allSetting.subHappColorProfile} style={{ width: '100%' }}
placeholder='{"serverRowBackgroundColor":"#21003D67"} or resetcolors' onChange={(v) => updateSetting({ subHappPerAppMode: v })}
onChange={(e) => updateSetting({ subHappColorProfile: e.target.value })} options={[
/> { value: 'off', label: t('pages.settings.subHappPerAppOff') },
<Space wrap size="small"> { value: 'on', label: t('pages.settings.subHappPerAppOn') },
<Button { value: 'bypass', label: t('pages.settings.subHappPerAppBypass') },
size="small" ]}
onClick={() => updateSetting({ subHappColorProfile: 'resetcolors' })} />
> </SettingListItem>
{t('reset')}
</Button> <SettingListItem
<Button paddings="small"
size="small" title={t('pages.settings.subHappPerAppList')}
onClick={() => description={t('pages.settings.subHappPerAppListDesc')}
updateSetting({ >
subHappColorProfile: <Input.TextArea
'{"serverRowBackgroundColor":"#21003D67","cardBackgroundColor":"#120023B3"}', value={allSetting.subHappPerAppList}
}) rows={4}
} placeholder="org.telegram.messenger, com.google.android.youtube"
> onChange={(e) => updateSetting({ subHappPerAppList: e.target.value })}
Violet />
</Button>
<Button
size="small"
onClick={() =>
updateSetting({
subHappColorProfile:
'{"serverRowBackgroundColor":"#002B3667","cardBackgroundColor":"#001F27B3"}',
})
}
>
Turquoise
</Button>
</Space>
</Space>
</SettingListItem> </SettingListItem>
</> </>
), ),
}, },
{ {
key: 'failover', key: 'failover',
label: ( label: catTabLabel(
<span> <CloudSyncOutlined />,
<CloudSyncOutlined /> {!isMobile && t('pages.settings.subHappGroupFailover')} t('pages.settings.subHappGroupFailover'),
</span> isMobile,
), ),
children: ( children: (
<> <>
@@ -538,47 +546,6 @@ export default function HappSettingsContent({
</> </>
), ),
}, },
{
key: 'android',
label: (
<span>
<MobileOutlined /> {!isMobile && t('pages.settings.subHappGroupAndroid')}
</span>
),
children: (
<>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappPerAppMode')}
description={t('pages.settings.subHappPerAppModeDesc')}
>
<Select
value={allSetting.subHappPerAppMode || 'off'}
style={{ width: '100%' }}
onChange={(v) => updateSetting({ subHappPerAppMode: v })}
options={[
{ value: 'off', label: t('pages.settings.subHappPerAppOff') },
{ value: 'on', label: t('pages.settings.subHappPerAppOn') },
{ value: 'bypass', label: t('pages.settings.subHappPerAppBypass') },
]}
/>
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.subHappPerAppList')}
description={t('pages.settings.subHappPerAppListDesc')}
>
<Input.TextArea
value={allSetting.subHappPerAppList}
rows={4}
placeholder="org.telegram.messenger, com.google.android.youtube"
onChange={(e) => updateSetting({ subHappPerAppList: e.target.value })}
/>
</SettingListItem>
</>
),
},
]} ]}
/> />
@@ -414,8 +414,6 @@ export default function SubscriptionGeneralTab({
updateSetting={updateSetting} updateSetting={updateSetting}
isMobile={isMobile} isMobile={isMobile}
remoteSourceBadge={remoteSourceBadge} remoteSourceBadge={remoteSourceBadge}
// QR settings links select the link control; ordinary Happ visits still start on routing.
defaultActiveTab={searchParams.get('happTab') === 'links' ? 'links' : 'routing'}
/> />
), ),
}, },
+1 -1
View File
@@ -241,7 +241,7 @@ describe('ClientQrModal Happ presentation', () => {
expect(view.onOpenChange).toHaveBeenCalledOnce(); expect(view.onOpenChange).toHaveBeenCalledOnce();
expect(view.onOpenChange).toHaveBeenCalledWith(false); expect(view.onOpenChange).toHaveBeenCalledWith(false);
expect(screen.getByTestId('location').textContent).toBe( expect(screen.getByTestId('location').textContent).toBe(
'/settings?subscriptionTab=happ&happTab=links#subscription', '/settings?subscriptionTab=happ#subscription',
); );
expect(HttpUtil.post).not.toHaveBeenCalled(); expect(HttpUtil.post).not.toHaveBeenCalled();
}); });
@@ -144,7 +144,7 @@ describe('SubscriptionGeneralTab', () => {
}); });
it.each([false, true])( it.each([false, true])(
'updates the Happ link gate from its own tab when stored as %s', 'updates the Happ link gate beside auto-detection when stored as %s',
(enabled) => { (enabled) => {
const updateSetting = vi.fn(); const updateSetting = vi.fn();
@@ -158,11 +158,6 @@ describe('SubscriptionGeneralTab', () => {
); );
fireEvent.click(screen.getByRole('tab', { name: /Happ/ })); fireEvent.click(screen.getByRole('tab', { name: /Happ/ }));
expect(
screen.getByRole('tab', { name: /Routing & Rules/ }).getAttribute('aria-selected'),
).toBe('true');
expect(screen.queryByRole('switch', { name: 'Encrypted subscription links' })).toBeNull();
fireEvent.click(screen.getByRole('tab', { name: /Subscription Links/ }));
const linkSwitch = screen.getByRole('switch', { name: 'Encrypted subscription links' }); const linkSwitch = screen.getByRole('switch', { name: 'Encrypted subscription links' });
expect(linkSwitch.getAttribute('aria-checked')).toBe(String(enabled)); expect(linkSwitch.getAttribute('aria-checked')).toBe(String(enabled));
expect(updateSetting).not.toHaveBeenCalled(); expect(updateSetting).not.toHaveBeenCalled();
@@ -172,51 +167,24 @@ describe('SubscriptionGeneralTab', () => {
}, },
); );
it('opens the Happ link tab from the QR settings deep link without enabling generation', () => { it('shows the Happ link gate from the QR settings deep link without enabling generation', () => {
const updateSetting = vi.fn(); const updateSetting = vi.fn();
renderWithProviders( renderWithProviders(
<MemoryRouter initialEntries={['/settings?subscriptionTab=happ&happTab=links#subscription']}> <MemoryRouter initialEntries={['/settings?subscriptionTab=happ#subscription']}>
<SubscriptionGeneralTab <SubscriptionGeneralTab
allSetting={new AllSetting({ happLinkEnable: false })} allSetting={new AllSetting({ happLinkEnable: false })}
updateSetting={updateSetting} updateSetting={updateSetting}
/> />
<LocationProbe />
</MemoryRouter>, </MemoryRouter>,
); );
expect(screen.getByRole('tab', { name: /Happ/ }).getAttribute('aria-selected')).toBe('true'); expect(screen.getByRole('tab', { name: /Happ/ }).getAttribute('aria-selected')).toBe('true');
expect(
screen.getByRole('tab', { name: /Subscription Links/ }).getAttribute('aria-selected'),
).toBe('true');
expect( expect(
screen screen
.getByRole('switch', { name: 'Encrypted subscription links' }) .getByRole('switch', { name: 'Encrypted subscription links' })
.getAttribute('aria-checked'), .getAttribute('aria-checked'),
).toBe('false'); ).toBe('false');
expect(screen.getByTestId('location').textContent).toBe(
'/settings?subscriptionTab=happ&happTab=links#subscription',
);
expect(updateSetting).not.toHaveBeenCalled(); expect(updateSetting).not.toHaveBeenCalled();
}); });
it.each(['', '&happTab=unknown'])(
'keeps the routing default for a general Happ deep link %s',
(query) => {
const updateSetting = vi.fn();
renderWithProviders(
<MemoryRouter initialEntries={['/settings?subscriptionTab=happ' + query + '#subscription']}>
<SubscriptionGeneralTab allSetting={new AllSetting()} updateSetting={updateSetting} />
</MemoryRouter>,
);
expect(screen.getByRole('tab', { name: /Happ/ }).getAttribute('aria-selected')).toBe('true');
expect(
screen.getByRole('tab', { name: /Routing & Rules/ }).getAttribute('aria-selected'),
).toBe('true');
expect(screen.queryByRole('switch', { name: 'Encrypted subscription links' })).toBeNull();
expect(updateSetting).not.toHaveBeenCalled();
},
);
}); });
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "IPs / CIDRs عبر البروكسي", "subHappProxyIPs": "IPs / CIDRs عبر البروكسي",
"subHappBlockIPs": "IPs / CIDRs محظورة", "subHappBlockIPs": "IPs / CIDRs محظورة",
"subHappDeeplinkGenerated": "تم إنشاء الرابط العميق وتطبيقه على قواعد التوجيه", "subHappDeeplinkGenerated": "تم إنشاء الرابط العميق وتطبيقه على قواعد التوجيه",
"subHappGroupLinks": "روابط الاشتراك",
"subHappGroupRouting": "التوجيه والقواعد", "subHappGroupRouting": "التوجيه والقواعد",
"subHappGroupBanners": "اللافتات والإعلانات",
"subHappGroupNetwork": "الشبكة ومحرك TUN", "subHappGroupNetwork": "الشبكة ومحرك TUN",
"subHappGroupThemes": "المظهر والسمة", "subHappGroupThemes": "المظهر والسمة",
"subHappGroupFailover": "الترحيل وإدارة التطبيق", "subHappGroupFailover": "الترحيل وإدارة التطبيق",
"subHappGroupAndroid": "البروكسي لكل تطبيق على Android",
"discordSettings": "بوت ديسكورد", "discordSettings": "بوت ديسكورد",
"discordBotEnable": "تفعيل إشعارات ديسكورد", "discordBotEnable": "تفعيل إشعارات ديسكورد",
"discordBotEnableDesc": "إرسال تنبيهات النظام والأحداث إلى قناة ديسكورد عبر البوت", "discordBotEnableDesc": "إرسال تنبيهات النظام والأحداث إلى قناة ديسكورد عبر البوت",
-3
View File
@@ -1761,13 +1761,10 @@
"subHappProxyIPs": "Proxy IPs / CIDRs", "subHappProxyIPs": "Proxy IPs / CIDRs",
"subHappBlockIPs": "Blocked IPs / CIDRs", "subHappBlockIPs": "Blocked IPs / CIDRs",
"subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules", "subHappDeeplinkGenerated": "Deeplink generated and applied to routing rules",
"subHappGroupLinks": "Subscription Links",
"subHappGroupRouting": "Routing & Rules", "subHappGroupRouting": "Routing & Rules",
"subHappGroupBanners": "Banners & Announcements",
"subHappGroupNetwork": "Network & TUN Engine", "subHappGroupNetwork": "Network & TUN Engine",
"subHappGroupThemes": "Appearance & Theme", "subHappGroupThemes": "Appearance & Theme",
"subHappGroupFailover": "Migration & App Management", "subHappGroupFailover": "Migration & App Management",
"subHappGroupAndroid": "Android Per-App Proxy",
"discordSettings": "Discord Bot", "discordSettings": "Discord Bot",
"discordBotEnable": "Enable Discord Notifications", "discordBotEnable": "Enable Discord Notifications",
"discordBotEnableDesc": "Send system and event alerts to a Discord channel via bot", "discordBotEnableDesc": "Send system and event alerts to a Discord channel via bot",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "IPs / CIDR por proxy", "subHappProxyIPs": "IPs / CIDR por proxy",
"subHappBlockIPs": "IPs / CIDR bloqueados", "subHappBlockIPs": "IPs / CIDR bloqueados",
"subHappDeeplinkGenerated": "Deeplink generado y aplicado a las reglas de enrutamiento", "subHappDeeplinkGenerated": "Deeplink generado y aplicado a las reglas de enrutamiento",
"subHappGroupLinks": "Enlaces de suscripción",
"subHappGroupRouting": "Enrutamiento y reglas", "subHappGroupRouting": "Enrutamiento y reglas",
"subHappGroupBanners": "Banners y anuncios",
"subHappGroupNetwork": "Red y motor TUN", "subHappGroupNetwork": "Red y motor TUN",
"subHappGroupThemes": "Apariencia y tema", "subHappGroupThemes": "Apariencia y tema",
"subHappGroupFailover": "Migración y gestión de la app", "subHappGroupFailover": "Migración y gestión de la app",
"subHappGroupAndroid": "Proxy por aplicación en Android",
"discordSettings": "Bot de Discord", "discordSettings": "Bot de Discord",
"discordBotEnable": "Habilitar notificaciones de Discord", "discordBotEnable": "Habilitar notificaciones de Discord",
"discordBotEnableDesc": "Enviar alertas del sistema y eventos a un canal de Discord a través del bot", "discordBotEnableDesc": "Enviar alertas del sistema y eventos a un canal de Discord a través del bot",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "آی‌پی‌های پراکسی (Proxy CIDRs)", "subHappProxyIPs": "آی‌پی‌های پراکسی (Proxy CIDRs)",
"subHappBlockIPs": "آی‌پی‌های مسدود (Block CIDRs)", "subHappBlockIPs": "آی‌پی‌های مسدود (Block CIDRs)",
"subHappDeeplinkGenerated": "دیپ‌لینک تولید و در قوانین روتینگ اعمال شد", "subHappDeeplinkGenerated": "دیپ‌لینک تولید و در قوانین روتینگ اعمال شد",
"subHappGroupLinks": "لینک‌های اشتراک",
"subHappGroupRouting": "قوانین و روتینگ", "subHappGroupRouting": "قوانین و روتینگ",
"subHappGroupBanners": "بنرها و اعلانات",
"subHappGroupNetwork": "تنظیمات شبکه و TUN", "subHappGroupNetwork": "تنظیمات شبکه و TUN",
"subHappGroupThemes": "ظاهر و پوسته برنامه", "subHappGroupThemes": "ظاهر و پوسته برنامه",
"subHappGroupFailover": "مهاجرت و مدیریت برنامه", "subHappGroupFailover": "مهاجرت و مدیریت برنامه",
"subHappGroupAndroid": "پراکسی برنامه‌های اندروید",
"discordSettings": "ربات دیسکورد", "discordSettings": "ربات دیسکورد",
"discordBotEnable": "فعال‌سازی اعلان‌های دیسکورد", "discordBotEnable": "فعال‌سازی اعلان‌های دیسکورد",
"discordBotEnableDesc": "ارسال هشدارهای سیستم و رویدادها به کانال دیسکورد از طریق ربات", "discordBotEnableDesc": "ارسال هشدارهای سیستم و رویدادها به کانال دیسکورد از طریق ربات",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "IP / CIDR Proxy", "subHappProxyIPs": "IP / CIDR Proxy",
"subHappBlockIPs": "IP / CIDR Diblokir", "subHappBlockIPs": "IP / CIDR Diblokir",
"subHappDeeplinkGenerated": "Deeplink dibuat dan diterapkan ke aturan routing", "subHappDeeplinkGenerated": "Deeplink dibuat dan diterapkan ke aturan routing",
"subHappGroupLinks": "Tautan Langganan",
"subHappGroupRouting": "Routing & Aturan", "subHappGroupRouting": "Routing & Aturan",
"subHappGroupBanners": "Banner & Pengumuman",
"subHappGroupNetwork": "Jaringan & Mesin TUN", "subHappGroupNetwork": "Jaringan & Mesin TUN",
"subHappGroupThemes": "Tampilan & Tema", "subHappGroupThemes": "Tampilan & Tema",
"subHappGroupFailover": "Migrasi & Manajemen Aplikasi", "subHappGroupFailover": "Migrasi & Manajemen Aplikasi",
"subHappGroupAndroid": "Proxy Per-Aplikasi Android",
"discordSettings": "Bot Discord", "discordSettings": "Bot Discord",
"discordBotEnable": "Aktifkan Notifikasi Discord", "discordBotEnable": "Aktifkan Notifikasi Discord",
"discordBotEnableDesc": "Kirim peringatan sistem dan acara ke saluran Discord melalui bot", "discordBotEnableDesc": "Kirim peringatan sistem dan acara ke saluran Discord melalui bot",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "プロキシする IP / CIDR", "subHappProxyIPs": "プロキシする IP / CIDR",
"subHappBlockIPs": "ブロックする IP / CIDR", "subHappBlockIPs": "ブロックする IP / CIDR",
"subHappDeeplinkGenerated": "ディープリンクを生成し、ルーティングルールに適用しました", "subHappDeeplinkGenerated": "ディープリンクを生成し、ルーティングルールに適用しました",
"subHappGroupLinks": "サブスクリプションリンク",
"subHappGroupRouting": "ルーティングとルール", "subHappGroupRouting": "ルーティングとルール",
"subHappGroupBanners": "バナーとお知らせ",
"subHappGroupNetwork": "ネットワークと TUN エンジン", "subHappGroupNetwork": "ネットワークと TUN エンジン",
"subHappGroupThemes": "外観とテーマ", "subHappGroupThemes": "外観とテーマ",
"subHappGroupFailover": "移行とアプリ管理", "subHappGroupFailover": "移行とアプリ管理",
"subHappGroupAndroid": "Android アプリ別プロキシ",
"discordSettings": "Discord Bot", "discordSettings": "Discord Bot",
"discordBotEnable": "Discord通知を有効化", "discordBotEnable": "Discord通知を有効化",
"discordBotEnableDesc": "Botを介してDiscordチャンネルにシステムおよびイベント通知を送信します", "discordBotEnableDesc": "Botを介してDiscordチャンネルにシステムおよびイベント通知を送信します",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "IPs / CIDRs via proxy", "subHappProxyIPs": "IPs / CIDRs via proxy",
"subHappBlockIPs": "IPs / CIDRs bloqueados", "subHappBlockIPs": "IPs / CIDRs bloqueados",
"subHappDeeplinkGenerated": "Deeplink gerado e aplicado às regras de roteamento", "subHappDeeplinkGenerated": "Deeplink gerado e aplicado às regras de roteamento",
"subHappGroupLinks": "Links de assinatura",
"subHappGroupRouting": "Roteamento e regras", "subHappGroupRouting": "Roteamento e regras",
"subHappGroupBanners": "Banners e anúncios",
"subHappGroupNetwork": "Rede e mecanismo TUN", "subHappGroupNetwork": "Rede e mecanismo TUN",
"subHappGroupThemes": "Aparência e tema", "subHappGroupThemes": "Aparência e tema",
"subHappGroupFailover": "Migração e gerenciamento do app", "subHappGroupFailover": "Migração e gerenciamento do app",
"subHappGroupAndroid": "Proxy por aplicativo no Android",
"discordSettings": "Bot do Discord", "discordSettings": "Bot do Discord",
"discordBotEnable": "Ativar notificações do Discord", "discordBotEnable": "Ativar notificações do Discord",
"discordBotEnableDesc": "Enviar alertas de sistema e eventos para um canal do Discord via bot", "discordBotEnableDesc": "Enviar alertas de sistema e eventos para um canal do Discord via bot",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "Проксируемые IP / CIDR", "subHappProxyIPs": "Проксируемые IP / CIDR",
"subHappBlockIPs": "Заблокированные IP / CIDR", "subHappBlockIPs": "Заблокированные IP / CIDR",
"subHappDeeplinkGenerated": "Диплинк сгенерирован и применен к правилам маршрутизации", "subHappDeeplinkGenerated": "Диплинк сгенерирован и применен к правилам маршрутизации",
"subHappGroupLinks": "Ссылки подписки",
"subHappGroupRouting": "Маршрутизация и правила", "subHappGroupRouting": "Маршрутизация и правила",
"subHappGroupBanners": "Баннеры и уведомления",
"subHappGroupNetwork": "Сетевые настройки и TUN", "subHappGroupNetwork": "Сетевые настройки и TUN",
"subHappGroupThemes": "Внешний вид и темы", "subHappGroupThemes": "Внешний вид и темы",
"subHappGroupFailover": "Миграция и управление", "subHappGroupFailover": "Миграция и управление",
"subHappGroupAndroid": "Прокси приложений Android",
"discordSettings": "Discord бот", "discordSettings": "Discord бот",
"discordBotEnable": "Включить уведомления в Discord", "discordBotEnable": "Включить уведомления в Discord",
"discordBotEnableDesc": "Отправлять оповещения о событиях в канал Discord через бота", "discordBotEnableDesc": "Отправлять оповещения о событиях в канал Discord через бота",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "Proxy IP'ler / CIDR'ler", "subHappProxyIPs": "Proxy IP'ler / CIDR'ler",
"subHappBlockIPs": "Engellenen IP'ler / CIDR'ler", "subHappBlockIPs": "Engellenen IP'ler / CIDR'ler",
"subHappDeeplinkGenerated": "Derin bağlantı oluşturuldu ve yönlendirme kurallarına uygulandı", "subHappDeeplinkGenerated": "Derin bağlantı oluşturuldu ve yönlendirme kurallarına uygulandı",
"subHappGroupLinks": "Abonelik Bağlantıları",
"subHappGroupRouting": "Yönlendirme ve Kurallar", "subHappGroupRouting": "Yönlendirme ve Kurallar",
"subHappGroupBanners": "Afişler ve Duyurular",
"subHappGroupNetwork": "Ağ ve TUN Motoru", "subHappGroupNetwork": "Ağ ve TUN Motoru",
"subHappGroupThemes": "Görünüm ve Tema", "subHappGroupThemes": "Görünüm ve Tema",
"subHappGroupFailover": "Geçiş ve Uygulama Yönetimi", "subHappGroupFailover": "Geçiş ve Uygulama Yönetimi",
"subHappGroupAndroid": "Android Uygulama Başına Proxy",
"discordSettings": "Discord Botu", "discordSettings": "Discord Botu",
"discordBotEnable": "Discord Bildirimlerini Etkinleştir", "discordBotEnable": "Discord Bildirimlerini Etkinleştir",
"discordBotEnableDesc": "Sistem ve olay uyarılarını bot aracılığıyla bir Discord kanalına gönderin", "discordBotEnableDesc": "Sistem ve olay uyarılarını bot aracılığıyla bir Discord kanalına gönderin",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "IP / CIDR через проксі", "subHappProxyIPs": "IP / CIDR через проксі",
"subHappBlockIPs": "Заблоковані IP / CIDR", "subHappBlockIPs": "Заблоковані IP / CIDR",
"subHappDeeplinkGenerated": "Діплінк згенеровано та застосовано до правил маршрутизації", "subHappDeeplinkGenerated": "Діплінк згенеровано та застосовано до правил маршрутизації",
"subHappGroupLinks": "Посилання на підписку",
"subHappGroupRouting": "Маршрутизація та правила", "subHappGroupRouting": "Маршрутизація та правила",
"subHappGroupBanners": "Банери та оголошення",
"subHappGroupNetwork": "Мережа та рушій TUN", "subHappGroupNetwork": "Мережа та рушій TUN",
"subHappGroupThemes": "Зовнішній вигляд і тема", "subHappGroupThemes": "Зовнішній вигляд і тема",
"subHappGroupFailover": "Міграція та керування програмою", "subHappGroupFailover": "Міграція та керування програмою",
"subHappGroupAndroid": "Проксі для окремих програм Android",
"discordSettings": "Discord бот", "discordSettings": "Discord бот",
"discordBotEnable": "Увімкнути сповіщення Discord", "discordBotEnable": "Увімкнути сповіщення Discord",
"discordBotEnableDesc": "Надсилати системні сповіщення та події в канал Discord через бота", "discordBotEnableDesc": "Надсилати системні сповіщення та події в канал Discord через бота",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "IP / CIDR proxy", "subHappProxyIPs": "IP / CIDR proxy",
"subHappBlockIPs": "IP / CIDR bị chặn", "subHappBlockIPs": "IP / CIDR bị chặn",
"subHappDeeplinkGenerated": "Đã tạo deeplink và áp dụng vào quy tắc định tuyến", "subHappDeeplinkGenerated": "Đã tạo deeplink và áp dụng vào quy tắc định tuyến",
"subHappGroupLinks": "Liên kết đăng ký",
"subHappGroupRouting": "Định tuyến & quy tắc", "subHappGroupRouting": "Định tuyến & quy tắc",
"subHappGroupBanners": "Biểu ngữ & thông báo",
"subHappGroupNetwork": "Mạng & lõi TUN", "subHappGroupNetwork": "Mạng & lõi TUN",
"subHappGroupThemes": "Giao diện & chủ đề", "subHappGroupThemes": "Giao diện & chủ đề",
"subHappGroupFailover": "Di chuyển & quản lý ứng dụng", "subHappGroupFailover": "Di chuyển & quản lý ứng dụng",
"subHappGroupAndroid": "Proxy theo ứng dụng Android",
"discordSettings": "Bot Discord", "discordSettings": "Bot Discord",
"discordBotEnable": "Bật thông báo Discord", "discordBotEnable": "Bật thông báo Discord",
"discordBotEnableDesc": "Gửi cảnh báo hệ thống và sự kiện đến kênh Discord qua bot", "discordBotEnableDesc": "Gửi cảnh báo hệ thống và sự kiện đến kênh Discord qua bot",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "代理 IP / CIDR", "subHappProxyIPs": "代理 IP / CIDR",
"subHappBlockIPs": "阻止 IP / CIDR", "subHappBlockIPs": "阻止 IP / CIDR",
"subHappDeeplinkGenerated": "DeepLink 已生成并填入路由规则", "subHappDeeplinkGenerated": "DeepLink 已生成并填入路由规则",
"subHappGroupLinks": "订阅链接",
"subHappGroupRouting": "路由分流与规则", "subHappGroupRouting": "路由分流与规则",
"subHappGroupBanners": "横幅公告与通知",
"subHappGroupNetwork": "网络与 TUN 引擎", "subHappGroupNetwork": "网络与 TUN 引擎",
"subHappGroupThemes": "界面与主题外观", "subHappGroupThemes": "界面与主题外观",
"subHappGroupFailover": "迁移与客户端管理", "subHappGroupFailover": "迁移与客户端管理",
"subHappGroupAndroid": "Android 分应用代理",
"discordSettings": "Discord 机器人", "discordSettings": "Discord 机器人",
"discordBotEnable": "启用 Discord 通知", "discordBotEnable": "启用 Discord 通知",
"discordBotEnableDesc": "通过机器人向 Discord 频道发送系统和事件警报", "discordBotEnableDesc": "通过机器人向 Discord 频道发送系统和事件警报",
-3
View File
@@ -1643,13 +1643,10 @@
"subHappProxyIPs": "代理 IP / CIDR", "subHappProxyIPs": "代理 IP / CIDR",
"subHappBlockIPs": "封鎖 IP / CIDR", "subHappBlockIPs": "封鎖 IP / CIDR",
"subHappDeeplinkGenerated": "已產生深層連結並套用至路由規則", "subHappDeeplinkGenerated": "已產生深層連結並套用至路由規則",
"subHappGroupLinks": "訂閱連結",
"subHappGroupRouting": "路由與規則", "subHappGroupRouting": "路由與規則",
"subHappGroupBanners": "橫幅與公告",
"subHappGroupNetwork": "網路與 TUN 引擎", "subHappGroupNetwork": "網路與 TUN 引擎",
"subHappGroupThemes": "外觀與主題", "subHappGroupThemes": "外觀與主題",
"subHappGroupFailover": "遷移與應用程式管理", "subHappGroupFailover": "遷移與應用程式管理",
"subHappGroupAndroid": "Android 個別應用程式代理",
"discordSettings": "Discord 機器人", "discordSettings": "Discord 機器人",
"discordBotEnable": "啟用 Discord 通知", "discordBotEnable": "啟用 Discord 通知",
"discordBotEnableDesc": "透過機器人向 Discord 頻道發送系統與事件警報", "discordBotEnableDesc": "透過機器人向 Discord 頻道發送系統與事件警報",