feat(i18n): allow different translations of creation links and titles (#4829)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4829
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
0ko 2024-08-07 16:54:05 +00:00
parent 690b63fc74
commit bad3b32037
13 changed files with 115 additions and 15 deletions

View file

@ -55,11 +55,8 @@ webauthn_error_timeout = Timeout reached before your key could be read. Please r
repository = Repository repository = Repository
organization = Organization organization = Organization
mirror = Mirror mirror = Mirror
new_repo = New repository
new_migrate = New migration
new_mirror = New mirror new_mirror = New mirror
new_fork = New repository fork new_fork = New repository fork
new_org = New organization
new_project = New project new_project = New project
new_project_column = New column new_project_column = New column
admin_panel = Site administration admin_panel = Site administration
@ -68,6 +65,14 @@ your_profile = Profile
your_starred = Starred your_starred = Starred
your_settings = Settings your_settings = Settings
new_repo.title = New repository
new_migrate.title = New migration
new_org.title = New organization
new_repo.link = New repository
new_migrate.link = New migration
new_org.link = New organization
all = All all = All
sources = Sources sources = Sources
mirrors = Mirrors mirrors = Mirrors

View file

@ -26,7 +26,7 @@ const (
// Create render the page for create organization // Create render the page for create organization
func Create(ctx *context.Context) { func Create(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_org") ctx.Data["Title"] = ctx.Tr("new_org.title")
ctx.Data["DefaultOrgVisibilityMode"] = setting.Service.DefaultOrgVisibilityMode ctx.Data["DefaultOrgVisibilityMode"] = setting.Service.DefaultOrgVisibilityMode
if !ctx.Doer.CanCreateOrganization() { if !ctx.Doer.CanCreateOrganization() {
ctx.ServerError("Not allowed", errors.New(ctx.Locale.TrString("org.form.create_org_not_allowed"))) ctx.ServerError("Not allowed", errors.New(ctx.Locale.TrString("org.form.create_org_not_allowed")))
@ -38,7 +38,7 @@ func Create(ctx *context.Context) {
// CreatePost response for create organization // CreatePost response for create organization
func CreatePost(ctx *context.Context) { func CreatePost(ctx *context.Context) {
form := *web.GetForm(ctx).(*forms.CreateOrgForm) form := *web.GetForm(ctx).(*forms.CreateOrgForm)
ctx.Data["Title"] = ctx.Tr("new_org") ctx.Data["Title"] = ctx.Tr("new_org.title")
if !ctx.Doer.CanCreateOrganization() { if !ctx.Doer.CanCreateOrganization() {
ctx.ServerError("Not allowed", errors.New(ctx.Locale.TrString("org.form.create_org_not_allowed"))) ctx.ServerError("Not allowed", errors.New(ctx.Locale.TrString("org.form.create_org_not_allowed")))

View file

@ -253,7 +253,7 @@ func MigratePost(ctx *context.Context) {
} }
func setMigrationContextData(ctx *context.Context, serviceType structs.GitServiceType) { func setMigrationContextData(ctx *context.Context, serviceType structs.GitServiceType) {
ctx.Data["Title"] = ctx.Tr("new_migrate") ctx.Data["Title"] = ctx.Tr("new_migrate.title")
ctx.Data["LFSActive"] = setting.LFS.StartServer ctx.Data["LFSActive"] = setting.LFS.StartServer
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate

View file

@ -152,7 +152,7 @@ func getRepoPrivate(ctx *context.Context) bool {
// Create render creating repository page // Create render creating repository page
func Create(ctx *context.Context) { func Create(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_repo") ctx.Data["Title"] = ctx.Tr("new_repo.title")
// Give default value for template to render. // Give default value for template to render.
ctx.Data["Gitignores"] = repo_module.Gitignores ctx.Data["Gitignores"] = repo_module.Gitignores
@ -223,7 +223,7 @@ func handleCreateError(ctx *context.Context, owner *user_model.User, err error,
// CreatePost response for creating repository // CreatePost response for creating repository
func CreatePost(ctx *context.Context) { func CreatePost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateRepoForm) form := web.GetForm(ctx).(*forms.CreateRepoForm)
ctx.Data["Title"] = ctx.Tr("new_repo") ctx.Data["Title"] = ctx.Tr("new_repo.title")
ctx.Data["Gitignores"] = repo_module.Gitignores ctx.Data["Gitignores"] = repo_module.Gitignores
ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles

View file

@ -126,16 +126,16 @@
</span> </span>
<div class="menu"> <div class="menu">
<a class="item" href="{{AppSubUrl}}/repo/create"> <a class="item" href="{{AppSubUrl}}/repo/create">
{{svg "octicon-plus"}} {{ctx.Locale.Tr "new_repo"}} {{svg "octicon-plus"}} {{ctx.Locale.Tr "new_repo.link"}}
</a> </a>
{{if not .DisableMigrations}} {{if not .DisableMigrations}}
<a class="item" href="{{AppSubUrl}}/repo/migrate"> <a class="item" href="{{AppSubUrl}}/repo/migrate">
{{svg "octicon-repo-push"}} {{ctx.Locale.Tr "new_migrate"}} {{svg "octicon-repo-push"}} {{ctx.Locale.Tr "new_migrate.link"}}
</a> </a>
{{end}} {{end}}
{{if .SignedUser.CanCreateOrganization}} {{if .SignedUser.CanCreateOrganization}}
<a class="item" href="{{AppSubUrl}}/org/create"> <a class="item" href="{{AppSubUrl}}/org/create">
{{svg "octicon-organization"}} {{ctx.Locale.Tr "new_org"}} {{svg "octicon-organization"}} {{ctx.Locale.Tr "new_org.link"}}
</a> </a>
{{end}} {{end}}
</div><!-- end content create new menu --> </div><!-- end content create new menu -->

View file

@ -5,7 +5,7 @@
<form class="ui form" action="{{.Link}}" method="post"> <form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<h3 class="ui top attached header"> <h3 class="ui top attached header">
{{ctx.Locale.Tr "new_org"}} {{ctx.Locale.Tr "new_org.title"}}
</h3> </h3>
<div class="ui attached segment"> <div class="ui attached segment">
{{template "base/alert" .}} {{template "base/alert" .}}

View file

@ -22,9 +22,9 @@
<div class="ui five wide column"> <div class="ui five wide column">
{{if .CanCreateOrgRepo}} {{if .CanCreateOrgRepo}}
<div class="center aligned"> <div class="center aligned">
<a class="ui primary button tw-mb-1" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{ctx.Locale.Tr "new_repo"}}</a> <a class="ui primary button tw-mb-1" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{ctx.Locale.Tr "new_repo.link"}}</a>
{{if not .DisableNewPullMirrors}} {{if not .DisableNewPullMirrors}}
<a class="ui primary button tw-mb-1" href="{{AppSubUrl}}/repo/migrate?org={{.Org.ID}}&mirror=1">{{ctx.Locale.Tr "new_migrate"}}</a> <a class="ui primary button tw-mb-1" href="{{AppSubUrl}}/repo/migrate?org={{.Org.ID}}&mirror=1">{{ctx.Locale.Tr "new_migrate.link"}}</a>
{{end}} {{end}}
</div> </div>
<div class="divider"></div> <div class="divider"></div>

View file

@ -5,7 +5,7 @@
<form class="ui form" action="{{.Link}}" method="post"> <form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<h3 class="ui top attached header"> <h3 class="ui top attached header">
{{ctx.Locale.Tr "new_repo"}} {{ctx.Locale.Tr "new_repo.title"}}
</h3> </h3>
<div class="ui attached segment"> <div class="ui attached segment">
{{template "base/alert" .}} {{template "base/alert" .}}

View file

@ -1,4 +1,5 @@
// Copyright 2021 The Gitea Authors. All rights reserved. // Copyright 2021 The Gitea Authors. All rights reserved.
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package integration package integration
@ -20,6 +21,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/services/migrations" "code.gitea.io/gitea/services/migrations"
"code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/services/repository"
@ -88,6 +90,10 @@ func TestMigrate(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
// Step 2: load the form // Step 2: load the form
htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc := NewHTMLParser(t, resp.Body)
// Check form title
title := htmlDoc.doc.Find("title").Text()
assert.Contains(t, title, translation.NewLocale("en-US").TrString("new_migrate.title"))
// Get the link of migration button
link, exists := htmlDoc.doc.Find(`form.ui.form[action^="/repo/migrate"]`).Attr("action") link, exists := htmlDoc.doc.Find(`form.ui.form[action^="/repo/migrate"]`).Attr("action")
assert.True(t, exists, "The template has changed") assert.True(t, exists, "The template has changed")
// Step 4: submit the migration to only migrate issues // Step 4: submit the migration to only migrate issues

View file

@ -0,0 +1,37 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"net/http"
"net/url"
"strings"
"testing"
"code.gitea.io/gitea/modules/translation"
"github.com/stretchr/testify/assert"
)
func TestNewOrganizationForm(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
locale := translation.NewLocale("en-US")
response := session.MakeRequest(t, NewRequest(t, "GET", "/org/create"), http.StatusOK)
page := NewHTMLParser(t, response.Body)
// Verify page title
title := page.Find("title").Text()
assert.Contains(t, title, locale.TrString("new_org.title"))
// Verify page form
_, exists := page.Find("form[action='/org/create']").Attr("method")
assert.True(t, exists)
// Verify page header
header := strings.TrimSpace(page.Find(".form[action='/org/create'] .header").Text())
assert.EqualValues(t, locale.TrString("new_org.title"), header)
})
}

View file

@ -5,9 +5,13 @@ package integration
import ( import (
"net/http" "net/http"
"strings"
"testing" "testing"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
) )
// This test makes sure that organization members are able to navigate between `/<orgname>` and `/org/<orgname>/<section>` freely. // This test makes sure that organization members are able to navigate between `/<orgname>` and `/org/<orgname>/<section>` freely.
@ -19,6 +23,8 @@ import (
func TestOrgNavigationDashboard(t *testing.T) { func TestOrgNavigationDashboard(t *testing.T) {
defer tests.PrepareTestEnv(t)() defer tests.PrepareTestEnv(t)()
locale := translation.NewLocale("en-US")
// Login as the future organization admin and create an organization // Login as the future organization admin and create an organization
session1 := loginUser(t, "user2") session1 := loginUser(t, "user2")
session1.MakeRequest(t, NewRequestWithValues(t, "POST", "/org/create", map[string]string{ session1.MakeRequest(t, NewRequestWithValues(t, "POST", "/org/create", map[string]string{
@ -33,6 +39,11 @@ func TestOrgNavigationDashboard(t *testing.T) {
doc := NewHTMLParser(t, resp.Body) doc := NewHTMLParser(t, resp.Body)
doc.AssertElement(t, "#org-info a[href='/org/org_navigation_test/dashboard']", true) doc.AssertElement(t, "#org-info a[href='/org/org_navigation_test/dashboard']", true)
// Verify the "New repository" and "New migration" buttons
links := doc.Find(".organization.profile .grid .column .center")
assert.EqualValues(t, locale.TrString("new_repo.link"), strings.TrimSpace(links.Find("a[href^='/repo/create?org=']").Text()))
assert.EqualValues(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href^='/repo/migrate?org=']").Text()))
// Check if the "View <orgname>" button is available on dashboard for the org admin (member) // Check if the "View <orgname>" button is available on dashboard for the org admin (member)
resp = session1.MakeRequest(t, NewRequest(t, "GET", "/org/org_navigation_test/dashboard"), http.StatusOK) resp = session1.MakeRequest(t, NewRequest(t, "GET", "/org/org_navigation_test/dashboard"), http.StatusOK)
doc = NewHTMLParser(t, resp.Body) doc = NewHTMLParser(t, resp.Body)

View file

@ -1,4 +1,5 @@
// Copyright 2019 The Gitea Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved.
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package integration package integration
@ -13,6 +14,7 @@ import (
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -21,6 +23,15 @@ import (
func assertRepoCreateForm(t *testing.T, htmlDoc *HTMLDoc, owner *user_model.User, templateID string) { func assertRepoCreateForm(t *testing.T, htmlDoc *HTMLDoc, owner *user_model.User, templateID string) {
_, exists := htmlDoc.doc.Find("form.ui.form[action^='/repo/create']").Attr("action") _, exists := htmlDoc.doc.Find("form.ui.form[action^='/repo/create']").Attr("action")
assert.True(t, exists, "Expected the repo creation form") assert.True(t, exists, "Expected the repo creation form")
locale := translation.NewLocale("en-US")
// Verify page title
title := htmlDoc.doc.Find("title").Text()
assert.Contains(t, title, locale.TrString("new_repo.title"))
// Verify form header
header := strings.TrimSpace(htmlDoc.doc.Find(".form[action='/repo/create'] .header").Text())
assert.EqualValues(t, locale.TrString("new_repo.title"), header)
htmlDoc.AssertDropdownHasSelectedOption(t, "uid", strconv.FormatInt(owner.ID, 10)) htmlDoc.AssertDropdownHasSelectedOption(t, "uid", strconv.FormatInt(owner.ID, 10))

View file

@ -0,0 +1,30 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"net/http"
"strings"
"testing"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/translation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUserDashboardActionLinks(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
session := loginUser(t, "user1")
locale := translation.NewLocale("en-US")
response := session.MakeRequest(t, NewRequest(t, "GET", "/"), http.StatusOK)
page := NewHTMLParser(t, response.Body)
links := page.Find("#navbar .dropdown[data-tooltip-content='Create…'] .menu")
assert.EqualValues(t, locale.TrString("new_repo.link"), strings.TrimSpace(links.Find("a[href='/repo/create']").Text()))
assert.EqualValues(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href='/repo/migrate']").Text()))
assert.EqualValues(t, locale.TrString("new_org.link"), strings.TrimSpace(links.Find("a[href='/org/create']").Text()))
}