mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-24 17:45:23 +08:00
feat(i18n): add the Slovak (sk-SK) panel locale (#6889)
Adds a complete Slovak translation of every panel and bot string, registers sk-SK in the language picker (a browser set to sk now lands on it instead of English) and loads Ant Design's Slovak locale. The locale count in the READMEs, docs and contributor guides moves from 13 to 14.
This commit is contained in:
@@ -89,7 +89,7 @@ question it already answers.
|
||||
`cd docs && pnpm gen:api`, because `docs-ci.yml` fires only on `docs/**`.
|
||||
Step 4 is the one that reaches production wrong.
|
||||
- **i18n.** A new English key goes in EVERY locale JSON in
|
||||
`internal/web/translation/` (13 files) AND must be referenced from
|
||||
`internal/web/translation/` (14 files) AND must be referenced from
|
||||
`frontend/src` or Go in the SAME change.
|
||||
`frontend/src/test/i18n-dead-keys.test.ts` fails on a missing locale file and
|
||||
on an orphan key alike.
|
||||
|
||||
@@ -63,7 +63,7 @@ file locations when it can answer in one hop.
|
||||
CPU/memory watchdogs, …); full table in `docs/architecture.md` §5.4.
|
||||
- `middleware/`, `entity/`, `global/`, `session/` (CSRF), `network/`,
|
||||
`runtime/` (master/sub-node over mTLS), `websocket/`.
|
||||
- `locale/` + `translation/` — i18n, 13 embedded locale JSON files.
|
||||
- `locale/` + `translation/` — i18n, 14 embedded locale JSON files.
|
||||
- `frontend/` — React + TS source (see `frontend/CLAUDE.md`).
|
||||
- `tools/openapigen/` — Go generator that emits frontend types + Zod/JSON schemas
|
||||
into `frontend/src/generated/` from Go structs. The OpenAPI doc itself
|
||||
@@ -107,7 +107,7 @@ file locations when it can answer in one hop.
|
||||
`cd docs && pnpm gen:api` to refresh the MDX under
|
||||
`docs/content/docs/en/reference/api/`. `docs-ci.yml` fires only on `docs/**`.
|
||||
- A new English i18n key goes in EVERY locale JSON in `internal/web/translation/`
|
||||
(13 files) AND must be referenced from `frontend/src` or Go in the SAME commit —
|
||||
(14 files) AND must be referenced from `frontend/src` or Go in the SAME commit —
|
||||
`frontend/src/test/i18n-dead-keys.test.ts` fails both ways. It is a frontend
|
||||
test, so run `npm test`, not just `make test-go`. At runtime the frontend falls
|
||||
back to en-US; Go (`internal/web/locale/`) returns "" for an unknown key.
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@
|
||||
- **واجهة RESTful API** مع رموز وصول محدودة النطاق وقابلة لانتهاء الصلاحية، ومرجع API داخل اللوحة.
|
||||
- **لوحة قابلة للتثبيت (PWA)** — ثبّت 3X-UI على سطح المكتب أو شاشة هاتفك الرئيسية.
|
||||
- **تخزين مرن** — SQLite (افتراضي) أو PostgreSQL.
|
||||
- **13 لغة لواجهة المستخدم** مع سمات داكنة وفاتحة.
|
||||
- **14 لغة لواجهة المستخدم** مع سمات داكنة وفاتحة.
|
||||
- **تكامل مع Fail2ban** لفرض حدود IP لكل عميل.
|
||||
|
||||
## لقطات الشاشة
|
||||
@@ -177,9 +177,9 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
|
||||
|
||||
## اللغات المدعومة
|
||||
|
||||
تتوفر واجهة اللوحة بـ 13 لغة:
|
||||
تتوفر واجهة اللوحة بـ 14 لغة:
|
||||
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil)
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil) · Slovenčina
|
||||
|
||||
## المساهمة
|
||||
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@ Construido como un fork mejorado del proyecto X-UI original, 3X-UI añade un sop
|
||||
- **API RESTful** con tokens de alcance limitado y caducidad opcional, y una referencia de la API dentro del panel.
|
||||
- **Panel instalable (PWA)** — ancla 3X-UI al escritorio o a la pantalla de inicio del móvil.
|
||||
- **Almacenamiento flexible** — SQLite (predeterminado) o PostgreSQL.
|
||||
- **13 idiomas de interfaz** con temas oscuro y claro.
|
||||
- **14 idiomas de interfaz** con temas oscuro y claro.
|
||||
- **Integración con Fail2ban** para aplicar límites de IP por cliente.
|
||||
|
||||
## Capturas de pantalla
|
||||
@@ -177,9 +177,9 @@ La lista completa está en la [referencia de variables de entorno](https://docs.
|
||||
|
||||
## Idiomas Compatibles
|
||||
|
||||
La interfaz del panel está disponible en 13 idiomas:
|
||||
La interfaz del panel está disponible en 14 idiomas:
|
||||
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil)
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil) · Slovenčina
|
||||
|
||||
## Contribuir
|
||||
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@
|
||||
- **RESTful API** با توکنهای محدودشده (scoped) و دارای انقضای اختیاری، بههمراه مرجع API درونپنل.
|
||||
- **پنل قابل نصب (PWA)** — 3X-UI را به دسکتاپ یا صفحهی اصلی گوشی خود سنجاق کنید.
|
||||
- **ذخیرهسازی منعطف** — SQLite (پیشفرض) یا PostgreSQL.
|
||||
- **۱۳ زبان رابط کاربری** با تمهای تیره و روشن.
|
||||
- **۱۴ زبان رابط کاربری** با تمهای تیره و روشن.
|
||||
- **یکپارچگی با Fail2ban** برای اعمال محدودیت IP بهازای هر کلاینت.
|
||||
|
||||
## اسکرینشاتها
|
||||
@@ -177,9 +177,9 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
|
||||
|
||||
## زبانهای پشتیبانیشده
|
||||
|
||||
رابط کاربری پنل به ۱۳ زبان در دسترس است:
|
||||
رابط کاربری پنل به ۱۴ زبان در دسترس است:
|
||||
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil)
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil) · Slovenčina
|
||||
|
||||
## مشارکت
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ Built as an enhanced fork of the original X-UI project, 3X-UI adds broader proto
|
||||
- **RESTful API** with scoped, optionally expiring tokens and an in-panel API reference.
|
||||
- **Installable panel (PWA)** — pin 3X-UI to a desktop or phone home screen.
|
||||
- **Flexible storage** — SQLite (default) or PostgreSQL.
|
||||
- **13 UI languages** with dark and light themes.
|
||||
- **14 UI languages** with dark and light themes.
|
||||
- **Fail2ban integration** for enforcing per-client IP limits.
|
||||
|
||||
## Screenshots
|
||||
@@ -177,9 +177,9 @@ The complete list is on the [environment variables reference](https://docs.sanae
|
||||
|
||||
## Supported Languages
|
||||
|
||||
The panel UI is available in 13 languages:
|
||||
The panel UI is available in 14 languages:
|
||||
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil)
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil) · Slovenčina
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@
|
||||
- **RESTful API** с токенами ограниченной области действия и необязательным сроком действия, а также справочником API внутри панели.
|
||||
- **Устанавливаемая панель (PWA)** — закрепите 3X-UI на рабочем столе или главном экране телефона.
|
||||
- **Гибкое хранилище** — SQLite (по умолчанию) или PostgreSQL.
|
||||
- **13 языков интерфейса** с тёмной и светлой темами.
|
||||
- **14 языков интерфейса** с тёмной и светлой темами.
|
||||
- **Интеграция с Fail2ban** для применения лимитов IP по каждому клиенту.
|
||||
|
||||
## Скриншоты
|
||||
@@ -177,9 +177,9 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
|
||||
|
||||
## Поддерживаемые языки
|
||||
|
||||
Интерфейс панели доступен на 13 языках:
|
||||
Интерфейс панели доступен на 14 языках:
|
||||
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil)
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil) · Slovenčina
|
||||
|
||||
## Участие в разработке
|
||||
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@ Orijinal X-UI projesinin geliştirilmiş bir çatallaması (fork) olarak inşa e
|
||||
- Kapsamı sınırlanmış, isteğe bağlı olarak süresi dolan token'lar ve panel içi API referansı sunan **RESTful API**.
|
||||
- **Kurulabilir panel (PWA)** — 3X-UI'yi masaüstüne veya telefon ana ekranına sabitleyin.
|
||||
- **Esnek depolama** — SQLite (varsayılan) veya PostgreSQL.
|
||||
- Koyu ve açık tema seçenekleriyle **13 farklı UI dili**.
|
||||
- Koyu ve açık tema seçenekleriyle **14 farklı UI dili**.
|
||||
- Kullanıcı başına IP limitlerini zorunlu kılmak için **Fail2ban entegrasyonu**.
|
||||
|
||||
## Ekran Görüntüleri
|
||||
@@ -177,9 +177,9 @@ Tam liste [ortam değişkenleri referansında](https://docs.sanaei.dev/docs/refe
|
||||
|
||||
## Desteklenen Diller
|
||||
|
||||
Panel arayüzü 13 farklı dilde mevcuttur:
|
||||
Panel arayüzü 14 farklı dilde mevcuttur:
|
||||
|
||||
İngilizce · Farsça · Arapça · Çince (Basitleştirilmiş) · Çince (Geleneksel) · İspanyolca · Rusça · Ukraynaca · Türkçe · Vietnamca · Japonca · Endonezce · Portekizce (Brezilya)
|
||||
İngilizce · Farsça · Arapça · Çince (Basitleştirilmiş) · Çince (Geleneksel) · İspanyolca · Rusça · Ukraynaca · Türkçe · Vietnamca · Japonca · Endonezce · Portekizce (Brezilya) · Slovakça
|
||||
|
||||
## Katkıda Bulunma
|
||||
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@
|
||||
- **RESTful API**,支持带作用域、可设置有效期的令牌,并提供面板内置的 API 参考文档。
|
||||
- **可安装面板 (PWA)** — 将 3X-UI 固定到桌面或手机主屏幕。
|
||||
- **灵活的存储** — SQLite(默认)或 PostgreSQL。
|
||||
- **13 种界面语言**,支持深色和浅色主题。
|
||||
- **14 种界面语言**,支持深色和浅色主题。
|
||||
- **Fail2ban 集成**,用于强制执行按客户端的 IP 限制。
|
||||
|
||||
## 截图
|
||||
@@ -177,9 +177,9 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
|
||||
|
||||
## 支持的语言
|
||||
|
||||
面板界面提供 13 种语言:
|
||||
面板界面提供 14 种语言:
|
||||
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil)
|
||||
English · فارسی · العربية · 中文(简体) · 中文(繁體) · Español · Русский · Українська · Türkçe · Tiếng Việt · 日本語 · Bahasa Indonesia · Português (Brasil) · Slovenčina
|
||||
|
||||
## 贡献
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ the code already handles is not a finding at all.
|
||||
- Missing tests for getters, constants, renames or pure map lookups —
|
||||
`CLAUDE.md` rejects such tests outright.
|
||||
- A missing or unreferenced i18n key.
|
||||
`frontend/src/test/i18n-dead-keys.test.ts` pins the 13 locale files in
|
||||
`frontend/src/test/i18n-dead-keys.test.ts` pins the 14 locale files in
|
||||
`internal/web/translation/` in both directions, so the `frontend` job is
|
||||
already red. Report the failing check, not the key.
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ node heartbeat every 5s, periodic traffic resets (hourly/daily/weekly/monthly).
|
||||
│ │ ├── network/ # Custom net listeners (e.g. proxy-protocol aware)
|
||||
│ │ ├── session/ # Session/cookie helpers
|
||||
│ │ ├── websocket/ # WS hub implementation
|
||||
│ │ ├── locale/ + translation/ # i18n middleware + 13 locale JSON catalogs
|
||||
│ │ ├── locale/ + translation/ # i18n middleware + 14 locale JSON catalogs
|
||||
│ │ ├── entity/ # Shared request/response DTOs
|
||||
│ │ └── dist/ # ⚠️ Vite build output, embedded via go:embed (generated — do not hand-edit)
|
||||
│ │
|
||||
@@ -623,7 +623,7 @@ them with a comment starting `@claude review` on a PR or `@claude analyze` on an
|
||||
(`FetchCertFingerprint`), `service/node_mtls.go`, and `runtime/tls_client.go`.
|
||||
- **Restart is signal-driven.** `main.go` traps SIGHUP to restart panel+sub servers; the
|
||||
in-process restart hook (`global.SetRestartHook`) funnels into the same path.
|
||||
- **i18n:** backend catalogs in `internal/web/translation/` (13 locales, shared with the
|
||||
- **i18n:** backend catalogs in `internal/web/translation/` (14 locales, shared with the
|
||||
frontend); frontend wiring in `frontend/src/i18n/`. Persian (`fa_IR`) is a first-class
|
||||
locale (Jalali calendar via `persian-calendar-suite`).
|
||||
- **Tests live next to code** (`foo.go` ↔ `foo_test.go`), plus golden snapshots in
|
||||
|
||||
@@ -31,7 +31,7 @@ Import a client's share link or QR code, or use a
|
||||
|
||||
3x-ui is an enhanced fork of the original X-UI project. It adds broader protocol
|
||||
support, improved stability, per-client traffic accounting, multi-node
|
||||
management, a 13-language UI, and many quality-of-life features.
|
||||
management, a 14-language UI, and many quality-of-life features.
|
||||
|
||||
## How do I update?
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ SQLite گزینه پیشفرض است و برای بیشتر استقراره
|
||||
|
||||
3x-ui یک فورک ارتقایافته از پروژه اصلی X-UI است. این پروژه پشتیبانی گستردهتری از
|
||||
پروتکلها، پایداری بهبودیافته، حسابداری ترافیک بهازای هر کلاینت، مدیریت چندنودی،
|
||||
یک رابط کاربری ۱۳ زبانه و بسیاری از امکانات رفاهی را اضافه میکند.
|
||||
یک رابط کاربری ۱۴ زبانه و بسیاری از امکانات رفاهی را اضافه میکند.
|
||||
|
||||
## چگونه بهروزرسانی کنم؟
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ SQLite используется по умолчанию и подходит дл
|
||||
|
||||
3x-ui — это улучшенный форк оригинального проекта X-UI. Он добавляет более широкую поддержку
|
||||
протоколов, повышенную стабильность, поклиентский учёт трафика, управление несколькими
|
||||
узлами, интерфейс на 13 языках и множество улучшений для удобства работы.
|
||||
узлами, интерфейс на 14 языках и множество улучшений для удобства работы.
|
||||
|
||||
## Как мне обновиться?
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ SQLite 是默认选项,适用于大多数部署场景。对于规模更大的
|
||||
## 3x-ui 与 x-ui 有什么区别?
|
||||
|
||||
3x-ui 是原版 X-UI 项目的增强分支。它增加了更广泛的协议支持、更佳的稳定性、
|
||||
按客户端的流量统计、多节点管理、13 种语言的界面,以及众多提升使用体验的功能。
|
||||
按客户端的流量统计、多节点管理、14 种语言的界面,以及众多提升使用体验的功能。
|
||||
|
||||
## 我该如何更新?
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const antdLocaleLoaders: Record<string, () => Promise<{ default: Locale }>> = {
|
||||
'ja-JP': () => import('antd/es/locale/ja_JP'),
|
||||
'pt-BR': () => import('antd/es/locale/pt_BR'),
|
||||
'ru-RU': () => import('antd/es/locale/ru_RU'),
|
||||
'sk-SK': () => import('antd/es/locale/sk_SK'),
|
||||
'tr-TR': () => import('antd/es/locale/tr_TR'),
|
||||
'uk-UA': () => import('antd/es/locale/uk_UA'),
|
||||
'vi-VN': () => import('antd/es/locale/vi_VN'),
|
||||
|
||||
@@ -5,7 +5,7 @@ import { join, resolve } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
/*
|
||||
* Guards the 13-locale translation set two ways: every key in en-US must be
|
||||
* Guards the 14-locale translation set two ways: every key in en-US must be
|
||||
* referenced somewhere in the frontend or Go sources (dead keys accumulate
|
||||
* silently — this test deleted over two hundred of them when it was
|
||||
* introduced), and every locale must carry exactly the en-US key set
|
||||
@@ -77,7 +77,7 @@ describe('i18n keys', () => {
|
||||
const dead = enKeys.filter(
|
||||
(key) => !tokens.has(key) && !prefixes.some((p) => key.startsWith(p)),
|
||||
);
|
||||
expect(dead, `dead i18n keys (delete from all 13 locales):\n ${dead.join('\n ')}`).toEqual(
|
||||
expect(dead, `dead i18n keys (delete from all 14 locales):\n ${dead.join('\n ')}`).toEqual(
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
@@ -41,6 +41,24 @@ describe('subscription language scope', () => {
|
||||
expect(dateTimeFormat).toHaveBeenLastCalledWith('fa-IR', expect.any(Object));
|
||||
});
|
||||
|
||||
it('maps a Slovak browser to the Slovak panel locale instead of resetting to English', async () => {
|
||||
vi.resetModules();
|
||||
const utils = await import('@/utils');
|
||||
const cookies = new Map<string, string>();
|
||||
vi.spyOn(utils.CookieManager, 'getCookie').mockImplementation(
|
||||
(name) => cookies.get(name) ?? '',
|
||||
);
|
||||
vi.spyOn(utils.CookieManager, 'setCookie').mockImplementation((name, value) => {
|
||||
cookies.set(name, value);
|
||||
});
|
||||
const reload = vi.fn();
|
||||
vi.stubGlobal('window', { navigator: { language: 'sk' }, location: { reload } });
|
||||
|
||||
expect(utils.LanguageManager.getLanguage()).toBe('sk-SK');
|
||||
expect(cookies.get('lang')).toBe('sk-SK');
|
||||
expect(reload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not resolve the language for empty or invalid dates', async () => {
|
||||
const utils = await import('@/utils');
|
||||
const getLanguage = vi.spyOn(utils.LanguageManager, 'getLanguage').mockReturnValue('en-US');
|
||||
|
||||
@@ -901,6 +901,7 @@ export class LanguageManager {
|
||||
{ name: 'Український', value: 'uk-UA', icon: '🇺🇦' },
|
||||
{ name: 'Türkçe', value: 'tr-TR', icon: '🇹🇷' },
|
||||
{ name: 'Português', value: 'pt-BR', icon: '🇧🇷' },
|
||||
{ name: 'Slovenčina', value: 'sk-SK', icon: '🇸🇰' },
|
||||
];
|
||||
|
||||
static getLanguage(scope: LanguageScope = 'panel'): string {
|
||||
@@ -931,6 +932,7 @@ export class LanguageManager {
|
||||
['uk', LanguageManager.supportedLanguages[10].value],
|
||||
['tr', LanguageManager.supportedLanguages[11].value],
|
||||
['pt', LanguageManager.supportedLanguages[12].value],
|
||||
['sk', LanguageManager.supportedLanguages[13].value],
|
||||
];
|
||||
|
||||
simularLangs.forEach((pair) => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user