Русский и английский языки интерфейса
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
+11 -9
View File
@@ -13,6 +13,7 @@ import (
"github.com/mrleo1nid/pzmanager/internal/auth"
"github.com/mrleo1nid/pzmanager/internal/backup"
"github.com/mrleo1nid/pzmanager/internal/config"
"github.com/mrleo1nid/pzmanager/internal/i18n"
"github.com/mrleo1nid/pzmanager/internal/job"
"github.com/mrleo1nid/pzmanager/internal/pzconfig"
"github.com/mrleo1nid/pzmanager/internal/pzserver"
@@ -175,7 +176,7 @@ func (s *Server) withCommonHeaders(next http.Handler) http.Handler {
if r.Method != http.MethodGet && r.Method != http.MethodHead &&
strings.HasPrefix(r.URL.Path, "/api/") && r.Header.Get(csrfHeader) == "" {
writeError(w, http.StatusForbidden, "запрос без заголовка "+csrfHeader)
writeError(w, r, http.StatusForbidden, "запрос без заголовка "+csrfHeader)
return
}
next.ServeHTTP(w, r)
@@ -186,7 +187,7 @@ func (s *Server) withCommonHeaders(next http.Handler) http.Handler {
func (s *Server) requireAuth(next http.HandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, ok := s.users.SessionFromRequest(r); !ok {
writeError(w, http.StatusUnauthorized, "требуется вход")
writeError(w, r, http.StatusUnauthorized, "требуется вход")
return
}
next(w, r)
@@ -228,20 +229,21 @@ func writeJSON(w http.ResponseWriter, status int, v any) {
}
// writeError отправляет ошибку в том же формате, что и остальные ответы.
func writeError(w http.ResponseWriter, status int, message string) {
writeJSON(w, status, map[string]string{"error": message})
// Текст переводится на язык панели: в коде сообщения написаны по-русски.
func writeError(w http.ResponseWriter, r *http.Request, status int, message string) {
writeJSON(w, status, map[string]string{"error": i18n.T(i18n.FromRequest(r), message)})
}
// writeOpError переводит ошибку операции в подходящий HTTP-код.
func writeOpError(w http.ResponseWriter, err error) {
func writeOpError(w http.ResponseWriter, r *http.Request, err error) {
switch {
case errors.Is(err, pzserver.ErrAlreadyRunning), errors.Is(err, pzserver.ErrNotRunning),
errors.Is(err, job.ErrBusy):
writeError(w, http.StatusConflict, err.Error())
writeError(w, r, http.StatusConflict, err.Error())
case errors.Is(err, pzserver.ErrNotInstalled):
writeError(w, http.StatusPreconditionFailed, err.Error())
writeError(w, r, http.StatusPreconditionFailed, err.Error())
default:
writeError(w, http.StatusInternalServerError, err.Error())
writeError(w, r, http.StatusInternalServerError, err.Error())
}
}
@@ -251,7 +253,7 @@ func decodeJSON(w http.ResponseWriter, r *http.Request, dst any) bool {
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()
if err := dec.Decode(dst); err != nil {
writeError(w, http.StatusBadRequest, "некорректный JSON: "+err.Error())
writeError(w, r, http.StatusBadRequest, "некорректный JSON: "+err.Error())
return false
}
return true