Русский и английский языки интерфейса
CI / test (push) Successful in 22s
Release / release (push) Successful in 29s

Язык выбирается в шапке панели, а до первого выбора берётся из настроек
браузера. Переведены интерфейс, подписи и описания всех параметров конфигов,
ошибки API и служебные сообщения менеджера в консоли.

Подписи параметров вынесены из app.js в hints-<язык>.json: их больше четырёхсот
на язык, и грузить чужой язык незачем. Сообщения менеджера переводятся на
выходе из API по словарю, где ключом служит русская фраза из кода, — вывод
самого игрового сервера через него проходит без изменений.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 21:14:21 +03:00
co-authored by Claude Opus 5
parent d5a308ac8d
commit 9a224bc070
19 changed files with 5417 additions and 857 deletions
+10 -10
View File
@@ -34,7 +34,7 @@ func (s *Server) handleSession(w http.ResponseWriter, r *http.Request) {
// handleSetup создаёт первого администратора. Доступен, только пока учёток нет.
func (s *Server) handleSetup(w http.ResponseWriter, r *http.Request) {
if !s.users.IsEmpty() {
writeError(w, http.StatusConflict, "панель уже настроена")
writeError(w, r, http.StatusConflict, "панель уже настроена")
return
}
var creds credentials
@@ -44,16 +44,16 @@ func (s *Server) handleSetup(w http.ResponseWriter, r *http.Request) {
// Токен печатается в журнал менеджера при старте: без него первым
// администратором стал бы любой, кто успел открыть страницу.
if s.setupToken == "" || subtle.ConstantTimeCompare([]byte(creds.Token), []byte(s.setupToken)) != 1 {
writeError(w, http.StatusForbidden, "неверный код первичной настройки")
writeError(w, r, http.StatusForbidden, "неверный код первичной настройки")
return
}
if err := s.users.AddUser(creds.Login, creds.Password); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
writeError(w, r, http.StatusBadRequest, err.Error())
return
}
id, err := s.users.Login(creds.Login, creds.Password)
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
writeError(w, r, http.StatusInternalServerError, err.Error())
return
}
// Администратор создан — код больше не нужен и не должен лежать на диске.
@@ -72,10 +72,10 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
id, err := s.users.Login(creds.Login, creds.Password)
if err != nil {
if errors.Is(err, auth.ErrBadCredentials) {
writeError(w, http.StatusUnauthorized, err.Error())
writeError(w, r, http.StatusUnauthorized, err.Error())
return
}
writeError(w, http.StatusInternalServerError, err.Error())
writeError(w, r, http.StatusInternalServerError, err.Error())
return
}
s.setSessionCookie(w, id, sessionTTL)
@@ -100,7 +100,7 @@ func (s *Server) handleCreateUser(w http.ResponseWriter, r *http.Request) {
return
}
if err := s.users.AddUser(creds.Login, creds.Password); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
writeError(w, r, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusCreated, map[string]any{"login": creds.Login})
@@ -110,11 +110,11 @@ func (s *Server) handleDeleteUser(w http.ResponseWriter, r *http.Request) {
login := r.PathValue("login")
current, _ := s.users.SessionFromRequest(r)
if login == current {
writeError(w, http.StatusBadRequest, "нельзя удалить учётную запись, под которой вы вошли")
writeError(w, r, http.StatusBadRequest, "нельзя удалить учётную запись, под которой вы вошли")
return
}
if err := s.users.DeleteUser(login); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
writeError(w, r, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]any{"deleted": login})
@@ -128,7 +128,7 @@ func (s *Server) handleChangePassword(w http.ResponseWriter, r *http.Request) {
return
}
if err := s.users.SetPassword(r.PathValue("login"), body.Password); err != nil {
writeError(w, http.StatusBadRequest, err.Error())
writeError(w, r, http.StatusBadRequest, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]any{"ok": true})