添加多语言最基础代码

This commit is contained in:
GoEdgeLab
2023-06-28 09:12:13 +08:00
parent 18a8eaa543
commit 05bf52d04c
27 changed files with 850 additions and 9 deletions

74
pkg/langs/codes/codes.go Normal file
View 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
View 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
View 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
View 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
View 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

View 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": "集群列表",
})
}

View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,7 @@
{
"overview": "概况",
"waf": "WAF",
"dns": "DNS",
"user": "用户",
"events": "事件"
}

View File

@@ -0,0 +1,3 @@
{
}

View 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": "商业版本"
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,3 @@
{
}

74
pkg/langs/utils.go Normal file
View 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
View 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"))
}

View File

@@ -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 (

View File

@@ -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; // 界面语言
}