Skip to content

Commit

Permalink
同步更新到main(greasyfork).user.js
Browse files Browse the repository at this point in the history
  • Loading branch information
maboloshi committed Jun 26, 2022
1 parent 52db8b6 commit b8cf199
Showing 1 changed file with 131 additions and 107 deletions.
238 changes: 131 additions & 107 deletions main(greasyfork).user.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
var page = getPage();

transTitle(); // 页面标题翻译
translateBySelector(); // Selector 翻译
transBySelector(); // Selector 翻译
traverseNode(document.body); // 立即翻译页面
watchUpdate();

// 翻译描述
translateDesc(".f4.my-3"); //仓库简介翻译
translateDesc(".gist-content [itemprop='about']"); // Gist 简介翻译
transDesc(".f4.my-3"); //仓库简介翻译
transDesc(".gist-content [itemprop='about']"); // Gist 简介翻译

/**
* 监听节点变化, 触发和调用翻译函数
Expand All @@ -62,10 +62,10 @@
* */
if(location.pathname !== currentPath) {
currentPath = location.pathname;
page = getPage(); // 仅当,页面地址发生变化时运行
page = getPage(); // 仅当, 页面地址发生变化时运行 更新全局变量 page
}
for(let mutation of mutations) { // for速度比forEach快
if (mutation.addedNodes || mutation.type === 'attributes') { // 仅当节点增加 或者属性更改
if (mutation.addedNodes.length > 0 || mutation.type === 'attributes') { // 仅当节点增加 或者属性更改
traverseNode(mutation.target);
}
}
Expand All @@ -77,7 +77,7 @@

new m(function(mutations) {
transTitle();
translateBySelector(); // Selector 翻译 目前先跟随 url 即页面标题变化
transBySelector(); // Selector 翻译 目前先跟随 url 即页面标题变化
}).observe(
document.querySelector('title'),
{ childList: true }
Expand All @@ -99,55 +99,61 @@
return;
}

var nodes = node.childNodes;
if (node.nodeType === Node.ELEMENT_NODE) { // 元素节点处理

for (var i = 0, len = nodes.length; i <= len; i++) { // 遍历节点
var el = nodes[i] ? nodes[i] : node; //可能还要优化 该节点不存在子节点
// todo 1. 修复多属性翻译问题; 2. 添加事件翻译, 如论预览信息;

if (el.nodeType === Node.ELEMENT_NODE) { // 元素节点处理
// 翻译时间元素
if (node.tagName === 'RELATIVE-TIME' || node.tagName === 'TIME-AGO'|| node.tagName === 'TIME') {
transTimeElement(node);
return;
}

// 元素节点属性翻译
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') { // 输入框 按钮 文本域
if (el.type === 'button' || el.type === 'submit' || el.type === 'reset') {
if (el.hasAttribute('data-confirm')) { // 翻译 浏览器 提示对话框
transElement(el, 'data-confirm', true);
}
transElement(el, 'value');
} else {
transElement(el, 'placeholder');
}
} else if (el.tagName === 'BUTTON'){
if (el.hasAttribute('aria-label') && /tooltipped/.test(el.className)) {
transElement(el, 'aria-label', true); // 翻译 浏览器 提示对话框
}
if (el.hasAttribute('data-confirm')) {
transElement(el, 'data-confirm', true); // 翻译 浏览器 提示对话框 ok
}
if (el.hasAttribute('data-confirm-text')) {
transElement(el, 'data-confirm-text', true); // 翻译 浏览器 提示对话框 ok
}
if (el.hasAttribute('data-confirm-cancel-text')) {
transElement(el, 'data-confirm-cancel-text', true); // 取消按钮 提醒
// 元素节点属性翻译
if (node.tagName === 'INPUT' || node.tagName === 'TEXTAREA') { // 输入框 按钮 文本域
if (node.type === 'button' || node.type === 'submit' || node.type === 'reset') {
if (node.hasAttribute('data-confirm')) { // 翻译 浏览器 提示对话框
transElement(node, 'data-confirm', true);
}
if (el.hasAttribute('cancel-confirm-text')) {
transElement(el, 'cancel-confirm-text', true); // 取消按钮 提醒
}
if (el.hasAttribute('data-disable-with')) { // 按钮等待提示
transElement(el.dataset, 'disableWith');
}
} else if (el.tagName === 'OPTGROUP') { // 翻译 <optgroup> 的 label 属性
transElement(el, 'label');
} else if (/tooltipped/.test(el.className)) { // 仅当 元素存在'tooltipped'样式 aria-label 才起效果
transElement(el, 'aria-label', true); // 带提示的元素,类似 tooltip 效果的
transElement(node, 'value');
} else {
transElement(node, 'placeholder');
}
} else if (node.tagName === 'BUTTON'){
if (node.hasAttribute('aria-label') && /tooltipped/.test(node.className)) {
transElement(node, 'aria-label', true); // 翻译 浏览器 提示对话框
}
if (node.hasAttribute('title')) {
transElement(node, 'title', true); // 翻译 浏览器 提示对话框
}
if (node.hasAttribute('data-confirm')) {
transElement(node, 'data-confirm', true); // 翻译 浏览器 提示对话框 ok
}
if (node.hasAttribute('data-confirm-text')) {
transElement(node, 'data-confirm-text', true); // 翻译 浏览器 提示对话框 ok
}
if (el != node) {
traverseNode(el); // 遍历子节点
if (node.hasAttribute('data-confirm-cancel-text')) {
transElement(node, 'data-confirm-cancel-text', true); // 取消按钮 提醒
}
} else if (el.nodeType === Node.TEXT_NODE) { // 文本节点翻译
if (el.length <= 500){ // 修复 许可证编辑框初始化载入内容被翻译
transElement(el, 'data');
if (node.hasAttribute('cancel-confirm-text')) {
transElement(node, 'cancel-confirm-text', true); // 取消按钮 提醒
}
if (node.hasAttribute('data-disable-with')) { // 按钮等待提示
transElement(node.dataset, 'disableWith');
}
} else if (node.tagName === 'OPTGROUP') { // 翻译 <optgroup> 的 label 属性
transElement(node, 'label');
} else if (/tooltipped/.test(node.className)) { // 仅当 元素存在'tooltipped'样式 aria-label 才起效果
transElement(node, 'aria-label', true); // 带提示的元素,类似 tooltip 效果的
}

if (node.childNodes.length >0) {
for (const child of node.childNodes) {
traverseNode(child); // 遍历子节点
}
}

} else if (node.nodeType === Node.TEXT_NODE) { // 文本节点翻译
if (node.length <= 500){ // 修复 许可证编辑框初始化载入内容被翻译
transElement(node, 'data');
}
}
}
Expand All @@ -171,6 +177,12 @@
const isOrganization = /\/<org-login>/.test(analyticsLocation); // 组织页
const isRepository = /\/<user-name>\/<repo-name>/.test(analyticsLocation); // 仓库页

// 优先匹配 body 的 class
let page = document.body.className.match(I18N.conf.rePageClass);
if (page) {
return page[1];
}

if (site === 'gist') { // Gist 站点
return 'gist';
}
Expand All @@ -194,29 +206,53 @@
return t ? 'orgs/'+t[1] : 'orgs';
}

// 匹配 body 的 class
var page = document.body.className.match(I18N.conf.rePageClass);

if (!page) { // 扩展 pathname 匹配
page = pathname.match(I18N.conf.rePagePath);
}

// 扩展 pathname 匹配
page = pathname.match(I18N.conf.rePagePath);
return page ? page[1] : false; // 取页面 key
}

/**
* 翻译页面标题
*/
function transTitle() {
var title = translate(document.title, 'title');
let str; // 翻译结果
let key = document.title;

if (!title) { // 无翻译则退出
return false;
// 静态翻译
str = I18N[lang]['title']['static'][key];
if (str) {
document.title = str;
return;
}

document.title = title;
let res = I18N[lang]['title'].regexp; // 正则标题
for (let [a, b] of res) {
str = key.replace(a, b);
if (str !== key) {
document.title = str;
break;
}
}
}

/**
* 时间元素翻译
*
* @param {Element} node 节点
*/
function transTimeElement(el) {
let str; // 翻译结果
let key = el.textContent;
let res = I18N[lang].pubilc.regexp;

for (let i = 0; i < 3; i++) { // 公共正则中时间规则
str= key.replace(res[i][0], res[i][1]);
if (str !== key) {
el.textContent = str;
break;
}
}
}

/**
* 翻译节点对应属性内容
Expand All @@ -228,27 +264,26 @@
* @returns {boolean}
*/
function transElement(el, field, isAttr=false) {
var transText; // 翻译后的文本
let str; // 翻译后的文本

if (!isAttr) { // 非属性翻译
transText = translate(el[field], page);
str = translate(el[field], page);
} else {
transText = translate(el.getAttribute(field), page);
str = translate(el.getAttribute(field), page);
}

if (!transText) { // 无翻译则退出
if (!str) { // 无翻译则退出
return false;
}

// 替换翻译后的内容
if (!isAttr) {
el[field] = transText;
el[field] = str;
} else {
el.setAttribute(field, transText);
el.setAttribute(field, str);
}
}


/**
* 翻译文本
*
Expand All @@ -259,64 +294,49 @@
*/
function translate(text, page) { // 翻译

if (!isNaN(text) || /^[\s]*[\u4e00-\u9fa5]|[\u4e00-\u9fa5][\s]*$/.test(text)) { ///^[\u4e00-\u9fa5]+.*$/.test(text)
// 内容为空, 空白字符和或数字, 不存在英文字母和符号,. 跳过
if (!isNaN(text) || !/[a-zA-Z,.]+/.test(text)) {
return false;
} // 内容为空, 空白字符和或数字, 已翻译汉字 不翻译

var str;
var _key = text.trim(); // 去除首尾空格的 key
var _key_neat = _key
.replace(/\xa0/g, ' ') // 替换 &nbsp; 空格导致的 bug
.replace(/[\s]+/g, ' ') // 去除多余空白字符(空格 换行符),(试验测试阶段,有问题再恢复)

if (page === 'title') {
return transPage('title', _key_neat);
} // 翻译网页标题
}
let str;
let _key = text.trim(); // 去除首尾空格的 key
let _key_neat = _key.replace(/\xa0|[\s]+/g, ' ') // 去除多余空白字符(&nbsp; 空格 换行符)

if (page) {
str = transPage(page, _key_neat); // 翻译已知页面 (局部优先)
} // 未知页面不翻译

if (str && str !== _key_neat) { // 已知页面翻译完成
return str;
return text.replace(_key, str); // 替换原字符,保留首尾空白部分
}

str = transPage('pubilc', _key_neat); // 公共翻译

if (!str) {
return false;
} // 未知内容不翻译

return str;
return false;
}


/**
* 翻译页面内容
*
* @param {string} page 页面
* @param {string} key 待翻译内容
* @param {boolean} isRegexp 是否仅翻译正则部分
*
* @returns {string|boolean}
*/
function transPage(page, key, isRegexp=false) {
var str; // 翻译结果
function transPage(page, key) {
let str; // 翻译结果

// 静态翻译
if (!isRegexp) {
str = I18N[lang][page]['static'][key];
if (typeof str === 'string') {
return str;
}
str = I18N[lang][page]['static'][key] || I18N[lang]['pubilc']['static'][key]; // 默认翻译 公共部分
if (typeof str === 'string') {
return str;
}

// 正则翻译
if (RegExp){
var res = I18N[lang][page].regexp; // 正则数组
let res = I18N[lang][page].regexp; // 正则数组
res.push(...I18N[lang]['pubilc'].regexp); // 追加公共正则 es6
if (res) {
for (var i = 0, len = res.length; i < len; i++) {
str = key.replace(res[i][0], res[i][1]);
for (let [a, b] of res) {
str = key.replace(a, b);
if (str !== key) {
return str;
}
Expand All @@ -330,11 +350,13 @@
/**
* 翻译描述
*
* @param {string} JS 选择器
*
* 2021-10-06 16:41:54
* 来自:k1995/github-i18n-plugin
* 改写为原生代码
*/
function translateDesc(el) {
function transDesc(el) {
let element = document.querySelector(el);

if (!element) {
Expand Down Expand Up @@ -372,18 +394,20 @@
/**
* js原生选择器 翻译元素
*
* @param {string} JS 选择器或 CSS 选择器
*
* 2022-02-04 19:46:44
* 灵感参考自:k1995/github-i18n-plugin
*/
function translateBySelector() {
var res = I18N[lang].selector; // 数组
function transBySelector() {
let res = I18N[lang].selector; // 数组
if (res) {
for (var i = 0, len = res.length; i < len; i++) {
let element = document.querySelector(res[i][0])
for (let [a, b] of res) {
let element = document.querySelector(a)
if (element) {
element.textContent = res[i][1];
} else if (document.getElementsByClassName(res[i][0]).length != 0) {
document.getElementsByClassName(res[i][0])[0].textContent = res[i][1];
element.textContent = b;
} else if (document.getElementsByClassName(a).length > 0) {
document.getElementsByClassName(a)[0].textContent = b;
}
}
}
Expand Down

0 comments on commit b8cf199

Please sign in to comment.