registered-users code> 组被授予特定权限,所有其他组都会收到隐式权限 strong>,即使它们未被明确定义/检查。 将显示此隐式权限,因为所有用户都是 registered-users code> 用户组的一部分,因此无需显式授予其他组的权限。",
+ "privileges.copy-from-category": "从版块复制",
+ "privileges.inherit": "如果 registered-users
组被授予特定权限,所有其他组都会收到隐式权限,即使它们未被明确定义/检查。 将显示此隐式权限,因为所有用户都是 registered-users
群组的一部分,因此无需显式授予其他组的权限。",
- "analytics.back": "返回板块列表",
- "analytics.title": "“%1”板块的统计",
- "analytics.pageviews-hourly": "图1 strong> – 此板块的每小时页面浏览量 small>",
- "analytics.pageviews-daily": "图2 strong> – 此板块的每日页面浏览量",
- "analytics.topics-daily": "图3 strong> – 每日在此板块中创建的主题 small>",
- "analytics.posts-daily": "图4 strong> – 每日在此板块中每日发布的帖子 small>",
+ "analytics.back": "返回版块列表",
+ "analytics.title": "“%1”版块的统计",
+ "analytics.pageviews-hourly": "图1 – 此版块的每小时页面浏览量",
+ "analytics.pageviews-daily": "图2 – 此版块的每日页面浏览量",
+ "analytics.topics-daily": "图3 – 每日在此版块中创建的主题",
+ "analytics.posts-daily": "图4 – 每日在此版块中每日发布的帖子",
"alert.created": "创建",
- "alert.create-success": "板块创建成功!",
- "alert.none-active": "您没有有效的板块。",
- "alert.create": "创建一个板块",
- "alert.confirm-moderate": "您确定要将审核权限授予此用户组吗? strong>此群组是公开的,任何用户都可以随意加入。",
- "alert.confirm-purge": "您确定要清除此板块“%1”吗? p>
警告! strong> 板块将被清除! h5> 清除板块将删除所有主题和帖子,并从数据库中删除板块。 如果您想暂时 em>移除板块,请使用停用板块。 p>",
- "alert.purge-success": "板块已删除!",
+ "alert.create-success": "版块创建成功!",
+ "alert.none-active": "您没有有效的版块。",
+ "alert.create": "创建一个版块",
+ "alert.confirm-moderate": "您确定要将审核权限授予此群组吗?此群组是公开的,任何用户都可以随意加入。",
+ "alert.confirm-purge": "您确定要清除此版块“%1”吗?
警告! 版块将被清除!
清除版块将删除所有主题和帖子,并从数据库中删除版块。 如果您想暂时移除版块,请使用停用版块。
",
+ "alert.purge-success": "版块已删除!",
"alert.copy-success": "设置已复制!",
- "alert.set-parent-category": "设置父板块",
- "alert.updated": "板块已更新",
- "alert.updated-success": "板块ID %1 成功更新。",
- "alert.upload-image": "上传板块图片",
+ "alert.set-parent-category": "设置父版块",
+ "alert.updated": "版块已更新",
+ "alert.updated-success": "版块ID %1 成功更新。",
+ "alert.upload-image": "上传版块图片",
"alert.find-user": "查找用户",
"alert.user-search": "在这里查找用户…",
- "alert.find-group": "查找用户组",
- "alert.group-search": "在此处搜索用户组..."
+ "alert.find-group": "查找群组",
+ "alert.group-search": "在此处搜索群组..."
}
\ No newline at end of file
diff --git a/public/language/zh-CN/admin/manage/groups.json b/public/language/zh-CN/admin/manage/groups.json
index f2b90b98a0..131199f22c 100644
--- a/public/language/zh-CN/admin/manage/groups.json
+++ b/public/language/zh-CN/admin/manage/groups.json
@@ -1,31 +1,31 @@
{
- "name": "用户组名",
- "description": "用户组描述",
- "system": "系统用户组",
+ "name": "群组名",
+ "description": "群组描述",
+ "system": "系统群组",
"edit": "编辑",
- "search-placeholder": "索索",
- "create": "创建用户组",
- "description-placeholder": "一个关于你的用户组的简短描述",
+ "search-placeholder": "搜索",
+ "create": "创建群组",
+ "description-placeholder": "一个关于你的群组的简短描述",
"create-button": "创建",
- "alerts.create-failure": "哦不!创建您的用户组时出现问题。 请稍后再试!
",
- "alerts.confirm-delete": "确认要删除这个用户组么?",
+ "alerts.create-failure": "哦不!创建您的群组时出现问题。 请稍后再试!
",
+ "alerts.confirm-delete": "确认要删除这个群组么?",
"edit.name": "名字",
"edit.description": "描述",
"edit.user-title": "成员标题",
- "edit.icon": "用户组标志",
+ "edit.icon": "群组标志",
"edit.label-color": "群组标签颜色",
"edit.show-badge": "显示徽章",
- "edit.private-details": "启用此选项后,加入用户组的请求将需要组长审批。",
- "edit.private-override": "警告:系统禁用了私有用户组,优先于该选项。",
+ "edit.private-details": "启用此选项后,加入群组的请求将需要群组所有者审批。",
+ "edit.private-override": "警告:系统已禁用了私有群组,优先级高于该选项。",
"edit.disable-requests": "禁止加入请求",
"edit.hidden": "隐藏",
- "edit.hidden-details": "启用此选项后,此用户组将不在用户组列表展现,并且用户只能被手动邀请加入",
- "edit.add-user": "向此小组添加成员",
+ "edit.hidden-details": "启用此选项后,此群组将不在群组列表展现,并且用户只能被手动邀请加入",
+ "edit.add-user": "向此群组添加成员",
"edit.add-user-search": "搜索用户",
"edit.members": "成员列表",
- "control-panel": "小组控制面板",
+ "control-panel": "群组控制面板",
"revert": "重置",
"edit.no-users-found": "没有找到用户",
diff --git a/public/language/zh-CN/admin/manage/registration.json b/public/language/zh-CN/admin/manage/registration.json
index 26ec422c98..aa46fcdb49 100644
--- a/public/language/zh-CN/admin/manage/registration.json
+++ b/public/language/zh-CN/admin/manage/registration.json
@@ -1,6 +1,6 @@
{
- "queue": "队列",
- "description": "注册队列里面没有用户。
要开启这项功能,请去设置 → 用户 → 用户注册 并设置注册类型为“管理员批准”。",
+ "queue": "申请",
+ "description": "注册申请队列里面还没有用户申请。
要开启这项功能,请去设置 → 用户 → 用户注册 并设置注册类型为“管理员批准”。",
"list.name": "姓名",
"list.email": "邮件",
diff --git a/public/language/zh-CN/admin/manage/users.json b/public/language/zh-CN/admin/manage/users.json
index d8570e5977..8201df27c6 100644
--- a/public/language/zh-CN/admin/manage/users.json
+++ b/public/language/zh-CN/admin/manage/users.json
@@ -30,9 +30,9 @@
"search.username": "通过用户名",
"search.username-placeholder": "输入你想找的用户名",
"search.email": "通过邮箱",
- "search.email-placeholder": "输入你想找的邮箱地址",
+ "search.email-placeholder": "输入你想查询的邮箱地址",
"search.ip": "通过IP地址",
- "search.ip-placeholder": "输入你想找的IP",
+ "search.ip-placeholder": "输入你想查询的IP",
"search.not-found": "未找到用户!",
"inactive.3-months": "3个月",
diff --git a/public/language/zh-CN/admin/menu.json b/public/language/zh-CN/admin/menu.json
index 1a6771b20e..37f31c027a 100644
--- a/public/language/zh-CN/admin/menu.json
+++ b/public/language/zh-CN/admin/menu.json
@@ -11,8 +11,8 @@
"manage/categories": "版块",
"manage/tags": "话题",
"manage/users": "用户",
- "manage/registration": "注册队列",
- "manage/groups": "用户组",
+ "manage/registration": "注册申请",
+ "manage/groups": "群组",
"manage/ip-blacklist": "IP 黑名单",
"section-settings": "设置",
@@ -20,7 +20,7 @@
"settings/reputation": "声望",
"settings/email": "邮件",
"settings/user": "用户",
- "settings/group": "用户组",
+ "settings/group": "群组",
"settings/guest": "游客",
"settings/uploads": "上传",
"settings/post": "发帖",
diff --git a/public/language/zh-CN/admin/settings/advanced.json b/public/language/zh-CN/admin/settings/advanced.json
index 07184f136b..eb9bced36e 100644
--- a/public/language/zh-CN/admin/settings/advanced.json
+++ b/public/language/zh-CN/admin/settings/advanced.json
@@ -10,7 +10,7 @@
"headers.acam": "Access-Control-Allow-Methods",
"headers.acah": "Access-Control-Allow-Headers",
"traffic-management": "流量管理",
- "traffic.help": "NodeBB 拥有在高流量情况下自动拒绝请求的模块。 您可以在这里调整这些设置,虽然默认值就很棒。",
+ "traffic.help": "NodeBB 拥有在高流量情况下自动拒绝请求的模块。尽管默认值就很棒,但您可以在这里调整这些设置。",
"traffic.enable": "启用流量管理",
"traffic.event-lag": "事件循环滞后阈值(毫秒)",
"traffic.event-lag-help": "降低此值会减少页面加载的等待时间,但也会向更多用户显示“过载”消息。(需要重新启动)",
diff --git a/public/language/zh-CN/admin/settings/cookies.json b/public/language/zh-CN/admin/settings/cookies.json
index a03212d78d..d8b00e8295 100644
--- a/public/language/zh-CN/admin/settings/cookies.json
+++ b/public/language/zh-CN/admin/settings/cookies.json
@@ -6,6 +6,6 @@
"consent.link-text": "政策链接文本",
"consent.blank-localised-default": "留空以便使用 NodeBB 本地默认值",
"settings": "设置",
- "cookie-domain": "会话 cookie 域名",
+ "cookie-domain": "Session cookie 域名",
"blank-default": "留空以保持默认"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/admin/settings/group.json b/public/language/zh-CN/admin/settings/group.json
index d19d417f86..bd1771dd57 100644
--- a/public/language/zh-CN/admin/settings/group.json
+++ b/public/language/zh-CN/admin/settings/group.json
@@ -1,12 +1,12 @@
{
"general": "通用",
- "private-groups": "私有用户组",
- "private-groups.help": "启用此选项后,加入用户组需要组长审批(默认启用)。",
- "private-groups.warning": "注意!如果这个选项未启用并且你有私有用户组,那么你的用户组将变为公共的。",
- "allow-creation": "允许创建用户组",
- "allow-creation-help": "如果启用,用户就可以创建用户组(默认:不启用)",
- "max-name-length": "用户组名字的最大长度",
- "cover-image": "用户组封面图片",
+ "private-groups": "私有群组",
+ "private-groups.help": "启用此选项后,加入用户组需要群组所有者审批(默认启用)。",
+ "private-groups.warning": "注意!如果这个选项未启用并且你有私有群组,那么你的群组将变为公共的。",
+ "allow-creation": "允许创建群组",
+ "allow-creation-help": "如果启用,用户就可以创建群组(默认:不启用)",
+ "max-name-length": "群组名字的最大长度",
+ "cover-image": "群组封面图片",
"default-cover": "默认封面图片",
"default-cover-help": "为没有上传封面图片的群组添加以逗号分隔的默认封面图片"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/admin/settings/pagination.json b/public/language/zh-CN/admin/settings/pagination.json
index ec44dd3661..b4e16d4f7f 100644
--- a/public/language/zh-CN/admin/settings/pagination.json
+++ b/public/language/zh-CN/admin/settings/pagination.json
@@ -3,7 +3,7 @@
"enable": "在主题和帖子使用分页替代无限滚动浏览。",
"topics": "话题分页",
"posts-per-page": "每页帖子数",
- "categories": "板块分页",
+ "categories": "版块分页",
"topics-per-page": "每页主题数",
"initial-num-load": "最初加载未读,最新,热门的话题"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json
index a5869c1fc1..857f72b98e 100644
--- a/public/language/zh-CN/admin/settings/post.json
+++ b/public/language/zh-CN/admin/settings/post.json
@@ -6,21 +6,21 @@
"sorting.most-votes": "最多投票",
"sorting.topic-default": "默认主题排序",
"restrictions": "发帖限制",
- "restrictions.seconds-between": "发帖间隔",
- "restrictions.seconds-between-new": "对于新用户的发帖间隔",
+ "restrictions.seconds-between": "发帖间隔(单位:秒)",
+ "restrictions.seconds-between-new": "对于新用户的发帖间隔(单位:秒)",
"restrictions.rep-threshold": "取消发帖限制所需的声望值",
- "restrictions.seconds-defore-new": "见习时间",
- "restrictions.seconds-edit-after": "用户在发布后允许编辑帖子的秒数。 (0为禁用) ",
- "restrictions.seconds-delete-after": "允许在发布后删除帖子的秒数。 (0为禁用) ",
+ "restrictions.seconds-defore-new": "见习时间(单位:秒)",
+ "restrictions.seconds-edit-after": "用户在发布后允许编辑帖子的时间(0为禁用,单位:秒)",
+ "restrictions.seconds-delete-after": "用户在发布后允许删除帖子的时间(0为禁用,单位:秒)",
"restrictions.replies-no-delete": "在用户被禁止删除自己的主题后的回复数。 (0为禁用) ",
"restrictions.min-title-length": "最小标题长度",
"restrictions.max-title-length": "最大标题长度",
"restrictions.min-post-length": "最小帖子长度",
"restrictions.max-post-length": "最大帖子长度",
- "restrictions.days-until-stale": "主题过期时间",
+ "restrictions.days-until-stale": "主题过期时间(单位:天)",
"restrictions.stale-help": "如果某个主题被视为“过时”,则会向尝试回复该主题的用户显示警告。",
"timestamp": "时间戳",
- "timestamp.cut-off": "日期截止日期 (天) ",
+ "timestamp.cut-off": "日期截止日期(单位:天)",
"timestamp.cut-off-help": "日期&时间将以相对方式 (例如,“3小时前” / “5天前”) 显示,并且会依照访客语言时区转换。在某一时刻之后,可以切换该文本以显示本地化日期本身 (例如2016年11月5日15:30) 。
(默认值: 30
或一个月) 。 设置为0可始终显示日期,留空以始终显示相对时间。",
"teaser": "预览帖子",
"teaser.last-post": "最后– 显示最新的帖子,包括原帖,如果没有回复",
@@ -35,7 +35,7 @@
"signature.no-images": "禁用签名中的图片",
"signature.max-length": "签名最大长度",
"composer": "编辑器设置",
- "composer-help": "以下设置控制所示后期编辑器的功能和/或外观\n\\t\\t\\t\\t当用户创建新主题或回复现有主题时。",
+ "composer-help": "以下设置控制所示后期编辑器的功能和/或外观\n\t\t\t\t当用户创建新主题或回复现有主题时。",
"composer.show-help": "显示“帮助”选项卡",
"composer.enable-plugin-help": "允许插件将内容添加到帮助选项卡",
"composer.custom-help": "自定义帮助文本",
diff --git a/public/language/zh-CN/admin/settings/reputation.json b/public/language/zh-CN/admin/settings/reputation.json
index e6f9640f81..ec3f657d1e 100644
--- a/public/language/zh-CN/admin/settings/reputation.json
+++ b/public/language/zh-CN/admin/settings/reputation.json
@@ -3,7 +3,7 @@
"disable": "禁用声望系统",
"disable-down-voting": "禁用 踩",
"votes-are-public": "所有投票是公开的",
- "thresholds": "活动阈值",
+ "thresholds": "活动闸值",
"min-rep-downvote": "踩帖子所需要声望的最小值",
"min-rep-flag": "举报帖子需要的最小声望"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/admin/settings/uploads.json b/public/language/zh-CN/admin/settings/uploads.json
index 6c52e63b11..2a4c42f933 100644
--- a/public/language/zh-CN/admin/settings/uploads.json
+++ b/public/language/zh-CN/admin/settings/uploads.json
@@ -3,7 +3,7 @@
"allow-files": "允许用户上传普通文件",
"private": "使上传的文件私有化",
"max-image-width": "缩小图片到指定宽度(单位像素)",
- "max-image-width-help": "(像素单位,默认760像素,设置为0以禁用)",
+ "max-image-width-help": "(像素单位,默认 760 px,设置为0以禁用)",
"max-file-size": "最大文件尺寸(单位 KiB)",
"max-file-size-help": "(单位 KiB,默认2048KiB)",
"allow-topic-thumbnails": "允许用户上传主题缩略图",
@@ -16,11 +16,11 @@
"default-avatar": "访客默认头像",
"upload": "上传",
"profile-image-dimension": "个人资料相片尺寸",
- "profile-image-dimension-help": "(使用像素作为单位,默认:128px)",
+ "profile-image-dimension-help": "(使用 px 作为单位,默认:128px)",
"max-profile-image-size": "个人资料相片最大大小",
- "max-profile-image-size-help": "(单位KiB,默认256KiB)",
+ "max-profile-image-size-help": "(单位 KiB ,默认256KiB)",
"max-cover-image-size": "最大封面图片文件大小",
- "max-cover-image-size-help": "(单位kb,默认:2048KiB)",
+ "max-cover-image-size-help": "(单位 KiB ,默认:2048KiB)",
"keep-all-user-images": "在服务器上保留旧头像和旧的资料封面",
"profile-covers": "资料封面",
"default-covers": "默认封面图片",
diff --git a/public/language/zh-CN/admin/settings/user.json b/public/language/zh-CN/admin/settings/user.json
index 8d30a90366..7b13c89b95 100644
--- a/public/language/zh-CN/admin/settings/user.json
+++ b/public/language/zh-CN/admin/settings/user.json
@@ -33,11 +33,11 @@
"registration-type.help": "通常 - 用户可以通过/register页面注册
\n管理员批准 - 用户注册请求会被放入 请求队列 待管理员批准。
\n管理员批准 IP地址 - 新用户不受影响,已存在帐户的IP地址注册需要管理员批准。
\n邀请制 - 用户可以通过 用户 页面邀请其它用户。
\n管理员邀请制 - 只有管理员可以通过 用户 和 admin/manage/users 页面邀请其它用户。
\n无注册 - 不开放用户注册。
",
"registration.max-invites": "每个用户最大邀请数",
"max-invites": "每个用户最大邀请数",
- "max-invites-help": "无限制填0。管理员没有邀请限制
仅在邀请制时可用",
+ "max-invites-help": "无限制填 0 。管理员没有邀请限制
仅在邀请制时可用",
"min-username-length": "最小用户名长度",
"max-username-length": "最大用户名长度",
"min-password-length": "最小密码长度",
- "max-about-me-length": "最大自我介绍长度",
+ "max-about-me-length": "自我介绍的最大长度",
"terms-of-use": "论坛使用条款 (留空即可禁用)",
"user-search": "用户搜索",
"user-search-results-per-page": "展示的结果数量",
diff --git a/public/language/zh-CN/error.json b/public/language/zh-CN/error.json
index d7f706ff2c..a59beaf451 100644
--- a/public/language/zh-CN/error.json
+++ b/public/language/zh-CN/error.json
@@ -8,7 +8,7 @@
"invalid-pid": "无效帖子 ID",
"invalid-uid": "无效用户 ID",
"invalid-username": "无效用户名",
- "invalid-email": "无效电子邮箱",
+ "invalid-email": "无效的电子邮箱",
"invalid-title": "无效标题!",
"invalid-user-data": "无效用户数据",
"invalid-password": "无效密码",
@@ -29,14 +29,14 @@
"username-too-long": "用户名太长",
"password-too-long": "密码太长",
"user-banned": "用户已禁止",
- "user-banned-reason": "抱歉,此帐号已经被封号 (原因:%1)",
+ "user-banned-reason": "抱歉,此帐号已经被封禁 (原因:%1)",
"user-too-new": "抱歉,您需要等待 %1 秒后,才可以发帖!",
"blacklisted-ip": "对不起,您的 IP 地址已被社区禁用。如果您认为这是一个错误,请与管理员联系。",
"ban-expiry-missing": "请提供此次禁言结束日期",
"no-category": "版块不存在",
"no-topic": "主题不存在",
"no-post": "帖子不存在",
- "no-group": "用户组不存在",
+ "no-group": "群组不存在",
"no-user": "用户不存在",
"no-teaser": "主题预览不存在",
"no-privileges": "您没有权限执行此操作。",
@@ -80,13 +80,13 @@
"invalid-image-type": "无效的图像类型。允许的类型有:%1",
"invalid-image-extension": "无效的图像扩展",
"invalid-file-type": "无效文件格式,允许的格式有:%1",
- "group-name-too-short": "用户组名太短",
- "group-name-too-long": "用户组名太长",
- "group-already-exists": "用户组已存在",
- "group-name-change-not-allowed": "不允许更改用户组名称",
- "group-already-member": "已经是此用户组的成员",
- "group-not-member": "不是此用户组的成员",
- "group-needs-owner": "用户组需要指定至少一名组长",
+ "group-name-too-short": "群组名太短",
+ "group-name-too-long": "群组名太长",
+ "group-already-exists": "群组已存在",
+ "group-name-change-not-allowed": "不允许更改群组名称",
+ "group-already-member": "已经是此群组的成员",
+ "group-not-member": "不是此群组的成员",
+ "group-needs-owner": "群组需要指定至少一名群组所有者",
"group-already-invited": "您已邀请该用户",
"group-already-requested": "已提交您的请求",
"post-already-deleted": "此帖已被删除",
diff --git a/public/language/zh-CN/global.json b/public/language/zh-CN/global.json
index f864947bf4..6555431231 100644
--- a/public/language/zh-CN/global.json
+++ b/public/language/zh-CN/global.json
@@ -31,7 +31,7 @@
"header.tags": "话题",
"header.popular": "热门",
"header.users": "会员",
- "header.groups": "用户组",
+ "header.groups": "群组",
"header.chats": "聊天",
"header.notifications": "通知",
"header.search": "搜索",
@@ -45,7 +45,7 @@
"alert.success": "成功",
"alert.error": "错误",
"alert.banned": "封禁",
- "alert.banned.message": "您刚刚被封禁,现在您将退出登录。",
+ "alert.banned.message": "您刚刚被封禁了,现在您将登出站点。",
"alert.unfollow": "您已取消关注 %1!",
"alert.follow": "您已关注 %1!",
"online": "在线",
diff --git a/public/language/zh-CN/groups.json b/public/language/zh-CN/groups.json
index f99c7d23b1..3deaf8cb3a 100644
--- a/public/language/zh-CN/groups.json
+++ b/public/language/zh-CN/groups.json
@@ -1,35 +1,35 @@
{
- "groups": "用户组",
- "view_group": "查看用户组",
- "owner": "组长",
- "new_group": "创建用户组",
- "no_groups_found": "尚无用户组信息",
+ "groups": "群组",
+ "view_group": "查看群组",
+ "owner": "群组所有者",
+ "new_group": "创建群组",
+ "no_groups_found": "尚无群组信息",
"pending.accept": "接受",
"pending.reject": "拒绝",
- "pending.accept_all": "接受全部",
- "pending.reject_all": "拒绝全部",
+ "pending.accept_all": "全部同意",
+ "pending.reject_all": "全部拒绝",
"pending.none": "暂时没有待加入的成员",
"invited.none": "暂时没有接受邀请的成员",
"invited.uninvite": "取消邀请",
- "invited.search": "选择用户加入用户组",
+ "invited.search": "选择用户加入群组",
"invited.notification_title": "您已被邀请加入 %1",
- "request.notification_title": "来自 %1 的用户组成员请求",
+ "request.notification_title": "来自 %1 的群组成员请求",
"request.notification_text": "%1 已被邀请加入 %2",
"cover-save": "保存",
"cover-saving": "正在保存",
- "details.title": "用户组信息",
+ "details.title": "群组信息",
"details.members": "成员列表",
"details.pending": "待加入成员",
"details.invited": "已邀请成员",
- "details.has_no_posts": "此用户组的会员尚未发表任何帖子。",
+ "details.has_no_posts": "此群组的会员尚未发表任何帖子。",
"details.latest_posts": "最新帖子",
"details.private": "私有",
"details.disableJoinRequests": "禁止申请加入用户组",
"details.grant": "授予/取消管理权",
"details.kick": "踢出用户组",
- "details.kick_confirm": "您确定要将此成员从用户组中移除吗?",
- "details.owner_options": "用户组管理",
- "details.group_name": "用户组名",
+ "details.kick_confirm": "您确定要将此成员从群组中移除吗?",
+ "details.owner_options": "群组管理",
+ "details.group_name": "群组名",
"details.member_count": "用户组成员数",
"details.creation_date": "创建时间",
"details.description": "描述",
@@ -38,21 +38,21 @@
"details.change_colour": "更改颜色",
"details.badge_text": "徽章文本",
"details.userTitleEnabled": "显示组内称号",
- "details.private_help": "启用此选项后,加入用户组需要组长审批。",
+ "details.private_help": "启用此选项后,加入群组需要组长审批。",
"details.hidden": "隐藏",
- "details.hidden_help": "启用此选项后,用户组将不在用户组列表中展现,成员只能通过邀请加入。",
- "details.delete_group": "删除用户组",
- "details.private_system_help": "系统禁用了私有用户组,这个选项不起任何作用",
- "event.updated": "用户组信息已更新",
- "event.deleted": "用户组 \"%1\" 已被删除",
+ "details.hidden_help": "启用此选项后,群组将不在群组列表中展现,成员只能通过邀请加入。",
+ "details.delete_group": "删除群组",
+ "details.private_system_help": "系统禁用了私有群组,这个选项不起任何作用",
+ "event.updated": "群组信息已更新",
+ "event.deleted": "群组 \"%1\" 已被删除",
"membership.accept-invitation": "接受邀请",
"membership.invitation-pending": "邀请中",
- "membership.join-group": "加入用户组",
- "membership.leave-group": "退出用户组",
+ "membership.join-group": "加入群组",
+ "membership.leave-group": "退出群组",
"membership.reject": "拒绝",
- "new-group.group_name": "组名: ",
- "upload-group-cover": "上传组封面",
- "bulk-invite-instructions": "输入您要邀请加入此用户组的用户名,多个用户以逗号分隔",
+ "new-group.group_name": "群组名: ",
+ "upload-group-cover": "上传群组封面",
+ "bulk-invite-instructions": "输入您要邀请加入此群组的用户名,多个用户以逗号分隔",
"bulk-invite": "批量邀请",
"remove_group_cover_confirm": "确定要移除封面图片吗?"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/modules.json b/public/language/zh-CN/modules.json
index 94874032f2..235ec2c80b 100644
--- a/public/language/zh-CN/modules.json
+++ b/public/language/zh-CN/modules.json
@@ -20,7 +20,7 @@
"chat.three_months": "3个月",
"chat.delete_message_confirm": "确认删除此消息吗?",
"chat.add-users-to-room": "向此聊天室中添加成员",
- "composer.compose": "编写",
+ "composer.compose": "编写帮助",
"composer.show_preview": "显示预览",
"composer.hide_preview": "隐藏预览",
"composer.user_said_in": "%1 在 %2 中说:",
@@ -38,7 +38,7 @@
"composer.upload-picture": "上传图片",
"composer.upload-file": "上传文件",
"composer.zen_mode": "无干扰模式",
- "composer.select_category": "选择一个板块",
+ "composer.select_category": "选择一个版块",
"bootbox.ok": "确认",
"bootbox.cancel": "取消",
"bootbox.confirm": "确认",
diff --git a/public/language/zh-CN/notifications.json b/public/language/zh-CN/notifications.json
index ae58756a48..850c504005 100644
--- a/public/language/zh-CN/notifications.json
+++ b/public/language/zh-CN/notifications.json
@@ -24,7 +24,7 @@
"upvoted_your_post_in_dual": "%1 和 %2 在 %3 赞了您的帖子。",
"upvoted_your_post_in_multiple": "%1 和 %2 个其他人在 %3 赞了您的帖子。",
"moved_your_post": "您的帖子已被 %1 移动到了 %2",
- "moved_your_topic": "%1 移动到了 %2",
+ "moved_your_topic": "%1 移动了 %2",
"user_flagged_post_in": "%1 在 %2 标记了一个帖子",
"user_flagged_post_in_dual": "%1 和 %2 在 %3 举报了一个帖子",
"user_flagged_post_in_multiple": "%1 和 %2 个其他人在 %3 举报了一个帖子",
@@ -42,7 +42,7 @@
"new_register_multiple": "有 %1 条注册申请等待批准。",
"flag_assigned_to_you": "举报 %1 已经被指派给你",
"email-confirmed": "电子邮箱已确认",
- "email-confirmed-message": "感谢您验证您的电子邮箱。您的帐户现已全面激活。",
- "email-confirm-error-message": "验证您电子邮箱地址时出现了问题。可能是因为验证码无效或已过期。",
+ "email-confirmed-message": "感谢您验证您的电子邮箱。您的帐户现已完全激活。",
+ "email-confirm-error-message": "验证的您电子邮箱地址时出现了问题。可能是因为验证码无效或已过期。",
"email-confirm-sent": "确认邮件已发送。"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/pages.json b/public/language/zh-CN/pages.json
index 533df5539c..c76c550395 100644
--- a/public/language/zh-CN/pages.json
+++ b/public/language/zh-CN/pages.json
@@ -22,9 +22,9 @@
"registration-complete": "注册完成",
"login": "登录帐号",
"reset": "重置帐户密码",
- "categories": "板块",
- "groups": "用户组",
- "group": "%1 的用户组",
+ "categories": "版块",
+ "groups": "群组",
+ "group": "%1 的群组",
"chats": "聊天",
"chat": "与 %1 聊天",
"flags": "举报",
@@ -38,7 +38,7 @@
"account/followers": "关注 %1 的人",
"account/posts": "%1 发布的帖子",
"account/topics": "%1 创建的主题",
- "account/groups": "%1 的用户组",
+ "account/groups": "%1 的群组",
"account/bookmarks": "%1 收藏的帖子",
"account/settings": "用户设置",
"account/watched": "主题已被 %1 关注",
diff --git a/public/language/zh-CN/register.json b/public/language/zh-CN/register.json
index 11a0706cc9..af02bf10dd 100644
--- a/public/language/zh-CN/register.json
+++ b/public/language/zh-CN/register.json
@@ -12,7 +12,7 @@
"password_placeholder": "输入密码",
"confirm_password": "确认密码",
"confirm_password_placeholder": "再次输入密码",
- "register_now_button": "马上注册",
+ "register_now_button": "立即注册",
"alternative_registration": "其他方式注册",
"terms_of_use": "使用条款",
"agree_to_terms_of_use": "我同意使用条款",
diff --git a/public/language/zh-CN/search.json b/public/language/zh-CN/search.json
index 4633e600ed..2d6b500297 100644
--- a/public/language/zh-CN/search.json
+++ b/public/language/zh-CN/search.json
@@ -6,8 +6,8 @@
"titles": "标题",
"titles-posts": "标题和回帖",
"posted-by": "发表",
- "in-categories": "在版面",
- "search-child-categories": "搜索子版面",
+ "in-categories": "在版块",
+ "search-child-categories": "搜索子版块",
"has-tags": "有标签",
"reply-count": "回复数",
"at-least": "至少",
@@ -31,7 +31,7 @@
"number-of-views": "查看数",
"topic-start-date": "主题开始日期",
"username": "用户名",
- "category": "板块",
+ "category": "版块",
"descending": "倒序",
"ascending": "顺序",
"save-preferences": "保存设置",
diff --git a/public/language/zh-CN/topic.json b/public/language/zh-CN/topic.json
index 5491cffe0d..cbd70048ef 100644
--- a/public/language/zh-CN/topic.json
+++ b/public/language/zh-CN/topic.json
@@ -38,7 +38,7 @@
"login_to_subscribe": "请注册或登录后,再订阅此主题。",
"markAsUnreadForAll.success": "将全部主题标为未读。",
"mark_unread": "标记为未读",
- "mark_unread.success": "未读话题",
+ "mark_unread.success": "主题已被标记为未读。",
"watch": "关注",
"unwatch": "取消关注",
"watch.title": "当此主题有新回复时,通知我",
@@ -48,7 +48,7 @@
"not-watching": "未关注",
"ignoring": "忽略中",
"watching.description": "有新回复时通知我。
在未读主题中显示。",
- "not-watching.description": "不要在有新回复时通知我。
如果这个分类未被忽略则在未读主题中显示。",
+ "not-watching.description": "不要在有新回复时通知我。
如果这个版块未被忽略则在未读主题中显示。",
"ignoring.description": "不要在有新回复时通知我。
不要在未读主题中显示该主题。",
"thread_tools.title": "主题工具",
"thread_tools.markAsUnreadForAll": "标记全部未读",
@@ -70,8 +70,8 @@
"post_delete_confirm": "确定删除此帖吗?",
"post_restore_confirm": "确定恢复此帖吗?",
"post_purge_confirm": "确认清除此回帖吗?",
- "load_categories": "正在载入板块",
- "disabled_categories_note": "停用的板块为灰色",
+ "load_categories": "正在载入版块",
+ "disabled_categories_note": "停用的版块为灰色",
"confirm_move": "移动",
"confirm_fork": "分割",
"bookmark": "书签",
diff --git a/public/language/zh-CN/user.json b/public/language/zh-CN/user.json
index 1b0307e789..1f7842a16c 100644
--- a/public/language/zh-CN/user.json
+++ b/public/language/zh-CN/user.json
@@ -1,5 +1,5 @@
{
- "banned": "封禁",
+ "banned": "已封禁",
"offline": "离线",
"username": "用户名",
"joindate": "注册日期",
@@ -8,7 +8,7 @@
"confirm_email": "确认电子邮箱",
"account_info": "账户信息",
"ban_account": "封禁账户",
- "ban_account_confirm": "您确定封禁这位用户吗?",
+ "ban_account_confirm": "您确定要封禁这位用户吗?",
"unban_account": "解禁账户",
"delete_account": "删除帐号",
"delete_account_confirm": "确认要删除您的帐户吗?
此操作是不可逆转的,您将无法恢复您的任何数据
请输入您的用户名,确认您想要删除此帐户。",
@@ -108,8 +108,8 @@
"scroll_to_my_post": "在提交回复之后显示新回复",
"follow_topics_you_reply_to": "关注你回复过的主题",
"follow_topics_you_create": "关注你创建的主题",
- "grouptitle": "用户组标题",
- "no-group-title": "不展示用户组称号",
+ "grouptitle": "群组标题",
+ "no-group-title": "不展示群组称号",
"select-skin": "选择皮肤",
"select-homepage": "选择首页",
"homepage": "首页",
@@ -117,8 +117,8 @@
"custom_route": "自定义首页路由",
"custom_route_help": "输入路由名称,前面不需要斜杠 ( 例如, \"recent\" 或 \"popular\" )",
"sso.title": "单点登录服务",
- "sso.associated": "关联到",
- "sso.not-associated": "点击这里关联",
+ "sso.associated": "已关联到",
+ "sso.not-associated": "点击这里来关联",
"info.latest-flags": "最新举报",
"info.no-flags": "没有找到被举报的帖子",
"info.ban-history": "最近封禁历史",
@@ -129,7 +129,7 @@
"info.banned-no-reason": "没有原因",
"info.username-history": "历史用户名",
"info.email-history": "历史邮箱",
- "info.moderation-note": "版主留言",
- "info.moderation-note.success": "修改未保存",
- "info.moderation-note.add": "添加注解"
+ "info.moderation-note": "版主备注",
+ "info.moderation-note.success": "版主备注已保存",
+ "info.moderation-note.add": "添加备注"
}
\ No newline at end of file
diff --git a/public/language/zh-CN/users.json b/public/language/zh-CN/users.json
index a6a2267467..bdc9eda8de 100644
--- a/public/language/zh-CN/users.json
+++ b/public/language/zh-CN/users.json
@@ -15,7 +15,7 @@
"recent_topics": "最新主题",
"popular_topics": "热门主题",
"unread_topics": "未读主题",
- "categories": "版面",
+ "categories": "版块",
"tags": "话题",
"no-users-found": "未找到匹配的用户!"
}
\ No newline at end of file
diff --git a/public/src/admin/modules/search.js b/public/src/admin/modules/search.js
index c52008fdab..7694b21b3a 100644
--- a/public/src/admin/modules/search.js
+++ b/public/src/admin/modules/search.js
@@ -1,6 +1,5 @@
'use strict';
-
define('admin/modules/search', ['mousetrap'], function (mousetrap) {
var search = {};
@@ -11,16 +10,17 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
var namespace = params.namespace;
var translations = params.translations;
var title = params.title;
+ var escaped = utils.escapeRegexChars(term);
var results = translations
// remove all lines without a match
- .replace(new RegExp('^(?:(?!' + term + ').)*$', 'gmi'), '')
+ .replace(new RegExp('^(?:(?!' + escaped + ').)*$', 'gmi'), '')
// remove lines that only match the title
.replace(new RegExp('(^|\\n).*?' + title + '.*?(\\n|$)', 'g'), '')
// get up to 25 characters of context on both sides of the match
// and wrap the match in a `.search-match` element
.replace(
- new RegExp('^[\\s\\S]*?(.{0,25})(' + term + ')(.{0,25})[\\s\\S]*?$', 'gmi'),
+ new RegExp('^[\\s\\S]*?(.{0,25})(' + escaped + ')(.{0,25})[\\s\\S]*?$', 'gmi'),
'...$1$2$3...
'
)
// collapse whitespace
@@ -28,7 +28,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
.trim();
title = title.replace(
- new RegExp('(^.*?)(' + term + ')(.*?$)', 'gi'),
+ new RegExp('(^.*?)(' + escaped + ')(.*?$)', 'gi'),
'$1$2$3'
);
@@ -123,7 +123,7 @@ define('admin/modules/search', ['mousetrap'], function (mousetrap) {
menu.children('.result').remove();
- var len = value.length;
+ var len = /\W/.test(value) ? 3 : value.length;
var results;
menu.toggleClass('state-start-typing', len === 0);
diff --git a/public/src/client/account/edit/password.js b/public/src/client/account/edit/password.js
index d2239b36a0..44723014ce 100644
--- a/public/src/client/account/edit/password.js
+++ b/public/src/client/account/edit/password.js
@@ -1,7 +1,7 @@
'use strict';
-define('forum/account/edit/password', ['forum/account/header', 'translator'], function (header, translator) {
+define('forum/account/edit/password', ['forum/account/header', 'translator', 'zxcvbn'], function (header, translator, zxcvbn) {
var AccountEditPassword = {};
AccountEditPassword.init = function () {
@@ -20,6 +20,7 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu
var passwordsmatch = false;
function onPasswordChanged() {
+ var passwordStrength = zxcvbn(password.val());
passwordvalid = false;
if (password.val().length < ajaxify.data.minimumPasswordLength) {
showError(password_notify, '[[user:change_password_error_length]]');
@@ -29,6 +30,8 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu
showError(password_notify, '[[user:password_same_as_username]]');
} else if (password.val() === ajaxify.data.email) {
showError(password_notify, '[[user:password_same_as_email]]');
+ } else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) {
+ showError(password_notify, '[[user:weak_password]]');
} else {
showSuccess(password_notify);
passwordvalid = true;
diff --git a/public/src/client/chats.js b/public/src/client/chats.js
index 582c578f65..3dca09bc38 100644
--- a/public/src/client/chats.js
+++ b/public/src/client/chats.js
@@ -98,7 +98,11 @@ define('forum/chats', [
}
loading = true;
var start = parseInt($('.chat-content').children('[data-index]').first().attr('data-index'), 10) + 1;
- socket.emit('modules.chats.getMessages', { roomId: roomId, uid: uid, start: start }, function (err, data) {
+ socket.emit('modules.chats.getMessages', {
+ roomId: roomId,
+ uid: uid,
+ start: start,
+ }, function (err, data) {
if (err) {
return app.alertError(err.message);
}
@@ -122,7 +126,7 @@ define('forum/chats', [
Chats.addEditDeleteHandler = function (element, roomId) {
element.on('click', '[data-action="edit"]', function () {
var messageId = $(this).parents('[data-mid]').attr('data-mid');
- var inputEl = components.get('chat/input');
+ var inputEl = $('[data-roomid="' + roomId + '"] [component="chat/input"]');
messages.prepEdit(inputEl, messageId, roomId);
}).on('click', '[data-action="delete"]', function () {
var messageId = $(this).parents('[data-mid]').attr('data-mid');
@@ -170,7 +174,10 @@ define('forum/chats', [
if (oldName === newName) {
return;
}
- socket.emit('modules.chats.renameRoom', { roomId: roomId, newName: newName }, function (err) {
+ socket.emit('modules.chats.renameRoom', {
+ roomId: roomId,
+ newName: newName,
+ }, function (err) {
if (err) {
return app.alertError(err.message);
}
@@ -235,10 +242,15 @@ define('forum/chats', [
if (event.item === app.user.username) {
return;
}
- socket.emit('modules.chats.addUserToRoom', { roomId: data.roomId, username: event.item }, function (err) {
+ socket.emit('modules.chats.addUserToRoom', {
+ roomId: data.roomId,
+ username: event.item,
+ }, function (err) {
if (err) {
app.alertError(err.message);
- tagEl.tagsinput('remove', event.item, { nouser: true });
+ tagEl.tagsinput('remove', event.item, {
+ nouser: true,
+ });
}
});
});
@@ -262,7 +274,10 @@ define('forum/chats', [
if (event.options && event.options.nouser) {
return;
}
- socket.emit('modules.chats.removeUserFromRoom', { roomId: data.roomId, username: event.item }, function (err) {
+ socket.emit('modules.chats.removeUserFromRoom', {
+ roomId: data.roomId,
+ username: event.item,
+ }, function (err) {
if (err) {
return app.alertError(err.message);
}
@@ -325,7 +340,12 @@ define('forum/chats', [
} else {
var recentEl = components.get('chat/recent');
templates.parse('partials/chats/recent_room', {
- rooms: { roomId: data.roomId, lastUser: data.message.fromUser, usernames: data.message.fromUser.username, unread: true },
+ rooms: {
+ roomId: data.roomId,
+ lastUser: data.message.fromUser,
+ usernames: data.message.fromUser.username,
+ unread: true,
+ },
}, function (html) {
translator.translate(html, function (translated) {
recentEl.prepend(translated);
@@ -347,7 +367,7 @@ define('forum/chats', [
};
Chats.resizeMainWindow = function () {
- var messagesList = $('.expanded-chat .chat-content');
+ var messagesList = $('.expanded-chat .chat-content');
if (messagesList.length) {
var margin = $('.expanded-chat ul').outerHeight(true) - $('.expanded-chat ul').height();
diff --git a/public/src/client/register.js b/public/src/client/register.js
index 329762271e..8070263906 100644
--- a/public/src/client/register.js
+++ b/public/src/client/register.js
@@ -1,7 +1,7 @@
'use strict';
-define('forum/register', ['translator'], function (translator) {
+define('forum/register', ['translator', 'zxcvbn'], function (translator, zxcvbn) {
var Register = {};
var validationError = false;
var successIcon = '';
@@ -170,6 +170,7 @@ define('forum/register', ['translator'], function (translator) {
function validatePassword(password, password_confirm) {
var password_notify = $('#password-notify');
var password_confirm_notify = $('#password-confirm-notify');
+ var passwordStrength = zxcvbn(password);
if (password.length < ajaxify.data.minimumPasswordLength) {
showError(password_notify, '[[user:change_password_error_length]]');
@@ -181,6 +182,8 @@ define('forum/register', ['translator'], function (translator) {
showError(password_notify, '[[user:password_same_as_username]]');
} else if (password === $('#email').val()) {
showError(password_notify, '[[user:password_same_as_email]]');
+ } else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) {
+ showError(password_notify, '[[user:weak_password]]');
} else {
showSuccess(password_notify, successIcon);
}
diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js
index 1d4150b179..d05829c79d 100644
--- a/public/src/modules/chat.js
+++ b/public/src/modules/chat.js
@@ -75,7 +75,9 @@ define('chat', [
});
}
} else {
- socket.emit('modules.chats.loadRoom', { roomId: data.roomId }, function (err, roomData) {
+ socket.emit('modules.chats.loadRoom', {
+ roomId: data.roomId,
+ }, function (err, roomData) {
if (err) {
return app.alertError(err.message);
}
@@ -111,7 +113,10 @@ define('chat', [
};
module.loadChatsDropdown = function (chatsListEl) {
- socket.emit('modules.chats.getRecentChats', { uid: app.user.uid, after: 0 }, function (err, data) {
+ socket.emit('modules.chats.getRecentChats', {
+ uid: app.user.uid,
+ after: 0,
+ }, function (err, data) {
if (err) {
return app.alertError(err.message);
}
@@ -163,7 +168,7 @@ define('chat', [
var dragged = false;
chatModal.attr('id', 'chat-modal-' + data.roomId);
- chatModal.attr('roomId', data.roomId);
+ chatModal.attr('data-roomid', data.roomId);
chatModal.attr('intervalId', 0);
chatModal.attr('UUID', uuid);
chatModal.css('position', 'fixed');
@@ -211,7 +216,7 @@ define('chat', [
components.get('chat/input').val(text);
});
- ajaxify.go('user/' + app.user.userslug + '/chats/' + chatModal.attr('roomId'));
+ ajaxify.go('user/' + app.user.userslug + '/chats/' + chatModal.attr('data-roomid'));
module.close(chatModal);
}
@@ -252,14 +257,14 @@ define('chat', [
messagesEl.css('height', module.calculateChatListHeight(chatModal));
});
- Chats.addRenameHandler(chatModal.attr('roomId'), chatModal.find('[component="chat/room/name"]'));
+ Chats.addRenameHandler(chatModal.attr('data-roomid'), chatModal.find('[component="chat/room/name"]'));
- Chats.addSendHandlers(chatModal.attr('roomId'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn'));
+ Chats.addSendHandlers(chatModal.attr('data-roomid'), chatModal.find('#chat-message-input'), chatModal.find('#chat-message-send-btn'));
Chats.createTagsInput(chatModal.find('.users-tag-input'), data);
Chats.createAutoComplete(chatModal.find('[component="chat/input"]'));
- Chats.addScrollHandler(chatModal.attr('roomId'), data.uid, chatModal.find('.chat-content'));
+ Chats.addScrollHandler(chatModal.attr('data-roomid'), data.uid, chatModal.find('.chat-content'));
taskbar.push('chat', chatModal.attr('UUID'), {
title: data.roomName || (data.users.length ? data.users[0].username : ''),
@@ -314,7 +319,7 @@ define('chat', [
ChatsMessages.scrollToBottom(chatModal.find('.chat-content'));
module.bringModalToTop(chatModal);
module.focusInput(chatModal);
- socket.emit('modules.chats.markRead', chatModal.attr('roomId'));
+ socket.emit('modules.chats.markRead', chatModal.attr('data-roomid'));
var env = utils.findBootstrapEnvironment();
if (env === 'xs' || env === 'sm') {
diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js
index e4defc99d5..7afb07b24f 100644
--- a/public/src/modules/translator.js
+++ b/public/src/modules/translator.js
@@ -97,6 +97,9 @@
// and the strings of untranslated text in between
var toTranslate = [];
+ // to store the state of if we're currently in a top-level token for later
+ var inToken = false;
+
// split a translator string into an array of tokens
// but don't split by commas inside other translator strings
function split(text) {
@@ -141,6 +144,8 @@
// set the last break to our current
// spot since we just broke the string
lastBreak = cursor;
+ // we're in a token now
+ inToken = true;
// the current level of nesting of the translation strings
var level = 0;
@@ -176,6 +181,8 @@
invalidTextRegex.test(sliced[0])) {
cursor += 1;
lastBreak -= 2;
+ // no longer in a token
+ inToken = false;
if (level > 0) {
level -= 1;
} else {
@@ -191,18 +198,26 @@
// if we're at the base level, then this is the end
if (level === 0) {
// so grab the name and args
- var result = split(str.slice(lastBreak, cursor));
+ var currentSlice = str.slice(lastBreak, cursor);
+ var result = split(currentSlice);
var name = result[0];
var args = result.slice(1);
+ // make a backup based on the raw string of the token
+ // if there are arguments to the token
+ var backup = '';
+ if (args && args.length) {
+ backup = this.translate('[[' + currentSlice + '[[');
+ }
// add the translation promise to the array
- toTranslate.push(this.translateKey(name, args));
+ toTranslate.push(this.translateKey(name, args, backup));
// skip past the ending brackets
cursor += 2;
// set this as our last break
lastBreak = cursor;
// and we're no longer in a translation string,
// so continue with the main loop
+ inToken = false;
break;
}
// otherwise we lower the level
@@ -219,8 +234,16 @@
cursor += 1;
}
+ // ending string of source
+ var last = str.slice(lastBreak);
+
+ // if we were mid-token, treat it as invalid
+ if (inToken) {
+ last = this.translate('[[' + last);
+ }
+
// add the remaining text after the last translation string
- toTranslate.push(str.slice(lastBreak, cursor + 2));
+ toTranslate.push(last);
// and return a promise for the concatenated translated string
return Promise.all(toTranslate).then(function (translated) {
@@ -232,9 +255,10 @@
* Translates a specific key and array of arguments
* @param {string} name - Translation key (ex. 'global:home')
* @param {string[]} args - Arguments for `%1`, `%2`, etc
+ * @param {string|Promise} backup - Text to use in case the key can't be found
* @returns {Promise}
*/
- Translator.prototype.translateKey = function translateKey(name, args) {
+ Translator.prototype.translateKey = function translateKey(name, args, backup) {
var self = this;
var result = name.split(':', 2);
@@ -251,29 +275,27 @@
}
var translation = this.getTranslation(namespace, key);
- var argsToTranslate = args.map(function (arg) {
- return string(arg).collapseWhitespace().decodeHTMLEntities().escapeHTML().s;
- }).map(function (arg) {
- return self.translate(arg);
- });
-
- // so we can await all promises at once
- argsToTranslate.unshift(translation);
-
- return Promise.all(argsToTranslate).then(function (result) {
- var translated = result[0];
- var translatedArgs = result.slice(1);
-
+ return translation.then(function (translated) {
+ // check if the translation is missing first
if (!translated) {
warn('Missing translation "' + name + '"');
- return key;
+ return backup || key;
}
- var out = translated;
- translatedArgs.forEach(function (arg, i) {
- var escaped = arg.replace(/%(?=\d)/g, '%').replace(/\\,/g, ',');
- out = out.replace(new RegExp('%' + (i + 1), 'g'), escaped);
+
+ var argsToTranslate = args.map(function (arg) {
+ return string(arg).collapseWhitespace().decodeHTMLEntities().escapeHTML().s;
+ }).map(function (arg) {
+ return self.translate(arg);
+ });
+
+ return Promise.all(argsToTranslate).then(function (translatedArgs) {
+ var out = translated;
+ translatedArgs.forEach(function (arg, i) {
+ var escaped = arg.replace(/%(?=\d)/g, '%').replace(/\\,/g, ',');
+ out = out.replace(new RegExp('%' + (i + 1), 'g'), escaped);
+ });
+ return out;
});
- return out;
});
};
@@ -281,7 +303,7 @@
* Load translation file (or use a cached version), and optionally return the translation of a certain key
* @param {string} namespace - The file name of the translation namespace
* @param {string} [key] - The key of the specific translation to getJSON
- * @returns {Promise