Skip to content

Commit

Permalink
Introduce GiteaLocaleNumber custom element to handle number localizat…
Browse files Browse the repository at this point in the history
…ion on pages. (#23861)

Follow #21429 & #22861

Use `<gitea-locale-number>` instead of backend `PrettyNumber`. All old
`PrettyNumber` related functions are removed. A lot of code could be
simplified.

And some functions haven't been used for long time (dead code), so they
are also removed by the way (eg: `SplitStringAtRuneN`, `Dedent`)

This PR only tries to improve the `PrettyNumber` rendering problem, it
doesn't touch the "plural" problem.

Screenshot:


![image](https://user-images.githubusercontent.com/2114189/229290804-1f63db65-1e34-4a54-84ba-e00b44331b17.png)


![image](https://user-images.githubusercontent.com/2114189/229290911-c88dea00-b11d-48dd-accb-9f52edd73ce4.png)
  • Loading branch information
wxiaoguang authored Apr 3, 2023
1 parent 01d9466 commit 19de52e
Show file tree
Hide file tree
Showing 24 changed files with 94 additions and 227 deletions.
7 changes: 0 additions & 7 deletions modules/base/tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"

"github.com/dustin/go-humanize"
"github.com/minio/sha256-simd"
Expand Down Expand Up @@ -142,12 +141,6 @@ func FileSize(s int64) string {
return humanize.IBytes(uint64(s))
}

// PrettyNumber produces a string form of the given number in base 10 with
// commas after every three orders of magnitude
func PrettyNumber(i interface{}) string {
return humanize.Comma(util.NumberIntoInt64(i))
}

// Subtract deals with subtraction of all types of number.
func Subtract(left, right interface{}) interface{} {
var rleft, rright int64
Expand Down
7 changes: 0 additions & 7 deletions modules/base/tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,6 @@ func TestFileSize(t *testing.T) {
assert.Equal(t, "2.0 EiB", FileSize(size))
}

func TestPrettyNumber(t *testing.T) {
assert.Equal(t, "23,342,432", PrettyNumber(23342432))
assert.Equal(t, "23,342,432", PrettyNumber(int32(23342432)))
assert.Equal(t, "0", PrettyNumber(0))
assert.Equal(t, "-100,000", PrettyNumber(-100000))
}

func TestSubtract(t *testing.T) {
toFloat64 := func(n interface{}) float64 {
switch v := n.(type) {
Expand Down
93 changes: 18 additions & 75 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
texttmpl "text/template"
"time"
Expand Down Expand Up @@ -112,18 +111,17 @@ func NewFuncMap() []template.FuncMap {
"IsShowFullName": func() bool {
return setting.UI.DefaultShowFullName
},
"Safe": Safe,
"SafeJS": SafeJS,
"JSEscape": JSEscape,
"Str2html": Str2html,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"FileSize": base.FileSize,
"PrettyNumber": base.PrettyNumber,
"JsPrettyNumber": JsPrettyNumber,
"Subtract": base.Subtract,
"EntryIcon": base.EntryIcon,
"MigrationIcon": MigrationIcon,
"Safe": Safe,
"SafeJS": SafeJS,
"JSEscape": JSEscape,
"Str2html": Str2html,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"FileSize": base.FileSize,
"LocaleNumber": LocaleNumber,
"Subtract": base.Subtract,
"EntryIcon": base.EntryIcon,
"MigrationIcon": MigrationIcon,
"Add": func(a ...int) int {
sum := 0
for _, val := range a {
Expand Down Expand Up @@ -410,62 +408,9 @@ func NewFuncMap() []template.FuncMap {
"Join": strings.Join,
"QueryEscape": url.QueryEscape,
"DotEscape": DotEscape,
"Iterate": func(arg interface{}) (items []uint64) {
count := uint64(0)
switch val := arg.(type) {
case uint64:
count = val
case *uint64:
count = *val
case int64:
if val < 0 {
val = 0
}
count = uint64(val)
case *int64:
if *val < 0 {
*val = 0
}
count = uint64(*val)
case int:
if val < 0 {
val = 0
}
count = uint64(val)
case *int:
if *val < 0 {
*val = 0
}
count = uint64(*val)
case uint:
count = uint64(val)
case *uint:
count = uint64(*val)
case int32:
if val < 0 {
val = 0
}
count = uint64(val)
case *int32:
if *val < 0 {
*val = 0
}
count = uint64(*val)
case uint32:
count = uint64(val)
case *uint32:
count = uint64(*val)
case string:
cnt, _ := strconv.ParseInt(val, 10, 64)
if cnt < 0 {
cnt = 0
}
count = uint64(cnt)
}
if count <= 0 {
return items
}
for i := uint64(0); i < count; i++ {
"Iterate": func(arg interface{}) (items []int64) {
count := util.ToInt64(arg)
for i := int64(0); i < count; i++ {
items = append(items, i)
}
return items
Expand Down Expand Up @@ -1067,10 +1012,8 @@ func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteNa
return a
}

// JsPrettyNumber renders a number using english decimal separators, e.g. 1,200 and subsequent
// JS will replace the number with locale-specific separators, based on the user's selected language
func JsPrettyNumber(i interface{}) template.HTML {
num := util.NumberIntoInt64(i)

return template.HTML(`<span class="js-pretty-number" data-value="` + strconv.FormatInt(num, 10) + `">` + base.PrettyNumber(num) + `</span>`)
// 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))
}
24 changes: 0 additions & 24 deletions modules/util/truncate.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,3 @@ func SplitStringAtByteN(input string, n int) (left, right string) {

return input[:end] + utf8Ellipsis, utf8Ellipsis + input[end:]
}

// SplitStringAtRuneN splits a string at rune n accounting for rune boundaries. (Combining characters are not accounted for.)
func SplitStringAtRuneN(input string, n int) (left, right string) {
if !utf8.ValidString(input) {
if len(input) <= n || n-3 < 0 {
return input, ""
}
return input[:n-3] + asciiEllipsis, asciiEllipsis + input[n-3:]
}

if utf8.RuneCountInString(input) <= n {
return input, ""
}

count := 0
end := 0
for count < n-1 {
_, size := utf8.DecodeRuneInString(input[end:])
end += size
count++
}

return input[:end] + utf8Ellipsis, utf8Ellipsis + input[end:]
}
14 changes: 0 additions & 14 deletions modules/util/truncate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,4 @@ func TestSplitString(t *testing.T) {
{"\xef\x03", 1, "\xef\x03", ""},
}
test(tc, SplitStringAtByteN)

tc = []*testCase{
{"abc123xyz", 0, "", utf8Ellipsis},
{"abc123xyz", 1, "", utf8Ellipsis},
{"abc123xyz", 4, "abc", utf8Ellipsis},
{"啊bc123xyz", 4, "啊bc", utf8Ellipsis},
{"啊bc123xyz", 6, "啊bc12", utf8Ellipsis},
{"啊bc", 3, "啊bc", ""},
{"啊bc", 4, "啊bc", ""},
{"abc\xef\x03\xfe", 3, "", asciiEllipsis},
{"abc\xef\x03\xfe", 4, "a", asciiEllipsis},
{"\xef\x03", 1, "\xef\x03", ""},
}
test(tc, SplitStringAtRuneN)
}
58 changes: 25 additions & 33 deletions modules/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"math/big"
"regexp"
"os"
"strconv"
"strings"

Expand Down Expand Up @@ -200,40 +201,14 @@ func ToTitleCaseNoLower(s string) string {
return titleCaserNoLower.String(s)
}

var (
whitespaceOnly = regexp.MustCompile("(?m)^[ \t]+$")
leadingWhitespace = regexp.MustCompile("(?m)(^[ \t]*)(?:[^ \t\n])")
)

// Dedent removes common indentation of a multi-line string along with whitespace around it
// Based on https://github.com/lithammer/dedent
func Dedent(s string) string {
var margin string

s = whitespaceOnly.ReplaceAllString(s, "")
indents := leadingWhitespace.FindAllStringSubmatch(s, -1)

for i, indent := range indents {
if i == 0 {
margin = indent[1]
} else if strings.HasPrefix(indent[1], margin) {
continue
} else if strings.HasPrefix(margin, indent[1]) {
margin = indent[1]
} else {
margin = ""
break
}
}

if margin != "" {
s = regexp.MustCompile("(?m)^"+margin).ReplaceAllString(s, "")
}
return strings.TrimSpace(s)
func logError(msg string, args ...any) {
// TODO: the "util" package can not import the "modules/log" package, so we use the "fmt" package here temporarily.
// In the future, we should decouple the dependency between them.
_, _ = fmt.Fprintf(os.Stderr, msg, args...)
}

// NumberIntoInt64 transform a given int into int64.
func NumberIntoInt64(number interface{}) int64 {
// ToInt64 transform a given int into int64.
func ToInt64(number interface{}) int64 {
var value int64
switch v := number.(type) {
case int:
Expand All @@ -246,6 +221,23 @@ func NumberIntoInt64(number interface{}) int64 {
value = int64(v)
case int64:
value = v
case uint:
value = int64(v)
case uint8:
value = int64(v)
case uint16:
value = int64(v)
case uint32:
value = int64(v)
case uint64:
value = int64(v)
case string:
var err error
if value, err = strconv.ParseInt(v, 10, 64); err != nil {
logError("strconv.ParseInt failed for %q: %v", v, err)
}
default:
logError("unable to convert %q to int64", v)
}
return value
}
7 changes: 0 additions & 7 deletions modules/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,3 @@ func TestToTitleCase(t *testing.T) {
assert.Equal(t, ToTitleCase(`foo bar baz`), `Foo Bar Baz`)
assert.Equal(t, ToTitleCase(`FOO BAR BAZ`), `Foo Bar Baz`)
}

func TestDedent(t *testing.T) {
assert.Equal(t, Dedent(`
foo
bar
`), "foo\n\tbar")
}
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"}}
{{JsPrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{LocaleNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{svg "octicon-check" 16 "gt-mr-3"}}
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{LocaleNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{svg "octicon-check" 16 "gt-mr-3"}}
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
{{LocaleNumber .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" (.TimeSinceUpdate|Sec2Time)}}{{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}}
{{JsPrettyNumber .IssueStats.OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .IssueStats.ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .OpenCount}}&nbsp;{{.locale.Tr "repo.issues.open_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .ClosedCount}}&nbsp;{{.locale.Tr "repo.issues.closed_title"}}
{{LocaleNumber .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"}}
{{JsPrettyNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{LocaleNumber .NumOpenIssues}}&nbsp;{{$.locale.Tr "repo.issues.open_title"}}
{{svg "octicon-check" 16 "gt-mr-3"}}
{{JsPrettyNumber .NumClosedIssues}}&nbsp;{{$.locale.Tr "repo.issues.closed_title"}}
{{LocaleNumber .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>
<span data-tooltip-content="{{$.locale.Tr "repo.release.download_count" (.DownloadCount | PrettyNumber)}}">
<gitea-locale-number data-number-in-tooltip="{{dict "message" ($.locale.Tr "repo.release.download_count") "number" .DownloadCount | Json}}">
{{svg "octicon-info"}}
</span>
</gitea-locale-number>
</span>
<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}">
<strong>{{svg "octicon-package" 16 "gt-mr-2"}}{{.Name}}</strong>
Expand Down
Loading

0 comments on commit 19de52e

Please sign in to comment.