- Comment model with nested replies (ParentID), dual authorship (logged-in UserID / anonymous GuestToken cookie), email hash for Gravatar, private flag, and moderation status - CommentConfig singleton (enabled / allow guest / guest-require-approval / use Gravatar) cached like the other platform config - Markdown comments with built-in emoji picker, preview, and markdown help; rendered client-side via marked + DOMPurify, with server-side HTML/dangerous-scheme stripping as a first XSS defense - Private comments visible only to admin and the author; pending comments visible only to admin and the author - Admin moderation list (pending/approved/rejected/all tabs) with approve/reject/delete and a pending-count badge - Comment settings page with the four toggles - One-time session flash notice (auto-dismissed after 4s) so the "comment posted" banner no longer persists across refreshes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
575 lines
23 KiB
Go
575 lines
23 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",
|
||
"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_title": "Title",
|
||
"article_slug": "Slug",
|
||
"article_slug_hint": "Auto-generated from title if empty",
|
||
"article_summary": "Summary",
|
||
"article_content": "Content",
|
||
"article_cover": "Cover Image URL",
|
||
"article_status": "Status",
|
||
"article_draft": "Draft",
|
||
"article_published": "Published",
|
||
"article_is_top": "Pin to top",
|
||
"article_save_draft": "Save Draft",
|
||
"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",
|
||
"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_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",
|
||
"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.",
|
||
},
|
||
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": "后台管理",
|
||
"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_title": "标题",
|
||
"article_slug": "Slug",
|
||
"article_slug_hint": "留空则从标题自动生成",
|
||
"article_summary": "摘要",
|
||
"article_content": "正文",
|
||
"article_cover": "封面图 URL",
|
||
"article_status": "状态",
|
||
"article_draft": "草稿",
|
||
"article_published": "已发布",
|
||
"article_is_top": "置顶",
|
||
"article_save_draft": "保存草稿",
|
||
"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": "文章管理",
|
||
"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_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": "评论管理",
|
||
"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": "关闭后,头像将显示作者名称首字母的彩色占位。",
|
||
},
|
||
}
|
||
|
||
// 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
|
||
}
|