feat(i18n): automatically set language during registration
This commit is contained in:
parent
4e90c6bb78
commit
c6cade3aeb
|
|
@ -37,6 +37,7 @@ export const SUPPORTED_LOCALES = {
|
|||
'bg-BG': 'Български',
|
||||
'ko-KR': '한국어',
|
||||
// IMPORTANT: Also add new languages to useDayjsLanguageSync
|
||||
// IMPORTANT: Also add new languages to pkg/i18n/i18n.go
|
||||
} as const
|
||||
|
||||
export type SupportedLocale = keyof typeof SUPPORTED_LOCALES
|
||||
|
|
|
|||
|
|
@ -178,13 +178,25 @@ export const useAuthStore = defineStore('auth', () => {
|
|||
* Registers a new user and logs them in.
|
||||
* Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited.
|
||||
*/
|
||||
async function register(credentials) {
|
||||
async function register(credentials, language: string|null = null) {
|
||||
const HTTP = HTTPFactory()
|
||||
setIsLoading(true)
|
||||
|
||||
if (!language) {
|
||||
language = i18n.global.locale.value ?? getBrowserLanguage()
|
||||
}
|
||||
|
||||
try {
|
||||
await HTTP.post('register', credentials)
|
||||
await HTTP.post('register', {
|
||||
...credentials,
|
||||
language,
|
||||
})
|
||||
return login(credentials)
|
||||
} catch (e) {
|
||||
if (e.response?.data?.code === 2002 && e.response?.data?.invalid_fields[0]?.startsWith('language:')) {
|
||||
return register(credentials, 'en')
|
||||
}
|
||||
|
||||
if (e.response?.data?.message) {
|
||||
throw e.response.data
|
||||
}
|
||||
|
|
@ -302,23 +314,6 @@ export const useAuthStore = defineStore('auth', () => {
|
|||
setUser(newUser)
|
||||
updateLastUserRefresh()
|
||||
|
||||
if (
|
||||
newUser.type === AUTH_TYPES.USER &&
|
||||
(
|
||||
typeof newUser.settings.language === 'undefined' ||
|
||||
newUser.settings.language === ''
|
||||
)
|
||||
) {
|
||||
// save current language
|
||||
await saveUserSettings({
|
||||
settings: {
|
||||
...settings.value,
|
||||
language: settings.value.language ? settings.value.language : getBrowserLanguage(),
|
||||
},
|
||||
showMessage: false,
|
||||
})
|
||||
}
|
||||
|
||||
return newUser
|
||||
} catch (e) {
|
||||
if((e?.response?.status >= 400 && e?.response?.status < 500) ||
|
||||
|
|
@ -327,8 +322,6 @@ export const useAuthStore = defineStore('auth', () => {
|
|||
return
|
||||
}
|
||||
|
||||
console.log('continuerd')
|
||||
|
||||
const cause = {e}
|
||||
|
||||
if (typeof e?.response?.data?.message !== 'undefined') {
|
||||
|
|
|
|||
|
|
@ -41,12 +41,40 @@ type Translator struct {
|
|||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// singleton instance
|
||||
var translator = &Translator{
|
||||
translations: make(map[string]TranslationStore),
|
||||
fallbackLang: "en",
|
||||
}
|
||||
|
||||
var availableLanguages = map[string]bool{
|
||||
"en": true,
|
||||
"de-DE": true,
|
||||
"de-swiss": true,
|
||||
"ru-RU": true,
|
||||
"fr-FR": true,
|
||||
"vi-VN": true,
|
||||
"it-IT": true,
|
||||
"cs-CZ": true,
|
||||
"pl-PL": true,
|
||||
"nl-NL": true,
|
||||
"pt-PT": true,
|
||||
"zh-CN": true,
|
||||
"no-NO": true,
|
||||
"es-ES": true,
|
||||
"da-DK": true,
|
||||
"ja-JP": true,
|
||||
"hu-HU": true,
|
||||
"ar-SA": true,
|
||||
"sl-SI": true,
|
||||
"pt-BR": true,
|
||||
"hr-HR": true,
|
||||
"uk-UA": true,
|
||||
"lt-LT": true,
|
||||
"bg-BG": true,
|
||||
"ko-KR": true,
|
||||
// IMPORTANT: Also add new languages to the frontend
|
||||
}
|
||||
|
||||
// Init initializes the global translator with translation files
|
||||
func Init() {
|
||||
dir := "lang"
|
||||
|
|
@ -61,6 +89,11 @@ func Init() {
|
|||
}
|
||||
|
||||
langCode := strings.TrimSuffix(entry.Name(), ".json")
|
||||
|
||||
if !availableLanguages[langCode] {
|
||||
continue
|
||||
}
|
||||
|
||||
filePath := filepath.Join(dir, entry.Name())
|
||||
|
||||
err = translator.loadFile(localeFS, langCode, filePath)
|
||||
|
|
@ -166,3 +199,8 @@ func stringSliceToInterfaceSlice(strings []string) []interface{} {
|
|||
}
|
||||
return interfaces
|
||||
}
|
||||
|
||||
func HasLanguage(lang string) bool {
|
||||
_, exists := translator.translations[lang]
|
||||
return exists
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,22 +20,28 @@ import (
|
|||
"errors"
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/api/pkg/web/handler"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type UserRegister struct {
|
||||
// The language of the new user. Must be a valid IETF BCP 47 language code and exist in Vikunja.
|
||||
Language string `json:"language" valid:"language"`
|
||||
user.APIUserPassword
|
||||
}
|
||||
|
||||
// RegisterUser is the register handler
|
||||
// @Summary Register
|
||||
// @Description Creates a new user account.
|
||||
// @tags auth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param credentials body user.APIUserPassword true "The user credentials"
|
||||
// @Param credentials body v1.UserRegister true "The user with credentials to create"
|
||||
// @Success 200 {object} user.User
|
||||
// @Failure 400 {object} web.HTTPError "No or invalid user register object provided / User already exists."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
|
|
@ -45,7 +51,7 @@ func RegisterUser(c echo.Context) error {
|
|||
return echo.ErrNotFound
|
||||
}
|
||||
// Check for Request Content
|
||||
var userIn *user.APIUserPassword
|
||||
var userIn *UserRegister
|
||||
if err := c.Bind(&userIn); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, models.Message{Message: "No or invalid user model provided."})
|
||||
}
|
||||
|
|
@ -65,7 +71,12 @@ func RegisterUser(c echo.Context) error {
|
|||
defer s.Close()
|
||||
|
||||
// Insert the user
|
||||
newUser, err := user.CreateUser(s, userIn.APIFormat())
|
||||
newUser, err := user.CreateUser(s, &user.User{
|
||||
Username: userIn.Username,
|
||||
Password: userIn.Password,
|
||||
Email: userIn.Email,
|
||||
Language: userIn.Language,
|
||||
})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err)
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ type User struct {
|
|||
OverdueTasksRemindersTime string `xorm:"varchar(5) not null default '09:00'" json:"-"`
|
||||
DefaultProjectID int64 `xorm:"bigint null index" json:"-"`
|
||||
WeekStart int `xorm:"null" json:"-"`
|
||||
Language string `xorm:"varchar(50) null" json:"-"`
|
||||
Language string `xorm:"varchar(50) null" json:"-" valid:"language"`
|
||||
Timezone string `xorm:"varchar(255) null" json:"-"`
|
||||
|
||||
DeletionScheduledAt time.Time `xorm:"datetime null" json:"-"`
|
||||
|
|
@ -201,8 +201,6 @@ func GetFromAuth(a web.Auth) (*User, error) {
|
|||
|
||||
// APIUserPassword represents a user object without timestamps and a json password field.
|
||||
type APIUserPassword struct {
|
||||
// The unique, numeric id of this user.
|
||||
ID int64 `json:"id"`
|
||||
// The user's username. Cannot contain anything that looks like an url or whitespaces.
|
||||
Username string `json:"username" valid:"length(3|250),username" minLength:"3" maxLength:"250"`
|
||||
// The user's password in clear text. Only used when registering the user. The maximum limi is 72 bytes, which may be less than 72 characters. This is due to the limit in the bcrypt hashing algorithm used to store passwords in Vikunja.
|
||||
|
|
@ -211,16 +209,6 @@ type APIUserPassword struct {
|
|||
Email string `json:"email" valid:"email,length(0|250)" maxLength:"250"`
|
||||
}
|
||||
|
||||
// APIFormat formats an API User into a normal user struct
|
||||
func (apiUser *APIUserPassword) APIFormat() *User {
|
||||
return &User{
|
||||
ID: apiUser.ID,
|
||||
Username: apiUser.Username,
|
||||
Password: apiUser.Password,
|
||||
Email: apiUser.Email,
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserByID returns user by its ID
|
||||
func GetUserByID(s *xorm.Session, id int64) (user *User, err error) {
|
||||
// Apparently xorm does otherwise look for all users but return only one, which leads to returning one even if the ID is 0
|
||||
|
|
|
|||
|
|
@ -69,9 +69,12 @@ func CreateUser(s *xorm.Session, user *User) (newUser *User, err error) {
|
|||
user.OverdueTasksRemindersTime = config.DefaultSettingsOverdueTaskRemindersTime.GetString()
|
||||
user.DefaultProjectID = config.DefaultSettingsDefaultProjectID.GetInt64()
|
||||
user.WeekStart = config.DefaultSettingsWeekStart.GetInt()
|
||||
user.Language = config.DefaultSettingsLanguage.GetString()
|
||||
user.Timezone = config.DefaultSettingsTimezone.GetString()
|
||||
|
||||
if user.Language == "" {
|
||||
user.Language = config.DefaultSettingsLanguage.GetString()
|
||||
}
|
||||
|
||||
// Insert it
|
||||
_, err = s.Insert(user)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ package user
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"code.vikunja.io/api/pkg/i18n"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
)
|
||||
|
||||
|
|
@ -50,4 +52,6 @@ func init() {
|
|||
|
||||
return len([]byte(str)) < 72
|
||||
}
|
||||
|
||||
govalidator.TagMap["language"] = i18n.HasLanguage
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue