feat: initial blog engine with multi-language, profile, and role system
- Gin + GORM + Tailwind CSS blog engine - OS-aware config (Linux /etc/blog_go/, Windows ./win/etc/blog_go/) - SQLite by default, MySQL support via config - Auto-migrate database + seed admin user on first run - Session-based auth (login/logout) - i18n: Chinese/English with auto-detect + manual switch - Avatar dropdown nav with click-to-toggle menu - Profile page: avatar upload, edit info, change password - Role system: admin (full access) and author (profile only) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+204
@@ -0,0 +1,204 @@
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Lang represents a supported language code.
|
||||
type Lang string
|
||||
|
||||
const (
|
||||
EN Lang = "en"
|
||||
ZH Lang = "zh"
|
||||
)
|
||||
|
||||
// translations holds all UI strings keyed by language then translation key.
|
||||
var translations = map[Lang]map[string]string{
|
||||
EN: {
|
||||
// Nav
|
||||
"site_title": "Go Blog",
|
||||
"home": "Home",
|
||||
"login": "Login",
|
||||
"dashboard": "Dashboard",
|
||||
"logout": "Logout",
|
||||
|
||||
// Home page
|
||||
"home_welcome": "Welcome to Go Blog",
|
||||
"home_subtitle": "A simple, fast blog engine built with Go, Gin, and Tailwind CSS.",
|
||||
"home_sign_in": "Sign In",
|
||||
"home_to_dash": "Dashboard",
|
||||
"home_latest": "Latest Posts",
|
||||
"home_post1_tit": "Getting Started",
|
||||
"home_post1_dsc": "Welcome to your new blog! This is a placeholder post. Start writing to see your content here.",
|
||||
"home_post2_tit": "Hello World",
|
||||
"home_post2_dsc": "Blog posts will appear here once you start publishing from the admin dashboard.",
|
||||
"home_post3_tit": "Built with Go",
|
||||
"home_post3_dsc": "This blog engine uses Go, Gin web framework, GORM, and Tailwind CSS for a modern experience.",
|
||||
|
||||
// Login page
|
||||
"login_title": "Sign In",
|
||||
"login_username": "Username",
|
||||
"login_password": "Password",
|
||||
"login_ph_user": "Enter your username",
|
||||
"login_ph_pass": "Enter your password",
|
||||
"login_submit": "Sign In",
|
||||
"login_error": "Invalid username or password.",
|
||||
|
||||
// Dashboard
|
||||
"dash_title": "Dashboard",
|
||||
"dash_welcome": "Welcome back,",
|
||||
"dash_posts": "Posts",
|
||||
"dash_pages": "Pages",
|
||||
"dash_users": "Users",
|
||||
"dash_mgmt_title": "Blog Management",
|
||||
"dash_mgmt_desc": "Post management, categories, and settings will appear here in future updates.",
|
||||
"dash_page_title": "Dashboard",
|
||||
|
||||
// Footer
|
||||
"footer_text": "© 2026 Go Blog. Powered by Go & Gin.",
|
||||
|
||||
// Page titles
|
||||
"page_home": "Home",
|
||||
"page_login": "Login",
|
||||
|
||||
// Language switcher
|
||||
"lang_switch": "中文",
|
||||
|
||||
// Profile dropdown & page
|
||||
"profile": "Profile",
|
||||
"admin_panel": "Admin Panel",
|
||||
"profile_title": "Edit Profile",
|
||||
"profile_avatar": "Avatar",
|
||||
"profile_change_avatar": "Change Avatar",
|
||||
"profile_display_name": "Display Name",
|
||||
"profile_gender": "Gender",
|
||||
"profile_birthday": "Birthday",
|
||||
"profile_email": "Email",
|
||||
"profile_change_password": "Change Password",
|
||||
"profile_current_password": "Current Password",
|
||||
"profile_new_password": "New Password",
|
||||
"profile_save": "Save Changes",
|
||||
"profile_saved": "Profile updated.",
|
||||
"profile_wrong_password": "Current password is incorrect.",
|
||||
"gender_male": "Male",
|
||||
"gender_female": "Female",
|
||||
"gender_other": "Other",
|
||||
},
|
||||
ZH: {
|
||||
// 导航
|
||||
"site_title": "Go 博客",
|
||||
"home": "首页",
|
||||
"login": "登录",
|
||||
"dashboard": "后台",
|
||||
"logout": "退出",
|
||||
|
||||
// 首页
|
||||
"home_welcome": "欢迎来到 Go Blog",
|
||||
"home_subtitle": "一个使用 Go、Gin 和 Tailwind CSS 构建的简洁快速的博客引擎。",
|
||||
"home_sign_in": "登录",
|
||||
"home_to_dash": "后台管理",
|
||||
"home_latest": "最新文章",
|
||||
"home_post1_tit": "入门指南",
|
||||
"home_post1_dsc": "欢迎来到你的新博客!这是一篇占位文章。开始写作后,你的内容将显示在这里。",
|
||||
"home_post2_tit": "你好,世界",
|
||||
"home_post2_dsc": "当你从后台管理面板发布文章后,博客文章将显示在这里。",
|
||||
"home_post3_tit": "使用 Go 构建",
|
||||
"home_post3_dsc": "此博客引擎使用 Go、Gin Web 框架、GORM 和 Tailwind CSS 构建,提供现代化的体验。",
|
||||
|
||||
// 登录页
|
||||
"login_title": "登录",
|
||||
"login_username": "用户名",
|
||||
"login_password": "密码",
|
||||
"login_ph_user": "请输入用户名",
|
||||
"login_ph_pass": "请输入密码",
|
||||
"login_submit": "登录",
|
||||
"login_error": "用户名或密码错误。",
|
||||
|
||||
// 后台
|
||||
"dash_title": "后台管理",
|
||||
"dash_welcome": "欢迎回来,",
|
||||
"dash_posts": "文章",
|
||||
"dash_pages": "页面",
|
||||
"dash_users": "用户",
|
||||
"dash_mgmt_title": "博客管理",
|
||||
"dash_mgmt_desc": "文章管理、分类和设置将在后续版本中添加。",
|
||||
"dash_page_title": "后台管理",
|
||||
|
||||
// 页脚
|
||||
"footer_text": "© 2026 Go Blog。由 Go & Gin 驱动。",
|
||||
|
||||
// 页面标题
|
||||
"page_home": "首页",
|
||||
"page_login": "登录",
|
||||
|
||||
// 语言切换
|
||||
"lang_switch": "English",
|
||||
|
||||
// 个人中心下拉菜单 & 页面
|
||||
"profile": "个人信息",
|
||||
"admin_panel": "后台管理",
|
||||
"profile_title": "编辑个人信息",
|
||||
"profile_avatar": "头像",
|
||||
"profile_change_avatar": "更换头像",
|
||||
"profile_display_name": "显示名称",
|
||||
"profile_gender": "性别",
|
||||
"profile_birthday": "生日",
|
||||
"profile_email": "邮箱",
|
||||
"profile_change_password": "修改密码",
|
||||
"profile_current_password": "当前密码",
|
||||
"profile_new_password": "新密码",
|
||||
"profile_save": "保存修改",
|
||||
"profile_saved": "个人信息已更新。",
|
||||
"profile_wrong_password": "当前密码错误。",
|
||||
"gender_male": "男",
|
||||
"gender_female": "女",
|
||||
"gender_other": "其他",
|
||||
},
|
||||
}
|
||||
|
||||
// T returns a copy of the translation map for the given language.
|
||||
// Falls back to English if the language is not supported.
|
||||
func T(l Lang) map[string]string {
|
||||
t, ok := translations[l]
|
||||
if !ok {
|
||||
t = translations[EN]
|
||||
}
|
||||
// Return a shallow copy so callers cannot mutate the original map values.
|
||||
out := make(map[string]string, len(t))
|
||||
for k, v := range t {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DetectLang parses the Accept-Language header and returns the best matching
|
||||
// supported language. Returns EN for unsupported languages.
|
||||
func DetectLang(acceptHeader string) Lang {
|
||||
if acceptHeader == "" {
|
||||
return EN
|
||||
}
|
||||
|
||||
// Accept-Language format: "zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7"
|
||||
// Split by comma, then extract primary language from each entry.
|
||||
entries := strings.Split(acceptHeader, ",")
|
||||
for _, entry := range entries {
|
||||
// Trim spaces and remove quality values.
|
||||
entry = strings.TrimSpace(entry)
|
||||
if idx := strings.Index(entry, ";"); idx != -1 {
|
||||
entry = entry[:idx]
|
||||
}
|
||||
// Extract primary language (before any "-" subtag).
|
||||
primary := strings.ToLower(entry)
|
||||
if idx := strings.Index(primary, "-"); idx != -1 {
|
||||
primary = primary[:idx]
|
||||
}
|
||||
switch primary {
|
||||
case "zh":
|
||||
return ZH
|
||||
case "en":
|
||||
return EN
|
||||
}
|
||||
}
|
||||
|
||||
return EN
|
||||
}
|
||||
Reference in New Issue
Block a user