civitai汉化

2024/12/16 02:59:20

// ==UserScript==
// @name        civitai汉化
// @namespace   Violentmonkey Scripts
// @match       *://civitai.com/*
// @grant       GM_addValueChangeListener
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_listValues
// @version     1.0
// @author      -
// @license MIT
// @description 2024/12/16 02:59:20
// ==/UserScript==
config = {
    "edit": false,
    "node": []
}
contrast = {
    "Your Profile": "你的个人资料",
    "Images": "图片",
    "Overview": "概览",
    "models": "模型",
    "posts": "帖子",
    "images": "图片",
    "videos": "视频",
    "articles": "文章",
    "collections": "收藏",
    "Newest": "最新",
    "No results found": "未找到结果",
    "Try adjusting your search or filters to find what you're looking for": "试着调整搜索或筛选条件以找到你所寻找的内容",
    "Customize profile": "自定义个人资料",
    "home": "首页",
    "bounties": "赏金任务",
    "challenges": "挑战",
    "tools": "工具",
    "shop": "商店",
    "events": "活动",
    "More": "更多",
    "woman": "女性",
    "anime": "动画",
    "character": "角色",
    "fantasy": "奇幻",
    "man": "男性",
    "cartoon": "卡通",
    "clothing": "服饰",
    "celebrity": "名人",
    "game character": "游戏角色",
    "3d": "3D",
    "photorealistic": "照片级逼真",
    "comics": "漫画",
    "retro": "复古",
    "cat": "猫",
    "architecture": "建筑",
    "outdoors": "户外",
    "robot": "机器人",
    "painting": "绘画",
    "scenery": "风景",
    "food": "食物",
    "alien": "外星",
    "armor": "盔甲",
    "dog": "狗",
    "landscape": "风景",
    "city": "城市",
    "modern art": "现代艺术",
    "night": "夜晚",
    "costume": "服装",
    "manga": "漫画",
    "sci-fi": "科幻",
    "jewelry": "珠宝",
    "sword": "剑",
    "sea": "海洋",
    "flower": "花",
    "weapon": "武器",
    "post apocalyptic": "末日后",
    "suit": "西装",
    "wedding": "婚礼",
    "hair": "发型",
    "formal wear": "正装",
    "astronomy": "天文学",
    "housing": "住房",
    "wildlife": "野生动物",
    "Models": "模型",
    "Users": "用户",
    "Articles": "文章",
    "Collections": "收藏",
    "Bounties": "赏金",
    "Tools": "工具",
    "Everyone": "所有人",
    "Followed": "已关注",
    "Most Reactions": "最多反应",
    "Most Comments": "最多评论",
    "Most Collected": "最多收藏",
    "Filters": "筛选",
    "Day": "日",
    "Week": "周",
    "Time period": "时间段",
    "Month": "月",
    "Year": "年",
    "All Time": "所有时间",
    "Clear all filters": "清除所有筛选",
    "Training": "训练中",
    "farnsworthltnnku575": "farnsworthltnnku575",
    "Buy Buzz": "购买Buzz",
    "Generate": "生成",
    "Post Images": "发布图片",
    "Post Videos": "发布视频",
    " Upload a Model": "上传模型",
    "Train a LoRA": "训练LoRA",
    "Write an Article": "撰写文章",
    "Create a Bounty": "创建赏金",
    "My Collections": "我的收藏",
    "Liked Models": "喜欢的模型",
    "Bookmarked Articles": "书签文章",
    "My Vault": "我的保险库",
    "Buzz Dashboard": "Buzz仪表板",
    "My Bounties": "我的赏金",
    "Download Link App": "下载链接应用",
    "Auctions": "拍卖",
    "Leaderboard": "排行榜",
    "Getting Started": "入门",
    "Download History": "下载历史",
    "Apply": "应用",
    "my filters": "我的筛选",
    "Creators You Follow": "你关注的创作者",
    "Manage Account": "管理账户",
    "Take a moment to review your account information and preferences to personalize your experience on the site": "花点时间查看你的账户信息和偏好设置,以个性化你在网站上的体验",
    "Account Info": "账户信息",
    "Name": "姓名",
    "Save": "保存",
    "Account Email": "账户邮箱",
    "Social": "社交",
    "Creator Profile": "创作者档案",
    " Links": "链接",
    "Add Link": "添加链接",
    "social": "社交",
    " links": "链接",
    "Sponsorship": "赞助",
    "sponsorship": "赞助",
    "Browsing Settings": "浏览设置",
    "Image Preferences": "图片偏好",
    "You have not added any ": "你还未添加任何",
    "Autoplay GIFs": "自动播放GIF",
    "Model File Preferences": "模型文件偏好",
    "Preferred Format": "首选格式",
    "Preferred Size": "首选尺寸",
    "Preferred Precision": "首选精度",
    "Assistant Preferences": "助手偏好",
    "CivBot Assistant": "CivBot助手",
    "Personality": "个性",
    "Features": "特征",
    "New": "新",
    "Larger Images in Generator": "生成器中的大图",
    "AI Resource Identifier": "AI资源标识符",
    "Chats": "聊天",
    "Show the Civitai AIR on resources for easy use within the Civitai Services API or Civitai Comfy Nodes.": "在资源上显示Civitai AIR,以便在Civitai服务API或Civitai Comfy节点中便捷使用。",
    "A helpful chat assistant that can answer questions about Stable Diffusion, Civitai, and more! We're still training it, so please report any issues you find!": "一位有用的聊天助手,可以回答关于稳定扩散、Civitai等问题!我们仍在训练中,请报告你发现的任何问题!",
    "Images displayed in the generator will be larger on small screens": "生成器中显示的图片在小屏幕上会更大",
    "Choose to see less content from certain topics while you browse Civitai. Selecting a topic will reduce, not eliminate, content about the topic.": "你可以选择在浏览Civitai时减少某些话题的内容。选择一个话题将减少该话题的内容,而不是完全消除。",
    "Content Controls": "内容控制",
    "Send and receive DMs from users across the site.": "发送和接收来自网站各地用户的私信。",
    "Hide anime": "隐藏动漫",
    "Hide furry": "隐藏兽人",
    "Hide political": "隐藏政治",
    "Hide gore": "隐藏暴力血腥",
    "Hidden Tags": "隐藏标签",
    "We'll hide content with these tags throughout the site.": "我们将在全站隐藏带有这些标签的内容。",
    "[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}": "[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}",
    "Hidden Users": "隐藏用户",
    "Generation Settings": "生成设置",
    "We'll hide content from these users throughout the site.": "我们将在全站隐藏来自这些用户的内容。",
    "Advanced Mode": "高级模式",
    "Allow unrestricted mixing of additional resources and base models.": "允许无限制地混合附加资源和基础模型。",
    "Content Moderation": "内容审核",
    "Show mature content": "显示成人内容",
    "By enabling mature content, you confirm you are over the age of 18.": "启用成人内容即表示你确认你已超过18岁。",
    "Connected Accounts": "已连接账户",
    "Blur mature content": "模糊成人内容",
    "Blur images and videos that are marked as mature": "模糊标记为成人的图片和视频",
    "Connect": "连接",
    "Connect multiple accounts to your user and sign in with any of them": "将多个账户连接到你的用户,并使用任意一个登录",
    "Discord": "Discord",
    "GitHub": "GitHub",
    "Reddit": "Reddit",
    "Google": "Google",
    "Notifications Settings": "通知设置",
    "On-site Notifications": "站内通知",
    " Notifications": "通知",
    "New @mentions": "新的@提及",
    "Comment": "评论",
    "New comment responses (Models)": "模型的新评论回复",
    "New responses to comments and reviews on your models": "模型评论和评价的新回复",
    "New comments on your models": "模型的新评论",
    "New comment replies": "新评论回复",
    "New comments on your images": "图片的新评论",
    "New review responses": "新评价回复",
    "New comments on your articles": "文章的新评论",
    "Model download milestones": "模型下载里程碑",
    "Comment reaction milestones": "评论反应里程碑",
    "Model like milestones": "模型点赞里程碑",
    "Milestone": "里程碑",
    "New comments on your bounty": "赏金的新评论",
    "Image reaction milestones": "图片反应里程碑",
    "Article reaction milestones": "文章反应里程碑",
    "Article view milestones": "文章浏览里程碑",
    "Article like milestones": "文章点赞里程碑",
    "Update": "更新",
    "New versions of models you follow": "你关注的模型的新版本",
    "New models from followed users": "被关注用户的新模型",
    "New reviews": "新评价",
    "Your item has been reviewed": "你的项目已被审查",
    "New articles from followed users": "被关注用户的新文章",
    "New items added to a collection you follow": "你关注的收藏中新增了项目",
    "New followers": "新粉丝",
    "System": "系统",
    "New Civitai features": "新的Civitai功能",
    "Shop: Your Item got bought (Creator Program exclusive)": "商店:你的项目被购买(仅限创作者计划)",
    "Shop: New Products Available": "商店:有新产品",
    "Bounty": "赏金",
    "Bounty awarded to you": "赏金奖励给你",
    "Bounty you are involved in is ending": "你参与的赏金即将结束",
    "Bounty entry reaction milestones": "赏金条目反应里程碑",
    "Buzz": "Buzz",
    "New entry on bounty you are involved in": "你参与的赏金有新条目",
    "Tip Received": "收到打赏",
    "Creator": "创作者",
    "Baking phase ending": "烘焙阶段结束",
    "Extraction phase started": "提取阶段开始",
    "Extraction phase ending": "提取阶段结束",
    "Email Notifications": "邮件通知",
    "Newsletter": "时事通讯",
    "API Keys": "API密钥",
    "Add API key": "添加API密钥",
    "There are no API keys in your account": "你的账户中没有API密钥",
    "You can use API keys to interact with the site through the API as your user. These should not be shared with anyone.": "你可以使用API密钥以用户身份通过API与网站交互。请不要与任何人共享。",
    "Start by creating your first API Key to connect your apps.": "首先创建你的第一个API密钥以连接你的应用。",
    "Refresh my Session": "刷新我的会话",
    "Delete account": "删除账户",
    "Delete your account": "删除你的账户",
    "Once you delete your account, there is no going back. Please be certain when taking this action.": "一旦删除账户,将无法恢复。请在操作前确定。",
    "Support may ask you to refresh your Civitai session. Click the button below to clear internal caches, which can help resolve minor issues without affecting your account data or settings.": "支持团队可能会要求你刷新Civitai会话。点击下方按钮以清除内部缓存,这可能有助于在不影响账户数据或设置的情况下解决小问题。",
    "Additional Resources": "额外资源",
    "Workflow": "工作流程",
    "Model": "模型",
    "Prompt": "提示语",
    "Negative Prompt": "负面提示语",
    "Aspect Ratio": "长宽比",
    "Portrait": "竖屏",
    "Landscape": "横屏",
    "Square": "正方形",
    "Draft Mode": "草稿模式",
    "Advanced": "高级",
    "CFG Scale": "CFG尺度",
    "Sampler": "采样器",
    "Creative": "创意",
    "Precise": "精准",
    "Fast": "快",
    "Popular": "流行",
    "Steps": "步骤",
    "Balanced": "均衡",
    "Seed": "种子",
    "High": "高",
    "Custom": "自定义",
    "Random": "随机",
    "VAE": "VAE",
    "Add VAE": "添加VAE",
    "Request Priority": "请求优先级",
    "Standard": "标准",
    "View generation queue": "查看生成队列",
    "Quantity": "数量",
    "Reset": "重置",
    "Create": "创建",
    "Please Note: Flux Rapid training is not currently functional due to an issue! We're investigating!": "请注意:由于一个问题,Flux快速培训目前不起作用!我们正在调查中!",
    "Status Alert": "状态警报",
    "Create your LoRA": "创建LoRA",
    "Choose your LoRA type": "选择LoRA类型",
    "Character": "角色",
    "A specific person or character, realistic or anime": "一个特定的人或角色,现实风格或动漫风格",
    "Concept": "概念",
    "Objects, clothing, anatomy, poses, etc.": "物品、服装、解剖、姿势等。",
    "Style": "风格",
    "A time period, art style, or general look and feel": "一个时代、艺术风格或整体的外观和感觉",
    "Next": "下一个",
    "Add training data": "添加训练数据",
    "Review and Submit": "审查并提交",
    "Remixing": "重新合成",
    "Type out what you'd like to generate in the prompt, add aspects you'd like to avoid in the negative prompt": "在提示中输入你想生成的内容,在负面提示中添加你希望避免的内容",
    "Draft Mode will generate images faster, cheaper, and with slightly less quality. Use this for exploring concepts quickly.": "草稿模式将更快、更便宜地生成图像,但质量会略有下降。请用此模式来快速探索概念。",
    "Requires generating in batches of 4": "需要每批生成4个",
    "Creations are kept in the Generator for 30 days. Download or Post them to your Profile to save them!": "作品将在生成器中保存30天。请下载或发布到你的个人资料中以保存它们!",
    "Early Access": "抢先体验"
}
//GM_getValue(window.location.hostname, {})

document.addEventListener('keydown', function (event) {
    if (event.altKey && (event.key == 'e' || event.key == 'E')) { // 监听 ALT + E
        config.edit = !config.edit
        console.log(config.edit)
        if (config.edit) { // 进入汉化模式
            document.body.addEventListener('click', edit_node)
            document.body.addEventListener('mouseenter', mouseenter_hook, true)
        } else {
            document.body.removeEventListener('click', edit_node)
            document.body.removeEventListener('mouseenter', mouseenter_hook, true)
            window['mou_his'].style.border = ''
            save_all_edit()
        }
        event.preventDefault()
    }
})



function edit_node(e) {
    e.preventDefault(); // 阻止事件继续传递
    var nodes = filtration(e.target); // 筛选出非空白文本节点
    for (let i = 0; i < nodes.length; i++) {
        let node = nodes[i]
        if (node.raw != undefined) { // 是否已汉化
            continue // 拦截后续代码运行
        }
        node.raw = node.nodeValue // 置入原始文本
        translation(node.raw).then((transl) => {
            node.nodeValue = transl
            node.parentNode.contentEditable = "true"
            console.log(node)
            config['node'].push(node)
        })
    }
}

function save_all_edit() {
    var host = window.location.hostname
    for (let i = 0; i < config['node'].length; i++) {
        let node = config['node'][i]
        set_nested_value(host, node.raw, node.nodeValue)
        node.parentNode.contentEditable = "false"
        console.log(node.raw, node.nodeValue)
    }
}

function filtration(element) {
    var nodes = element.childNodes
    var filter = []
    nodes.forEach(child => {
        if (child.nodeType === 3 && child.nodeName.toLowerCase() === '#text') {
            let text = child.nodeValue
            if (waste(text) == false) {
                filter.push(child)
            }
        }
        if (child.hasChildNodes()) {
            filter = [...filter, ...filtration(child)]
        }
    })
    return filter
    function waste(str) {
        return /^\s*$/.test(str)
    }
}

function get_nested_value(storageId, key, defaultValue) {
    var storedData = GM_getValue(storageId, {})
    return storedData[key] !== undefined ? storedData[key] : defaultValue
}

function set_nested_value(storageId, key, value) {
    var storedData = GM_getValue(storageId, {})
    storedData[key] = value
    GM_setValue(storageId, storedData)
}

function translation(text) {
    return new Promise(completed => {
        try {
            youdao(text).then(res => {
                if (res == false) {
                    return completed(text)
                }
                return completed(res)
            })
        } catch (error) {
            return false
        }
    })
    function youdao(raw) {
        return new Promise(completed => {
            const options = {
                method: "POST",
                url: 'http://m.youdao.com/translate',
                data: "inputtext=" + encodeURIComponent(raw) + "&type=AUTO",
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded"
                },
                onload: function (response) {
                    var results = /id="translateResult">\s*?<li>([\s\S]*?)<\/li>\s*?<\/ul/.exec(response.responseText)[1]
                    return completed(results)
                }
            }
            GM_xmlhttpRequest(options)
        })
    }
}

//------------------
function mouseenter_hook(e) {
    if (e.target !== document.body) {
        if (window['mou_his']) {
            window['mou_his'].style.border = ''
        }
        e.target.style.border = '1px solid red';
        window['mou_his'] = e.target
    } // 注册离开消失
}


extract(
    function () {
        var text = this.text
        if (text in contrast) {
            console.log(`${text} >>> ${contrast[text]}`)
            this.node.raw = this.node.nodeValue
            this.node.nodeValue = contrast[text]
        } else {
            console.log("缺少", this.text)
        }
    }
)
function containsChinese(text) {
    return /[\u4e00-\u9fa5]/.test(text);
}
function extract(callback) {
    var exclude = ['head', 'pre', 'script', 'textarea']//排除名单
    var selectors = []
    exclude.forEach((item, index) => {
        selectors.push(item)
        selectors.push(item + ' *')
    })
    get(document.querySelectorAll('*:not(' + selectors.join(',') + ')'))
    let observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            for (let i = 0; i < mutation.addedNodes.length; i++) {
                let node = mutation.addedNodes[i];
                if (node.nodeType === 1) {
                    callMyFunction(node)
                    function callMyFunction(param1) {
                        setTimeout(function () {
                            get([...param1.querySelectorAll('*'), param1])
                        }, 300);
                    }
                }
            }
        });
    });
    let config = {
        childList: true,
        subtree: true
    };
    observer.observe(document, config);
    function get(elements) {
        for (let i = 0; i < elements.length; i++) {
            let element = elements[i];
            for (let j = 0; j < element.childNodes.length; j++) {
                let node = element.childNodes[j];
                if (node.nodeType === 3 && node.nodeName.toLowerCase() === '#text') {
                    let text = node.nodeValue
                    var v = { a: false, b: false }
                    text.slice(0, 1) == " " ? v.a = true : v.a = false
                    text.slice(-1) == " " ? v.b = true : v.b = false
                    text = text.replace(/[\n\t\r]/g, '').trim();
                    if (/\S/.test(text)) {
                        v.a == true ? text = " " + text : text
                        v.b == true ? text = text + " " : text
                        if (!element.matches('script,textarea')) {//单元素阻断,白名单
                            node.nodeValue = text
                            callback.call({ text: text, node: node, element: element })
                        }
                    }
                }
            }
        }
    }
}