您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Перевод Figma на русский язык.
// ==UserScript== // @name FigmaRUS // @description Перевод Figma на русский язык. // @namespace figma.com // @version 0.1 // @match *://*.figma.com/* // @grant none // @run-at document-start // ==/UserScript== const labelMap = { 'Assets': 'Ассеты', 'Line Arrow': 'Линия стрелки', 'Line height': 'Длина линии', 'Line tool': 'Линия', 'Line': 'Линия', } const textMap = { 'Assets': 'Ассеты', 'Line Arrow': 'Линия стрелки', 'Line height': 'Длина линии', 'Line tool': 'Линия', 'Line': 'Линия', } /** interface Config { [selector: string]: { text: string | undefined | ((oldVal: string, parentElement: HTMLElement, textNode?: Text) => string | undefined); '<attribute-name>': string | undefined | null | ((oldVal: string, parentElement: HTMLElement) => string | undefined | null); } } selector - css селектор text - текст в элементе значения: function - вызывает функцию и использует возвращаемое значение string - устанавливает текст в элементе undefined - ничего не делает attribute - любой атрибут значения: function - вызывает функцию и использует возвращаемое значение string - устанавливает значение атрибута null - удаляет атрибут undefined - ничего не делает **/ const selectorMap = { '[data-label="Layers"]': { 'data-label': 'Слои' }, '[data-label]': { 'data-label': (oldVal, element) => labelMap[oldVal] || oldVal }, '[class^="multilevel_dropdown--name--"]': { "text": (oldVal, parentElement, textNode) => textMap[oldVal] || oldVal }, '[for="frame-mask-disabled-checkbox"]': { "style": "width: 150px;line-height: 15px;", }, '[class^="toolbar_view--shareButton--"]': { "style": "width: 90px;", }, '[class^="upgrade_section--upgradeMainSection--"]': { "style": "height: 1px;", }, '[class^="basic_form--btn--"]': { "style": "width: 100px;background-color: #18a0fb;color: #fff;", }, '[class^="select--dropdownContentWrapper--"]': { 'style': (oldVal, element) => { element.style.width = "220px" // т.к. функция ничего не возвращает(undefined), то дальше ничего не происходит } }, '[class^="select--dropdownContainer--"]': { 'style': (oldVal, element) => { element.style.width = "220px" // т.к. функция ничего не возвращает(undefined), то дальше ничего не происходит } }, '[class^="select--typeSelectDropdown--"]': { 'style': (oldVal, element) => { element.style.width = "220px" // т.к. функция ничего не возвращает(undefined), то дальше ничего не происходит } }, '[class*="type_select--typeSelectDropdown--"]': { 'style': (oldVal, element) => { element.setAttribute('style', 'width: 220px !important') // т.к. функция ничего не возвращает(undefined), то дальше ничего не происходит } }, '[class*="select--option--"]': { 'style': (oldVal, element) => { element.style.width = "220px" // т.к. функция ничего не возвращает(undefined), то дальше ничего не происходит } }, '[class*="select--dropdownScrollClip--"]': { 'style': (oldVal, element) => { element.style.height = "auto" // т.к. функция ничего не возвращает(undefined), то дальше ничего не происходит } }, '[class^="raw_components--panelTitle--"]': { "style": "text-transform: none;", } } function matchAndProcess (el, textNode) { Object.entries(selectorMap).forEach( ([selector, config]) => el.matches(selector) && processElement(config, el, textNode) ); } function queryAndProcess (el) { Object.entries(selectorMap).forEach(([selector, config]) => { if (el.nodeType === 3) { const { parentNode } = el; if (parentNode?.matches(selector)) processElement(config, parentNode, el); } else { el.querySelectorAll(selector).forEach(el => processTextNodes(config, el)); if (el.matches(selector)) processTextNodes(config, el); } }); } function processTextNodes (config, el) { if ('text' in config) { const textNodes = Array.prototype.filter.call( el.childNodes, node => node.nodeType === 3 && node.data.trim() ); if (textNodes.length) return textNodes.forEach(textNode => processElement(config, el, textNode)); } processElement(config, el); } function processElement (config, el, textNode = null) { let { text, ...attributes } = config; if (typeof text === 'function') { text = text(textNode ? textNode.data : '', el, textNode); } if (typeof text !== 'undefined') { if (textNode) textNode.nodeValue = text; else console.warn('[can not set text for', el, ': no text childNodes.]'); } Object.entries(attributes).forEach(([attribute, value]) => { let oldValue = el.getAttribute(attribute); if (typeof value === 'function') { value = value(oldValue, el); } if (typeof value !== 'undefined') { if (value !== null) el.setAttribute(attribute, value); else el.removeAttribute(attribute); } }) } let MutationObserverConfig = { childList: true, attributes: true, subtree: true, attributeFilter: ['data-label'], characterData: true }; let observer = new MutationObserver(mutations => { observer.disconnect(); mutations.forEach(record => { switch (record.type) { case 'childList': record.addedNodes.forEach(queryAndProcess); break; case 'characterData': matchAndProcess(record.target.parentNode, record.target); break; case 'attributes': matchAndProcess(record.target); break; default: console.error(`[unknown type "${record.type}"]`) break; } }); observer.observe(document.body, MutationObserverConfig); }); queryAndProcess(document.body); observer.observe(document.body, MutationObserverConfig);