mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Added user language setting (#3875)
* Added user language setting * Added translation string for setting * Fixed import order + typo * improved checking if the user has a language saved in the db * The current saved language is now set a default inside the dropdown * fmt * When a user signs in and doesn't have a language saved, the current browser language is saved * updated gitea-sdk * Merge branch 'master' of https://github.com/go-gitea/gitea into save-user-language # Conflicts: # models/migrations/migrations.go # models/migrations/v62.go * Made tests work again * trigger CI * trigger CI * fmt * re-trigger that FUCKING CI SO IT REALLY PICKS UP THE LATEST COMMIT ISTEAD OF PREDENDING TO DO SO * re-trigger that FUCKING CI SO IT REALLY PICKS UP THE LATEST COMMIT ISTEAD OF PREDENDING TO DO SO * When loggin in, only the language col gets updated instead of everything
This commit is contained in:
		@@ -30,6 +30,7 @@ func TestRenameUsername(t *testing.T) {
 | 
			
		||||
		"_csrf":    GetCSRF(t, session, "/user/settings"),
 | 
			
		||||
		"name":     "newUsername",
 | 
			
		||||
		"email":    "user2@example.com",
 | 
			
		||||
		"language": "en-us",
 | 
			
		||||
	})
 | 
			
		||||
	session.MakeRequest(t, req, http.StatusFound)
 | 
			
		||||
 | 
			
		||||
@@ -84,6 +85,7 @@ func TestRenameReservedUsername(t *testing.T) {
 | 
			
		||||
			"_csrf":    GetCSRF(t, session, "/user/settings"),
 | 
			
		||||
			"name":     reservedUsername,
 | 
			
		||||
			"email":    "user2@example.com",
 | 
			
		||||
			"language": "en-us",
 | 
			
		||||
		})
 | 
			
		||||
		resp := session.MakeRequest(t, req, http.StatusFound)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ func TestXSSUserFullName(t *testing.T) {
 | 
			
		||||
		"name":      user.Name,
 | 
			
		||||
		"full_name": fullName,
 | 
			
		||||
		"email":     user.Email,
 | 
			
		||||
		"language":  "en-us",
 | 
			
		||||
	})
 | 
			
		||||
	session.MakeRequest(t, req, http.StatusFound)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -178,6 +178,8 @@ var migrations = []Migration{
 | 
			
		||||
	NewMigration("add size column for attachments", addSizeToAttachment),
 | 
			
		||||
	// v62 -> v63
 | 
			
		||||
	NewMigration("add last used passcode column for TOTP", addLastUsedPasscodeTOTP),
 | 
			
		||||
	// v63 -> v64
 | 
			
		||||
	NewMigration("add language column for user setting", addLanguageSetting),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Migrate database to current version
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								models/migrations/v63.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								models/migrations/v63.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
// Copyright 2018 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-xorm/xorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func addLanguageSetting(x *xorm.Engine) error {
 | 
			
		||||
	type User struct {
 | 
			
		||||
		Language string `xorm:"VARCHAR(5)"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := x.Sync2(new(User)); err != nil {
 | 
			
		||||
		return fmt.Errorf("Sync2: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -94,6 +94,7 @@ type User struct {
 | 
			
		||||
	Website          string
 | 
			
		||||
	Rands            string `xorm:"VARCHAR(10)"`
 | 
			
		||||
	Salt             string `xorm:"VARCHAR(10)"`
 | 
			
		||||
	Language         string `xorm:"VARCHAR(5)"`
 | 
			
		||||
 | 
			
		||||
	CreatedUnix   util.TimeStamp `xorm:"INDEX created"`
 | 
			
		||||
	UpdatedUnix   util.TimeStamp `xorm:"INDEX updated"`
 | 
			
		||||
@@ -185,6 +186,7 @@ func (u *User) APIFormat() *api.User {
 | 
			
		||||
		FullName:  u.FullName,
 | 
			
		||||
		Email:     u.getEmail(),
 | 
			
		||||
		AvatarURL: u.AvatarLink(),
 | 
			
		||||
		Language:  u.Language,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,7 @@ type UpdateProfileForm struct {
 | 
			
		||||
	KeepEmailPrivate bool
 | 
			
		||||
	Website          string `binding:"ValidUrl;MaxSize(255)"`
 | 
			
		||||
	Location         string `binding:"MaxSize(50)"`
 | 
			
		||||
	Language         string `binding:"Size(5)"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates the fields
 | 
			
		||||
 
 | 
			
		||||
@@ -331,6 +331,7 @@ change_username = Your username has been changed.
 | 
			
		||||
change_username_prompt = Note: username changes also change your account URL.
 | 
			
		||||
continue = Continue
 | 
			
		||||
cancel = Cancel
 | 
			
		||||
language = Language
 | 
			
		||||
 | 
			
		||||
lookup_avatar_by_mail = Look Up Avatar by Email Address
 | 
			
		||||
federated_avatar_lookup = Federated Avatar Lookup
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								public/swagger.v1.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								public/swagger.v1.json
									
									
									
									
										vendored
									
									
								
							@@ -7318,6 +7318,11 @@
 | 
			
		||||
          "format": "int64",
 | 
			
		||||
          "x-go-name": "ID"
 | 
			
		||||
        },
 | 
			
		||||
        "language": {
 | 
			
		||||
          "description": "User locale",
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "x-go-name": "Language"
 | 
			
		||||
        },
 | 
			
		||||
        "login": {
 | 
			
		||||
          "description": "the user's username",
 | 
			
		||||
          "type": "string",
 | 
			
		||||
 
 | 
			
		||||
@@ -339,6 +339,18 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
 | 
			
		||||
	ctx.Session.Set("uid", u.ID)
 | 
			
		||||
	ctx.Session.Set("uname", u.Name)
 | 
			
		||||
 | 
			
		||||
	// Language setting of the user overwrites the one previously set
 | 
			
		||||
	// If the user does not have a locale set, we save the current one.
 | 
			
		||||
	if len(u.Language) == 0 {
 | 
			
		||||
		u.Language = ctx.Locale.Language()
 | 
			
		||||
		if err := models.UpdateUserCols(u, "language"); err != nil {
 | 
			
		||||
			log.Error(4, fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.SetCookie("lang", u.Language, nil, setting.AppSubURL)
 | 
			
		||||
 | 
			
		||||
	// Clear whatever CSRF has right now, force to generate a new one
 | 
			
		||||
	ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL)
 | 
			
		||||
 | 
			
		||||
@@ -704,6 +716,7 @@ func SignOut(ctx *context.Context) {
 | 
			
		||||
	ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL)
 | 
			
		||||
	ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL)
 | 
			
		||||
	ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL)
 | 
			
		||||
	ctx.SetCookie("lang", "", -1, setting.AppSubURL) // Setting the lang cookie will trigger the middleware to reset the language ot previous state.
 | 
			
		||||
	ctx.Redirect(setting.AppSubURL + "/")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/Unknwon/com"
 | 
			
		||||
	"github.com/Unknwon/i18n"
 | 
			
		||||
	"github.com/pquerna/otp"
 | 
			
		||||
	"github.com/pquerna/otp/totp"
 | 
			
		||||
 | 
			
		||||
@@ -105,6 +106,7 @@ func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
 | 
			
		||||
	ctx.User.KeepEmailPrivate = form.KeepEmailPrivate
 | 
			
		||||
	ctx.User.Website = form.Website
 | 
			
		||||
	ctx.User.Location = form.Location
 | 
			
		||||
	ctx.User.Language = form.Language
 | 
			
		||||
	if err := models.UpdateUserSetting(ctx.User); err != nil {
 | 
			
		||||
		if _, ok := err.(models.ErrEmailAlreadyUsed); ok {
 | 
			
		||||
			ctx.Flash.Error(ctx.Tr("form.email_been_used"))
 | 
			
		||||
@@ -115,8 +117,11 @@ func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update the language to the one we just set
 | 
			
		||||
	ctx.SetCookie("lang", ctx.User.Language, nil, setting.AppSubURL)
 | 
			
		||||
 | 
			
		||||
	log.Trace("User settings updated: %s", ctx.User.Name)
 | 
			
		||||
	ctx.Flash.Success(ctx.Tr("settings.update_profile_success"))
 | 
			
		||||
	ctx.Flash.Success(i18n.Tr(ctx.User.Language, "settings.update_profile_success"))
 | 
			
		||||
	ctx.Redirect(setting.AppSubURL + "/user/settings")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,20 @@
 | 
			
		||||
					<input id="location" name="location"  value="{{.SignedUser.Location}}">
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
					<div class="field">
 | 
			
		||||
						<label for="language">{{.i18n.Tr "settings.language"}}</label>
 | 
			
		||||
						<div class="ui language selection dropdown" id="language">
 | 
			
		||||
							<input name="language" type="hidden">
 | 
			
		||||
							<i class="dropdown icon"></i>
 | 
			
		||||
							<div class="text">{{range .AllLangs}}{{if eq $.SignedUser.Language .Lang}}{{.Name}}{{end}}{{end}}</div>
 | 
			
		||||
							<div class="menu">
 | 
			
		||||
							{{range .AllLangs}}
 | 
			
		||||
								<div class="item{{if eq $.SignedUser.Language .Lang}} active selected{{end}}" data-value="{{.Lang}}">{{.Name}}</div>
 | 
			
		||||
							{{end}}
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
 | 
			
		||||
				<div class="field">
 | 
			
		||||
					<button class="ui green button">{{$.i18n.Tr "settings.update_profile"}}</button>
 | 
			
		||||
				</div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/code.gitea.io/sdk/gitea/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/code.gitea.io/sdk/gitea/user.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -22,6 +22,8 @@ type User struct {
 | 
			
		||||
	Email string `json:"email"`
 | 
			
		||||
	// URL to the user's avatar
 | 
			
		||||
	AvatarURL string `json:"avatar_url"`
 | 
			
		||||
	// User locale
 | 
			
		||||
	Language string `json:"language"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements the json.Marshaler interface for User, adding field(s) for backward compatibility
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							@@ -9,10 +9,10 @@
 | 
			
		||||
			"revisionTime": "2018-04-21T01:08:19Z"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"checksumSHA1": "xXzi8Xx7HA3M0z3lR/1wr1Vz1fc=",
 | 
			
		||||
			"checksumSHA1": "WMD6+Qh2+5hd9uiq910pF/Ihylw=",
 | 
			
		||||
			"path": "code.gitea.io/sdk/gitea",
 | 
			
		||||
			"revision": "142acef5ce79f78585afcce31748af46c72a3dea",
 | 
			
		||||
			"revisionTime": "2018-04-17T00:54:29Z"
 | 
			
		||||
			"revision": "1c8d12f79a51605ed91587aa6b86cf38fc0f987f",
 | 
			
		||||
			"revisionTime": "2018-05-01T11:15:19Z"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"checksumSHA1": "bOODD4Gbw3GfcuQPU2dI40crxxk=",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user