Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor locale number #24134

Merged
merged 5 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions modules/charset/escape_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,10 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`,
},
}

type nullLocale struct{}

func (nullLocale) Language() string { return "" }
func (nullLocale) Tr(key string, _ ...interface{}) string { return key }
func (nullLocale) TrN(cnt interface{}, key1, keyN string, args ...interface{}) string { return "" }

var _ (translation.Locale) = nullLocale{}

func TestEscapeControlString(t *testing.T) {
for _, tt := range escapeControlTests {
t.Run(tt.name, func(t *testing.T) {
status, result := EscapeControlString(tt.text, nullLocale{})
status, result := EscapeControlString(tt.text, &translation.MockLocale{})
if !reflect.DeepEqual(*status, tt.status) {
t.Errorf("EscapeControlString() status = %v, wanted= %v", status, tt.status)
}
Expand Down Expand Up @@ -179,7 +171,7 @@ func TestEscapeControlReader(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
input := strings.NewReader(tt.text)
output := &strings.Builder{}
status, err := EscapeControlReader(input, output, nullLocale{})
status, err := EscapeControlReader(input, output, &translation.MockLocale{})
result := output.String()
if err != nil {
t.Errorf("EscapeControlReader(): err = %v", err)
Expand All @@ -201,5 +193,5 @@ func TestEscapeControlReader_panic(t *testing.T) {
for i := 0; i < 6826; i++ {
bs = append(bs, []byte("—")...)
}
_, _ = EscapeControlString(string(bs), nullLocale{})
_, _ = EscapeControlString(string(bs), &translation.MockLocale{})
}
17 changes: 2 additions & 15 deletions modules/csv/csv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/translation"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -550,20 +551,6 @@ a|"he said, ""here I am"""`,
}
}

type mockLocale struct{}

func (l mockLocale) Language() string {
return "en"
}

func (l mockLocale) Tr(s string, _ ...interface{}) string {
return s
}

func (l mockLocale) TrN(_cnt interface{}, key1, _keyN string, _args ...interface{}) string {
return key1
}

func TestFormatError(t *testing.T) {
cases := []struct {
err error
Expand Down Expand Up @@ -591,7 +578,7 @@ func TestFormatError(t *testing.T) {
}

for n, c := range cases {
message, err := FormatError(c.err, mockLocale{})
message, err := FormatError(c.err, &translation.MockLocale{})
if c.expectsError {
assert.Error(t, err, "case %d: expected an error to be returned", n)
} else {
Expand Down
7 changes: 0 additions & 7 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ func NewFuncMap() []template.FuncMap {
// -----------------------------------------------------------------
// time / number / format
"FileSize": base.FileSize,
"LocaleNumber": LocaleNumber,
"CountFmt": base.FormatNumberSI,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
Expand Down Expand Up @@ -782,12 +781,6 @@ func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteNa
return a
}

// LocaleNumber renders a number with a Custom Element, browser will render it with a locale number
func LocaleNumber(v interface{}) template.HTML {
num, _ := util.ToInt64(v)
return template.HTML(fmt.Sprintf(`<gitea-locale-number data-number="%d">%d</gitea-locale-number>`, num, num))
}

// Eval the expression and return the result, see the comment of eval.Expr for details.
// To use this helper function in templates, pass each token as a separate parameter.
//
Expand Down
17 changes: 2 additions & 15 deletions modules/test/context_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/web/middleware"

chi "github.com/go-chi/chi/v5"
Expand All @@ -34,7 +35,7 @@ func MockContext(t *testing.T, path string) *context.Context {
Values: make(url.Values),
},
Resp: context.NewResponse(resp),
Locale: &mockLocale{},
Locale: &translation.MockLocale{},
}
defer ctx.Close()

Expand Down Expand Up @@ -91,20 +92,6 @@ func LoadGitRepo(t *testing.T, ctx *context.Context) {
assert.NoError(t, err)
}

type mockLocale struct{}

func (l mockLocale) Language() string {
return "en"
}

func (l mockLocale) Tr(s string, _ ...interface{}) string {
return s
}

func (l mockLocale) TrN(_cnt interface{}, key1, _keyN string, _args ...interface{}) string {
return key1
}

type mockResponseWriter struct {
httptest.ResponseRecorder
size int
Expand Down
27 changes: 27 additions & 0 deletions modules/translation/mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package translation

import "fmt"

// MockLocale provides a mocked locale without any translations
type MockLocale struct{}

var _ Locale = (*MockLocale)(nil)

func (l MockLocale) Language() string {
return "en"
}

func (l MockLocale) Tr(s string, _ ...interface{}) string {
return s
}

func (l MockLocale) TrN(_cnt interface{}, key1, _keyN string, _args ...interface{}) string {
return key1
}

func (l MockLocale) PrettyNumber(v any) string {
return fmt.Sprint(v)
}
30 changes: 25 additions & 5 deletions modules/translation/translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ import (
"code.gitea.io/gitea/modules/translation/i18n"

"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
)

type contextKey struct{}

var ContextKey interface{} = &contextKey{}
var ContextKey any = &contextKey{}

// Locale represents an interface to translation
type Locale interface {
Language() string
Tr(string, ...interface{}) string
TrN(cnt interface{}, key1, keyN string, args ...interface{}) string
Tr(string, ...any) string
TrN(cnt any, key1, keyN string, args ...any) string
PrettyNumber(v any) string
}

// LangType represents a lang type
Expand Down Expand Up @@ -135,6 +138,7 @@ func Match(tags ...language.Tag) language.Tag {
type locale struct {
i18n.Locale
Lang, LangName string // these fields are used directly in templates: .i18n.Lang
msgPrinter *message.Printer
}

// NewLocale return a locale
Expand All @@ -147,13 +151,24 @@ func NewLocale(lang string) Locale {
langName := "unknown"
if l, ok := allLangMap[lang]; ok {
langName = l.Name
} else if len(setting.Langs) > 0 {
lang = setting.Langs[0]
langName = setting.Names[0]
}

i18nLocale, _ := i18n.GetLocale(lang)
return &locale{
l := &locale{
Locale: i18nLocale,
Lang: lang,
LangName: langName,
}
if langTag, err := language.Parse(lang); err != nil {
log.Error("Failed to parse language tag from name %q: %v", l.Lang, err)
l.msgPrinter = message.NewPrinter(language.English)
} else {
l.msgPrinter = message.NewPrinter(langTag)
}
return l
}

func (l *locale) Language() string {
Expand Down Expand Up @@ -199,7 +214,7 @@ var trNLangRules = map[string]func(int64) int{
}

// TrN returns translated message for plural text translation
func (l *locale) TrN(cnt interface{}, key1, keyN string, args ...interface{}) string {
func (l *locale) TrN(cnt any, key1, keyN string, args ...any) string {
var c int64
if t, ok := cnt.(int); ok {
c = int64(t)
Expand All @@ -223,3 +238,8 @@ func (l *locale) TrN(cnt interface{}, key1, keyN string, args ...interface{}) st
}
return l.Tr(keyN, args...)
}

func (l *locale) PrettyNumber(v any) string {
// TODO: this mechanism is not good enough, the complete solution is to switch the translation system to ICU message format
return l.msgPrinter.Sprintf("%v", number.Decimal(v))
}
27 changes: 27 additions & 0 deletions modules/translation/translation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package translation

import (
"testing"

"code.gitea.io/gitea/modules/translation/i18n"

"github.com/stretchr/testify/assert"
)

func TestPrettyNumber(t *testing.T) {
// TODO: make this package friendly to testing

i18n.ResetDefaultLocales()

allLangMap = make(map[string]*LangType)
allLangMap["id-ID"] = &LangType{Lang: "id-ID", Name: "Bahasa Indonesia"}

l := NewLocale("id-ID")
assert.EqualValues(t, "1.000.000", l.PrettyNumber(1000000))

l = NewLocale("nosuch")
assert.EqualValues(t, "1,000,000", l.PrettyNumber(1000000))
}
14 changes: 7 additions & 7 deletions templates/devtest/gitea-ui.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@

<div>
<h1>LocaleNumber</h1>
<div>{{LocaleNumber 1}}</div>
<div>{{LocaleNumber 12}}</div>
<div>{{LocaleNumber 123}}</div>
<div>{{LocaleNumber 1234}}</div>
<div>{{LocaleNumber 12345}}</div>
<div>{{LocaleNumber 123456}}</div>
<div>{{LocaleNumber 1234567}}</div>
<div>{{.locale.PrettyNumber 1}}</div>
<div>{{.locale.PrettyNumber 12}}</div>
<div>{{.locale.PrettyNumber 123}}</div>
<div>{{.locale.PrettyNumber 1234}}</div>
<div>{{.locale.PrettyNumber 12345}}</div>
<div>{{.locale.PrettyNumber 123456}}</div>
<div>{{.locale.PrettyNumber 1234567}}</div>
</div>

<div>
Expand Down
8 changes: 4 additions & 4 deletions templates/projects/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
<div class="ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=open">
{{svg "octicon-project-symlink" 16 "gt-mr-3"}}
{{LocaleNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
</a>
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{$.Link}}?state=closed">
{{svg "octicon-check" 16 "gt-mr-3"}}
{{LocaleNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{.locale.PrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
</a>
</div>

Expand Down Expand Up @@ -46,9 +46,9 @@
{{end}}
<span class="issue-stats">
{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
{{LocaleNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{$.locale.PrettyNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{svg "octicon-check" 16 "gt-mr-3"}}
{{LocaleNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
{{$.locale.PrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
</span>
</div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
Expand Down
8 changes: 4 additions & 4 deletions templates/repo/issue/milestones.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
<div class="ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=open&q={{$.Keyword}}">
{{svg "octicon-milestone" 16 "gt-mr-3"}}
{{LocaleNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
</a>
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/milestones?state=closed&q={{$.Keyword}}">
{{svg "octicon-check" 16 "gt-mr-3"}}
{{LocaleNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{.locale.PrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
</a>
</div>
</div>
Expand Down Expand Up @@ -84,9 +84,9 @@
{{end}}
<span class="issue-stats">
{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
{{LocaleNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{$.locale.PrettyNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{svg "octicon-check" 16 "gt-mr-3"}}
{{LocaleNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
{{$.locale.PrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
{{if .TotalTrackedTime}}{{svg "octicon-clock"}} {{.TotalTrackedTime|Sec2Time}}{{end}}
{{if .UpdatedUnix}}{{svg "octicon-clock"}} {{$.locale.Tr "repo.milestones.update_ago" (TimeSinceUnix .UpdatedUnix $.locale) | Safe}}{{end}}
</span>
Expand Down
4 changes: 2 additions & 2 deletions templates/repo/issue/openclose.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
{{else}}
{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
{{end}}
{{LocaleNumber .IssueStats.OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{.locale.PrettyNumber .IssueStats.OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
</a>
<a class="{{if .IsShowClosed}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type={{.ViewType}}&sort={{$.SortType}}&state=closed&labels={{.SelectLabels}}&milestone={{.MilestoneID}}&project={{.ProjectID}}&assignee={{.AssigneeID}}&poster={{.PosterID}}">
{{svg "octicon-check" 16 "gt-mr-3"}}
{{LocaleNumber .IssueStats.ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{.locale.PrettyNumber .IssueStats.ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
</a>
</div>
8 changes: 4 additions & 4 deletions templates/repo/projects/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
<div class="ui compact tiny menu">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=open">
{{svg "octicon-project" 16 "gt-mr-3"}}
{{LocaleNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{.locale.PrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
</a>
<a class="item{{if .IsShowClosed}} active{{end}}" href="{{.RepoLink}}/projects?state=closed">
{{svg "octicon-check" 16 "gt-mr-3"}}
{{LocaleNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{.locale.PrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
</a>
</div>

Expand Down Expand Up @@ -48,9 +48,9 @@
{{end}}
<span class="issue-stats">
{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
{{LocaleNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{.locale.PrettyNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{svg "octicon-check" 16 "gt-mr-3"}}
{{LocaleNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
{{.locale.PrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
</span>
</div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
Expand Down
4 changes: 2 additions & 2 deletions templates/repo/release/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@
<li>
<span class="ui text middle aligned right">
<span class="ui text grey">{{.Size | FileSize}}</span>
<gitea-locale-number data-number-in-tooltip="{{dict "message" ($.locale.Tr "repo.release.download_count") "number" .DownloadCount | Json}}">
<span data-tooltip-content="{{$.locale.Tr "repo.release.download_count" ($.locale.PrettyNumber .DownloadCount)}}">
{{svg "octicon-info"}}
</gitea-locale-number>
</span>
</span>
<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}">
<strong>{{svg "octicon-package" 16 "gt-mr-2"}}{{.Name}}</strong>
Expand Down
Loading