From 42eed7dbeaa39184a4e15285b72ca289ea9f1f48 Mon Sep 17 00:00:00 2001 From: xtex Date: Sun, 3 Nov 2024 09:36:09 +0800 Subject: [PATCH] i18n: Add dummy language for checking translation keys (#5785) --- modules/translation/i18n/dummy.go | 66 ++++++++++++++++++++++++++ modules/translation/i18n/dummy_test.go | 19 ++++++++ modules/translation/translation.go | 10 ++++ modules/web/middleware/locale.go | 6 ++- tests/integration/dummy_lang_test.go | 24 ++++++++++ 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 modules/translation/i18n/dummy.go create mode 100644 modules/translation/i18n/dummy_test.go create mode 100644 tests/integration/dummy_lang_test.go diff --git a/modules/translation/i18n/dummy.go b/modules/translation/i18n/dummy.go new file mode 100644 index 0000000000..fe15c250f4 --- /dev/null +++ b/modules/translation/i18n/dummy.go @@ -0,0 +1,66 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package i18n + +import ( + "fmt" + "html/template" + "reflect" + "slices" + "strings" +) + +type KeyLocale struct{} + +var _ Locale = (*KeyLocale)(nil) + +// HasKey implements Locale. +func (k *KeyLocale) HasKey(trKey string) bool { + return true +} + +// TrHTML implements Locale. +func (k *KeyLocale) TrHTML(trKey string, trArgs ...any) template.HTML { + args := slices.Clone(trArgs) + for i, v := range args { + switch v := v.(type) { + case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, template.HTML: + // for most basic types (including template.HTML which is safe), just do nothing and use it + case string: + args[i] = template.HTMLEscapeString(v) + case fmt.Stringer: + args[i] = template.HTMLEscapeString(v.String()) + default: + args[i] = template.HTMLEscapeString(fmt.Sprint(v)) + } + } + return template.HTML(k.TrString(trKey, args...)) +} + +// TrString implements Locale. +func (k *KeyLocale) TrString(trKey string, trArgs ...any) string { + return FormatDummy(trKey, trArgs...) +} + +func FormatDummy(trKey string, args ...any) string { + if len(args) == 0 { + return fmt.Sprintf("(%s)", trKey) + } + + fmtArgs := make([]any, 0, len(args)+1) + fmtArgs = append(fmtArgs, trKey) + for _, arg := range args { + val := reflect.ValueOf(arg) + if val.Kind() == reflect.Slice { + for i := 0; i < val.Len(); i++ { + fmtArgs = append(fmtArgs, val.Index(i).Interface()) + } + } else { + fmtArgs = append(fmtArgs, arg) + } + } + + template := fmt.Sprintf("(%%s: %s)", strings.Join(slices.Repeat([]string{"%v"}, len(fmtArgs)-1), ", ")) + return fmt.Sprintf(template, fmtArgs...) +} diff --git a/modules/translation/i18n/dummy_test.go b/modules/translation/i18n/dummy_test.go new file mode 100644 index 0000000000..7bc29ef839 --- /dev/null +++ b/modules/translation/i18n/dummy_test.go @@ -0,0 +1,19 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package i18n_test + +import ( + "testing" + + "code.gitea.io/gitea/modules/translation/i18n" + + "github.com/stretchr/testify/assert" +) + +func TestFormatDummy(t *testing.T) { + assert.Equal(t, "(admin.config.git_max_diff_lines)", i18n.FormatDummy("admin.config.git_max_diff_lines")) + assert.Equal(t, "(dashboard)", i18n.FormatDummy("dashboard")) + assert.Equal(t, "(branch.create_branch: main)", i18n.FormatDummy("branch.create_branch", "main")) + assert.Equal(t, "(test.test: a, 1, true)", i18n.FormatDummy("test.test", "a", 1, true)) +} diff --git a/modules/translation/translation.go b/modules/translation/translation.go index 16eb55e28e..6687d3d817 100644 --- a/modules/translation/translation.go +++ b/modules/translation/translation.go @@ -160,6 +160,16 @@ func NewLocale(lang string) Locale { defer lock.RUnlock() } + if lang == "dummy" { + l := &locale{ + Locale: &i18n.KeyLocale{}, + Lang: lang, + LangName: lang, + msgPrinter: message.NewPrinter(language.English), + } + return l + } + langName := "unknown" if l, ok := allLangMap[lang]; ok { langName = l.Name diff --git a/modules/web/middleware/locale.go b/modules/web/middleware/locale.go index 34a16f04e7..1f37407201 100644 --- a/modules/web/middleware/locale.go +++ b/modules/web/middleware/locale.go @@ -26,8 +26,10 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale { } } - // Check again in case someone changes the supported language list. - if lang != "" && !i18n.DefaultLocales.HasLang(lang) { + if lang == "dummy" { + changeLang = false + } else if lang != "" && !i18n.DefaultLocales.HasLang(lang) { + // Check again in case someone changes the supported language list. lang = "" changeLang = false } diff --git a/tests/integration/dummy_lang_test.go b/tests/integration/dummy_lang_test.go new file mode 100644 index 0000000000..d26ea6edc4 --- /dev/null +++ b/tests/integration/dummy_lang_test.go @@ -0,0 +1,24 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestKeyLocale(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + req := NewRequest(t, "GET", "/user2/repo1/issues?lang=dummy") + resp := MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + newButton := htmlDoc.doc.Find(".list-header-issues > .issue-list-new") + assert.Equal(t, "(repo.issues.new)", newButton.Text()) +}