Browse Source

feat(ui): improve client form modal UX

- Rename tabs: "Basic" → "Basics", "Config" → "Credentials"
- Move reverseTag field from Credentials tab to Basics tab
- Move IP log button inline with limitIp field (tooltip button, edit mode only)
- Hide random email button when editing an existing client
- Add tooltips to totalGB and limitIp fields with descriptive hints
- Rename labels: "Total Sent/Received (GB)" → "Traffic Limit (GB)", "Duration" → "Duration (days)"
- Add renewDays translation key for auto-renew label with unit hint
- Remove redundant filterOption and width style from AutoComplete group selectors
- Update all 15 locale files with new and renamed translation keys
MHSanaei 3 ngày trước cách đây
mục cha
commit
7ae3ea66d1

+ 0 - 4
frontend/src/pages/clients/BulkAddToGroupModal.tsx

@@ -66,11 +66,7 @@ export default function BulkAddToGroupModal({
               placeholder={t('pages.clients.groupName')}
               options={groups.map((g) => ({ value: g }))}
               onChange={(v) => setValue(v ?? '')}
-              filterOption={(input, option) =>
-                String(option?.value ?? '').toLowerCase().includes((input || '').toLowerCase())
-              }
               allowClear
-              style={{ width: '100%' }}
               autoFocus
             />
           </Form.Item>

+ 0 - 4
frontend/src/pages/clients/ClientBulkAddModal.tsx

@@ -288,11 +288,7 @@ export default function ClientBulkAddModal({
               placeholder={t('pages.clients.groupPlaceholder')}
               options={groups.map((g) => ({ value: g }))}
               onChange={(v) => update('group', v ?? '')}
-              filterOption={(input, option) =>
-                String(option?.value ?? '').toLowerCase().includes((input || '').toLowerCase())
-              }
               allowClear
-              style={{ width: '100%' }}
             />
           </Form.Item>
 

+ 45 - 40
frontend/src/pages/clients/ClientFormModal.tsx

@@ -14,6 +14,7 @@ import {
   Switch,
   Tabs,
   Tag,
+  Tooltip,
   message,
 } from 'antd';
 import { EyeOutlined, ReloadOutlined } from '@ant-design/icons';
@@ -428,7 +429,7 @@ export default function ClientFormModal({
             items={[
               {
                 key: 'basic',
-                label: t('pages.clients.tabBasic'),
+                label: t('pages.clients.tabBasics'),
                 children: (
                   <>
                     <Row gutter={16}>
@@ -441,20 +442,31 @@ export default function ClientFormModal({
                               style={{ flex: 1 }}
                               onChange={(e) => update('email', e.target.value)}
                             />
-                            <Button icon={<ReloadOutlined />} onClick={() => update('email', RandomUtil.randomLowerAndNum(12))} />
+                            {!isEdit && (
+                              <Button icon={<ReloadOutlined />} onClick={() => update('email', RandomUtil.randomLowerAndNum(12))} />
+                            )}
                           </Space.Compact>
                         </Form.Item>
                       </Col>
-                      <Col xs={24} md={8}>
-                        <Form.Item label={t('pages.clients.totalGB')}>
+                      <Col xs={24} md={6}>
+                        <Form.Item label={t('pages.clients.totalGB')} tooltip={t('pages.clients.totalGBDesc')}>
                           <InputNumber value={form.totalGB} min={0} step={1} style={{ width: '100%' }}
                             onChange={(v) => update('totalGB', Number(v) || 0)} />
                         </Form.Item>
                       </Col>
-                      <Col xs={24} md={4}>
-                        <Form.Item label={t('pages.clients.limitIp')}>
-                          <InputNumber value={form.limitIp} min={0} style={{ width: '100%' }}
-                            onChange={(v) => update('limitIp', Number(v) || 0)} />
+                      <Col xs={24} md={6}>
+                        <Form.Item label={t('pages.clients.limitIp')} tooltip={t('pages.clients.limitIpDesc')}>
+                          <Space.Compact style={{ display: 'flex' }}>
+                            <InputNumber value={form.limitIp} min={0} style={{ flex: 1 }}
+                              onChange={(v) => update('limitIp', Number(v) || 0)} />
+                            {isEdit && (
+                              <Tooltip title={t('pages.clients.ipLog')}>
+                                <Button icon={<EyeOutlined />} loading={ipsLoading} onClick={openIpsModal}>
+                                  {clientIps.length > 0 ? clientIps.length : ''}
+                                </Button>
+                              </Tooltip>
+                            )}
+                          </Space.Compact>
                         </Form.Item>
                       </Col>
                     </Row>
@@ -489,7 +501,7 @@ export default function ClientFormModal({
                       </Col>
                       <Col xs={12} md={6}>
                         <Form.Item
-                          label={t('pages.clients.renew')}
+                          label={t('pages.clients.renewDays')}
                           tooltip={t('pages.clients.renewDesc')}
                         >
                           <InputNumber value={form.reset} min={0} style={{ width: '100%' }}
@@ -499,16 +511,7 @@ export default function ClientFormModal({
                     </Row>
 
                     <Row gutter={16}>
-                      {tgBotEnable && (
-                        <Col xs={24} md={12}>
-                          <Form.Item label={t('pages.clients.telegramId')}>
-                            <InputNumber value={form.tgId} min={0} controls={false}
-                              placeholder={t('pages.clients.telegramIdPlaceholder')} style={{ width: '100%' }}
-                              onChange={(v) => update('tgId', Number(v) || 0)} />
-                          </Form.Item>
-                        </Col>
-                      )}
-                      <Col xs={24} md={tgBotEnable ? 12 : 24}>
+                      <Col xs={24} md={12}>
                         <Form.Item label={t('pages.clients.comment')}>
                           <Input value={form.comment} onChange={(e) => update('comment', e.target.value)} />
                         </Form.Item>
@@ -520,16 +523,34 @@ export default function ClientFormModal({
                             placeholder={t('pages.clients.groupPlaceholder')}
                             options={groups.map((g) => ({ value: g }))}
                             onChange={(v) => update('group', v ?? '')}
-                            filterOption={(input, option) =>
-                              String(option?.value ?? '').toLowerCase().includes((input || '').toLowerCase())
-                            }
                             allowClear
-                            style={{ width: '100%' }}
                           />
                         </Form.Item>
                       </Col>
                     </Row>
 
+                    {(tgBotEnable || showReverseTag) && (
+                      <Row gutter={16}>
+                        {tgBotEnable && (
+                          <Col xs={24} md={12}>
+                            <Form.Item label={t('pages.clients.telegramId')}>
+                              <InputNumber value={form.tgId} min={0} controls={false}
+                                placeholder={t('pages.clients.telegramIdPlaceholder')} style={{ width: '100%' }}
+                                onChange={(v) => update('tgId', Number(v) || 0)} />
+                            </Form.Item>
+                          </Col>
+                        )}
+                        {showReverseTag && (
+                          <Col xs={24} md={12}>
+                            <Form.Item label={t('pages.clients.reverseTag')}>
+                              <Input value={form.reverseTag} placeholder={t('pages.clients.reverseTagPlaceholder')}
+                                onChange={(e) => update('reverseTag', e.target.value)} />
+                            </Form.Item>
+                          </Col>
+                        )}
+                      </Row>
+                    )}
+
                     <Form.Item label={t('pages.clients.attachedInbounds')} required={!isEdit}>
                       <SelectAllClearButtons
                         options={inboundOptions}
@@ -555,20 +576,12 @@ export default function ClientFormModal({
                       <Switch checked={form.enable} onChange={(v) => update('enable', v)} />
                       <span style={{ marginLeft: 8 }}>{t('enable')}</span>
                     </Form.Item>
-
-                    {isEdit && (
-                      <Form.Item label={t('pages.clients.ipLog')}>
-                        <Button icon={<EyeOutlined />} loading={ipsLoading} onClick={openIpsModal}>
-                          {clientIps.length > 0 ? clientIps.length : ''}
-                        </Button>
-                      </Form.Item>
-                    )}
                   </>
                 ),
               },
               {
                 key: 'config',
-                label: t('pages.clients.tabConfig'),
+                label: t('pages.clients.tabCredentials'),
                 children: (
                   <>
                     <Row gutter={16}>
@@ -635,14 +648,6 @@ export default function ClientFormModal({
                           </Form.Item>
                         </Col>
                       )}
-                      {showReverseTag && (
-                        <Col xs={24} md={12}>
-                          <Form.Item label={t('pages.clients.reverseTag')}>
-                            <Input value={form.reverseTag} placeholder={t('pages.clients.reverseTagPlaceholder')}
-                              onChange={(e) => update('reverseTag', e.target.value)} />
-                          </Form.Item>
-                        </Col>
-                      )}
                     </Row>
                   </>
                 ),

+ 8 - 5
internal/web/translation/ar-EG.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "أساسي",
-      "tabConfig": "التكوين",
+      "tabBasics": "أساسي",
+      "tabCredentials": "بيانات الاعتماد",
       "add": "إضافة عميل",
       "edit": "تعديل العميل",
       "submitAdd": "إضافة عميل",
@@ -666,10 +666,11 @@
       "prefix": "بادئة",
       "postfix": "لاحقة",
       "delayedStart": "البدء بعد أول استخدام",
-      "expireDays": "المدة",
+      "expireDays": "المدة (أيام)",
       "days": "يوم",
       "renew": "تجديد تلقائي",
       "renewDesc": "تجديد تلقائي بعد انتهاء الصلاحية. (0 = تعطيل) (الوحدة: يوم)",
+      "renewDays": "تجديد تلقائي (أيام)",
       "searchPlaceholder": "ابحث بالبريد، التعليق، sub ID، UUID، كلمة المرور، auth…",
       "filterTitle": "تصفية العملاء",
       "clearAllFilters": "مسح الكل",
@@ -691,10 +692,12 @@
       "hasNot": "لا يملك",
       "title": "العملاء",
       "actions": "الإجراءات",
-      "totalGB": "مجموع المرسل/المستقبل (جيجابايت)",
+      "totalGB": "حد البيانات (جيجابايت)",
+      "totalGBDesc": "حصة البيانات لهذا العميل. 0 = غير محدود.",
       "expiryTime": "انتهاء الصلاحية",
       "addClients": "إضافة عملاء",
       "limitIp": "حد عناوين IP",
+      "limitIpDesc": "الحد الأقصى لعناوين IP المتزامنة. 0 = غير محدود.",
       "password": "كلمة المرور",
       "subId": "معرّف الاشتراك",
       "online": "متصل",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "اختار الإدخال"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/en-US.json

@@ -638,8 +638,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Basic",
-      "tabConfig": "Config",
+      "tabBasics": "Basics",
+      "tabCredentials": "Credentials",
       "add": "Add Client",
       "edit": "Edit Client",
       "submitAdd": "Add Client",
@@ -667,10 +667,11 @@
       "prefix": "Prefix",
       "postfix": "Postfix",
       "delayedStart": "Start After First Use",
-      "expireDays": "Duration",
+      "expireDays": "Duration (days)",
       "days": "Day(s)",
       "renew": "Auto Renew",
       "renewDesc": "Auto-renewal after expiration. (0 = disable)(unit: day)",
+      "renewDays": "Auto Renew (days)",
       "searchPlaceholder": "Search email, comment, sub ID, UUID, password, auth…",
       "filterTitle": "Filter clients",
       "clearAllFilters": "Clear all",
@@ -692,10 +693,12 @@
       "hasNot": "Doesn't have",
       "title": "Clients",
       "actions": "Actions",
-      "totalGB": "Total Sent/Received (GB)",
+      "totalGB": "Traffic Limit (GB)",
+      "totalGBDesc": "Data quota for this client. 0 = unlimited.",
       "expiryTime": "Expiry",
       "addClients": "Add Clients",
       "limitIp": "IP Limit",
+      "limitIpDesc": "Maximum simultaneous IPs. 0 = unlimited.",
       "password": "Password",
       "subId": "Subscription ID",
       "online": "Online",
@@ -1734,4 +1737,4 @@
       "chooseInbound": "Choose an Inbound"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/es-ES.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Básico",
-      "tabConfig": "Configuración",
+      "tabBasics": "Básico",
+      "tabCredentials": "Credenciales",
       "add": "Añadir cliente",
       "edit": "Editar cliente",
       "submitAdd": "Añadir cliente",
@@ -666,10 +666,11 @@
       "prefix": "Prefijo",
       "postfix": "Sufijo",
       "delayedStart": "Iniciar tras el primer uso",
-      "expireDays": "Duración",
+      "expireDays": "Duración (días)",
       "days": "Día(s)",
       "renew": "Renovación automática",
       "renewDesc": "Renovación automática tras la expiración. (0 = desactivado) (unidad: día)",
+      "renewDays": "Renovación automática (días)",
       "searchPlaceholder": "Buscar email, comentario, sub ID, UUID, contraseña, auth…",
       "filterTitle": "Filtrar clientes",
       "clearAllFilters": "Limpiar todo",
@@ -691,10 +692,12 @@
       "hasNot": "No tiene",
       "title": "Clientes",
       "actions": "Acciones",
-      "totalGB": "Total enviado/recibido (GB)",
+      "totalGB": "Límite de tráfico (GB)",
+      "totalGBDesc": "Cuota de datos para este cliente. 0 = ilimitado.",
       "expiryTime": "Expiración",
       "addClients": "Añadir clientes",
       "limitIp": "Límite de IP",
+      "limitIpDesc": "Máximo de IP simultáneas. 0 = ilimitado.",
       "password": "Contraseña",
       "subId": "ID de suscripción",
       "online": "En línea",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "Elige un Inbound"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/fa-IR.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "پایه",
-      "tabConfig": "پیکربندی",
+      "tabBasics": "پایه",
+      "tabCredentials": "اطلاعات اتصال",
       "add": "افزودن کلاینت",
       "edit": "ویرایش کلاینت",
       "submitAdd": "افزودن کلاینت",
@@ -666,10 +666,11 @@
       "prefix": "پیشوند",
       "postfix": "پسوند",
       "delayedStart": "شروع پس از اولین استفاده",
-      "expireDays": "مدت",
+      "expireDays": "مدت اعتبار (روز)",
       "days": "روز",
       "renew": "تمدید خودکار",
       "renewDesc": "تمدید خودکار پس از انقضا. (۰ = غیرفعال) (واحد: روز)",
+      "renewDays": "تمدید خودکار (روز)",
       "searchPlaceholder": "جستجوی ایمیل، توضیح، Sub ID، UUID، رمز، احراز...",
       "filterTitle": "فیلتر کاربران",
       "clearAllFilters": "پاک کردن همه",
@@ -691,10 +692,12 @@
       "hasNot": "ندارد",
       "title": "کلاینت‌ها",
       "actions": "عملیات",
-      "totalGB": "مجموع ارسال/دریافت (گیگابایت)",
+      "totalGB": "سقف حجم (گیگابایت)",
+      "totalGBDesc": "سهمیه‌ی حجم مصرفی کلاینت. ۰ = نامحدود",
       "expiryTime": "انقضا",
       "addClients": "افزودن کلاینت‌ها",
       "limitIp": "محدودیت IP",
+      "limitIpDesc": "حداکثر تعداد IP همزمان. ۰ = نامحدود",
       "password": "رمز عبور",
       "subId": "شناسه اشتراک",
       "online": "آنلاین",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "یک ورودی انتخاب کنید"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/id-ID.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Dasar",
-      "tabConfig": "Konfigurasi",
+      "tabBasics": "Dasar",
+      "tabCredentials": "Kredensial",
       "add": "Tambah klien",
       "edit": "Ubah klien",
       "submitAdd": "Tambah klien",
@@ -666,10 +666,11 @@
       "prefix": "Awalan",
       "postfix": "Akhiran",
       "delayedStart": "Mulai setelah penggunaan pertama",
-      "expireDays": "Durasi",
+      "expireDays": "Durasi (hari)",
       "days": "Hari",
       "renew": "Perpanjangan otomatis",
       "renewDesc": "Perpanjangan otomatis setelah kedaluwarsa. (0 = nonaktif) (satuan: hari)",
+      "renewDays": "Perpanjangan otomatis (hari)",
       "searchPlaceholder": "Cari email, komentar, sub ID, UUID, kata sandi, auth…",
       "filterTitle": "Filter klien",
       "clearAllFilters": "Hapus semua",
@@ -691,10 +692,12 @@
       "hasNot": "Tidak memiliki",
       "title": "Klien",
       "actions": "Aksi",
-      "totalGB": "Total Kirim/Terima (GB)",
+      "totalGB": "Batas Trafik (GB)",
+      "totalGBDesc": "Kuota data untuk klien ini. 0 = tidak terbatas.",
       "expiryTime": "Kedaluwarsa",
       "addClients": "Tambah klien",
       "limitIp": "Batas IP",
+      "limitIpDesc": "Jumlah maksimum IP bersamaan. 0 = tidak terbatas.",
       "password": "Kata sandi",
       "subId": "ID Langganan",
       "online": "Online",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "Pilih Inbound"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/ja-JP.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "基本",
-      "tabConfig": "設定",
+      "tabBasics": "基本",
+      "tabCredentials": "認証情報",
       "add": "クライアントを追加",
       "edit": "クライアントを編集",
       "submitAdd": "クライアントを追加",
@@ -666,10 +666,11 @@
       "prefix": "プレフィックス",
       "postfix": "サフィックス",
       "delayedStart": "初回使用から開始",
-      "expireDays": "期間",
+      "expireDays": "期間 (日)",
       "days": "日",
       "renew": "自動更新",
       "renewDesc": "有効期限切れ後に自動更新します。(0 = 無効) (単位: 日)",
+      "renewDays": "自動更新 (日)",
       "searchPlaceholder": "メール、コメント、sub ID、UUID、パスワード、auth を検索…",
       "filterTitle": "クライアントをフィルタ",
       "clearAllFilters": "すべてクリア",
@@ -691,10 +692,12 @@
       "hasNot": "なし",
       "title": "クライアント",
       "actions": "操作",
-      "totalGB": "送受信合計 (GB)",
+      "totalGB": "トラフィック上限 (GB)",
+      "totalGBDesc": "このクライアントのデータ割当量。0 = 無制限。",
       "expiryTime": "有効期限",
       "addClients": "クライアントを追加",
       "limitIp": "IP 制限",
+      "limitIpDesc": "同時接続 IP の最大数。0 = 無制限。",
       "password": "パスワード",
       "subId": "サブスクリプション ID",
       "online": "オンライン",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "インバウンドを選択"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/pt-BR.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Básico",
-      "tabConfig": "Configuração",
+      "tabBasics": "Básico",
+      "tabCredentials": "Credenciais",
       "add": "Adicionar cliente",
       "edit": "Editar cliente",
       "submitAdd": "Adicionar cliente",
@@ -666,10 +666,11 @@
       "prefix": "Prefixo",
       "postfix": "Sufixo",
       "delayedStart": "Iniciar após o primeiro uso",
-      "expireDays": "Duração",
+      "expireDays": "Duração (dias)",
       "days": "Dia(s)",
       "renew": "Renovação automática",
       "renewDesc": "Renovação automática após a expiração. (0 = desativar) (unidade: dia)",
+      "renewDays": "Renovação automática (dias)",
       "searchPlaceholder": "Buscar email, comentário, sub ID, UUID, senha, auth…",
       "filterTitle": "Filtrar clientes",
       "clearAllFilters": "Limpar tudo",
@@ -691,10 +692,12 @@
       "hasNot": "Não tem",
       "title": "Clientes",
       "actions": "Ações",
-      "totalGB": "Total enviado/recebido (GB)",
+      "totalGB": "Limite de tráfego (GB)",
+      "totalGBDesc": "Cota de dados para este cliente. 0 = ilimitado.",
       "expiryTime": "Expiração",
       "addClients": "Adicionar clientes",
       "limitIp": "Limite de IP",
+      "limitIpDesc": "Máximo de IPs simultâneos. 0 = ilimitado.",
       "password": "Senha",
       "subId": "ID da assinatura",
       "online": "Online",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "Escolha um Inbound"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/ru-RU.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Основные",
-      "tabConfig": "Конфигурация",
+      "tabBasics": "Основные",
+      "tabCredentials": "Учетные данные",
       "add": "Добавить клиента",
       "edit": "Изменить клиента",
       "submitAdd": "Добавить клиента",
@@ -666,10 +666,11 @@
       "prefix": "Префикс",
       "postfix": "Постфикс",
       "delayedStart": "Старт после первого использования",
-      "expireDays": "Длительность",
+      "expireDays": "Длительность (дней)",
       "days": "Дни",
       "renew": "Автопродление",
       "renewDesc": "Автоматическое продление после окончания. (0 = отключено) (единица: день)",
+      "renewDays": "Автопродление (дней)",
       "searchPlaceholder": "Поиск email, комментария, sub ID, UUID, пароля, auth…",
       "filterTitle": "Фильтр клиентов",
       "clearAllFilters": "Очистить все",
@@ -691,10 +692,12 @@
       "hasNot": "Нет",
       "title": "Клиенты",
       "actions": "Действия",
-      "totalGB": "Всего отправлено/получено (ГБ)",
+      "totalGB": "Лимит трафика (ГБ)",
+      "totalGBDesc": "Квота трафика для этого клиента. 0 = без ограничений.",
       "expiryTime": "Срок действия",
       "addClients": "Добавить клиентов",
       "limitIp": "Лимит IP",
+      "limitIpDesc": "Максимум одновременных IP-адресов. 0 = без ограничений.",
       "password": "Пароль",
       "subId": "ID подписки",
       "online": "В сети",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "Выберите входящее подключение"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/tr-TR.json

@@ -638,8 +638,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Temel",
-      "tabConfig": "Yapılandırma",
+      "tabBasics": "Temel",
+      "tabCredentials": "Kimlik Bilgileri",
       "add": "Kullanıcı Ekle",
       "edit": "Kullanıcıyı Düzenle",
       "submitAdd": "Kullanıcı Ekle",
@@ -667,10 +667,11 @@
       "prefix": "Önek",
       "postfix": "Sonek",
       "delayedStart": "İlk Kullanımdan Sonra Başla",
-      "expireDays": "Süre",
+      "expireDays": "Süre (gün)",
       "days": "Gün(ler)",
       "renew": "Otomatik Yenileme",
       "renewDesc": "Süre dolduktan sonra otomatik yeniler. (0 = devre dışı) (birim: gün)",
+      "renewDays": "Otomatik Yenileme (gün)",
       "searchPlaceholder": "E-posta, yorum, sub ID, UUID, parola, auth ara…",
       "filterTitle": "Kullanıcıları Filtrele",
       "clearAllFilters": "Tümünü Temizle",
@@ -692,10 +693,12 @@
       "hasNot": "Yok",
       "title": "Kullanıcılar",
       "actions": "İşlemler",
-      "totalGB": "Toplam Gönderilen/Alınan (GB)",
+      "totalGB": "Trafik Limiti (GB)",
+      "totalGBDesc": "Bu kullanıcı için veri kotası. 0 = sınırsız.",
       "expiryTime": "Son Kullanma",
       "addClients": "Kullanıcı Ekle",
       "limitIp": "IP Limiti",
+      "limitIpDesc": "Eş zamanlı en fazla IP sayısı. 0 = sınırsız.",
       "password": "Şifre",
       "subId": "Abonelik ID'si",
       "online": "Çevrimiçi",
@@ -1732,4 +1735,4 @@
       "chooseInbound": "Bir Gelen Bağlantı Seçin"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/uk-UA.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Основні",
-      "tabConfig": "Конфігурація",
+      "tabBasics": "Основні",
+      "tabCredentials": "Облікові дані",
       "add": "Додати клієнта",
       "edit": "Редагувати клієнта",
       "submitAdd": "Додати клієнта",
@@ -666,10 +666,11 @@
       "prefix": "Префікс",
       "postfix": "Постфікс",
       "delayedStart": "Запуск після першого використання",
-      "expireDays": "Тривалість",
+      "expireDays": "Тривалість (днів)",
       "days": "Дні",
       "renew": "Авто-продовження",
       "renewDesc": "Автоматичне продовження після закінчення. (0 = вимкнено) (одиниця: день)",
+      "renewDays": "Авто-продовження (днів)",
       "searchPlaceholder": "Пошук email, коментаря, sub ID, UUID, паролю, auth…",
       "filterTitle": "Фільтр клієнтів",
       "clearAllFilters": "Очистити все",
@@ -691,10 +692,12 @@
       "hasNot": "Не має",
       "title": "Клієнти",
       "actions": "Дії",
-      "totalGB": "Усього надіслано/отримано (ГБ)",
+      "totalGB": "Ліміт трафіку (ГБ)",
+      "totalGBDesc": "Квота трафіку для цього клієнта. 0 = без обмежень.",
       "expiryTime": "Термін дії",
       "addClients": "Додати клієнтів",
       "limitIp": "Ліміт IP",
+      "limitIpDesc": "Максимум одночасних IP-адрес. 0 = без обмежень.",
       "password": "Пароль",
       "subId": "ID підписки",
       "online": "У мережі",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "Виберіть Вхідний"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/vi-VN.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "Cơ bản",
-      "tabConfig": "Cấu hình",
+      "tabBasics": "Cơ bản",
+      "tabCredentials": "Thông tin xác thực",
       "add": "Thêm khách hàng",
       "edit": "Chỉnh sửa khách hàng",
       "submitAdd": "Thêm khách hàng",
@@ -666,10 +666,11 @@
       "prefix": "Tiền tố",
       "postfix": "Hậu tố",
       "delayedStart": "Bắt đầu sau lần dùng đầu",
-      "expireDays": "Thời hạn",
+      "expireDays": "Thời hạn (ngày)",
       "days": "Ngày",
       "renew": "Tự động gia hạn",
       "renewDesc": "Tự động gia hạn sau khi hết hạn. (0 = tắt) (đơn vị: ngày)",
+      "renewDays": "Tự động gia hạn (ngày)",
       "searchPlaceholder": "Tìm email, ghi chú, sub ID, UUID, mật khẩu, auth…",
       "filterTitle": "Lọc client",
       "clearAllFilters": "Xóa tất cả",
@@ -691,10 +692,12 @@
       "hasNot": "Không có",
       "title": "Khách hàng",
       "actions": "Hành động",
-      "totalGB": "Tổng gửi/nhận (GB)",
+      "totalGB": "Giới hạn lưu lượng (GB)",
+      "totalGBDesc": "Hạn mức dữ liệu cho khách hàng này. 0 = không giới hạn.",
       "expiryTime": "Hết hạn",
       "addClients": "Thêm khách hàng",
       "limitIp": "Giới hạn IP",
+      "limitIpDesc": "Số IP đồng thời tối đa. 0 = không giới hạn.",
       "password": "Mật khẩu",
       "subId": "ID đăng ký",
       "online": "Trực tuyến",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "Chọn một Inbound"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/zh-CN.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "基本",
-      "tabConfig": "配置",
+      "tabBasics": "基本",
+      "tabCredentials": "凭据",
       "add": "添加客户端",
       "edit": "编辑客户端",
       "submitAdd": "添加客户端",
@@ -666,10 +666,11 @@
       "prefix": "前缀",
       "postfix": "后缀",
       "delayedStart": "首次使用后开始",
-      "expireDays": "时长",
+      "expireDays": "时长 (天)",
       "days": "天",
       "renew": "自动续期",
       "renewDesc": "到期后自动续期。(0 = 禁用) (单位: 天)",
+      "renewDays": "自动续期 (天)",
       "searchPlaceholder": "搜索邮箱、备注、sub ID、UUID、密码、auth…",
       "filterTitle": "筛选客户端",
       "clearAllFilters": "清除全部",
@@ -691,10 +692,12 @@
       "hasNot": "不拥有",
       "title": "客户端",
       "actions": "操作",
-      "totalGB": "总上传/下载 (GB)",
+      "totalGB": "流量上限 (GB)",
+      "totalGBDesc": "该客户端的流量配额。0 = 不限制。",
       "expiryTime": "过期时间",
       "addClients": "添加客户端",
       "limitIp": "IP 限制",
+      "limitIpDesc": "最大同时连接 IP 数。0 = 不限制。",
       "password": "密码",
       "subId": "订阅 ID",
       "online": "在线",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "选择一个入站"
     }
   }
-}
+}

+ 8 - 5
internal/web/translation/zh-TW.json

@@ -637,8 +637,8 @@
       }
     },
     "clients": {
-      "tabBasic": "基本",
-      "tabConfig": "配置",
+      "tabBasics": "基本",
+      "tabCredentials": "認證資訊",
       "add": "新增客戶端",
       "edit": "編輯客戶端",
       "submitAdd": "新增客戶端",
@@ -666,10 +666,11 @@
       "prefix": "前綴",
       "postfix": "後綴",
       "delayedStart": "首次使用後開始",
-      "expireDays": "時長",
+      "expireDays": "時長 (天)",
       "days": "天",
       "renew": "自動續期",
       "renewDesc": "到期後自動續期。(0 = 停用) (單位: 天)",
+      "renewDays": "自動續期 (天)",
       "searchPlaceholder": "搜尋電子郵件、備註、sub ID、UUID、密碼、auth…",
       "filterTitle": "篩選客戶端",
       "clearAllFilters": "清除全部",
@@ -691,10 +692,12 @@
       "hasNot": "不擁有",
       "title": "客戶端",
       "actions": "操作",
-      "totalGB": "總上傳/下載 (GB)",
+      "totalGB": "流量上限 (GB)",
+      "totalGBDesc": "該客戶端的流量配額。0 = 不限制。",
       "expiryTime": "到期時間",
       "addClients": "新增客戶端",
       "limitIp": "IP 限制",
+      "limitIpDesc": "最大同時連線 IP 數。0 = 不限制。",
       "password": "密碼",
       "subId": "訂閱 ID",
       "online": "上線",
@@ -1733,4 +1736,4 @@
       "chooseInbound": "選擇一個入站"
     }
   }
-}
+}