mirror of
https://github.com/TeaOSLab/EdgeCommon.git
synced 2025-11-01 19:30:25 +08:00
添加多语言最基础代码
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
*_plus.go
|
||||
*_plus_test.go
|
||||
*_plus_test.go
|
||||
*_plus.json
|
||||
3
build/build-messages.sh
Executable file
3
build/build-messages.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
go run ../cmd/langs/main.go generate
|
||||
@@ -13537,7 +13537,7 @@
|
||||
},
|
||||
{
|
||||
"name": "AdminModuleList",
|
||||
"code": "message AdminModuleList {\n\tint64 adminId = 1;\n\tbool isSuper = 2;\n\trepeated AdminModule Modules = 3;\n\tstring fullname = 4;\n\tstring theme = 5;\n}",
|
||||
"code": "message AdminModuleList {\n\tint64 adminId = 1;\n\tbool isSuper = 2;\n\trepeated AdminModule Modules = 3;\n\tstring fullname = 4;\n\tstring theme = 5; // 风格模板\n\tstring lang = 6; // 界面语言\n}",
|
||||
"doc": ""
|
||||
},
|
||||
{
|
||||
|
||||
276
cmd/langs/main.go
Normal file
276
cmd/langs/main.go
Normal file
@@ -0,0 +1,276 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"go/format"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var args = os.Args
|
||||
if len(args) >= 2 {
|
||||
switch args[1] {
|
||||
case "generate":
|
||||
runGenerate()
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Usage: langs [generate]")
|
||||
}
|
||||
}
|
||||
|
||||
func runGenerate() {
|
||||
var rootDir = filepath.Clean(Tea.Root + "/../pkg/langs/protos")
|
||||
dir, err := os.Open(rootDir)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]read dir failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = dir.Close()
|
||||
}()
|
||||
|
||||
files, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]read dir failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var dirRegexp = regexp.MustCompile(`^[a-z]+-[a-z]+$`)
|
||||
var jsonFileNameRegexp = regexp.MustCompile(`^([a-zA-Z0-9]+)(_([a-zA-Z0-9]+))+\.json$`)
|
||||
var messageCodeRegexp = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
|
||||
|
||||
var messageCodes = []string{}
|
||||
var messageMaps = map[string]map[string]string{} // lang => { K:V }
|
||||
var defaultLang = langs.DefaultManager().DefaultLang()
|
||||
|
||||
for _, file := range files {
|
||||
var dirName = file.Name()
|
||||
|
||||
if !file.IsDir() || !dirRegexp.MatchString(dirName) {
|
||||
continue
|
||||
}
|
||||
var langCode = dirName
|
||||
var isBaseLang = langCode == defaultLang
|
||||
|
||||
func() {
|
||||
jsonFiles, err := filepath.Glob(rootDir + "/" + dirName + "/*.json")
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]list json files failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, jsonFile := range jsonFiles {
|
||||
var jsonFileName = filepath.Base(jsonFile)
|
||||
if len(jsonFileName) == 0 || !jsonFileNameRegexp.MatchString(jsonFileName) {
|
||||
continue
|
||||
}
|
||||
var module = strings.TrimSuffix(jsonFileName, ".json")
|
||||
|
||||
data, err := os.ReadFile(jsonFile)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]read json file '" + jsonFile + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var m = map[string]string{} // code => value
|
||||
err = json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]decode json file '" + jsonFile + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var newM = map[string]string{}
|
||||
for code, value := range m {
|
||||
if !messageCodeRegexp.MatchString(code) {
|
||||
fmt.Println("[ERROR]invalid message code '" + code + "'")
|
||||
return
|
||||
}
|
||||
|
||||
var fullCode = module + "_" + code
|
||||
if isBaseLang {
|
||||
messageCodes = append(messageCodes, fullCode)
|
||||
}
|
||||
newM[fullCode] = value
|
||||
}
|
||||
|
||||
finalMap, ok := messageMaps[langCode]
|
||||
if !ok {
|
||||
finalMap = map[string]string{}
|
||||
messageMaps[langCode] = finalMap
|
||||
}
|
||||
for code, value := range newM {
|
||||
finalMap[code] = value
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// check message codes
|
||||
fmt.Println("checking message codes ...")
|
||||
var defaultMessageMap = map[string]string{}
|
||||
for langCode, messageMap := range messageMaps {
|
||||
if langCode == defaultLang { // only check lang not equal to 'en-us'
|
||||
defaultMessageMap = messageMap
|
||||
continue
|
||||
}
|
||||
|
||||
for messageCode := range messageMap {
|
||||
if !lists.ContainsString(messageCodes, messageCode) {
|
||||
fmt.Println("[ERROR]message code '" + messageCode + "' in lang '" + langCode + "' not exist in default lang file ('" + defaultLang + "')")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("found '" + types.String(len(messageCodes)) + "' message codes")
|
||||
|
||||
// generate codes/codes.go
|
||||
sort.Strings(messageCodes)
|
||||
var codesSource = `
|
||||
// generated by run 'langs generate'
|
||||
|
||||
package codes
|
||||
|
||||
import(
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
)
|
||||
|
||||
const (
|
||||
`
|
||||
|
||||
for _, messageCode := range messageCodes {
|
||||
// add comment to message code
|
||||
comment, _, _ := strings.Cut(defaultMessageMap[messageCode], "\n")
|
||||
|
||||
codesSource += upperWords(messageCode) + " langs.MessageCode = " + strconv.Quote(messageCode) + " // " + comment + "\n"
|
||||
}
|
||||
|
||||
codesSource += `
|
||||
)
|
||||
`
|
||||
|
||||
formattedCodesSource, err := format.Source([]byte(codesSource))
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]format 'codes.go' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("generating 'codes/codes.go' ...")
|
||||
err = os.WriteFile(Tea.Root+"/../pkg/langs/codes/codes.go", formattedCodesSource, 0666)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]write to 'codes.go' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// generate messages_LANG.go
|
||||
for langCode, messageMap := range messageMaps {
|
||||
var langFile = strings.ReplaceAll("messages/messages_"+langCode+".go", "-", "_")
|
||||
|
||||
fmt.Println("generating '" + langFile + "' ...")
|
||||
var source = `
|
||||
// generated by run 'langs generate'
|
||||
|
||||
package messages
|
||||
|
||||
import(
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
)
|
||||
|
||||
func init() {
|
||||
langs.Load("` + langCode + `", map[string]string{
|
||||
`
|
||||
|
||||
for code, value := range messageMap {
|
||||
source += strconv.Quote(code) + ": " + strconv.Quote(value) + ",\n"
|
||||
}
|
||||
|
||||
source += `
|
||||
})
|
||||
}
|
||||
`
|
||||
|
||||
formattedSource, err := format.Source([]byte(source))
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]format '" + langFile + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(Tea.Root+"/../pkg/langs/"+langFile, formattedSource, 0666)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]write file '" + langFile + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// generate language javascript files for EdgeAdmin
|
||||
for lang, messageMap := range messageMaps {
|
||||
if lang != defaultLang {
|
||||
// TODO merge messageMap with default message map
|
||||
}
|
||||
|
||||
for _, component := range []string{"EdgeAdmin", "EdgeUser"} {
|
||||
fmt.Println("generating '" + lang + ".js' for " + component + " ...")
|
||||
var targetFile = filepath.Clean(Tea.Root + "/../../" + component + "/web/public/js/langs/" + lang + ".js")
|
||||
var targetDir = filepath.Dir(targetFile)
|
||||
dirStat, _ := os.Stat(targetDir)
|
||||
if dirStat != nil {
|
||||
messageMapJSON, err := json.Marshal(messageMap)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]marshal message map failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(targetFile, []byte(`// generated by 'langs generate'
|
||||
window.LANG_MESSAGES = `+string(messageMapJSON)+";\n"), 0666)
|
||||
if err != nil {
|
||||
fmt.Println("[ERROR]write file '" + targetFile + "' failed: " + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("success")
|
||||
}
|
||||
|
||||
func upperWords(s string) string {
|
||||
var words = strings.Split(s, "_")
|
||||
for index, word := range words {
|
||||
words[index] = upperWord(word)
|
||||
}
|
||||
return strings.Join(words, "")
|
||||
}
|
||||
|
||||
func upperWord(word string) string {
|
||||
var l = len(word)
|
||||
if l == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if l == 1 {
|
||||
return strings.ToUpper(word)
|
||||
}
|
||||
|
||||
// special words
|
||||
switch word {
|
||||
case "api", "http", "https", "tcp", "udp", "ip", "dns", "ns", "waf", "acme", "ssh", "toa":
|
||||
return strings.ToUpper(word)
|
||||
case "ipv6":
|
||||
return "IPv6"
|
||||
case "ddos":
|
||||
return "DDoS"
|
||||
}
|
||||
|
||||
return strings.ToUpper(word[:1]) + word[1:]
|
||||
}
|
||||
4
go.mod
4
go.mod
@@ -4,11 +4,11 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f
|
||||
golang.org/x/net v0.8.0
|
||||
google.golang.org/grpc v1.45.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
4
go.sum
4
go.sum
@@ -59,6 +59,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475 h1:EseyfFaQOjWanGiby9KMw7PjDBMg/95tLDgIw/ns0Cw=
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f h1:xo6XmXLtveKcwcZAXV6VMxkWNzy/2dStfHEnyowsGAE=
|
||||
github.com/iwind/TeaGo v0.0.0-20230623080147-cd1e53b4915f/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -201,5 +203,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
74
pkg/langs/codes/codes.go
Normal file
74
pkg/langs/codes/codes.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// generated by run 'langs generate'
|
||||
|
||||
package codes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
)
|
||||
|
||||
const (
|
||||
AdminDashboardDNS langs.MessageCode = "admin_dashboard_dns" // DNS
|
||||
AdminDashboardEvents langs.MessageCode = "admin_dashboard_events" // 事件
|
||||
AdminDashboardOverview langs.MessageCode = "admin_dashboard_overview" // 概况
|
||||
AdminDashboardUser langs.MessageCode = "admin_dashboard_user" // 用户
|
||||
AdminDashboardWAF langs.MessageCode = "admin_dashboard_waf" // WAF
|
||||
AdminMenuAdminRecipients langs.MessageCode = "admin_menu_admin_recipients" // 通知媒介
|
||||
AdminMenuAdmins langs.MessageCode = "admin_menu_admins" // 系统用户
|
||||
AdminMenuDashboard langs.MessageCode = "admin_menu_dashboard" // 数据看板
|
||||
AdminMenuDNS langs.MessageCode = "admin_menu_dns" // 域名解析
|
||||
AdminMenuDNSClusters langs.MessageCode = "admin_menu_dns_clusters" // 集群列表
|
||||
AdminMenuDNSIssues langs.MessageCode = "admin_menu_dns_issues" // 问题修复
|
||||
AdminMenuDNSProviders langs.MessageCode = "admin_menu_dns_providers" // DNS服务商
|
||||
AdminMenuFinance langs.MessageCode = "admin_menu_finance" // 财务管理
|
||||
AdminMenuFinanceAccounts langs.MessageCode = "admin_menu_finance_accounts" // 用户账户
|
||||
AdminMenuFinanceBills langs.MessageCode = "admin_menu_finance_bills" // 费用账单
|
||||
AdminMenuFinanceFee langs.MessageCode = "admin_menu_finance_fee" // 计费设置
|
||||
AdminMenuFinanceIncome langs.MessageCode = "admin_menu_finance_income" // 统计报表
|
||||
AdminMenuFinanceLogs langs.MessageCode = "admin_menu_finance_logs" // 收支明细
|
||||
AdminMenuFinanceOrders langs.MessageCode = "admin_menu_finance_orders" // 订单管理
|
||||
AdminMenuFinancePackages langs.MessageCode = "admin_menu_finance_packages" // 流量包
|
||||
AdminMenuLogs langs.MessageCode = "admin_menu_logs" // 日志审计
|
||||
AdminMenuNodeAntiDDoSProducts langs.MessageCode = "admin_menu_node_anti_ddos_products" // 高防IP
|
||||
AdminMenuNodeClusters langs.MessageCode = "admin_menu_node_clusters" // 集群列表
|
||||
AdminMenuNodeDistributedMonitors langs.MessageCode = "admin_menu_node_distributed_monitors" // 区域监控
|
||||
AdminMenuNodeIPList langs.MessageCode = "admin_menu_node_ip_list" // 节点IP
|
||||
AdminMenuNodeLogs langs.MessageCode = "admin_menu_node_logs" // 节点日志
|
||||
AdminMenuNodeRegions langs.MessageCode = "admin_menu_node_regions" // 区域设置
|
||||
AdminMenuNodeSSHGrants langs.MessageCode = "admin_menu_node_ssh_grants" // 节点SSH
|
||||
AdminMenuNodes langs.MessageCode = "admin_menu_nodes" // 边缘节点
|
||||
AdminMenuNS langs.MessageCode = "admin_menu_ns" // 智能DNS
|
||||
AdminMenuNSAccessLogs langs.MessageCode = "admin_menu_ns_access_logs" // 访问日志
|
||||
AdminMenuNSClusters langs.MessageCode = "admin_menu_ns_clusters" // 集群管理
|
||||
AdminMenuNSDomainBatchOperations langs.MessageCode = "admin_menu_ns_domain_batch_operations" // 批量操作
|
||||
AdminMenuNSDomainGroups langs.MessageCode = "admin_menu_ns_domain_groups" // 域名分组
|
||||
AdminMenuNSDomains langs.MessageCode = "admin_menu_ns_domains" // 域名管理
|
||||
AdminMenuNSNodeLogs langs.MessageCode = "admin_menu_ns_node_logs" // 运行日志
|
||||
AdminMenuNSPlans langs.MessageCode = "admin_menu_ns_plans" // 套餐设置
|
||||
AdminMenuNSResolveTest langs.MessageCode = "admin_menu_ns_resolve_test" // 解析测试
|
||||
AdminMenuNSRoutes langs.MessageCode = "admin_menu_ns_routes" // 线路管理
|
||||
AdminMenuNSSettings langs.MessageCode = "admin_menu_ns_settings" // 全局配置
|
||||
AdminMenuNSUserPlans langs.MessageCode = "admin_menu_ns_user_plans" // 用户套餐
|
||||
AdminMenuPlanUserPlans langs.MessageCode = "admin_menu_plan_user_plans" // 已购套餐
|
||||
AdminMenuPlans langs.MessageCode = "admin_menu_plans" // 套餐管理
|
||||
AdminMenuServerAccessLogPolicies langs.MessageCode = "admin_menu_server_access_log_policies" // 日志策略
|
||||
AdminMenuServerAccessLogs langs.MessageCode = "admin_menu_server_access_logs" // 访问日志
|
||||
AdminMenuServerCachePolicies langs.MessageCode = "admin_menu_server_cache_policies" // 缓存策略
|
||||
AdminMenuServerCerts langs.MessageCode = "admin_menu_server_certs" // 证书管理
|
||||
AdminMenuServerGlobalSettings langs.MessageCode = "admin_menu_server_global_settings" // 通用设置
|
||||
AdminMenuServerGroups langs.MessageCode = "admin_menu_server_groups" // 网站分组
|
||||
AdminMenuServerIPLists langs.MessageCode = "admin_menu_server_ip_lists" // IP名单
|
||||
AdminMenuServerMetrics langs.MessageCode = "admin_menu_server_metrics" // 统计指标
|
||||
AdminMenuServerPurgeFetchCaches langs.MessageCode = "admin_menu_server_purge_fetch_caches" // 刷新预热
|
||||
AdminMenuServerScripts langs.MessageCode = "admin_menu_server_scripts" // 脚本库
|
||||
AdminMenuServerTrafficStats langs.MessageCode = "admin_menu_server_traffic_stats" // 用量统计
|
||||
AdminMenuServerWAFPolicies langs.MessageCode = "admin_menu_server_waf_policies" // WAF策略
|
||||
AdminMenuServers langs.MessageCode = "admin_menu_servers" // 网站列表
|
||||
AdminMenuSettingAdvancedSettings langs.MessageCode = "admin_menu_setting_advanced_settings" // 高级设置
|
||||
AdminMenuSettingAuthority langs.MessageCode = "admin_menu_setting_authority" // 商业版本
|
||||
AdminMenuSettingBasicSettings langs.MessageCode = "admin_menu_setting_basic_settings" // 基础设置
|
||||
AdminMenuSettings langs.MessageCode = "admin_menu_settings" // 系统设置
|
||||
AdminMenuTicketCategory langs.MessageCode = "admin_menu_ticket_category" // 分类
|
||||
AdminMenuTickets langs.MessageCode = "admin_menu_tickets" // 工单系统
|
||||
AdminMenuUsers langs.MessageCode = "admin_menu_users" // 用户设置
|
||||
|
||||
)
|
||||
32
pkg/langs/lang.go
Normal file
32
pkg/langs/lang.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package langs
|
||||
|
||||
type Lang struct {
|
||||
code string
|
||||
|
||||
messageMap map[string]string // message code => message text
|
||||
}
|
||||
|
||||
func NewLang(code string) *Lang {
|
||||
return &Lang{
|
||||
code: code,
|
||||
messageMap: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Lang) Set(messageCode string, messageText string) {
|
||||
this.messageMap[messageCode] = messageText
|
||||
}
|
||||
|
||||
// Get 读取单条消息
|
||||
// get single message with message code
|
||||
func (this *Lang) Get(messageCode string) string {
|
||||
return this.messageMap[messageCode]
|
||||
}
|
||||
|
||||
// GetAll 读取所有消息
|
||||
// get all messages
|
||||
func (this *Lang) GetAll() map[string]string {
|
||||
return this.messageMap
|
||||
}
|
||||
105
pkg/langs/manager.go
Normal file
105
pkg/langs/manager.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package langs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var defaultManager = NewManager()
|
||||
|
||||
type Manager struct {
|
||||
langMap map[string]*Lang // lang code => *Lang, lang code must be in lowercase
|
||||
defaultLangCode string
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
return &Manager{
|
||||
langMap: map[string]*Lang{},
|
||||
defaultLangCode: "zh-cn",
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultManager() *Manager {
|
||||
return defaultManager
|
||||
}
|
||||
|
||||
func (this *Manager) AddLang(code string) *Lang {
|
||||
var lang = NewLang(code)
|
||||
this.langMap[code] = lang
|
||||
return lang
|
||||
}
|
||||
|
||||
func (this *Manager) HasLang(code string) bool {
|
||||
_, ok := this.langMap[code]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (this *Manager) GetLang(code string) (lang *Lang, ok bool) {
|
||||
lang, ok = this.langMap[code]
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Manager) MatchLang(code string) (matchedCode string) {
|
||||
// lookup exact match
|
||||
code = strings.ToLower(code)
|
||||
_, ok := this.langMap[code]
|
||||
if ok {
|
||||
return code
|
||||
}
|
||||
|
||||
// lookup language family, such as en-us, en
|
||||
if strings.Contains(code, "-") {
|
||||
code, _, _ = strings.Cut(code, "-")
|
||||
}
|
||||
for rawCode := range this.langMap {
|
||||
if strings.HasPrefix(rawCode, code+"-") { // en-us vs en
|
||||
return rawCode
|
||||
}
|
||||
}
|
||||
|
||||
return this.DefaultLang()
|
||||
}
|
||||
|
||||
func (this *Manager) SetDefaultLang(code string) {
|
||||
this.defaultLangCode = code
|
||||
}
|
||||
|
||||
func (this *Manager) DefaultLang() string {
|
||||
if len(this.defaultLangCode) > 0 {
|
||||
return this.defaultLangCode
|
||||
}
|
||||
return "zh-cn"
|
||||
}
|
||||
|
||||
// GetMessage
|
||||
// message: name: %s, age: %d, salary: %.2f
|
||||
func (this *Manager) GetMessage(langCode string, messageCode string, args ...any) string {
|
||||
var lang = this.langMap[langCode]
|
||||
if lang == nil && len(this.defaultLangCode) > 0 {
|
||||
lang = this.langMap[this.defaultLangCode]
|
||||
}
|
||||
if lang == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var message = lang.Get(messageCode)
|
||||
if len(message) == 0 {
|
||||
// try to get message from default lang
|
||||
if lang.code != this.defaultLangCode {
|
||||
var defaultLang = this.langMap[this.defaultLangCode]
|
||||
if defaultLang != nil {
|
||||
return defaultLang.Get(messageCode)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
return message
|
||||
}
|
||||
|
||||
return fmt.Sprintf(message, args...)
|
||||
}
|
||||
43
pkg/langs/manager_test.go
Normal file
43
pkg/langs/manager_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package langs_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestManager_GetMessage(t *testing.T) {
|
||||
var manager = langs.NewManager()
|
||||
var lang = manager.AddLang("en-US")
|
||||
lang.Set("user_description", "user: %s, age: %d")
|
||||
t.Log(manager.GetMessage("en-US", "user_description", "Lily", 23))
|
||||
t.Log(manager.GetMessage("zh-CN", "user_description", "Lucy", 23)) // use 'en-US' as fallback language
|
||||
}
|
||||
|
||||
func TestManager_GetMessage2(t *testing.T) {
|
||||
var manager = langs.NewManager()
|
||||
manager.SetDefaultLang("zh-CN")
|
||||
var lang = manager.AddLang("en-US")
|
||||
lang.Set("user_description", "user: %s, age: %d")
|
||||
t.Log(manager.GetMessage("en-US", "user_description", "Lily", 23))
|
||||
t.Log(manager.GetMessage("zh-CN", "user_description", "Lucy", 23)) // should be empty
|
||||
}
|
||||
|
||||
func TestManager_MatchLang(t *testing.T) {
|
||||
var manager = langs.NewManager()
|
||||
manager.AddLang("en-us")
|
||||
manager.AddLang("en")
|
||||
manager.AddLang("zh-cn")
|
||||
manager.AddLang("zh-hk")
|
||||
//manager.AddLang("zh-tw")
|
||||
|
||||
for _, code := range []string{
|
||||
"en",
|
||||
"en-us",
|
||||
"zh-cn",
|
||||
"zh-tw",
|
||||
} {
|
||||
t.Log(code, "=>", manager.MatchLang(code))
|
||||
}
|
||||
}
|
||||
5
pkg/langs/message.go
Normal file
5
pkg/langs/message.go
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package langs
|
||||
|
||||
type MessageCode = string
|
||||
75
pkg/langs/messages/messages_zh_cn.go
Normal file
75
pkg/langs/messages/messages_zh_cn.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// generated by run 'langs generate'
|
||||
|
||||
package messages
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
)
|
||||
|
||||
func init() {
|
||||
langs.Load("zh-cn", map[string]string{
|
||||
"admin_menu_finance_fee": "计费设置",
|
||||
"admin_menu_server_traffic_stats": "用量统计",
|
||||
"admin_menu_ns_node_logs": "运行日志",
|
||||
"admin_menu_servers": "网站列表",
|
||||
"admin_menu_finance_logs": "收支明细",
|
||||
"admin_menu_plan_user_plans": "已购套餐",
|
||||
"admin_menu_server_ip_lists": "IP名单",
|
||||
"admin_menu_users": "用户设置",
|
||||
"admin_menu_ns_access_logs": "访问日志",
|
||||
"admin_menu_server_purge_fetch_caches": "刷新预热",
|
||||
"admin_menu_node_distributed_monitors": "区域监控",
|
||||
"admin_menu_finance_packages": "流量包",
|
||||
"admin_menu_settings": "系统设置",
|
||||
"admin_menu_ns_plans": "套餐设置",
|
||||
"admin_dashboard_user": "用户",
|
||||
"admin_dashboard_waf": "WAF",
|
||||
"admin_menu_setting_advanced_settings": "高级设置",
|
||||
"admin_menu_server_global_settings": "通用设置",
|
||||
"admin_menu_node_logs": "节点日志",
|
||||
"admin_menu_ns_clusters": "集群管理",
|
||||
"admin_menu_server_cache_policies": "缓存策略",
|
||||
"admin_menu_ns_resolve_test": "解析测试",
|
||||
"admin_menu_finance_income": "统计报表",
|
||||
"admin_menu_finance_accounts": "用户账户",
|
||||
"admin_menu_server_certs": "证书管理",
|
||||
"admin_menu_dashboard": "数据看板",
|
||||
"admin_dashboard_events": "事件",
|
||||
"admin_menu_node_ip_list": "节点IP",
|
||||
"admin_dashboard_overview": "概况",
|
||||
"admin_menu_server_access_logs": "访问日志",
|
||||
"admin_menu_dns_providers": "DNS服务商",
|
||||
"admin_menu_admins": "系统用户",
|
||||
"admin_menu_setting_authority": "商业版本",
|
||||
"admin_menu_dns_issues": "问题修复",
|
||||
"admin_menu_server_metrics": "统计指标",
|
||||
"admin_menu_node_ssh_grants": "节点SSH",
|
||||
"admin_menu_server_waf_policies": "WAF策略",
|
||||
"admin_menu_ns_domains": "域名管理",
|
||||
"admin_menu_nodes": "边缘节点",
|
||||
"admin_dashboard_dns": "DNS",
|
||||
"admin_menu_logs": "日志审计",
|
||||
"admin_menu_ns_user_plans": "用户套餐",
|
||||
"admin_menu_ns_domain_groups": "域名分组",
|
||||
"admin_menu_tickets": "工单系统",
|
||||
"admin_menu_node_regions": "区域设置",
|
||||
"admin_menu_ns_domain_batch_operations": "批量操作",
|
||||
"admin_menu_plans": "套餐管理",
|
||||
"admin_menu_finance_bills": "费用账单",
|
||||
"admin_menu_ticket_category": "分类",
|
||||
"admin_menu_finance": "财务管理",
|
||||
"admin_menu_node_anti_ddos_products": "高防IP",
|
||||
"admin_menu_ns": "智能DNS",
|
||||
"admin_menu_dns": "域名解析",
|
||||
"admin_menu_server_groups": "网站分组",
|
||||
"admin_menu_server_scripts": "脚本库",
|
||||
"admin_menu_ns_settings": "全局配置",
|
||||
"admin_menu_ns_routes": "线路管理",
|
||||
"admin_menu_server_access_log_policies": "日志策略",
|
||||
"admin_menu_finance_orders": "订单管理",
|
||||
"admin_menu_dns_clusters": "集群列表",
|
||||
"admin_menu_admin_recipients": "通知媒介",
|
||||
"admin_menu_setting_basic_settings": "基础设置",
|
||||
"admin_menu_node_clusters": "集群列表",
|
||||
})
|
||||
}
|
||||
2
pkg/langs/protos/zh-cn/admin_api_node.json
Normal file
2
pkg/langs/protos/zh-cn/admin_api_node.json
Normal file
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_cluster.json
Normal file
3
pkg/langs/protos/zh-cn/admin_cluster.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_common.json
Normal file
3
pkg/langs/protos/zh-cn/admin_common.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
7
pkg/langs/protos/zh-cn/admin_dashboard.json
Normal file
7
pkg/langs/protos/zh-cn/admin_dashboard.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"overview": "概况",
|
||||
"waf": "WAF",
|
||||
"dns": "DNS",
|
||||
"user": "用户",
|
||||
"events": "事件"
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_login.json
Normal file
3
pkg/langs/protos/zh-cn/admin_login.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
70
pkg/langs/protos/zh-cn/admin_menu.json
Normal file
70
pkg/langs/protos/zh-cn/admin_menu.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"dashboard": "数据看板",
|
||||
"servers": "网站列表",
|
||||
"server_access_logs": "访问日志",
|
||||
"server_groups": "网站分组",
|
||||
"server_certs": "证书管理",
|
||||
"server_traffic_stats": "用量统计",
|
||||
"server_cache_policies": "缓存策略",
|
||||
"server_purge_fetch_caches": "刷新预热",
|
||||
"server_waf_policies": "WAF策略",
|
||||
"server_ip_lists": "IP名单",
|
||||
"server_access_log_policies": "日志策略",
|
||||
"server_metrics": "统计指标",
|
||||
"server_scripts": "脚本库",
|
||||
"server_global_settings": "通用设置",
|
||||
|
||||
"nodes": "边缘节点",
|
||||
"node_clusters": "集群列表",
|
||||
"node_logs": "节点日志",
|
||||
"node_ip_list": "节点IP",
|
||||
"node_regions": "区域设置",
|
||||
"node_ssh_grants": "节点SSH",
|
||||
"node_distributed_monitors": "区域监控",
|
||||
"node_anti_ddos_products": "高防IP",
|
||||
|
||||
"dns": "域名解析",
|
||||
"dns_clusters": "集群列表",
|
||||
"dns_providers": "DNS服务商",
|
||||
"dns_issues": "问题修复",
|
||||
|
||||
"ns": "智能DNS",
|
||||
"ns_domains": "域名管理",
|
||||
"ns_domain_groups": "域名分组",
|
||||
"ns_domain_batch_operations": "批量操作",
|
||||
"ns_clusters": "集群管理",
|
||||
"ns_routes": "线路管理",
|
||||
"ns_settings": "全局配置",
|
||||
"ns_user_plans": "用户套餐",
|
||||
"ns_plans": "套餐设置",
|
||||
"ns_access_logs": "访问日志",
|
||||
"ns_node_logs": "运行日志",
|
||||
"ns_resolve_test": "解析测试",
|
||||
|
||||
"users": "用户设置",
|
||||
|
||||
"finance": "财务管理",
|
||||
"finance_bills": "费用账单",
|
||||
"finance_accounts": "用户账户",
|
||||
"finance_logs": "收支明细",
|
||||
"finance_orders": "订单管理",
|
||||
"finance_income": "统计报表",
|
||||
"finance_fee": "计费设置",
|
||||
"finance_packages": "流量包",
|
||||
|
||||
"plans": "套餐管理",
|
||||
"plan_user_plans": "已购套餐",
|
||||
|
||||
"tickets": "工单系统",
|
||||
"ticket_category": "分类",
|
||||
|
||||
"admins": "系统用户",
|
||||
"admin_recipients": "通知媒介",
|
||||
|
||||
"logs": "日志审计",
|
||||
|
||||
"settings": "系统设置",
|
||||
"setting_basic_settings": "基础设置",
|
||||
"setting_advanced_settings": "高级设置",
|
||||
"setting_authority": "商业版本"
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_node.json
Normal file
3
pkg/langs/protos/zh-cn/admin_node.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_server.json
Normal file
3
pkg/langs/protos/zh-cn/admin_server.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_server_origin.json
Normal file
3
pkg/langs/protos/zh-cn/admin_server_origin.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_setings.json
Normal file
3
pkg/langs/protos/zh-cn/admin_setings.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
pkg/langs/protos/zh-cn/admin_waf.json
Normal file
3
pkg/langs/protos/zh-cn/admin_waf.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
74
pkg/langs/utils.go
Normal file
74
pkg/langs/utils.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package langs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Message 读取消息
|
||||
// Read message
|
||||
func Message(langCode string, messageCode MessageCode, args ...any) string {
|
||||
return defaultManager.GetMessage(langCode, messageCode, args...)
|
||||
}
|
||||
|
||||
func ParseLangFromRequest(req *http.Request) (langCode string) {
|
||||
// parse language from cookie
|
||||
const cookieName = "edgelang"
|
||||
cookie, _ := req.Cookie(cookieName)
|
||||
if cookie != nil && len(cookie.Value) > 0 && defaultManager.HasLang(cookie.Value) {
|
||||
return cookie.Value
|
||||
}
|
||||
|
||||
// parse language from 'Accept-Language'
|
||||
var acceptLanguage = req.Header.Get("Accept-Language")
|
||||
if len(acceptLanguage) > 0 {
|
||||
var pieces = strings.Split(acceptLanguage, ",")
|
||||
for _, lang := range pieces {
|
||||
var index = strings.Index(lang, ";")
|
||||
if index >= 0 {
|
||||
lang = lang[:index]
|
||||
}
|
||||
|
||||
var match = defaultManager.MatchLang(lang)
|
||||
if len(match) > 0 {
|
||||
return match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultManager.DefaultLang()
|
||||
}
|
||||
|
||||
func ParseLangFromAction(action actions.ActionWrapper) (langCode string) {
|
||||
return ParseLangFromRequest(action.Object().Request)
|
||||
}
|
||||
|
||||
// Format 格式化变量
|
||||
// Format string that contains message variables, such as ${lang.MESSAGE_CODE}
|
||||
//
|
||||
// 暂时不支持变量中加参数
|
||||
func Format(langCode string, varString string) string {
|
||||
return configutils.ParseVariables(varString, func(varName string) (value string) {
|
||||
const prefix = "lang."
|
||||
if !strings.HasPrefix(varName, prefix) {
|
||||
return "${" + varName + "}" // keep origin variable
|
||||
}
|
||||
return Message(langCode, varName[len(prefix):])
|
||||
})
|
||||
}
|
||||
|
||||
// Load 加载消息定义
|
||||
// Load message definitions from map
|
||||
func Load(langCode string, messageMap map[string]string) {
|
||||
lang, ok := defaultManager.GetLang(langCode)
|
||||
if !ok {
|
||||
lang = defaultManager.AddLang(langCode)
|
||||
}
|
||||
for messageCode, messageText := range messageMap {
|
||||
lang.Set(messageCode, messageText)
|
||||
}
|
||||
}
|
||||
36
pkg/langs/utils_test.go
Normal file
36
pkg/langs/utils_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package langs_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMessage(t *testing.T) {
|
||||
var lang = langs.DefaultManager().AddLang("en-us")
|
||||
lang.Set("user_description", "user: %s")
|
||||
t.Log(langs.Message("en-us", "user_description", "Lily"))
|
||||
}
|
||||
|
||||
func TestMessageDefault(t *testing.T) {
|
||||
var lang = langs.DefaultManager().AddLang("zh-cn")
|
||||
lang.Set("user_description", "user: %s")
|
||||
|
||||
t.Log(langs.Message("en-us", "user_description", "Lily"))
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
{
|
||||
var lang = langs.DefaultManager().AddLang("en-US")
|
||||
lang.Set("book_name", "Golang")
|
||||
}
|
||||
|
||||
{
|
||||
var lang = langs.DefaultManager().AddLang("zh-CN")
|
||||
lang.Set("book_name", "Go语言")
|
||||
}
|
||||
|
||||
t.Log(langs.Format("en-US", "this is ${lang.book_name} book"))
|
||||
t.Log(langs.Format("zh-CN", "this is ${lang.book_name} book"))
|
||||
}
|
||||
@@ -34,7 +34,8 @@ type AdminModuleList struct {
|
||||
IsSuper bool `protobuf:"varint,2,opt,name=isSuper,proto3" json:"isSuper,omitempty"`
|
||||
Modules []*AdminModule `protobuf:"bytes,3,rep,name=Modules,proto3" json:"Modules,omitempty"`
|
||||
Fullname string `protobuf:"bytes,4,opt,name=fullname,proto3" json:"fullname,omitempty"`
|
||||
Theme string `protobuf:"bytes,5,opt,name=theme,proto3" json:"theme,omitempty"`
|
||||
Theme string `protobuf:"bytes,5,opt,name=theme,proto3" json:"theme,omitempty"` // 风格模板
|
||||
Lang string `protobuf:"bytes,6,opt,name=lang,proto3" json:"lang,omitempty"` // 界面语言
|
||||
}
|
||||
|
||||
func (x *AdminModuleList) Reset() {
|
||||
@@ -104,6 +105,13 @@ func (x *AdminModuleList) GetTheme() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminModuleList) GetLang() string {
|
||||
if x != nil {
|
||||
return x.Lang
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_models_model_admin_list_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_models_model_admin_list_proto_rawDesc = []byte{
|
||||
@@ -111,7 +119,7 @@ var file_models_model_admin_list_proto_rawDesc = []byte{
|
||||
0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||
0x02, 0x70, 0x62, 0x1a, 0x1f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x6d, 0x6f, 0x64, 0x65,
|
||||
0x6c, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa2, 0x01, 0x0a, 0x0f, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4d, 0x6f,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x01, 0x0a, 0x0f, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4d, 0x6f,
|
||||
0x64, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x6d, 0x69,
|
||||
0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x64, 0x6d, 0x69, 0x6e,
|
||||
0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x73, 0x53, 0x75, 0x70, 0x65, 0x72, 0x18, 0x02, 0x20,
|
||||
@@ -121,8 +129,9 @@ var file_models_model_admin_list_proto_rawDesc = []byte{
|
||||
0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70,
|
||||
0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x28, 0x09, 0x52, 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x6e,
|
||||
0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x61, 0x6e, 0x67, 0x42, 0x06, 0x5a,
|
||||
0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -10,5 +10,6 @@ message AdminModuleList {
|
||||
bool isSuper = 2;
|
||||
repeated AdminModule Modules = 3;
|
||||
string fullname = 4;
|
||||
string theme = 5;
|
||||
string theme = 5; // 风格模板
|
||||
string lang = 6; // 界面语言
|
||||
}
|
||||
Reference in New Issue
Block a user