diff --git a/.golangci.yml b/.golangci.yml
index 8e31d0cbc..4ad9c9d4c 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -162,6 +162,3 @@ issues:
     - path: models/user/openid.go
       linters:
         - golint
-    - linters:
-        - staticcheck
-      text: "strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead."
diff --git a/Makefile b/Makefile
index ab112584c..570c44c76 100644
--- a/Makefile
+++ b/Makefile
@@ -29,8 +29,8 @@ XGO_VERSION := go-1.18.x
 AIR_PACKAGE ?= github.com/cosmtrek/air@v1.29.0
 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.4.0
 ERRCHECK_PACKAGE ?= github.com/kisielk/errcheck@v1.6.0
-GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.0
-GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.44.2
+GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.1
+GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.0
 GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
 MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.29.0
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index 1436d764f..b3ce80917 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -43,7 +43,7 @@ var defaultTransformers = []transformer{
 	{Name: "PASCAL", Transform: xstrings.ToCamelCase},
 	{Name: "LOWER", Transform: strings.ToLower},
 	{Name: "UPPER", Transform: strings.ToUpper},
-	{Name: "TITLE", Transform: strings.Title},
+	{Name: "TITLE", Transform: util.ToTitleCase},
 }
 
 func generateExpansion(src string, templateRepo, generateRepo *repo_model.Repository) string {
diff --git a/modules/setting/log.go b/modules/setting/log.go
index 008a419b0..d69b5c688 100644
--- a/modules/setting/log.go
+++ b/modules/setting/log.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/util"
 
 	ini "gopkg.in/ini.v1"
 )
@@ -245,7 +246,7 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
 			Provider: provider,
 			Config:   config,
 		})
-		log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName)
+		log.Info("%s Log: %s(%s:%s)", util.ToTitleCase(key), util.ToTitleCase(name), provider, levelName)
 	}
 
 	AddLogDescription(key, &description)
@@ -331,7 +332,7 @@ func newLogService() {
 			Provider: provider,
 			Config:   config,
 		})
-		log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName)
+		log.Info("Gitea Log Mode: %s(%s:%s)", util.ToTitleCase(name), util.ToTitleCase(provider), levelName)
 	}
 
 	AddLogDescription(log.DEFAULT, &description)
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 682459d94..1ee9cb00e 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -52,7 +52,7 @@ var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`)
 func NewFuncMap() []template.FuncMap {
 	return []template.FuncMap{map[string]interface{}{
 		"GoVer": func() string {
-			return strings.Title(runtime.Version())
+			return util.ToTitleCase(runtime.Version())
 		},
 		"UseHTTPS": func() bool {
 			return strings.HasPrefix(setting.AppURL, "https")
@@ -398,7 +398,7 @@ func NewFuncMap() []template.FuncMap {
 func NewTextFuncMap() []texttmpl.FuncMap {
 	return []texttmpl.FuncMap{map[string]interface{}{
 		"GoVer": func() string {
-			return strings.Title(runtime.Version())
+			return util.ToTitleCase(runtime.Version())
 		},
 		"AppName": func() string {
 			return setting.AppName
diff --git a/modules/util/util.go b/modules/util/util.go
index af6581f7c..351a34547 100644
--- a/modules/util/util.go
+++ b/modules/util/util.go
@@ -11,6 +11,9 @@ import (
 	"math/big"
 	"strconv"
 	"strings"
+
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
 )
 
 // OptionalBool a boolean that can be "null"
@@ -181,3 +184,10 @@ func ToUpperASCII(s string) string {
 	}
 	return string(b)
 }
+
+var titleCaser = cases.Title(language.English)
+
+// ToTitleCase returns s with all english words capitalized
+func ToTitleCase(s string) string {
+	return titleCaser.String(s)
+}
diff --git a/modules/util/util_test.go b/modules/util/util_test.go
index 0c2792a9c..ca5bd87ea 100644
--- a/modules/util/util_test.go
+++ b/modules/util/util_test.go
@@ -220,3 +220,8 @@ func BenchmarkToUpper(b *testing.B) {
 		})
 	}
 }
+
+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`)
+}
diff --git a/routers/init.go b/routers/init.go
index 759945ce2..603649936 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -10,7 +10,6 @@ import (
 	"reflect"
 	"runtime"
 	"strconv"
-	"strings"
 
 	"code.gitea.io/gitea/models"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -31,6 +30,7 @@ import (
 	"code.gitea.io/gitea/modules/storage"
 	"code.gitea.io/gitea/modules/svg"
 	"code.gitea.io/gitea/modules/translation"
+	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
 	packages_router "code.gitea.io/gitea/routers/api/packages"
 	apiv1 "code.gitea.io/gitea/routers/api/v1"
@@ -111,7 +111,7 @@ func GlobalInitInstalled(ctx context.Context) {
 	log.Info("Custom path: %s", setting.CustomPath)
 	log.Info("Log path: %s", setting.LogRootPath)
 	log.Info("Configuration file: %s", setting.CustomConf)
-	log.Info("Run Mode: %s", strings.Title(setting.RunMode))
+	log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode))
 
 	// Setup i18n
 	translation.InitLocales()
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index d4093f204..78347e67c 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -26,6 +26,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/updatechecker"
+	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/services/cron"
 	"code.gitea.io/gitea/services/forms"
@@ -245,7 +246,7 @@ func Config(ctx *context.Context) {
 	ctx.Data["OfflineMode"] = setting.OfflineMode
 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
 	ctx.Data["RunUser"] = setting.RunUser
-	ctx.Data["RunMode"] = strings.Title(setting.RunMode)
+	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
 	if version, err := git.LocalVersion(); err == nil {
 		ctx.Data["GitVersion"] = version.Original()
 	}
diff --git a/services/migrations/codebase_test.go b/services/migrations/codebase_test.go
index cb70a2bf7..03b5946d7 100644
--- a/services/migrations/codebase_test.go
+++ b/services/migrations/codebase_test.go
@@ -6,7 +6,6 @@ package migrations
 
 import (
 	"context"
-	"fmt"
 	"net/url"
 	"os"
 	"testing"
@@ -40,7 +39,7 @@ func TestCodebaseDownloadRepo(t *testing.T) {
 		AuthPassword: apiPassword,
 	})
 	if err != nil {
-		t.Fatal(fmt.Sprintf("Error creating Codebase downloader: %v", err))
+		t.Fatalf("Error creating Codebase downloader: %v", err)
 	}
 	repo, err := downloader.GetRepoInfo()
 	assert.NoError(t, err)
diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go
index e63d67418..829964b38 100644
--- a/services/migrations/gitlab_test.go
+++ b/services/migrations/gitlab_test.go
@@ -34,7 +34,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
 
 	downloader, err := NewGitlabDownloader(context.Background(), "https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
 	if err != nil {
-		t.Fatal(fmt.Sprintf("NewGitlabDownloader is nil: %v", err))
+		t.Fatalf("NewGitlabDownloader is nil: %v", err)
 	}
 	repo, err := downloader.GetRepoInfo()
 	assert.NoError(t, err)
diff --git a/services/migrations/onedev_test.go b/services/migrations/onedev_test.go
index 0cf1ab852..6a17eb334 100644
--- a/services/migrations/onedev_test.go
+++ b/services/migrations/onedev_test.go
@@ -6,7 +6,6 @@ package migrations
 
 import (
 	"context"
-	"fmt"
 	"net/http"
 	"net/url"
 	"testing"
@@ -26,7 +25,7 @@ func TestOneDevDownloadRepo(t *testing.T) {
 	u, _ := url.Parse("https://code.onedev.io")
 	downloader := NewOneDevDownloader(context.Background(), u, "", "", "go-gitea-test_repo")
 	if err != nil {
-		t.Fatal(fmt.Sprintf("NewOneDevDownloader is nil: %v", err))
+		t.Fatalf("NewOneDevDownloader is nil: %v", err)
 	}
 	repo, err := downloader.GetRepoInfo()
 	assert.NoError(t, err)