From 7b24b669edda32df02d7fc8d3a3ba6776fbee502 Mon Sep 17 00:00:00 2001
From: 0ko <0ko@noreply.codeberg.org>
Date: Tue, 19 Mar 2024 15:52:32 +0500
Subject: [PATCH] Provide a way to translate data units

---
 .deadcode-out                                 |  1 +
 models/repo/repo.go                           | 14 +++++---
 modules/translation/mock.go                   |  4 +++
 modules/translation/translation.go            | 32 +++++++++++++++++++
 templates/admin/packages/list.tmpl            |  6 ++--
 templates/admin/repo/list.tmpl                |  4 +--
 templates/mail/release.tmpl                   |  2 +-
 templates/package/content/container.tmpl      |  2 +-
 .../package/shared/cleanup_rules/preview.tmpl |  2 +-
 templates/package/view.tmpl                   |  4 +--
 templates/repo/diff/image_diff.tmpl           |  4 +--
 templates/repo/file_info.tmpl                 |  2 +-
 .../repo/issue/view_content/attachments.tmpl  |  2 +-
 templates/repo/release/list.tmpl              |  2 +-
 templates/repo/release/new.tmpl               |  2 +-
 templates/repo/settings/lfs.tmpl              |  2 +-
 templates/repo/settings/options.tmpl          |  2 +-
 templates/repo/sub_menu.tmpl                  |  7 ++--
 templates/user/settings/repos.tmpl            |  4 +--
 19 files changed, 69 insertions(+), 29 deletions(-)

diff --git a/.deadcode-out b/.deadcode-out
index 1b65e3203d..940551da04 100644
--- a/.deadcode-out
+++ b/.deadcode-out
@@ -295,6 +295,7 @@ package "code.gitea.io/gitea/modules/translation"
 	func (MockLocale).TrString
 	func (MockLocale).Tr
 	func (MockLocale).TrN
+	func (MockLocale).TrSize
 	func (MockLocale).PrettyNumber
 
 package "code.gitea.io/gitea/modules/util/filebuffer"
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 4e1b6fcbcf..350dc86d4b 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -16,7 +16,6 @@ import (
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup"
@@ -24,6 +23,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/translation"
 	"code.gitea.io/gitea/modules/util"
 
 	"xorm.io/builder"
@@ -249,13 +249,17 @@ func (repo *Repository) SizeDetails() []SizeDetail {
 }
 
 // SizeDetailsString returns a concatenation of all repository size details as a string
-func (repo *Repository) SizeDetailsString() string {
+func (repo *Repository) SizeDetailsString(locale translation.Locale) string {
 	var str strings.Builder
 	sizeDetails := repo.SizeDetails()
-	for _, detail := range sizeDetails {
-		str.WriteString(fmt.Sprintf("%s: %s, ", detail.Name, base.FileSize(detail.Size)))
+	for i, detail := range sizeDetails {
+		if i > 0 {
+			// TODO: use semicolon if decimal point of user localization is a comma
+			str.WriteString(", ")
+		}
+		str.WriteString(fmt.Sprintf("%s: %s", detail.Name, locale.TrSize(detail.Size)))
 	}
-	return strings.TrimSuffix(str.String(), ", ")
+	return str.String()
 }
 
 func (repo *Repository) LogString() string {
diff --git a/modules/translation/mock.go b/modules/translation/mock.go
index 18fbc1044a..c4f8822db3 100644
--- a/modules/translation/mock.go
+++ b/modules/translation/mock.go
@@ -31,6 +31,10 @@ func (l MockLocale) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
 	return template.HTML(key1)
 }
 
+func (l MockLocale) TrSize(s int64) ByteSize {
+	return ByteSize{fmt.Sprint(s), ""}
+}
+
 func (l MockLocale) PrettyNumber(v any) string {
 	return fmt.Sprint(v)
 }
diff --git a/modules/translation/translation.go b/modules/translation/translation.go
index 36ae58a9f1..a6013a288f 100644
--- a/modules/translation/translation.go
+++ b/modules/translation/translation.go
@@ -15,6 +15,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/translation/i18n"
 	"code.gitea.io/gitea/modules/util"
+	"github.com/dustin/go-humanize"
 
 	"golang.org/x/text/language"
 	"golang.org/x/text/message"
@@ -33,6 +34,8 @@ type Locale interface {
 	Tr(key string, args ...any) template.HTML
 	TrN(cnt any, key1, keyN string, args ...any) template.HTML
 
+	TrSize(size int64) ByteSize
+
 	PrettyNumber(v any) string
 }
 
@@ -252,6 +255,35 @@ func (l *locale) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
 	return l.Tr(keyN, args...)
 }
 
+type ByteSize struct {
+	PrettyNumber   string
+	TranslatedUnit string
+}
+
+func (bs ByteSize) String() string {
+	return bs.PrettyNumber + " " + bs.TranslatedUnit
+}
+
+// TrSize returns array containing pretty formatted size and localized output of FileSize
+// output of humanize.IBytes has to be split in order to be localized
+func (l *locale) TrSize(s int64) ByteSize {
+	us := uint64(s)
+	if s < 0 {
+		us = uint64(-s)
+	}
+	untranslated := humanize.IBytes(us)
+	if s < 0 {
+		untranslated = "-" + untranslated
+	}
+	numberVal, unitVal, found := strings.Cut(untranslated, " ")
+	if !found {
+		log.Error("no space in go-humanized size of %d: %q", s, untranslated)
+	}
+	numberVal = l.PrettyNumber(numberVal)
+	unitVal = l.TrString("munits.data." + strings.ToLower(unitVal))
+	return ByteSize{numberVal, unitVal}
+}
+
 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
 	if s, ok := v.(string); ok {
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl
index 863f11da25..d111c57378 100644
--- a/templates/admin/packages/list.tmpl
+++ b/templates/admin/packages/list.tmpl
@@ -2,8 +2,8 @@
 	<div class="admin-setting-content">
 		<h4 class="ui top attached header">
 			{{ctx.Locale.Tr "admin.packages.package_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .TotalCount}},
-			{{ctx.Locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}},
-			{{ctx.Locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}})
+			{{ctx.Locale.Tr "admin.packages.total_size" (ctx.Locale.TrSize .TotalBlobSize)}},
+			{{ctx.Locale.Tr "admin.packages.unreferenced_size" (ctx.Locale.TrSize .TotalUnreferencedBlobSize)}})
 			<div class="ui right">
 				<form method="post" action="/admin/packages/cleanup">
 					{{.CsrfTokenHtml}}
@@ -70,7 +70,7 @@
 								<a href="{{.Repository.Link}}">{{.Repository.Name}}</a>
 							{{end}}
 							</td>
-							<td>{{FileSize .CalculateBlobSize}}</td>
+							<td>{{ctx.Locale.TrSize .CalculateBlobSize}}</td>
 							<td>{{DateTime "short" .Version.CreatedUnix}}</td>
 							<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td>
 						</tr>
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl
index 4b27d87a45..2c6e1d67a9 100644
--- a/templates/admin/repo/list.tmpl
+++ b/templates/admin/repo/list.tmpl
@@ -80,8 +80,8 @@
 							<td>{{.NumStars}}</td>
 							<td>{{.NumForks}}</td>
 							<td>{{.NumIssues}}</td>
-							<td>{{FileSize .GitSize}}</td>
-							<td>{{FileSize .LFSSize}}</td>
+							<td>{{ctx.Locale.TrSize .GitSize}}</td>
+							<td>{{ctx.Locale.TrSize .LFSSize}}</td>
 							<td>{{DateTime "short" .UpdatedUnix}}</td>
 							<td>{{DateTime "short" .CreatedUnix}}</td>
 							<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
diff --git a/templates/mail/release.tmpl b/templates/mail/release.tmpl
index 90a3caa4c5..92af3216bb 100644
--- a/templates/mail/release.tmpl
+++ b/templates/mail/release.tmpl
@@ -43,7 +43,7 @@
 				{{range .Release.Attachments}}
 					<li>
 						<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}">
-							<strong>{{.Name}} ({{.Size | FileSize}})</strong>
+							<strong>{{.Name}} ({{.Size | $.locale.TrSize}})</strong>
 						</a>
 					</li>
 				{{end}}
diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl
index 0d5f0f09d5..9e255b3d60 100644
--- a/templates/package/content/container.tmpl
+++ b/templates/package/content/container.tmpl
@@ -39,7 +39,7 @@
 					<tr>
 						<td><a href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .Digest}}">{{.Digest}}</a></td>
 						<td>{{.Platform}}</td>
-						<td>{{FileSize .Size}}</td>
+						<td>{{ctx.Locale.TrSize .Size}}</td>
 					</tr>
 					{{end}}
 				</tbody>
diff --git a/templates/package/shared/cleanup_rules/preview.tmpl b/templates/package/shared/cleanup_rules/preview.tmpl
index cff8e8249f..0d9c4b0d46 100644
--- a/templates/package/shared/cleanup_rules/preview.tmpl
+++ b/templates/package/shared/cleanup_rules/preview.tmpl
@@ -21,7 +21,7 @@
 					<td>{{.Package.Name}}</td>
 					<td><a href="{{.VersionWebLink}}">{{.Version.Version}}</a></td>
 					<td><a href="{{.Creator.HomeLink}}">{{.Creator.Name}}</a></td>
-					<td>{{FileSize .CalculateBlobSize}}</td>
+					<td>{{ctx.Locale.TrSize .CalculateBlobSize}}</td>
 					<td>{{DateTime "short" .Version.CreatedUnix}}</td>
 				</tr>
 			{{else}}
diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl
index 6beb249a7f..1d87f4d3af 100644
--- a/templates/package/view.tmpl
+++ b/templates/package/view.tmpl
@@ -70,7 +70,7 @@
 					{{template "package/metadata/swift" .}}
 					{{template "package/metadata/vagrant" .}}
 					{{if not (and (eq .PackageDescriptor.Package.Type "container") .PackageDescriptor.Metadata.Manifests)}}
-					<div class="item">{{svg "octicon-database" 16 "tw-mr-2"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div>
+					<div class="item">{{svg "octicon-database" 16 "tw-mr-2"}} {{ctx.Locale.TrSize .PackageDescriptor.CalculateBlobSize}}</div>
 					{{end}}
 				</div>
 				{{if not (eq .PackageDescriptor.Package.Type "container")}}
@@ -80,7 +80,7 @@
 					{{range .PackageDescriptor.Files}}
 						<div class="item">
 							<a href="{{$.Link}}/files/{{.File.ID}}">{{.File.Name}}</a>
-							<span class="text small file-size">{{FileSize .Blob.Size}}</span>
+							<span class="text small file-size">{{ctx.Locale.TrSize .Blob.Size}}</span>
 						</div>
 					{{end}}
 					</div>
diff --git a/templates/repo/diff/image_diff.tmpl b/templates/repo/diff/image_diff.tmpl
index 9ad7916398..0612854609 100644
--- a/templates/repo/diff/image_diff.tmpl
+++ b/templates/repo/diff/image_diff.tmpl
@@ -30,7 +30,7 @@
 									{{ctx.Locale.Tr "repo.diff.file_image_height"}}: <span class="text bounds-info-height"></span>
 									&nbsp;|&nbsp;
 								</span>
-								{{ctx.Locale.Tr "repo.diff.file_byte_size"}}: <span class="text">{{FileSize .blobBase.Size}}</span>
+								{{ctx.Locale.Tr "repo.diff.file_byte_size"}}: <span class="text">{{ctx.Locale.TrSize .blobBase.Size}}</span>
 							</p>
 						</span>
 						{{end}}
@@ -45,7 +45,7 @@
 									{{ctx.Locale.Tr "repo.diff.file_image_height"}}: <span class="text bounds-info-height"></span>
 									&nbsp;|&nbsp;
 								</span>
-								{{ctx.Locale.Tr "repo.diff.file_byte_size"}}: <span class="text">{{FileSize .blobHead.Size}}</span>
+								{{ctx.Locale.Tr "repo.diff.file_byte_size"}}: <span class="text">{{ctx.Locale.TrSize .blobHead.Size}}</span>
 							</p>
 						</span>
 						{{end}}
diff --git a/templates/repo/file_info.tmpl b/templates/repo/file_info.tmpl
index 86c613e3a1..5527991644 100644
--- a/templates/repo/file_info.tmpl
+++ b/templates/repo/file_info.tmpl
@@ -11,7 +11,7 @@
 	{{end}}
 	{{if .FileSize}}
 		<div class="file-info-entry">
-			{{FileSize .FileSize}}{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}
+			{{ctx.Locale.TrSize .FileSize}}{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}
 		</div>
 	{{end}}
 	{{if .LFSLock}}
diff --git a/templates/repo/issue/view_content/attachments.tmpl b/templates/repo/issue/view_content/attachments.tmpl
index 2155f78656..79085df3ab 100644
--- a/templates/repo/issue/view_content/attachments.tmpl
+++ b/templates/repo/issue/view_content/attachments.tmpl
@@ -19,7 +19,7 @@
 				</a>
 			</div>
 			<div class="tw-p-2 tw-flex tw-items-center">
-				<span class="ui text grey">{{.Size | FileSize}}</span>
+				<span class="ui text grey">{{.Size | ctx.Locale.TrSize}}</span>
 			</div>
 		</div>
 	{{end -}}
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index f3b4bc8443..329dc932fb 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -81,7 +81,7 @@
 											<strong>{{svg "octicon-package" 16 "tw-mr-1"}}{{.Name}}</strong>
 										</a>
 										<div>
-											<span class="text grey">{{.Size | FileSize}}</span>
+											<span class="text grey">{{.Size | ctx.Locale.TrSize}}</span>
 											<span data-tooltip-content="{{ctx.Locale.Tr "repo.release.download_count" (ctx.Locale.PrettyNumber .DownloadCount)}}">
 												{{svg "octicon-info"}}
 											</span>
diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl
index c01f9a421b..b63ad5dcd3 100644
--- a/templates/repo/release/new.tmpl
+++ b/templates/repo/release/new.tmpl
@@ -64,7 +64,7 @@
 						<div class="flex-text-inline tw-flex-1">
 							<input name="attachment-edit-{{.UUID}}"  class="attachment_edit" required value="{{.Name}}">
 							<input name="attachment-del-{{.UUID}}" type="hidden" value="false">
-							<span class="ui text grey tw-whitespace-nowrap">{{.Size | FileSize}}</span>
+							<span class="ui text grey tw-whitespace-nowrap">{{.Size | ctx.Locale.TrSize}}</span>
 							<span data-tooltip-content="{{ctx.Locale.Tr "repo.release.download_count" (ctx.Locale.PrettyNumber .DownloadCount)}}">
 								{{svg "octicon-info"}}
 							</span>
diff --git a/templates/repo/settings/lfs.tmpl b/templates/repo/settings/lfs.tmpl
index e0864ff221..d63de3f53c 100644
--- a/templates/repo/settings/lfs.tmpl
+++ b/templates/repo/settings/lfs.tmpl
@@ -16,7 +16,7 @@
 								{{ShortSha .Oid}}
 							</a>
 						</td>
-						<td>{{FileSize .Size}}</td>
+						<td>{{ctx.Locale.TrSize .Size}}</td>
 						<td>{{TimeSince .CreatedUnix.AsTime ctx.Locale}}</td>
 						<td class="right aligned">
 							<a class="ui primary button" href="{{$.Link}}/find?oid={{.Oid}}&size={{.Size}}">{{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}</a>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 21a5471cb8..8b73afe6d0 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -14,7 +14,7 @@
 				</div>
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.repo_size"}}</label>
-					<span {{if not (eq .Repository.Size 0)}} data-tooltip-content="{{.Repository.SizeDetailsString}}"{{end}}>{{FileSize .Repository.Size}}</span>
+					<span {{if not (eq .Repository.Size 0)}} data-tooltip-content="{{.Repository.SizeDetailsString ctx.Locale}}"{{end}}>{{ctx.Locale.TrSize .Repository.Size}}</span>
 				</div>
 				<div class="inline field">
 					<label>{{ctx.Locale.Tr "repo.template"}}</label>
diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl
index e43353ef04..32e7e97954 100644
--- a/templates/repo/sub_menu.tmpl
+++ b/templates/repo/sub_menu.tmpl
@@ -13,10 +13,9 @@
 					{{svg "octicon-tag"}} {{ctx.Locale.TrN .NumTags "repo.n_tag_one" "repo.n_tag_few" (printf "<b>%d</b>" .NumTags | SafeHTML)}}
 				</a>
 			{{end}}
-			<span class="item not-mobile" {{if not (eq .Repository.Size 0)}}data-tooltip-content="{{.Repository.SizeDetailsString}}"{{end}}>
-				{{$fileSizeFormatted := FileSize .Repository.Size}}{{/* the formatted string is always "{val} {unit}" */}}
-				{{$fileSizeFields := StringUtils.Split $fileSizeFormatted " "}}
-				{{svg "octicon-database"}} <b>{{ctx.Locale.PrettyNumber (index $fileSizeFields 0)}}</b> {{index $fileSizeFields 1}}
+			<span class="item not-mobile" {{if not (eq .Repository.Size 0)}}data-tooltip-content="{{.Repository.SizeDetailsString ctx.Locale}}"{{end}}>
+				{{$fileSizeFields :=  ctx.Locale.TrSize .Repository.Size }}
+				{{svg "octicon-database"}} <b>{{$fileSizeFields.PrettyNumber}}</b> {{$fileSizeFields.TranslatedUnit}}
 			</span>
 		{{end}}
 	</div>
diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl
index c874ccd878..4fd0c37d57 100644
--- a/templates/user/settings/repos.tmpl
+++ b/templates/user/settings/repos.tmpl
@@ -24,7 +24,7 @@
 											<span class="icon">{{svg "octicon-repo"}}</span>
 										{{end}}
 										<a class="muted name" href="{{$repo.Link}}">{{$repo.OwnerName}}/{{$repo.Name}}</a>
-										<span class="text light-3" {{if not (eq $repo.Size 0)}} data-tooltip-content="{{$repo.SizeDetailsString}}"{{end}}>{{FileSize $repo.Size}}</span>
+										<span class="text light-3" {{if not (eq $repo.Size 0)}} data-tooltip-content="{{$repo.SizeDetailsString ctx.Locale}}"{{end}}>{{ctx.Locale.TrSize $repo.Size}}</span>
 										{{if $repo.IsFork}}
 											{{ctx.Locale.Tr "repo.forked_from"}}
 											<span><a href="{{$repo.BaseRepo.Link}}">{{$repo.BaseRepo.OwnerName}}/{{$repo.BaseRepo.Name}}</a></span>
@@ -97,7 +97,7 @@
 										{{svg "octicon-repo" 16 "tw-mr-1 iconFloat"}}
 									{{end}}
 									<a class="name" href="{{.Link}}">{{.OwnerName}}/{{.Name}}</a>
-									<span>{{FileSize .Size}}</span>
+									<span>{{ctx.Locale.TrSize .Size}}</span>
 									{{if .IsFork}}
 										{{ctx.Locale.Tr "repo.forked_from"}}
 										<span><a href="{{.BaseRepo.Link}}">{{.BaseRepo.OwnerName}}/{{.BaseRepo.Name}}</a></span>