Files
kevinandClaude Fable 5 2bdc368399 feat: implement tag management and search functionality
Features:
- Tag system with multi-language support (Chinese/English)
- Auto-create tags when associating with articles
- Tag filtering on homepage with visual indicators
- Full-text search across title, summary, and content
- Tag cloud sidebar showing article counts
- Search page with results display

Technical Implementation:
- Database models: Tag, ArticleTag (many-to-many)
- Tag count caching for performance
- Automatic tag slug generation
- Responsive design with mobile support
- I18n support for all new UI elements

Bug Fix:
- Fixed SQL column ambiguity error in tag filtering
- Added table prefix to ORDER BY clause in publishedArticleOrder

Routes:
- GET /search - Search results page
- GET /?tag=<slug> - Filter articles by tag
- GET /api/articles?tag=<slug> - API with tag filter support

Templates:
- Added tag input field to article create/edit form
- Added search box to homepage header
- Added tag cloud sidebar on homepage and search page
- Created new search results page template

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:59:32 +08:00

903 lines
38 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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_read_more": "Read more",
"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.",
"home_no_posts": "No published posts yet.",
// 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.",
"login_required": "Please fill in all fields.",
"login_no_account": "Don't have an account?",
"login_register_link": "Register",
// Register page
"page_register": "Register",
"register_title": "Create Account",
"register_username": "Username",
"register_ph_username": "Choose a username",
"register_username_hint": "3-32 characters, letters, numbers, underscore and dash only",
"register_display_name": "Display Name",
"register_ph_display_name": "Your display name",
"register_display_name_hint": "Optional, defaults to username if empty",
"register_email": "Email",
"register_ph_email": "Your email address",
"register_email_hint": "Optional, used for Gravatar avatar",
"register_password": "Password",
"register_ph_password": "Choose a password",
"register_password_hint": "At least 6 characters",
"register_confirm_password": "Confirm Password",
"register_ph_confirm_password": "Re-enter your password",
"register_submit": "Register",
"register_have_account": "Already have an account?",
"register_login_link": "Sign In",
"register_required": "Username and password are required.",
"register_username_length": "Username must be 3-32 characters.",
"register_password_length": "Password must be at least 6 characters.",
"register_password_mismatch": "Passwords do not match.",
"register_error": "Registration failed. Please try again.",
// Settings
"settings_allow_registration": "Allow user registration",
"settings_allow_registration_hint": "When enabled, visitors can create their own accounts from the login page",
// Dashboard
"dash_title": "Dashboard",
"dash_welcome": "Welcome back,",
"dash_posts": "Posts",
"dash_pages": "Pages",
"dash_users": "Users",
"dash_published": "Published",
"dash_mgmt_title": "Blog Management",
"dash_mgmt_desc": "Post management, categories, and settings will appear here in future updates.",
"dash_new_article": "New Article",
"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",
"my_articles": "My Articles",
"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.",
"profile_upload_invalid": "File type not allowed.",
"profile_upload_disabled": "Uploads are currently disabled.",
"profile_upload_too_large": "File is too large. Limit: %s",
"gender_male": "Male",
"gender_female": "Female",
"gender_other": "Other",
// Avatar cropping
"crop_avatar_title": "Crop Avatar",
"crop_cancel": "Cancel",
"crop_confirm": "Confirm",
"crop_uploading": "Uploading...",
"crop_success": "Avatar updated.",
"crop_error": "Failed to upload avatar. Please try again.",
// Article creation
"article_create_title": "Create Article",
"article_field_title": "Title",
"article_title": "Title",
"article_field_slug": "Slug",
"article_slug": "Slug",
"article_slug_hint": "Auto-generated from title if empty",
"article_slug_help": "Auto-generated from title if empty",
"article_field_summary": "Summary",
"article_summary": "Summary",
"article_field_content": "Content",
"article_content": "Content",
"article_field_cover": "Cover Image URL",
"article_cover": "Cover Image URL",
"article_cover_help": "Optional cover image URL",
"article_field_status": "Status",
"article_status": "Status",
"article_draft": "Draft",
"article_published": "Published",
"article_field_is_top": "Pin to top",
"article_is_top": "Pin to top",
"article_published_at": "Published Time",
"article_published_at_hint": "Leave blank to auto-set on publish",
"article_save_draft": "Save Draft",
"article_save": "Save",
"article_cancel": "Cancel",
"article_publish": "Publish",
"article_created": "Article created successfully.",
"article_error": "Failed to create article.",
"article_new": "New Article",
"article_title_required": "Title is required.",
"article_content_required": "Content is required.",
"article_back_home": "Back to Home",
"article_not_found": "Article not found.",
"article_attachments": "Attachments",
"article_upload": "Upload",
"article_att_name": "File",
"article_att_size": "Size",
"article_att_insert": "Insert",
"article_att_set_cover": "Set as Cover",
"article_att_cover_set": "Cover URL set.",
"article_att_pick": "Please choose a file.",
"article_att_uploading": "Uploading...",
"article_att_error": "Upload failed. Please try again.",
"article_att_delete_confirm": "Delete this attachment?",
// Article management
"article_list_title": "Articles",
"my_articles_title": "My Articles",
"article_edit_title": "Edit Article",
"article_manage": "Manage Articles",
"article_edit": "Edit",
"article_delete": "Delete",
"article_delete_confirm": "Delete this article?",
"article_updated": "Article updated.",
"article_deleted": "Article deleted.",
"article_archived": "Archived",
"article_col_title": "Title",
"article_col_status": "Status",
"article_col_published": "Published",
"article_col_actions": "Actions",
"article_last_updated": "Last Updated",
// Tags
"tags_title": "Tags",
"article_tags": "Tags",
"article_tags_hint": "Comma-separated tag names, e.g., golang, web, tutorial",
"tag_filter": "Filter by tag",
"tag_all": "All",
// Search
"search_placeholder": "Search articles...",
"search_title": "Search Results",
"search_results_for": "Search results for",
"search_no_results": "No articles found matching your search.",
"search_keyword": "Keyword",
// Settings (platform configuration)
"settings_nav": "Platform Settings",
"settings_saved": "Settings saved.",
"settings_site_title": "Site Settings",
"settings_site_desc": "Logo, top-left title, header banner, and footer text.",
"settings_navlinks_title": "Navigation Links",
"settings_navlinks_desc": "Manage custom links in the header navigation.",
"navlinks_add": "Add Link",
"navlinks_title_zh": "Link Text (Chinese)",
"navlinks_title_en": "Link Text (English)",
"navlinks_url": "URL",
"navlinks_url_hint": "Can be relative (/about) or absolute (https://example.com)",
"navlinks_open_new": "Open in new window",
"navlinks_enabled": "Enabled",
"navlinks_sort": "Sort Order",
"navlinks_edit": "Edit",
"navlinks_delete": "Delete",
"navlinks_confirm_delete": "Are you sure you want to delete this link?",
"navlinks_no_links": "No navigation links configured yet.",
"navlinks_save": "Save",
"navlinks_cancel": "Cancel",
"settings_logo": "Logo",
"settings_logo_url": "Logo URL (external link)",
"settings_logo_upload": "Or upload a logo image",
"settings_logo_clear": "Remove current logo",
"settings_favicon": "Favicon",
"settings_favicon_url": "Favicon URL (external link)",
"settings_favicon_current": "Current favicon",
"settings_favicon_hint": "Recommended: .ico, .png or .svg format, 32x32 or 16x16 pixels",
"settings_favicon_clear": "Remove current favicon",
"settings_logo_text_zh": "Site Title (Chinese)",
"settings_logo_text_en": "Site Title (English)",
"settings_header_text_zh": "Header Banner Text (Chinese)",
"settings_header_text_en": "Header Banner Text (English)",
"settings_home_welcome_zh": "Home Welcome Heading (Chinese)",
"settings_home_welcome_en": "Home Welcome Heading (English)",
"settings_home_subtitle_zh": "Home Subtitle (Chinese)",
"settings_home_subtitle_en": "Home Subtitle (English)",
"settings_footer_text_zh": "Footer Text (Chinese)",
"settings_footer_text_en": "Footer Text (English)",
"settings_leave_blank": "Leave blank to use the built-in default.",
"settings_save": "Save",
"settings_upload_title": "Upload Settings",
"settings_upload_desc": "Attachment upload policy and permitted file types.",
"settings_uploads_enabled":"Enable attachments",
"settings_default_size": "Default max size (MB)",
"settings_storage_dir": "Storage sub-directory",
"settings_file_types": "Permitted File Types",
"settings_add_type": "Add File Type",
"settings_extension": "Extension",
"settings_mime": "MIME Type",
"settings_category": "Category",
"settings_max_size": "Max Size (MB)",
"settings_enabled": "Enabled",
"settings_actions": "Actions",
"settings_toggle": "Toggle",
"settings_delete": "Delete",
"settings_update": "Update",
"settings_download_title": "Download Settings",
"settings_download_desc": "Base URLs used to build attachment download links.",
"settings_add_url": "Add Base URL",
"settings_url_name": "Name",
"settings_base_url": "Base URL",
"settings_priority": "Priority",
"settings_is_default": "Default",
"settings_set_default": "Set Default",
"cat_image": "Image",
"cat_document": "Document",
"cat_archive": "Archive",
"cat_video": "Video",
"cat_other": "Other",
// Comments
"comments_title": "Comments",
"comments_count": "%d Comments",
"comments_empty": "No comments yet. Be the first to comment.",
"comments_reply": "Reply",
"comments_cancel_reply": "Cancel reply",
"comments_replying_to": "Replying to %s",
"comments_name": "Name",
"comments_email": "Email",
"comments_email_hint": "Used for your Gravatar avatar; never shown publicly.",
"comments_website": "Website (optional)",
"comments_content": "Comment",
"comments_content_ph": "Markdown is supported...",
"comments_private": "Private comment",
"comments_private_hint": "Only you and the administrator can see this.",
"comments_preview": "Preview",
"comments_edit": "Edit",
"comments_emoji": "Emoji",
"comments_markdown_help": "Markdown help",
"comments_submit": "Post Comment",
"comments_posted": "Comment posted.",
"comments_pending_notice": "Your comment is awaiting moderation.",
"comments_disabled": "Comments are disabled.",
"comments_guests_disabled": "You must sign in to comment.",
"comments_required_name": "Name is required.",
"comments_required_email": "Email is required.",
"comments_required_content":"Comment is required.",
"comments_invalid_email": "Please enter a valid email address.",
"comments_invalid_url": "Please enter a valid URL.",
"comments_too_long": "Comment is too long (max %d characters).",
"comments_pending": "Pending",
"comments_approved": "Approved",
"comments_rejected": "Rejected",
"comments_private_badge": "Private",
"comments_markdown_bold": "**bold**",
"comments_markdown_italic": "*italic*",
"comments_markdown_code": "`code`",
"comments_markdown_link": "[text](url)",
"comments_markdown_quote": "> quote",
// Admin: comments
"admin_comments": "Comments",
"comment_manage": "Manage Comments",
"admin_comments_title": "Comments",
"comment_status_pending": "Pending",
"comment_status_approved": "Approved",
"comment_status_rejected": "Rejected",
"comment_status_all": "All",
"comment_approve": "Approve",
"comment_reject": "Reject",
"comment_delete": "Delete",
"comment_delete_confirm": "Delete this comment?",
"comment_col_author": "Author",
"comment_col_article": "Article",
"comment_col_content": "Content",
"comment_col_status": "Status",
"comment_col_time": "Time",
"comment_col_actions": "Actions",
"comment_empty": "No comments.",
"comment_approved": "Comment approved.",
"comment_rejected": "Comment rejected.",
"comment_deleted": "Comment deleted.",
// Admin: comment settings
"comment_settings_title": "Comment Settings",
"comment_settings_desc": "Control whether comments are enabled and how they are moderated.",
"comment_settings_enabled": "Enable comments",
"comment_settings_allow_guest": "Allow anonymous comments",
"comment_settings_guest_approval": "Require approval for guest comments",
"comment_settings_use_gravatar": "Use Gravatar avatars",
"comment_settings_use_gravatar_hint": "When off, avatars show the author's initial on a colored background.",
// Admin: user management
"admin_users": "Users",
"admin_users_title": "Users",
"user_list_title": "User Management",
"user_new": "New User",
"user_create_title": "Create User",
"user_edit_title": "Edit User",
"user_col_username": "Username",
"user_col_display": "Display Name",
"user_col_email": "Email",
"user_col_role": "Role",
"user_col_status": "Status",
"user_col_created": "Created",
"user_col_actions": "Actions",
"user_username": "Username",
"user_password": "Password",
"user_password_hint": "Leave blank to keep the current password.",
"user_display_name": "Display Name",
"user_email": "Email",
"user_gender": "Gender",
"user_birthday": "Birthday",
"user_role": "Role",
"user_status": "Status",
"role_admin": "Admin",
"role_author": "Author",
"status_normal": "Normal",
"status_disabled": "Disabled",
"status_locked": "Locked",
"status_unactivated": "Unactivated",
"user_created": "User created.",
"user_updated": "User updated.",
"user_deleted": "User deleted.",
"user_delete_confirm": "Delete this user?",
"user_empty": "No users.",
"user_username_required": "Username is required.",
"user_password_required": "Password is required.",
"user_username_exists": "Username already exists.",
"user_cannot_delete_self": "You cannot delete your own account.",
"user_cannot_disable_self": "You cannot disable or lock your own account.",
"user_cannot_remove_last_admin": "Cannot remove the last administrator.",
"dash_user_mgmt": "Manage Users",
// Analytics
"analytics_views_title": "Reading Analytics",
"analytics_views_desc": "Track article views, identify unique visitors and detect bots.",
"analytics_stats_total": "Total Views",
"analytics_stats_human_views": "Human Views",
"analytics_stats_bot_views": "Bot Views",
"analytics_stats_unique_ips": "Unique IPs",
"analytics_stats_unique_users": "Unique Users",
"analytics_article_stats": "Article Statistics (Top 20)",
"analytics_col_article": "Article",
"analytics_col_total_views": "Total Views",
"analytics_col_human_views": "Human",
"analytics_col_bot_views": "Bots",
"analytics_col_unique_ips": "Unique IPs",
"analytics_col_time": "Time",
"analytics_col_user": "User",
"analytics_col_ip": "IP Address",
"analytics_col_user_agent": "User Agent",
"analytics_col_is_bot": "Type",
"analytics_filters": "Filters",
"analytics_filter_article": "Article Title",
"analytics_filter_article_placeholder": "Search article title",
"analytics_filter_ip": "IP Address",
"analytics_filter_ip_placeholder": "Search by IP",
"analytics_filter_show_bots": "Show Bots",
"analytics_include_bots": "Include bot traffic",
"analytics_apply_filters": "Apply Filters",
"analytics_recent_views": "Recent Views",
"analytics_bot": "Bot",
"analytics_human": "Human",
"analytics_anonymous": "Anonymous",
"analytics_article_deleted": "(Deleted)",
"analytics_no_data": "No data available.",
"analytics_no_views": "No view records found.",
"analytics_showing": "Showing",
"analytics_of": "of",
"analytics_load_more": "Load More",
},
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_read_more": "阅读全文",
"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 构建,提供现代化的体验。",
"home_no_posts": "暂无已发布的文章。",
// 登录页
"login_title": "登录",
"login_username": "用户名",
"login_password": "密码",
"login_ph_user": "请输入用户名",
"login_ph_pass": "请输入密码",
"login_submit": "登录",
"login_error": "用户名或密码错误。",
"login_required": "请填写所有字段。",
"login_no_account": "还没有账号?",
"login_register_link": "注册",
// 注册页
"page_register": "注册",
"register_title": "创建账号",
"register_username": "用户名",
"register_ph_username": "请输入用户名",
"register_username_hint": "3-32个字符,仅限字母、数字、下划线和连字符",
"register_display_name": "显示名称",
"register_ph_display_name": "请输入显示名称",
"register_display_name_hint": "可选,留空则使用用户名",
"register_email": "邮箱",
"register_ph_email": "请输入邮箱地址",
"register_email_hint": "可选,用于显示 Gravatar 头像",
"register_password": "密码",
"register_ph_password": "请输入密码",
"register_password_hint": "至少6个字符",
"register_confirm_password": "确认密码",
"register_ph_confirm_password": "请再次输入密码",
"register_submit": "注册",
"register_have_account": "已有账号?",
"register_login_link": "登录",
"register_required": "用户名和密码不能为空。",
"register_username_length": "用户名必须是3-32个字符。",
"register_password_length": "密码至少需要6个字符。",
"register_password_mismatch": "两次输入的密码不一致。",
"register_error": "注册失败,请重试。",
// 平台设置
"settings_allow_registration": "允许用户注册",
"settings_allow_registration_hint": "启用后,访客可以从登录页面创建自己的账号",
// 后台
"dash_title": "后台管理",
"dash_welcome": "欢迎回来,",
"dash_posts": "文章",
"dash_pages": "页面",
"dash_users": "用户",
"dash_published": "已发布",
"dash_mgmt_title": "博客管理",
"dash_mgmt_desc": "文章管理、分类和设置将在后续版本中添加。",
"dash_new_article": "新建文章",
"dash_page_title": "后台管理",
// 页脚
"footer_text": "© 2026 Go Blog。由 Go & Gin 驱动。",
"page_home": "首页",
"page_login": "登录",
// 语言切换
"lang_switch": "English",
// 个人中心下拉菜单 & 页面
"profile": "个人信息",
"admin_panel": "后台管理",
"my_articles": "我的文章",
"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": "当前密码错误。",
"profile_upload_invalid": "不允许的文件类型。",
"profile_upload_disabled": "上传功能已关闭。",
"profile_upload_too_large": "文件过大。限制:%s",
"gender_male": "男",
"gender_female": "女",
"gender_other": "其他",
// 头像裁剪
"crop_avatar_title": "裁剪头像",
"crop_cancel": "取消",
"crop_confirm": "确认",
"crop_uploading": "上传中...",
"crop_success": "头像已更新。",
"crop_error": "头像上传失败,请重试。",
// 文章创建
"article_create_title": "新建文章",
"article_field_title": "标题",
"article_title": "标题",
"article_field_slug": "Slug",
"article_slug": "Slug",
"article_slug_hint": "留空则从标题自动生成",
"article_slug_help": "留空则从标题自动生成",
"article_field_summary": "摘要",
"article_summary": "摘要",
"article_field_content": "正文",
"article_content": "正文",
"article_field_cover": "封面图 URL",
"article_cover": "封面图 URL",
"article_cover_help": "可选的封面图片链接",
"article_field_status": "状态",
"article_status": "状态",
"article_draft": "草稿",
"article_published": "已发布",
"article_field_is_top": "置顶",
"article_is_top": "置顶",
"article_published_at": "发布时间",
"article_published_at_hint": "留空则在发布时自动设置",
"article_save_draft": "保存草稿",
"article_save": "保存",
"article_cancel": "取消",
"article_publish": "发布",
"article_created": "文章创建成功。",
"article_error": "创建文章失败。",
"article_new": "新建文章",
"article_title_required": "标题不能为空。",
"article_content_required": "正文不能为空。",
"article_back_home": "返回首页",
"article_not_found": "文章不存在。",
"article_attachments": "附件",
"article_upload": "上传",
"article_att_name": "文件",
"article_att_size": "大小",
"article_att_insert": "插入正文",
"article_att_set_cover": "设为封面",
"article_att_cover_set": "已设为封面。",
"article_att_pick": "请先选择文件。",
"article_att_uploading": "上传中...",
"article_att_error": "上传失败,请重试。",
"article_att_delete_confirm": "删除该附件?",
// 文章管理
"article_list_title": "文章管理",
"my_articles_title": "我的文章",
"article_edit_title": "编辑文章",
"article_manage": "文章管理",
"article_edit": "编辑",
"article_delete": "删除",
"article_delete_confirm": "确认删除这篇文章?",
"article_updated": "文章已更新。",
"article_deleted": "文章已删除。",
"article_archived": "已归档",
"article_col_title": "标题",
"article_col_status": "状态",
"article_col_published": "发布时间",
"article_col_actions": "操作",
"article_last_updated": "最后更新",
// 标签
"tags_title": "标签",
"article_tags": "标签",
"article_tags_hint": "逗号分隔的标签名称,例如:golang, web, 教程",
"tag_filter": "按标签筛选",
"tag_all": "全部",
// 搜索
"search_placeholder": "搜索文章...",
"search_title": "搜索结果",
"search_results_for": "搜索结果",
"search_no_results": "未找到匹配的文章。",
"search_keyword": "关键词",
// 平台设置
"settings_nav": "平台设置",
"settings_saved": "设置已保存。",
"settings_site_title": "站点设置",
"settings_site_desc": "Logo、左上角标题、顶部横幅文本和页脚文本。",
"settings_navlinks_title": "导航链接",
"settings_navlinks_desc": "管理顶部导航栏的自定义链接。",
"navlinks_add": "添加链接",
"navlinks_title_zh": "链接文字(中文)",
"navlinks_title_en": "链接文字(英文)",
"navlinks_url": "链接地址",
"navlinks_url_hint": "可以是相对路径(/about)或完整链接(https://example.com",
"navlinks_open_new": "在新窗口打开",
"navlinks_enabled": "启用",
"navlinks_sort": "排序",
"navlinks_edit": "编辑",
"navlinks_delete": "删除",
"navlinks_confirm_delete": "确定要删除这个链接吗?",
"navlinks_no_links": "还没有配置导航链接。",
"navlinks_save": "保存",
"navlinks_cancel": "取消",
"settings_logo": "Logo",
"settings_logo_url": "Logo 链接(外链地址)",
"settings_logo_upload": "或上传 Logo 图片",
"settings_logo_clear": "移除当前 Logo",
"settings_favicon": "网站图标(Favicon",
"settings_favicon_url": "Favicon 链接(外链地址)",
"settings_favicon_current": "当前 Favicon",
"settings_favicon_hint": "推荐:.ico、.png 或 .svg 格式,32x32 或 16x16 像素",
"settings_favicon_clear": "移除当前 Favicon",
"settings_logo_text_zh": "站点标题(中文)",
"settings_logo_text_en": "站点标题(英文)",
"settings_header_text_zh": "顶部横幅文本(中文)",
"settings_header_text_en": "顶部横幅文本(英文)",
"settings_home_welcome_zh": "主页欢迎标题(中文)",
"settings_home_welcome_en": "主页欢迎标题(英文)",
"settings_home_subtitle_zh": "主页副标题(中文)",
"settings_home_subtitle_en": "主页副标题(英文)",
"settings_footer_text_zh": "页脚文本(中文)",
"settings_footer_text_en": "页脚文本(英文)",
"settings_leave_blank": "留空则使用内置默认值。",
"settings_save": "保存",
"settings_upload_title": "上传设置",
"settings_upload_desc": "附件上传策略与允许的文件类型。",
"settings_uploads_enabled":"启用附件上传",
"settings_default_size": "默认最大大小(MB",
"settings_storage_dir": "存储子目录",
"settings_file_types": "允许的文件类型",
"settings_add_type": "添加文件类型",
"settings_extension": "扩展名",
"settings_mime": "MIME 类型",
"settings_category": "分组",
"settings_max_size": "最大大小(MB",
"settings_enabled": "启用",
"settings_actions": "操作",
"settings_toggle": "切换",
"settings_delete": "删除",
"settings_update": "更新",
"settings_download_title": "下载设置",
"settings_download_desc": "用于拼接附件下载链接的基础地址。",
"settings_add_url": "添加基础地址",
"settings_url_name": "名称",
"settings_base_url": "基础地址",
"settings_priority": "优先级",
"settings_is_default": "默认",
"settings_set_default": "设为默认",
"cat_image": "图片",
"cat_document": "文档",
"cat_archive": "压缩包",
"cat_video": "视频",
"cat_other": "其他",
// 评论
"comments_title": "评论",
"comments_count": "%d 条评论",
"comments_empty": "暂无评论,快来抢沙发吧。",
"comments_reply": "回复",
"comments_cancel_reply": "取消回复",
"comments_replying_to": "回复 %s",
"comments_name": "名称",
"comments_email": "邮箱",
"comments_email_hint": "用于显示 Gravatar 头像,不会公开。",
"comments_website": "网址(可选)",
"comments_content": "评论内容",
"comments_content_ph": "支持 Markdown 语法...",
"comments_private": "私密评论",
"comments_private_hint": "仅你和管理员可见。",
"comments_preview": "预览",
"comments_edit": "编辑",
"comments_emoji": "表情",
"comments_markdown_help": "Markdown 帮助",
"comments_submit": "发表评论",
"comments_posted": "评论已发表。",
"comments_pending_notice": "你的评论正在等待审核。",
"comments_disabled": "评论功能已关闭。",
"comments_guests_disabled": "请先登录后再评论。",
"comments_required_name": "请填写名称。",
"comments_required_email": "请填写邮箱。",
"comments_required_content":"请填写评论内容。",
"comments_invalid_email": "请输入有效的邮箱地址。",
"comments_invalid_url": "请输入有效的网址。",
"comments_too_long": "评论内容过长(最多 %d 字)。",
"comments_pending": "待审核",
"comments_approved": "已通过",
"comments_rejected": "已拒绝",
"comments_private_badge": "私密",
"comments_markdown_bold": "**粗体**",
"comments_markdown_italic": "*斜体*",
"comments_markdown_code": "`代码`",
"comments_markdown_link": "[文本](网址)",
"comments_markdown_quote": "> 引用",
// 后台:评论
"admin_comments": "评论管理",
"comment_manage": "评论管理",
"admin_comments_title": "评论管理",
"comment_status_pending": "待审核",
"comment_status_approved": "已通过",
"comment_status_rejected": "已拒绝",
"comment_status_all": "全部",
"comment_approve": "通过",
"comment_reject": "拒绝",
"comment_delete": "删除",
"comment_delete_confirm": "删除该评论?",
"comment_col_author": "评论者",
"comment_col_article": "文章",
"comment_col_content": "内容",
"comment_col_status": "状态",
"comment_col_time": "时间",
"comment_col_actions": "操作",
"comment_empty": "暂无评论。",
"comment_approved": "评论已通过。",
"comment_rejected": "评论已拒绝。",
"comment_deleted": "评论已删除。",
// 后台:评论设置
"comment_settings_title": "评论设置",
"comment_settings_desc": "控制是否启用评论以及评论的审核方式。",
"comment_settings_enabled": "启用评论",
"comment_settings_allow_guest": "允许匿名评论",
"comment_settings_guest_approval": "非登录用户评论需审核",
"comment_settings_use_gravatar": "使用 Gravatar 头像",
"comment_settings_use_gravatar_hint": "关闭后,头像将显示作者名称首字母的彩色占位。",
// 后台:用户管理
"admin_users": "用户",
"admin_users_title": "用户",
"user_list_title": "用户管理",
"user_new": "新建用户",
"user_create_title": "新建用户",
"user_edit_title": "编辑用户",
"user_col_username": "用户名",
"user_col_display": "显示名称",
"user_col_email": "邮箱",
"user_col_role": "角色",
"user_col_status": "状态",
"user_col_created": "创建时间",
"user_col_actions": "操作",
"user_username": "用户名",
"user_password": "密码",
"user_password_hint": "留空则保持原密码不变。",
"user_display_name": "显示名称",
"user_email": "邮箱",
"user_gender": "性别",
"user_birthday": "生日",
"user_role": "角色",
"user_status": "状态",
"role_admin": "管理员",
"role_author": "作者",
"status_normal": "正常",
"status_disabled": "禁用",
"status_locked": "锁定",
"status_unactivated": "未激活",
"user_created": "用户已创建。",
"user_updated": "用户已更新。",
"user_deleted": "用户已删除。",
"user_delete_confirm": "确认删除该用户?",
"user_empty": "暂无用户。",
"user_username_required": "请填写用户名。",
"user_password_required": "请填写密码。",
"user_username_exists": "用户名已存在。",
"user_cannot_delete_self": "不能删除自己的账号。",
"user_cannot_disable_self": "不能禁用或锁定自己的账号。",
"user_cannot_remove_last_admin": "不能移除最后一个管理员。",
"dash_user_mgmt": "用户管理",
// 阅读统计
"analytics_views_title": "阅读统计",
"analytics_views_desc": "追踪文章阅读量,识别独立访客和爬虫。",
"analytics_stats_total": "总阅读量",
"analytics_stats_human_views": "真人阅读",
"analytics_stats_bot_views": "爬虫访问",
"analytics_stats_unique_ips": "独立IP",
"analytics_stats_unique_users": "独立用户",
"analytics_article_stats": "文章统计(前20篇)",
"analytics_col_article": "文章",
"analytics_col_total_views": "总阅读量",
"analytics_col_human_views": "真人",
"analytics_col_bot_views": "爬虫",
"analytics_col_unique_ips": "独立IP",
"analytics_col_time": "时间",
"analytics_col_user": "用户",
"analytics_col_ip": "IP地址",
"analytics_col_user_agent": "User Agent",
"analytics_col_is_bot": "类型",
"analytics_filters": "筛选条件",
"analytics_filter_article": "文章标题",
"analytics_filter_article_placeholder": "搜索文章标题",
"analytics_filter_ip": "IP地址",
"analytics_filter_ip_placeholder": "按IP搜索",
"analytics_filter_show_bots": "显示爬虫",
"analytics_include_bots": "包含爬虫流量",
"analytics_apply_filters": "应用筛选",
"analytics_recent_views": "最近访问记录",
"analytics_bot": "爬虫",
"analytics_human": "真人",
"analytics_anonymous": "匿名用户",
"analytics_article_deleted": "(已删除)",
"analytics_no_data": "暂无数据。",
"analytics_no_views": "未找到访问记录。",
"analytics_showing": "显示",
"analytics_of": "共",
"analytics_load_more": "加载更多",
},
}
// 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
}