mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 00:09:16 +08:00
feat: Enable Multi-Language Support for Application Installation Forms (#7717)
This commit is contained in:
parent
04a1ec9a9a
commit
ca0dc71338
@ -75,7 +75,7 @@ func (b *BaseApi) GetApp(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
appDTO, err := appService.GetApp(appKey)
|
||||
appDTO, err := appService.GetApp(c, appKey)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
@ -1,9 +1,5 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type AppDatabase struct {
|
||||
ServiceName string `json:"PANEL_DB_HOST"`
|
||||
DbName string `json:"PANEL_DB_NAME"`
|
||||
@ -62,7 +58,7 @@ type AppDefine struct {
|
||||
}
|
||||
|
||||
type LocalAppAppDefine struct {
|
||||
AppProperty model.App `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
AppProperty AppProperty `json:"additionalProperties" yaml:"additionalProperties"`
|
||||
}
|
||||
|
||||
type LocalAppParam struct {
|
||||
@ -84,6 +80,7 @@ type AppProperty struct {
|
||||
Tags []string `json:"tags"`
|
||||
ShortDescZh string `json:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn"`
|
||||
Description Locale `json:"description"`
|
||||
Key string `json:"key"`
|
||||
Required []string `json:"Required"`
|
||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||
@ -114,9 +111,9 @@ type Locale struct {
|
||||
En string `json:"en"`
|
||||
Ja string `json:"ja"`
|
||||
Ms string `json:"ms"`
|
||||
PtBr string `json:"pt-br"`
|
||||
PtBr string `json:"pt-br" yaml:"pt-br"`
|
||||
Ru string `json:"ru"`
|
||||
ZhHant string `json:"zh-hant"`
|
||||
ZhHant string `json:"zh-hant" yaml:"zh-hant"`
|
||||
Zh string `json:"zh"`
|
||||
}
|
||||
|
||||
@ -129,6 +126,7 @@ type AppFormFields struct {
|
||||
Type string `json:"type"`
|
||||
LabelZh string `json:"labelZh"`
|
||||
LabelEn string `json:"labelEn"`
|
||||
Label Locale `json:"label"`
|
||||
Required bool `json:"required"`
|
||||
Default interface{} `json:"default"`
|
||||
EnvKey string `json:"envKey"`
|
||||
|
@ -32,8 +32,7 @@ type AppItem struct {
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
ID uint `json:"id"`
|
||||
ShortDescZh string `json:"shortDescZh"`
|
||||
ShortDescEn string `json:"shortDescEn"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
|
@ -1,7 +1,10 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@ -12,6 +15,7 @@ type App struct {
|
||||
Key string `json:"key" gorm:"type:varchar(64);not null;"`
|
||||
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh" gorm:"type:longtext;"`
|
||||
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn" gorm:"type:longtext;"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon" gorm:"type:longtext;"`
|
||||
Type string `json:"type" gorm:"type:varchar(64);not null"`
|
||||
Status string `json:"status" gorm:"type:varchar(64);not null"`
|
||||
@ -36,8 +40,20 @@ func (i *App) IsLocalApp() bool {
|
||||
}
|
||||
func (i *App) GetAppResourcePath() string {
|
||||
if i.IsLocalApp() {
|
||||
//这里要去掉本地应用的local前缀
|
||||
return filepath.Join(constant.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
|
||||
}
|
||||
return filepath.Join(constant.RemoteAppResourceDir, i.Key)
|
||||
}
|
||||
|
||||
func (i *App) GetDescription(ctx *gin.Context) string {
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(i.Description), &translations)
|
||||
lang := strings.ToLower(common.GetLang(ctx))
|
||||
if desc, ok := translations[lang]; ok {
|
||||
return desc
|
||||
}
|
||||
if lang == "zh" {
|
||||
return i.ShortDescZh
|
||||
}
|
||||
return i.ShortDescEn
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ type AppService struct {
|
||||
type IAppService interface {
|
||||
PageApp(ctx *gin.Context, req request.AppSearch) (interface{}, error)
|
||||
GetAppTags(ctx *gin.Context) ([]response.TagDTO, error)
|
||||
GetApp(key string) (*response.AppDTO, error)
|
||||
GetApp(ctx *gin.Context, key string) (*response.AppDTO, error)
|
||||
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
|
||||
Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error)
|
||||
SyncAppListFromRemote() error
|
||||
@ -99,11 +99,10 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
|
||||
Key: ap.Key,
|
||||
Type: ap.Type,
|
||||
Icon: ap.Icon,
|
||||
ShortDescZh: ap.ShortDescZh,
|
||||
ShortDescEn: ap.ShortDescEn,
|
||||
Resource: ap.Resource,
|
||||
Limit: ap.Limit,
|
||||
}
|
||||
appDTO.Description = ap.GetDescription(ctx)
|
||||
appDTOs = append(appDTOs, appDTO)
|
||||
appTags, err := appTagRepo.GetByAppId(ap.ID)
|
||||
if err != nil {
|
||||
@ -166,7 +165,7 @@ func (a AppService) GetAppTags(ctx *gin.Context) ([]response.TagDTO, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
func (a AppService) GetApp(ctx *gin.Context, key string) (*response.AppDTO, error) {
|
||||
var appDTO response.AppDTO
|
||||
if key == "postgres" {
|
||||
key = "postgresql"
|
||||
@ -176,6 +175,7 @@ func (a AppService) GetApp(key string) (*response.AppDTO, error) {
|
||||
return nil, err
|
||||
}
|
||||
appDTO.App = app
|
||||
appDTO.App.Description = app.GetDescription(ctx)
|
||||
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1051,6 +1051,8 @@ func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
|
||||
app.Key = key
|
||||
app.ShortDescZh = config.ShortDescZh
|
||||
app.ShortDescEn = config.ShortDescEn
|
||||
description, _ := json.Marshal(config.Description)
|
||||
app.Description = string(description)
|
||||
app.Website = config.Website
|
||||
app.Document = config.Document
|
||||
app.Github = config.Github
|
||||
@ -1150,14 +1152,32 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
|
||||
err = buserr.WithMap(constant.ErrFileParseApp, map[string]interface{}{"name": "data.yml", "err": err.Error()}, err)
|
||||
return
|
||||
}
|
||||
app = &localAppDefine.AppProperty
|
||||
appDefine := localAppDefine.AppProperty
|
||||
app = &model.App{}
|
||||
app.Name = appDefine.Name
|
||||
app.TagsKey = append(appDefine.Tags, "Local")
|
||||
app.Type = appDefine.Type
|
||||
app.CrossVersionUpdate = appDefine.CrossVersionUpdate
|
||||
app.Limit = appDefine.Limit
|
||||
app.Recommend = appDefine.Recommend
|
||||
app.Website = appDefine.Website
|
||||
app.Github = appDefine.Github
|
||||
app.Document = appDefine.Document
|
||||
|
||||
if appDefine.ShortDescZh != "" {
|
||||
appDefine.Description.Zh = appDefine.ShortDescZh
|
||||
}
|
||||
if appDefine.ShortDescEn != "" {
|
||||
appDefine.Description.En = appDefine.ShortDescEn
|
||||
}
|
||||
desc, _ := json.Marshal(appDefine.Description)
|
||||
app.Description = string(desc)
|
||||
|
||||
app.Key = "local" + appDefine.Key
|
||||
app.Resource = constant.AppResourceLocal
|
||||
app.Status = constant.AppNormal
|
||||
app.Recommend = 9999
|
||||
app.TagsKey = append(app.TagsKey, "Local")
|
||||
app.Key = "local" + app.Key
|
||||
readMePath := path.Join(appDir, "README.md")
|
||||
readMeByte, err := fileOp.GetContent(readMePath)
|
||||
readMeByte, err := fileOp.GetContent(path.Join(appDir, "README.md"))
|
||||
if err == nil {
|
||||
app.ReadMe = string(readMeByte)
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ func Init() {
|
||||
migrations.AddApiKeyValidityTime,
|
||||
|
||||
migrations.UpdateAppTag,
|
||||
migrations.UpdateApp,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
@ -362,7 +362,7 @@ var AddApiKeyValidityTime = &gormigrate.Migration{
|
||||
}
|
||||
|
||||
var UpdateAppTag = &gormigrate.Migration{
|
||||
ID: "20241226-update-app-tag",
|
||||
ID: "20250114-update-app-tag",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Tag{}); err != nil {
|
||||
return err
|
||||
@ -370,3 +370,13 @@ var UpdateAppTag = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateApp = &gormigrate.Migration{
|
||||
ID: "20250114-update-app",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.App{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ export namespace App {
|
||||
tags: Tag[];
|
||||
shortDescZh: string;
|
||||
shortDescEn: string;
|
||||
description: string;
|
||||
author: string;
|
||||
source: string;
|
||||
type: string;
|
||||
@ -58,10 +59,21 @@ export namespace App {
|
||||
formFields: FromField[];
|
||||
}
|
||||
|
||||
interface Locale {
|
||||
zh: string;
|
||||
en: string;
|
||||
'zh-Hant': string;
|
||||
ja: string;
|
||||
ms: string;
|
||||
'pt-br': string;
|
||||
ru: string;
|
||||
}
|
||||
|
||||
export interface FromField {
|
||||
type: string;
|
||||
labelZh: string;
|
||||
labelEn: string;
|
||||
label: Locale;
|
||||
required: boolean;
|
||||
default: any;
|
||||
envKey: string;
|
||||
|
@ -129,11 +129,7 @@
|
||||
</div>
|
||||
<div class="app-desc">
|
||||
<span class="desc">
|
||||
{{
|
||||
language == 'zh' || language == 'tw'
|
||||
? app.shortDescZh
|
||||
: app.shortDescEn
|
||||
}}
|
||||
{{ app.description }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="app-tag">
|
||||
@ -177,16 +173,11 @@ import Detail from '../detail/index.vue';
|
||||
import Install from '../detail/install/index.vue';
|
||||
import router from '@/routers';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { getLanguage } from '@/utils/util';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const mobile = computed(() => {
|
||||
return globalStore.isMobile();
|
||||
});
|
||||
|
||||
const language = getLanguage();
|
||||
|
||||
const paginationConfig = reactive({
|
||||
cacheSizeKey: 'app-page-size',
|
||||
currentPage: 1,
|
||||
|
@ -20,7 +20,7 @@
|
||||
</div>
|
||||
<div class="description mb-4">
|
||||
<span>
|
||||
{{ language == 'zh' || language == 'tw' ? app.shortDescZh : app.shortDescEn }}
|
||||
{{ app.description }}
|
||||
</span>
|
||||
</div>
|
||||
<br />
|
||||
@ -84,13 +84,10 @@ import { ref } from 'vue';
|
||||
import Install from './install/index.vue';
|
||||
import router from '@/routers';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { getLanguage } from '@/utils/util';
|
||||
import { storeToRefs } from 'pinia';
|
||||
const globalStore = GlobalStore();
|
||||
const { isDarkTheme } = storeToRefs(globalStore);
|
||||
|
||||
const language = getLanguage();
|
||||
|
||||
const app = ref<any>({});
|
||||
const appDetail = ref<any>({});
|
||||
const version = ref('');
|
||||
|
@ -248,6 +248,10 @@ const changeService = (value: string, services: App.AppService[]) => {
|
||||
|
||||
const getLabel = (row: ParamObj): string => {
|
||||
const language = localStorage.getItem('lang') || 'zh';
|
||||
let lang = language == 'tw' ? 'zh-Hant' : language;
|
||||
if (row.label && row.label[lang] != '') {
|
||||
return row.label[lang];
|
||||
}
|
||||
if (language == 'zh' || language == 'tw') {
|
||||
return row.labelZh;
|
||||
} else {
|
||||
|
@ -10,7 +10,7 @@
|
||||
<span class="h-app-title">{{ app.name }}</span>
|
||||
<div class="h-app-desc">
|
||||
<span>
|
||||
{{ language == 'zh' || language == 'tw' ? app.shortDescZh : app.shortDescEn }}
|
||||
{{ app.description }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -37,11 +37,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { App } from '@/api/interface/app';
|
||||
import { SearchApp } from '@/api/modules/app';
|
||||
import { getLanguage } from '@/utils/util';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const language = getLanguage();
|
||||
|
||||
let req = reactive({
|
||||
name: '',
|
||||
|
Loading…
x
Reference in New Issue
Block a user