1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 00:09:16 +08:00

feat: Add Multi-Language Support for Application Labels (#7714)

This commit is contained in:
zhengkunwang 2025-01-14 16:00:09 +08:00 committed by GitHub
parent 999e5a779a
commit 04a1ec9a9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 95 additions and 38 deletions

View File

@ -22,7 +22,7 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil { if err := helper.CheckBindAndValidate(&req, c); err != nil {
return return
} }
list, err := appService.PageApp(req) list, err := appService.PageApp(c, req)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
@ -173,7 +173,7 @@ func (b *BaseApi) InstallApp(c *gin.Context) {
} }
func (b *BaseApi) GetAppTags(c *gin.Context) { func (b *BaseApi) GetAppTags(c *gin.Context) {
tags, err := appService.GetAppTags() tags, err := appService.GetAppTags(c)
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return

View File

@ -104,9 +104,20 @@ type AppConfigVersion struct {
} }
type Tag struct { type Tag struct {
Key string `json:"key"` Key string `json:"key"`
Name string `json:"name"` Name string `json:"name"`
Sort int `json:"sort"` Sort int `json:"sort"`
Locales Locale `json:"locales"`
}
type Locale struct {
En string `json:"en"`
Ja string `json:"ja"`
Ms string `json:"ms"`
PtBr string `json:"pt-br"`
Ru string `json:"ru"`
ZhHant string `json:"zh-hant"`
Zh string `json:"zh"`
} }
type AppForm struct { type AppForm struct {

View File

@ -29,23 +29,25 @@ type AppDTO struct {
} }
type AppItem struct { type AppItem struct {
Name string `json:"name"` Name string `json:"name"`
Key string `json:"key"` Key string `json:"key"`
ID uint `json:"id"` ID uint `json:"id"`
ShortDescZh string `json:"shortDescZh"` ShortDescZh string `json:"shortDescZh"`
ShortDescEn string `json:"shortDescEn"` ShortDescEn string `json:"shortDescEn"`
Icon string `json:"icon"` Icon string `json:"icon"`
Type string `json:"type"` Type string `json:"type"`
Status string `json:"status"` Status string `json:"status"`
Resource string `json:"resource"` Resource string `json:"resource"`
Installed bool `json:"installed"` Installed bool `json:"installed"`
Versions []string `json:"versions"` Versions []string `json:"versions"`
Limit int `json:"limit"` Limit int `json:"limit"`
Tags []model.Tag `json:"tags"` Tags []TagDTO `json:"tags"`
} }
type TagDTO struct { type TagDTO struct {
model.Tag ID uint `json:"id"`
Key string `json:"key"`
Name string `json:"name"`
} }
type AppInstalledCheck struct { type AppInstalledCheck struct {

View File

@ -2,7 +2,8 @@ package model
type Tag struct { type Tag struct {
BaseModel BaseModel
Key string `json:"key" gorm:"type:varchar(64);not null"` Key string `json:"key" gorm:"type:varchar(64);not null"`
Name string `json:"name" gorm:"type:varchar(64);not null"` Name string `json:"name" gorm:"type:varchar(64);not null"`
Sort int `json:"sort" gorm:"type:int;not null;default:1"` Translations string `json:"translations"`
Sort int `json:"sort" gorm:"type:int;not null;default:1"`
} }

View File

@ -5,6 +5,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/gin-gonic/gin"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -34,8 +35,8 @@ type AppService struct {
} }
type IAppService interface { type IAppService interface {
PageApp(req request.AppSearch) (interface{}, error) PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error)
GetAppTags() ([]response.TagDTO, error) GetAppTags(ctx *gin.Context) ([]response.TagDTO, error)
GetApp(key string) (*response.AppDTO, error) GetApp(key string) (*response.AppDTO, error)
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error) GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error) Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error)
@ -50,7 +51,7 @@ func NewIAppService() IAppService {
return &AppService{} return &AppService{}
} }
func (a AppService) PageApp(req request.AppSearch) (interface{}, error) { func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error) {
var opts []repo.DBOption var opts []repo.DBOption
opts = append(opts, appRepo.OrderByRecommend()) opts = append(opts, appRepo.OrderByRecommend())
if req.Name != "" { if req.Name != "" {
@ -90,6 +91,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
return nil, err return nil, err
} }
var appDTOs []*response.AppItem var appDTOs []*response.AppItem
lang := strings.ToLower(common.GetLang(ctx))
for _, ap := range apps { for _, ap := range apps {
appDTO := &response.AppItem{ appDTO := &response.AppItem{
ID: ap.ID, ID: ap.ID,
@ -115,7 +117,27 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
if err != nil { if err != nil {
continue continue
} }
appDTO.Tags = tags for _, t := range tags {
if t.Name != "" {
tagDTO := response.TagDTO{
ID: t.ID,
Key: t.Key,
Name: t.Name,
}
appDTO.Tags = append(appDTO.Tags, tagDTO)
} else {
var translations = make(map[string]string)
_ = json.Unmarshal([]byte(t.Translations), &translations)
if name, ok := translations[lang]; ok {
tagDTO := response.TagDTO{
ID: t.ID,
Key: t.Key,
Name: name,
}
appDTO.Tags = append(appDTO.Tags, tagDTO)
}
}
}
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(ap.ID)) installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(ap.ID))
appDTO.Installed = len(installs) > 0 appDTO.Installed = len(installs) > 0
} }
@ -125,13 +147,21 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
return res, nil return res, nil
} }
func (a AppService) GetAppTags() ([]response.TagDTO, error) { func (a AppService) GetAppTags(ctx *gin.Context) ([]response.TagDTO, error) {
tags, _ := tagRepo.All() tags, _ := tagRepo.All()
res := make([]response.TagDTO, 0) res := make([]response.TagDTO, 0)
lang := strings.ToLower(common.GetLang(ctx))
for _, tag := range tags { for _, tag := range tags {
res = append(res, response.TagDTO{ tagDTO := response.TagDTO{
Tag: tag, ID: tag.ID,
}) Key: tag.Key,
}
var translations = make(map[string]string)
_ = json.Unmarshal([]byte(tag.Translations), &translations)
if name, ok := translations[lang]; ok {
tagDTO.Name = name
}
res = append(res, tagDTO)
} }
return res, nil return res, nil
} }
@ -827,10 +857,11 @@ func (a AppService) SyncAppListFromRemote() (err error) {
oldAppIds []uint oldAppIds []uint
) )
for _, t := range list.Extra.Tags { for _, t := range list.Extra.Tags {
translations, _ := json.Marshal(t.Locales)
tags = append(tags, &model.Tag{ tags = append(tags, &model.Tag{
Key: t.Key, Key: t.Key,
Name: t.Name, Translations: string(translations),
Sort: t.Sort, Sort: t.Sort,
}) })
} }
oldApps, err := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote)) oldApps, err := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))

View File

@ -99,6 +99,8 @@ func Init() {
migrations.AddAutoRestart, migrations.AddAutoRestart,
migrations.AddApiInterfaceConfig, migrations.AddApiInterfaceConfig,
migrations.AddApiKeyValidityTime, migrations.AddApiKeyValidityTime,
migrations.UpdateAppTag,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)

View File

@ -360,3 +360,13 @@ var AddApiKeyValidityTime = &gormigrate.Migration{
return nil return nil
}, },
} }
var UpdateAppTag = &gormigrate.Migration{
ID: "20241226-update-app-tag",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Tag{}); err != nil {
return err
}
return nil
},
}

View File

@ -20,7 +20,7 @@
:type="activeTag === item.key ? 'primary' : ''" :type="activeTag === item.key ? 'primary' : ''"
:plain="activeTag !== item.key" :plain="activeTag !== item.key"
> >
{{ language == 'zh' || language == 'tw' ? item.name : item.key }} {{ item.name }}
</el-button> </el-button>
</div> </div>
<div class="inline"> <div class="inline">
@ -42,7 +42,7 @@
@click="changeTag(item.key)" @click="changeTag(item.key)"
:key="item.key" :key="item.key"
> >
{{ language == 'zh' || language == 'tw' ? item.name : item.key }} {{ item.name }}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@ -139,7 +139,7 @@
<div class="app-tag"> <div class="app-tag">
<el-tag v-for="(tag, ind) in app.tags" :key="ind" class="p-mr-5"> <el-tag v-for="(tag, ind) in app.tags" :key="ind" class="p-mr-5">
<span> <span>
{{ language == 'zh' || language == 'tw' ? tag.name : tag.key }} {{ tag.name }}
</span> </span>
</el-tag> </el-tag>
<el-tag v-if="app.status === 'TakeDown'" class="p-mr-5"> <el-tag v-if="app.status === 'TakeDown'" class="p-mr-5">
@ -270,7 +270,7 @@ const changeTag = (key: string) => {
const getTagValue = (key: string) => { const getTagValue = (key: string) => {
const tag = tags.value.find((tag) => tag.key === key); const tag = tags.value.find((tag) => tag.key === key);
if (tag) { if (tag) {
return language == 'zh' || language == 'tw' ? tag.name : tag.key; return tag.name;
} }
}; };

View File

@ -21,7 +21,7 @@
:type="activeTag === item.key ? 'primary' : ''" :type="activeTag === item.key ? 'primary' : ''"
:plain="activeTag !== item.key" :plain="activeTag !== item.key"
> >
{{ language == 'zh' || language == 'tw' ? item.name : item.key }} {{ item.name }}
</el-button> </el-button>
</div> </div>
<div class="inline"> <div class="inline">