- Add Favicon field to SiteSetting model with FaviconIsURL() helper - Implement favicon upload and management in site settings page - Support both external URL and local file upload for favicon - Add favicon display in page header with automatic URL detection - Add i18n translations for favicon settings (EN/ZH) - Update middleware and helpers to pass favicon data to templates - Include migration script for existing databases Users can now customize their site's favicon from /admin/settings/site by either providing an external URL or uploading a local image file (recommended formats: .ico, .png, .svg, 16x16 or 32x32 pixels). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
773 lines
32 KiB
Go
773 lines
32 KiB
Go
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.",
|
||
|
||
// 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_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",
|
||
|
||
// 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_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": "请填写所有字段。",
|
||
|
||
// 后台
|
||
"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_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": "操作",
|
||
|
||
// 平台设置
|
||
"settings_nav": "平台设置",
|
||
"settings_saved": "设置已保存。",
|
||
"settings_site_title": "站点设置",
|
||
"settings_site_desc": "Logo、左上角标题、顶部横幅文本和页脚文本。",
|
||
"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
|
||
}
|