"use strict";
const fs = require("fs");
const path = require("path");
const util = require("util");
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const rules = "docs/user-guide/rules.md";
let count = 0;
let cn = 0;
const translatorDate = {
"keyword": "关键字",
"scheme": "协议",
"lowercase": "小写",
"uppercase": "大写",
"allowed": "允许的",
"disallowed": "禁用的",
"blacklist": "黑名单",
"whitelist": "白名单",
"custom": "自定义",
"pattern": "模式",
"complain": "指正",
"number": "数量",
"depth": "深度",
"length": "长度",
"unit": "单位",
"specificity": "特异性",
"decimal places": "小数位",
"attribute": "属性",
"classes": "类",
"class": "类",
"compound": "复合",
"pseudo-element": "伪元素",
"pseudo-classes": "伪类",
"pseudo-class": "伪类",
"type": "类型",
"universal": "通用",
"must always": "必须",
"must never": "不能",
"overrides": "覆盖",
"vendor": "浏览器引擎",
"prefixes": "前缀",
"prefix": "前缀",
"qualifying": "限定",
"duplicated": "重复",
"duplicate": "重复",
"redundant": "冗余",
"shorthand": "简写",
"feature": "功能",
"keyframe": "关键帧",
"url": "URL",
"hex": "16进制",
"color": "颜色",
"short or long notation": "缩写或扩写",
"a single space": "一个空格",
"whitespace": "空白符",
"white": "空白符",
"space": "空白符",
"newline": "换行符",
"adjacent": "相邻",
"empty": "空",
"line": "行",
"a": "一个",
"an": "一个",
"bang": "叹号",
"colon": "冒号",
"comma": "逗号",
"semicolon": "分号",
"notation": "符号",
"quoted": "使用引号",
"quote": "引号",
"combinator": "组合符",
"operator": "运算符",
"range": "范围",
"after": "之后",
"before": "之前",
"inside": "内侧",
"multi": "多",
"single": "单",
"multi-line": "多行",
"single-line": "单行",
"block": "块",
"declaration": "声明",
"named": "命名",
"name": "名",
"at-rule": "@规则",
"rule": "规则",
"media": "媒体",
"query": "查询",
"list": "列表",
"parameter": "参数",
"function": "函数",
"selector": "选择器",
"combinators": "组合选择器",
"descendant": "后代",
"nested": "嵌套",
"longhand": "简写",
"properties": "属性",
"property": "属性",
"value": "值",
"comment": "注释",
"marker": "标记",
"opening": "开",
"closing": "闭",
"parentheses": "括号",
"parenthes": "括号",
"bracket": "中括号",
"brace": "大括号",
"these": "这些",
"this": "这个",
"unknown": "未知的",
"invalid": "无效的",
"string": "字符串",
"leading": "前导",
"trailing": "尾随",
"zero": "零",
"one": "一个",
"two": "两个",
"this": "这个",
"is": "是",
"pairs": "对",
"and": "和",
"or": "或",
"the": "",
}
function translator(words) {
if (translatorDate.hasOwnProperty(words)) {
words = translatorDate[words];
} else if (/ allowed$/.test(words)) {
words = "允许的" + translator(RegExp.leftContext);
} else if (/^this /i.test(words)) {
words = "这个" + translator(RegExp.rightContext);
} else if (/^These /i.test(words)) {
words = "这些" + translator(RegExp.rightContext);
} else if (/(^|\s+)(of|(?:with)?in|on|with|for)(\s+|$)/.test(words)) {
const {
leftContext,
rightContext,
} = RegExp;
words = `${translator(rightContext || "")}的${translator(leftContext || "")}`;
} else if (/^[\w-]+s$/.test(words) && !/^(?:th)is\b/i.test(words)) {
words = translator(words.slice(0, -1));
} else if (/\s/.test(words)) {
words = words.split(/\s+/).map(translator).join("");
}
return words;
}
readFile(rules, "utf8").then(async (md) => {
md = md.replace(/^(-\s+\[.+?\]\((.+?)\)[::]\s*).*$/gm, (s, prefix, file) => {
count++;
file = path.resolve("docs/user-guide", file)
let rule = fs.readFileSync(file, "utf8");
let desc = rule.match(/(?:^|\n)# .*\n+([^\n]+(?:\n\S[^\n]+)*)/)[1]
.replace(/\[(.+?)\]\(.+?\)/g, "$1")
.replace(/\n+/g, " ");
const fixedRule = rule.replace(
/(\w`?) *(?=[\u4e00-\u9fa5])/g,
"$1 "
).replace(
/([\u4e00-\u9fa5]) *(`?\w)/g,
"$1 $2"
).replace(
/([,。、;])[ ]*/g,
"$1"
).replace(
/notice the (\W+?), with no space after the closing parenthesis/igm,
"注意 $1,在闭括号后没有空格"
).replace(
/`(.+?)` is inside `?(.+?)`?,? (and|so it) is evaluated separately/igm,
"`$1` 在 `$2` 里面,所以它是单独计算的"
).replace(
/^This rule resolves nested selectors before (.*?)\..*$/igm,
(s, sth) => {
return `此规则在计算${translator(sth.replace(/^\w+\s+the\s+/, ""))}之前先解析选择器嵌套。[选择器列表](https://www.w3.org/TR/selectors4/#selector-list)中的每个选择器都将单独计算。`;
}
).replace(
/each selector in a selector list is evaluated separately/igm,
"选择器列表中的每个选择器都将单独计算"
).replace(
/^The( content of the)? `:not\(\)` pseudo\-class is also evaluated separately\. The rule processes the argument as if it were an independent selector, and the result does not count toward the total for the entire selector\.?/igm,
"`:not()` 伪类的内容也是单独计算的。此规则将其参数视为一个独立的选择器,结果不计入整个选择器的总数。"
).replace(
/There \*(.*?)\* be (.*?) (after|before|inside) (.*?)\.?$/igm,
(s, how, sth, pos, where) => {
return `在${translator(where)}${translator(pos)}*${translator(how)}*有${translator(sth)}。`
.replace(/(\*不能\*有)(?:一个|的)/, "$1");
}
).replace(
/There \*(.*?)\* be (.*?)\.?$/igm,
(s, how, sth) => {
return `这里*${translator(how)}*有${translator(sth)}。`
.replace(/(\*不能\*有)(?:一个|的)/, "$1");
}
).replace(
/(.*?) \*(.*?)\* be (.*?)\.?$/igm,
(s, sth, how, what) => {
return `${translator(sth.toLowerCase())}*${translator(how)}*${translator(what)}。`
.replace(/(\*不能\*有)(?:一个|的)/, "$1");
}
).replace(
/((?:^|\/)\s*\*+\s*)(.*?) (after|before|inside) (.*?)(?=\s+\*\/)/igm,
(s, prefix, sth, pos, where) => {
return prefix + `${translator(where)}${translator(pos)}的${translator(sth.toLowerCase())}`
.trim();
}
).replace(
/^Limit the number of (.*) (?:with)?in (.*?)\.?$/igm,
(s, sth, where) => {
return `限制${translator(where)}中${translator(sth)}的数量`;
}
).replace(
/^Require or disallow (.+?)\.?$/igm,
(s, sth) => {
return `要求或禁止${translator(sth)}。`;
}
).replace(
/^Disallow (.+?)\.?$/igm,
(s, sth) => {
return `禁止${translator(sth)}。`;
}
).replace(
/^Specify(?: an?)? (.+?) of (.+?) within (.+?)\.?$/igm,
(s, how, sth, where) => {
return `指定${translator(where)}内${translator(sth)}的${translator(how)}。`;
}
).replace(
/^Specify(?: an?)? (.+?)\.?$/igm,
(s, sth) => {
return `指定${translator(sth)}。`;
}
).replace(
/`int`: Maximum .* empty lines\b.*$/igm,
"`int`:允许的最大相邻空行数量。"
).replace(
/`int`: Maximum (.+?) allowed.*$/igm,
(s, sth) => {
return "`int`:允许的最大" + translator(sth) + "数量。";
}
).replace(
/(\*[↑←\s\n]+\*\s+)(Th(?:is|ese) .+?)(\s+\*)/ig,
(s, before, sth, after) => {
if (before.match(/↑/g).length < 2) {
sth = sth.replace(/^These\b/, "this");
}
sth = sth.replace(/^This\b/, "this");
return before + translator(sth).replace(/的/, "").replace(/^这些(.+?[和或])这些/, "这些$1") + after;
}
).replace(
/^This rule considers (.+?) defined in the CSS Specifications, up to and including Editor's Drafts, to be known\.$/igm,
(s, sth) => {
return `此规则考虑了 CSS 规范中定义的${translator(sth).replace(/的/, "")},包括已知的编辑草案。`
}
).replace(
/^This rule ignores variables \(`\$sass`, `@less`, `\-\-custom\-property`\)\.?$/igm,
"此规则忽略变量(`$sass`、`@less`、`--custom-property`)。"
).replace(
/^This rule ignores `\$sass`, `@less`, and `var\(\-\-custom\-property\)` variable syntaxes\.$/igm,
"此规则忽略 `$sass`、`@less` 和 `var(--custom-property)` 变量语法"
).replace(
/^This rule ignores semicolons that are preceded by Less mixins.*$/igm,
"此规则忽略 Less mixins 的分号。"
).replace(
/^(-\s*)semicolons that are preceded by Less mixins.*$/igm,
"$1Less mixins 的分号"
).replace(
/^(-\s*)the last semicolon of declaration blocks$/igm,
"$1声明块的最后一个分号"
).replace(
/^Given:$/igm,
"给定:"
).replace(
/^This rule ignores:$/igm,
"此规则忽略:"
).replace(
/^## Options$/igm,
"## 选项"
).replace(
/^## Optional secondary options$/igm,
"## 可选的辅助选项"
).replace(
/^The following .* violation.*$/igm,
(s) => {
return `以下模式${/\bnot\b/.test(s) ? "*不*" : ""}被视为违规:`;
}
).replace(
/这些([一二三四五六七八九十两俩])/g,
"这$1"
).replace(
/(组合选择器)选择器/g,
"$1"
).replace(
/小数位的数量/g,
"小数位数"
).replace(
/小写或大写/g,
"大小写"
).replace(
/字体系列/g,
"字体族"
).replace(
/这个是/g,
"这是"
).replace(
/你/g,
"您"
);
if (rule !== fixedRule) {
rule = fixedRule
writeFile(file, rule);
console.log("已修正翻译", file);
}
if (!/[\u4e00-\u9fa5]/.test(desc)) {
return s;
}
if (!/^(禁止|限制|要求|指定)/.test(desc)) {
console.error("错误的单行描述前缀", desc);
process.exitCode = 1;
}
cn++;
if(/\(Autofixable\)|(可自动修复)/i.test(s)) {
desc = desc.replace(/([.。]*)$/, "(可自动修复)$1");
}
if (
rule.replace(/\n```([\s\S]+?)```(?=\n)/g, (s)=> {
s = s.match(/\/\*[\s\S]*?\*\//g);
if (s) {
s = s.map(s => s.replace(/^\/[*\s\n]*|[*\s\n]*\/$/g, "")).filter(Boolean);
}
return s && s.length ? "\n" + s.join("\n") : "";
}).split(/[\r\n]+/g).some(line => {
if (!line || /^#\s+/.test(line) || /^[#-]*\s*\W*[`"]/.test(line) || /[\u4e00-\u9fa5↑←]/.test(line) || /\d|TODO:|foo|words|blergh|badword|stylelint|\w+\s*:\s*\w+/.test(line)) {
return;
}
console.error(line);
return true;
})
) {
console.error("不完整的翻译", file);
process.exitCode = 1;
}
return prefix + desc;
});
console.log(`规则翻译进度:${cn}/${count}`);
return writeFile(rules, md);
});