Русский и английский языки интерфейса
Язык выбирается в шапке панели, а до первого выбора берётся из настроек браузера. Переведены интерфейс, подписи и описания всех параметров конфигов, ошибки API и служебные сообщения менеджера в консоли. Подписи параметров вынесены из app.js в hints-<язык>.json: их больше четырёхсот на язык, и грузить чужой язык незачем. Сообщения менеджера переводятся на выходе из API по словарю, где ключом служит русская фраза из кода, — вывод самого игрового сервера через него проходит без изменений. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTKnownMessage(t *testing.T) {
|
||||
got := T(EN, "сервер не запущен")
|
||||
if got != "the server is not running" {
|
||||
t.Fatalf("T(EN, ...) = %q", got)
|
||||
}
|
||||
if got := T(RU, "сервер не запущен"); got != "сервер не запущен" {
|
||||
t.Fatalf("русский язык не должен трогать сообщение: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Вывод игрового сервера проходит через перевод как есть: словарь его не знает.
|
||||
func TestTKeepsUnknown(t *testing.T) {
|
||||
line := "LOG : General f:1 Zombies spawned: 1000"
|
||||
if got := T(EN, line); got != line {
|
||||
t.Fatalf("незнакомая строка изменена: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTFormat(t *testing.T) {
|
||||
got := T(EN, `профиль "modded" не найден`)
|
||||
if got != `profile "modded" not found` {
|
||||
t.Fatalf("T = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Вложенная ошибка переводится вместе с обёрткой, а более конкретный шаблон
|
||||
// («прочитать конфиг») должен выиграть у общего («прочитать»).
|
||||
func TestTNestedError(t *testing.T) {
|
||||
msg := "конфиг сервера недоступен: прочитать конфиг /srv/servertest.ini: open: no such file"
|
||||
want := "the server config is unavailable: read the config /srv/servertest.ini: open: no such file"
|
||||
if got := T(EN, msg); got != want {
|
||||
t.Fatalf("T = %q, ожидалось %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTTaskName(t *testing.T) {
|
||||
if got := T(EN, "Резервное копирование мира: modded"); got != "World backup: modded" {
|
||||
t.Fatalf("T = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// В переводе должно быть столько же подстановок, сколько в оригинале, иначе
|
||||
// Sprintf выведет %!s(MISSING) прямо в панель.
|
||||
func TestCatalogPlaceholders(t *testing.T) {
|
||||
for _, f := range formats {
|
||||
ru := strings.Count(f[0], "%s")
|
||||
en := strings.Count(f[1], "%s")
|
||||
if ru == 0 {
|
||||
t.Errorf("шаблон без подстановки: %q", f[0])
|
||||
}
|
||||
if ru != en {
|
||||
t.Errorf("%q: подстановок %d, в переводе %d", f[0], ru, en)
|
||||
}
|
||||
if strings.ContainsAny(strings.ReplaceAll(f[0], "%s", ""), "%") {
|
||||
t.Errorf("в шаблоне остался другой глагол форматирования: %q", f[0])
|
||||
}
|
||||
}
|
||||
for ru, en := range messages {
|
||||
if strings.Contains(ru, "%") || strings.Contains(en, "%") {
|
||||
t.Errorf("сообщение с подстановкой должно лежать в formats: %q", ru)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromRequest(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
cookie string
|
||||
header string
|
||||
want Lang
|
||||
}{
|
||||
{"по умолчанию", "", "", Default},
|
||||
{"выбор человека", "en", "ru-RU,ru;q=0.9", EN},
|
||||
{"мусор в куке", "klingon", "en-US,en;q=0.9", EN},
|
||||
{"язык браузера", "", "en-GB,en;q=0.9", EN},
|
||||
{"неизвестный язык браузера", "", "de-DE,de;q=0.9", Default},
|
||||
{"первый понятный из списка", "", "de-DE;q=0.9,en;q=0.8", EN},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
r, err := http.NewRequest(http.MethodGet, "/api/status", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.cookie != "" {
|
||||
r.AddCookie(&http.Cookie{Name: CookieName, Value: c.cookie})
|
||||
}
|
||||
if c.header != "" {
|
||||
r.Header.Set("Accept-Language", c.header)
|
||||
}
|
||||
if got := FromRequest(r); got != c.want {
|
||||
t.Fatalf("FromRequest = %q, ожидалось %q", got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user