feat(xray): browse geosite/geoip categories from routing rules (#6165)

* feat(xray): browse geosite/geoip categories from routing rules

Routing rules made you type category names from memory: nothing showed which
categories a database actually contains, what is inside one, or whether a
name resolves at all — a typo only surfaced when Xray refused the config.

The panel now reads Xray's .dat databases itself and exposes them over four
endpoints: databases in the asset folder, a database's categories, one page
of a category's rules, and validation of the tokens already in a rule. The
reader walks the protobuf wire format directly rather than decoding into Go
structs, because a 10 MB geosite.dat holds well over a million domains and
materialising them costs ~284 MB where streaming costs ~19 MB. Only the
category index is cached, entry pages are scanned on demand, and scans are
serialised, so twenty concurrent requests peak at 87 MB instead of 1 GB.
A database's type is decided by its contents, not its file name, since
custom .dat files are named freely.

In the rule form, the source-IP, IP and domain fields gain a database button
opening the browser: search over categories, a preview of what a category
holds, and a multi-select that merges into the field. Plain domains, CIDRs
and categories the panel does not know are left untouched; categories already
present come back ticked, and unticking one removes it from the rule.

* fix(xray): read geo databases through os.Root and match codes verbatim

CodeQL flagged the database read as a path built from a user-supplied value,
and it was right about the shape of it. The file name arrives in a request;
resolve() rejects traversal and stats the file through an os.Root, but the
read itself went through a joined path with os.ReadFile. That left the
symlink defence incomplete: the stat could pass while the read followed a
link planted — or swapped in — afterwards.

Reads now go through the same root, so a request-supplied name never becomes
a path this code resolves on its own, and the size limit is applied to the
opened file rather than to a separate stat of it.

Lookup no longer trims the category code either. It backs the routing-token
validator, and the core matches codes verbatim: "geosite: cn" will not start
Xray, so repairing that space here hid exactly the typo the validator exists
to report.

* fix(xray): address review findings on the geo category browser

Asset folder. The browser read config.GetBinFolderPath() unconditionally,
but the core honours a preset XRAY_LOCATION_ASSET and only falls back to the
bin folder (ensureXrayAssetLocation). On an install pointing at a shared
asset directory the panel listed an empty folder and reported perfectly
valid geosite:/geoip: tokens as missing — the validator warning about a
correct config. The directory is now resolved with the core's precedence.

Paging. Serving one page read and rescanned the whole database, so walking
category-ads-all re-read it per page. The index now records each category's
byte range and a page reads only that record through the os.Root handle,
with the current category's records held for the duration of a paging
session. Profiling that also showed the real cost was not the read but the
slice of payload pointers built per call — a category holds a hundred
thousand of them — so records are now walked with a callback instead.
Ten pages over category-ads-all: 239 MB allocated, now 4.3 MB.

Cached failures. Any error from reading a file was latched under the file's
size+mtime, so a transient ENOMEM or EMFILE marked a healthy database as
damaged until it changed on disk. Only deterministic failures are cached.

Wrong kind. A geoip: token typed into a domain field parsed as a plain
domain and was waved through, though the core cannot resolve it as one. It
is now reported, with its own reason and wording.

Frontend. The category filter fed the query key on every keystroke, so each
character triggered a request that re-scanned the database; it is debounced
now. GeoTokenInput accepts and forwards a ref, so React Hook Form can focus
these three fields on a validation error again. A failed validation shows
that it failed instead of rendering the same empty state as "no issues".

Also drops an unreachable branch in the token-count guard and corrects the
categories endpoint docs, where limit is unbounded by default.

---------

Co-authored-by: STRENCH0 <17428017+STRENCH0@users.noreply.github.com>
This commit is contained in:
Grigoriy
2026-08-15 18:12:59 +03:00
committed by GitHub
parent 7c8a9a6909
commit d7698ec7aa
43 changed files with 5433 additions and 6 deletions
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "اسحب لإعادة الترتيب"
},
"geoBrowser": {
"title": "فئات قواعد geo",
"openTooltip": "استعراض فئات geo",
"database": "قاعدة البيانات",
"searchCategory": "بحث عن فئة",
"searchEntries": "تصفية داخل الفئة",
"selectFound": "تحديد النتائج",
"selected": "المحدد: {count}",
"clearAll": "مسح الكل",
"apply": "تطبيق",
"emptySelection": "حدّد الفئات لتتحول إلى عناصر في قاعدة التوجيه",
"pickCategory": "اختر فئة من القائمة لعرض محتواها",
"noMatches": "لم يتم العثور على شيء",
"noFiles": "لا توجد قواعد geo في مجلد Xray",
"noFilesHint": "ستظهر بعد أن ينزّل Xray ملفي geosite.dat و geoip.dat",
"fileMeta": "{count} فئة · {size} · تم التحديث {date}",
"entriesCount": "{count} إدخال",
"subnetsCount": "{count} شبكة فرعية",
"shownRange": "عرض {from}{to} من {total}",
"loadFailed": "تعذر تحميل قواعد geo",
"checkFailed": "تعذر التحقق من هذه القيم مقابل قواعد geo",
"parseFailed": "الملف تالف أو ليس قاعدة geosite/geoip",
"tooLarge": "الملف أكبر من أن يتم استعراضه",
"unknownCategories": "غير موجود في القاعدة: {tokens}",
"missingDatabase": "ملف القاعدة غير موجود: {tokens}. أضِفه من قسم Geodata",
"unknownAttribute": "السمة غير موجودة، ولن تطابق القاعدة أي نطاق: {tokens}",
"invalidToken": "لن يقبل Xray هذه الصيغة: {tokens}",
"wrongKind": "نوع قاعدة البيانات غير مناسب لهذا الحقل: {tokens}"
},
"ruleForm": {
"sourceIps": "IPs المصدر",
"sourcePort": "منفذ المصدر",
+29
View File
@@ -1563,6 +1563,35 @@
"routing": {
"dragToReorder": "Drag to reorder"
},
"geoBrowser": {
"title": "Geo categories",
"openTooltip": "Browse geo categories",
"database": "Database",
"searchCategory": "Search category",
"searchEntries": "Filter inside category",
"selectFound": "Select found",
"selected": "Selected {count}",
"clearAll": "Clear all",
"apply": "Apply",
"emptySelection": "Tick categories — they become rule tokens",
"pickCategory": "Pick a category on the left to see what it contains",
"noMatches": "Nothing found",
"noFiles": "No geo databases in the Xray folder",
"noFilesHint": "They appear after Xray downloads geosite.dat and geoip.dat",
"fileMeta": "{count} categories · {size} · updated {date}",
"entriesCount": "{count} entries",
"subnetsCount": "{count} subnets",
"shownRange": "Showing {from}{to} of {total}",
"loadFailed": "Could not load geo databases",
"checkFailed": "Could not check these values against the geo databases",
"parseFailed": "Damaged or not a geosite/geoip database",
"tooLarge": "Too large to browse",
"unknownCategories": "Not in the database: {tokens}",
"missingDatabase": "Database file not found: {tokens} — add it under Geodata",
"unknownAttribute": "Attribute not found, the rule would match nothing: {tokens}",
"invalidToken": "Xray will not accept this: {tokens}",
"wrongKind": "Wrong database kind for this field: {tokens}"
},
"ruleForm": {
"sourceIps": "Source IPs",
"sourcePort": "Source port",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "Arrastra para reordenar"
},
"geoBrowser": {
"title": "Categorías geo",
"openTooltip": "Explorar categorías geo",
"database": "Base de datos",
"searchCategory": "Buscar categoría",
"searchEntries": "Filtrar dentro de la categoría",
"selectFound": "Seleccionar encontradas",
"selected": "Seleccionadas: {count}",
"clearAll": "Limpiar todo",
"apply": "Aplicar",
"emptySelection": "Marca categorías: se convertirán en tokens de la regla",
"pickCategory": "Elige una categoría a la izquierda para ver su contenido",
"noMatches": "No se encontró nada",
"noFiles": "No hay bases geo en la carpeta de Xray",
"noFilesHint": "Aparecerán cuando Xray descargue geosite.dat y geoip.dat",
"fileMeta": "{count} categorías · {size} · actualizado {date}",
"entriesCount": "{count} entradas",
"subnetsCount": "{count} subredes",
"shownRange": "Mostrando {from}{to} de {total}",
"loadFailed": "No se pudieron cargar las bases geo",
"checkFailed": "No se pudieron verificar estos valores con las bases geo",
"parseFailed": "Archivo dañado o no es una base geosite/geoip",
"tooLarge": "Demasiado grande para explorarlo",
"unknownCategories": "No están en la base: {tokens}",
"missingDatabase": "No se encontró el archivo de la base: {tokens} — añádelo en la sección Geodata",
"unknownAttribute": "Atributo no encontrado, la regla no coincidirá con nada: {tokens}",
"invalidToken": "Xray no aceptará esta entrada: {tokens}",
"wrongKind": "Tipo de base incorrecto para este campo: {tokens}"
},
"ruleForm": {
"sourceIps": "IPs de origen",
"sourcePort": "Puerto de origen",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "برای تغییر ترتیب بکشید"
},
"geoBrowser": {
"title": "دسته‌های پایگاه geo",
"openTooltip": "مرور دسته‌های geo",
"database": "پایگاه داده",
"searchCategory": "جستجوی دسته",
"searchEntries": "فیلتر درون دسته",
"selectFound": "انتخاب موارد یافت‌شده",
"selected": "انتخاب‌شده: {count}",
"clearAll": "پاک کردن همه",
"apply": "اعمال",
"emptySelection": "دسته‌ها را علامت بزنید تا به مقادیر قانون تبدیل شوند",
"pickCategory": "برای دیدن محتوا، یک دسته را از فهرست انتخاب کنید",
"noMatches": "چیزی یافت نشد",
"noFiles": "در پوشه Xray هیچ پایگاه geo وجود ندارد",
"noFilesHint": "پس از آنکه Xray فایل‌های geosite.dat و geoip.dat را دانلود کند، نمایش داده می‌شوند",
"fileMeta": "{count} دسته · {size} · به‌روزرسانی {date}",
"entriesCount": "{count} مورد",
"subnetsCount": "{count} زیرشبکه",
"shownRange": "نمایش {from}{to} از {total}",
"loadFailed": "بارگذاری پایگاه‌های geo ناموفق بود",
"checkFailed": "بررسی این مقادیر در برابر پایگاه‌های geo ممکن نشد",
"parseFailed": "فایل خراب است یا پایگاه geosite/geoip نیست",
"tooLarge": "فایل برای مرور بسیار بزرگ است",
"unknownCategories": "در پایگاه داده وجود ندارد: {tokens}",
"missingDatabase": "فایل پایگاه داده وجود ندارد: {tokens}. آن را در بخش Geodata اضافه کنید",
"unknownAttribute": "ویژگی یافت نشد و قانون با هیچ چیزی مطابقت نخواهد کرد: {tokens}",
"invalidToken": "Xray چنین مقداری را نمی‌پذیرد: {tokens}",
"wrongKind": "نوع پایگاه داده برای این فیلد نادرست است: {tokens}"
},
"ruleForm": {
"sourceIps": "IPهای مبدا",
"sourcePort": "پورت مبدا",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "Seret untuk mengurutkan ulang"
},
"geoBrowser": {
"title": "Kategori geo",
"openTooltip": "Jelajahi kategori geo",
"database": "Basis data",
"searchCategory": "Cari kategori",
"searchEntries": "Filter di dalam kategori",
"selectFound": "Pilih hasil pencarian",
"selected": "Dipilih {count}",
"clearAll": "Hapus semua",
"apply": "Terapkan",
"emptySelection": "Centang kategori — semuanya menjadi token aturan",
"pickCategory": "Pilih kategori di sebelah kiri untuk melihat isinya",
"noMatches": "Tidak ada yang ditemukan",
"noFiles": "Tidak ada basis data geo di folder Xray",
"noFilesHint": "Akan muncul setelah Xray mengunduh geosite.dat dan geoip.dat",
"fileMeta": "{count} kategori · {size} · diperbarui {date}",
"entriesCount": "{count} entri",
"subnetsCount": "{count} subnet",
"shownRange": "Menampilkan {from}{to} dari {total}",
"loadFailed": "Gagal memuat basis data geo",
"checkFailed": "Tidak dapat memeriksa nilai ini terhadap basis data geo",
"parseFailed": "Berkas rusak atau bukan basis data geosite/geoip",
"tooLarge": "Terlalu besar untuk ditelusuri",
"unknownCategories": "Tidak ada di basis data: {tokens}",
"missingDatabase": "Berkas basis data tidak ditemukan: {tokens} — tambahkan di bagian Geodata",
"unknownAttribute": "Atribut tidak ditemukan, aturan tidak akan cocok dengan apa pun: {tokens}",
"invalidToken": "Xray tidak akan menerima entri ini: {tokens}",
"wrongKind": "Jenis basis data salah untuk kolom ini: {tokens}"
},
"ruleForm": {
"sourceIps": "IP sumber",
"sourcePort": "Port sumber",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "ドラッグして並べ替え"
},
"geoBrowser": {
"title": "geo カテゴリ",
"openTooltip": "geo カテゴリを参照",
"database": "データベース",
"searchCategory": "カテゴリを検索",
"searchEntries": "カテゴリ内を絞り込み",
"selectFound": "検索結果を選択",
"selected": "選択中 {count} 件",
"clearAll": "すべてクリア",
"apply": "適用",
"emptySelection": "カテゴリにチェックを入れると、ルールのトークンになります",
"pickCategory": "左のカテゴリを選ぶと内容が表示されます",
"noMatches": "見つかりませんでした",
"noFiles": "Xray フォルダーに geo データベースがありません",
"noFilesHint": "Xray が geosite.dat と geoip.dat をダウンロードすると表示されます",
"fileMeta": "{count} カテゴリ · {size} · 更新 {date}",
"entriesCount": "{count} 件",
"subnetsCount": "{count} サブネット",
"shownRange": "{total} 件中 {from}{to} を表示",
"loadFailed": "geo データベースを読み込めませんでした",
"checkFailed": "これらの値を geo データベースと照合できませんでした",
"parseFailed": "ファイルが破損しているか、geosite/geoip データベースではありません",
"tooLarge": "サイズが大きすぎて参照できません",
"unknownCategories": "データベースに存在しません: {tokens}",
"missingDatabase": "データベースファイルが見つかりません: {tokens} — Geodata から追加してください",
"unknownAttribute": "属性が見つからないため、ルールは何にも一致しません: {tokens}",
"invalidToken": "Xray はこの記述を受け付けません: {tokens}",
"wrongKind": "このフィールドには合わないデータベース種別です: {tokens}"
},
"ruleForm": {
"sourceIps": "送信元 IP",
"sourcePort": "送信元ポート",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "Arraste para reordenar"
},
"geoBrowser": {
"title": "Categorias geo",
"openTooltip": "Explorar categorias geo",
"database": "Base de dados",
"searchCategory": "Pesquisar categoria",
"searchEntries": "Filtrar dentro da categoria",
"selectFound": "Selecionar encontradas",
"selected": "Selecionadas: {count}",
"clearAll": "Limpar tudo",
"apply": "Aplicar",
"emptySelection": "Marque as categorias — elas viram tokens da regra",
"pickCategory": "Escolha uma categoria à esquerda para ver o conteúdo",
"noMatches": "Nada encontrado",
"noFiles": "Nenhuma base geo na pasta do Xray",
"noFilesHint": "Elas aparecem depois que o Xray baixa geosite.dat e geoip.dat",
"fileMeta": "{count} categorias · {size} · atualizado em {date}",
"entriesCount": "{count} entradas",
"subnetsCount": "{count} sub-redes",
"shownRange": "Mostrando {from}{to} de {total}",
"loadFailed": "Não foi possível carregar as bases geo",
"checkFailed": "Não foi possível verificar estes valores nas bases geo",
"parseFailed": "Arquivo corrompido ou não é uma base geosite/geoip",
"tooLarge": "Grande demais para navegar",
"unknownCategories": "Não estão na base: {tokens}",
"missingDatabase": "Arquivo da base não encontrado: {tokens} — adicione-o na seção Geodata",
"unknownAttribute": "Atributo não encontrado, a regra não corresponderá a nada: {tokens}",
"invalidToken": "O Xray não aceitará esta entrada: {tokens}",
"wrongKind": "Tipo de base incorreto para este campo: {tokens}"
},
"ruleForm": {
"sourceIps": "IPs de origem",
"sourcePort": "Porta de origem",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "Перетащите для изменения порядка"
},
"geoBrowser": {
"title": "Категории geo-баз",
"openTooltip": "Открыть браузер geo-категорий",
"database": "База",
"searchCategory": "Поиск категории",
"searchEntries": "Фильтр внутри категории",
"selectFound": "Отметить найденные",
"selected": "Выбрано {count}",
"clearAll": "Снять всё",
"apply": "Применить",
"emptySelection": "Отметьте категории — они станут токенами правила",
"pickCategory": "Выберите категорию слева, чтобы посмотреть её содержимое",
"noMatches": "Ничего не найдено",
"noFiles": "В каталоге Xray нет geo-баз",
"noFilesHint": "Они появятся после того, как Xray скачает geosite.dat и geoip.dat",
"fileMeta": "{count} категорий · {size} · обновлено {date}",
"entriesCount": "{count} записей",
"subnetsCount": "{count} подсетей",
"shownRange": "Показано {from}{to} из {total}",
"loadFailed": "Не удалось загрузить geo-базы",
"checkFailed": "Не удалось проверить эти значения по geo-базам",
"parseFailed": "Файл повреждён или это не база geosite/geoip",
"tooLarge": "Слишком большой файл для просмотра",
"unknownCategories": "Нет в базе: {tokens}",
"missingDatabase": "Файла базы нет: {tokens} — добавьте её в разделе Geodata",
"unknownAttribute": "Атрибут не найден, правило ничего не сматчит: {tokens}",
"invalidToken": "Xray не примет такую запись: {tokens}",
"wrongKind": "База не того типа для этого поля: {tokens}"
},
"ruleForm": {
"sourceIps": "IP источника",
"sourcePort": "Порт источника",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "Yeniden sıralamak için sürükleyin"
},
"geoBrowser": {
"title": "Geo kategorileri",
"openTooltip": "Geo kategorilerine göz at",
"database": "Veritabanı",
"searchCategory": "Kategori ara",
"searchEntries": "Kategori içinde filtrele",
"selectFound": "Bulunanları seç",
"selected": "Seçili: {count}",
"clearAll": "Tümünü Temizle",
"apply": "Uygula",
"emptySelection": "Kategorileri işaretleyin — kural belirteçlerine dönüşürler",
"pickCategory": "İçeriğini görmek için soldan bir kategori seçin",
"noMatches": "Hiçbir şey bulunamadı",
"noFiles": "Xray klasöründe geo veritabanı yok",
"noFilesHint": "Xray, geosite.dat ve geoip.dat dosyalarını indirdikten sonra görünürler",
"fileMeta": "{count} kategori · {size} · güncellendi {date}",
"entriesCount": "{count} kayıt",
"subnetsCount": "{count} alt ağ",
"shownRange": "{total} kayıttan {from}{to} arası gösteriliyor",
"loadFailed": "Geo veritabanları yüklenemedi",
"checkFailed": "Bu değerler geo veritabanlarıyla doğrulanamadı",
"parseFailed": "Dosya bozuk veya geosite/geoip veritabanı değil",
"tooLarge": "Göz atmak için fazla büyük",
"unknownCategories": "Veritabanında yok: {tokens}",
"missingDatabase": "Veritabanı dosyası bulunamadı: {tokens} — Geodata bölümünden ekleyin",
"unknownAttribute": "Öznitelik bulunamadı, kural hiçbir şeyle eşleşmez: {tokens}",
"invalidToken": "Xray böyle bir kaydı kabul etmez: {tokens}",
"wrongKind": "Bu alan için yanlış veritabanı türü: {tokens}"
},
"ruleForm": {
"sourceIps": "Kaynak IP'ler",
"sourcePort": "Kaynak Port",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "Перетягніть для зміни порядку"
},
"geoBrowser": {
"title": "Категорії geo-баз",
"openTooltip": "Відкрити браузер geo-категорій",
"database": "База",
"searchCategory": "Пошук категорії",
"searchEntries": "Фільтр усередині категорії",
"selectFound": "Позначити знайдені",
"selected": "Вибрано {count}",
"clearAll": "Зняти все",
"apply": "Застосувати",
"emptySelection": "Позначте категорії — вони стануть токенами правила",
"pickCategory": "Виберіть категорію ліворуч, щоб переглянути її вміст",
"noMatches": "Нічого не знайдено",
"noFiles": "У теці Xray немає geo-баз",
"noFilesHint": "Вони з’являться після того, як Xray завантажить geosite.dat і geoip.dat",
"fileMeta": "{count} категорій · {size} · оновлено {date}",
"entriesCount": "{count} записів",
"subnetsCount": "{count} підмереж",
"shownRange": "Показано {from}{to} з {total}",
"loadFailed": "Не вдалося завантажити geo-бази",
"checkFailed": "Не вдалося перевірити ці значення за geo-базами",
"parseFailed": "Файл пошкоджено або це не база geosite/geoip",
"tooLarge": "Завеликий файл для перегляду",
"unknownCategories": "Немає в базі: {tokens}",
"missingDatabase": "Файлу бази немає: {tokens} — додайте її в розділі Geodata",
"unknownAttribute": "Атрибут не знайдено, правило ні з чим не збігатиметься: {tokens}",
"invalidToken": "Xray не прийме такий запис: {tokens}",
"wrongKind": "База не того типу для цього поля: {tokens}"
},
"ruleForm": {
"sourceIps": "IP джерела",
"sourcePort": "Порт джерела",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "Kéo để sắp xếp lại"
},
"geoBrowser": {
"title": "Danh mục geo",
"openTooltip": "Duyệt danh mục geo",
"database": "Cơ sở dữ liệu",
"searchCategory": "Tìm danh mục",
"searchEntries": "Lọc trong danh mục",
"selectFound": "Chọn các kết quả",
"selected": "Đã chọn {count}",
"clearAll": "Xóa tất cả",
"apply": "Áp dụng",
"emptySelection": "Đánh dấu danh mục — chúng sẽ trở thành token của quy tắc",
"pickCategory": "Chọn một danh mục ở bên trái để xem nội dung",
"noMatches": "Không tìm thấy gì",
"noFiles": "Không có cơ sở dữ liệu geo trong thư mục Xray",
"noFilesHint": "Chúng sẽ xuất hiện sau khi Xray tải geosite.dat và geoip.dat",
"fileMeta": "{count} danh mục · {size} · cập nhật {date}",
"entriesCount": "{count} mục",
"subnetsCount": "{count} dải mạng",
"shownRange": "Hiển thị {from}{to} trong {total}",
"loadFailed": "Không thể tải cơ sở dữ liệu geo",
"checkFailed": "Không thể kiểm tra các giá trị này với cơ sở dữ liệu geo",
"parseFailed": "Tệp bị hỏng hoặc không phải cơ sở dữ liệu geosite/geoip",
"tooLarge": "Tệp quá lớn để duyệt",
"unknownCategories": "Không có trong cơ sở dữ liệu: {tokens}",
"missingDatabase": "Không tìm thấy tệp cơ sở dữ liệu: {tokens} — hãy thêm trong mục Geodata",
"unknownAttribute": "Không tìm thấy thuộc tính, quy tắc sẽ không khớp với bất kỳ thứ gì: {tokens}",
"invalidToken": "Xray sẽ không chấp nhận mục này: {tokens}",
"wrongKind": "Sai loại cơ sở dữ liệu cho trường này: {tokens}"
},
"ruleForm": {
"sourceIps": "IP nguồn",
"sourcePort": "Cổng nguồn",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "拖动以重新排序"
},
"geoBrowser": {
"title": "geo 分类",
"openTooltip": "浏览 geo 分类",
"database": "数据库",
"searchCategory": "搜索分类",
"searchEntries": "在分类内筛选",
"selectFound": "选中搜索结果",
"selected": "已选 {count} 项",
"clearAll": "全部清除",
"apply": "应用",
"emptySelection": "勾选分类,它们将成为规则中的条目",
"pickCategory": "在左侧选择一个分类以查看其内容",
"noMatches": "未找到任何内容",
"noFiles": "Xray 目录中没有 geo 数据库",
"noFilesHint": "Xray 下载 geosite.dat 和 geoip.dat 后即会出现",
"fileMeta": "{count} 个分类 · {size} · 更新于 {date}",
"entriesCount": "{count} 条记录",
"subnetsCount": "{count} 个网段",
"shownRange": "显示第 {from}{to} 项,共 {total} 项",
"loadFailed": "无法加载 geo 数据库",
"checkFailed": "无法根据 geo 数据库校验这些值",
"parseFailed": "文件已损坏或不是 geosite/geoip 数据库",
"tooLarge": "文件过大,无法浏览",
"unknownCategories": "数据库中不存在:{tokens}",
"missingDatabase": "未找到数据库文件:{tokens} — 请在 Geodata 中添加",
"unknownAttribute": "未找到该属性,规则不会匹配任何内容:{tokens}",
"invalidToken": "Xray 无法接受此写法:{tokens}",
"wrongKind": "该字段的数据库类型不匹配:{tokens}"
},
"ruleForm": {
"sourceIps": "源 IP",
"sourcePort": "源端口",
+29
View File
@@ -1446,6 +1446,35 @@
"routing": {
"dragToReorder": "拖曳以重新排序"
},
"geoBrowser": {
"title": "geo 分類",
"openTooltip": "瀏覽 geo 分類",
"database": "資料庫",
"searchCategory": "搜尋分類",
"searchEntries": "在分類內篩選",
"selectFound": "勾選搜尋結果",
"selected": "已選 {count} 項",
"clearAll": "全部清除",
"apply": "套用",
"emptySelection": "勾選分類,它們將成為規則中的項目",
"pickCategory": "在左側選擇一個分類以查看其內容",
"noMatches": "找不到任何內容",
"noFiles": "Xray 目錄中沒有 geo 資料庫",
"noFilesHint": "Xray 下載 geosite.dat 與 geoip.dat 後即會出現",
"fileMeta": "{count} 個分類 · {size} · 更新於 {date}",
"entriesCount": "{count} 筆記錄",
"subnetsCount": "{count} 個網段",
"shownRange": "顯示第 {from}{to} 項,共 {total} 項",
"loadFailed": "無法載入 geo 資料庫",
"checkFailed": "無法依據 geo 資料庫檢查這些值",
"parseFailed": "檔案已損毀或不是 geosite/geoip 資料庫",
"tooLarge": "檔案過大,無法瀏覽",
"unknownCategories": "資料庫中不存在:{tokens}",
"missingDatabase": "找不到資料庫檔案:{tokens} — 請在 Geodata 中新增",
"unknownAttribute": "找不到該屬性,規則不會比對到任何內容:{tokens}",
"invalidToken": "Xray 無法接受此寫法:{tokens}",
"wrongKind": "此欄位的資料庫類型不符:{tokens}"
},
"ruleForm": {
"sourceIps": "來源 IP",
"sourcePort": "來源連接埠",