GitHub Russian Translation

Translates GitHub websites into Russian

Από την 12/04/2025. Δείτε την τελευταία έκδοση.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name            GitHub Russian Translation
// @name:ru         Русификатор GitHub
// @author          Deflecta
// @contributionURL https://boosty.to/rushanm
// @description     Translates GitHub websites into Russian
// @description:ru  Переводит сайты GitHub на русский язык
// @grant           none
// @homepageURL     https://github.com/RushanM/GitHub-Russian-Translation
// @icon            https://github.githubassets.com/favicons/favicon.png
// @license         MIT
// @match           https://github.com/*
// @match           https://github.blog/*
// @match           https://education.github.com/*
// @run-at          document-end
// @namespace       githubrutraslation
// @supportURL      https://github.com/RushanM/GitHub-Russian-Translation/issues
// @version         1-B27
// ==/UserScript==

(function () {
    'use strict';

    const interFontLink = document.createElement('link');
    interFontLink.rel = 'stylesheet';
    interFontLink.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap';
    document.head.appendChild(interFontLink);

    // Загружаем переводы из удалённого файла rus_p.json и объединяем все секции
    let translations = {};
    fetch("https://raw.githubusercontent.com/RushanM/GitHub-Russian-Translation/refs/heads/master/%D0%9E%D0%B1%D1%89%D0%B5%D0%B5/rus_p.json")
        .then(response => response.json())
        .then(data => {
            // Сохраняем перевод из dashboard для Chat with Copilot
            window.dashboardCopilotTranslation = data.dashboard["Chat with Copilot"];
            // Сохраняем перевод из dashboard для Home
            window.dashboardHomeTranslation = data.dashboard["Home"];
            translations = Object.assign(
                {},
                data.dashboard,
                data.search,
                data.left_sidebar,
                data.settings,
                data.repo_tabs,
                data.copilot,
                data.createnew,
                data.right_sidebar,
                data.copilot_openwith,
                data.general
            );
            runTranslation();
        });

    function runTranslation() {
        function getRepositoriesTranslation(count) {
            if (count === 1) return `${count} репозиторий`;
            if (count >= 2 && count <= 4) return `${count} репозитория`;
            return `${count} репозиториев`;
        }

        function formatStarCount() {
            const starCounters = document.querySelectorAll('.Counter.js-social-count');
            starCounters.forEach(counter => {
                let text = counter.textContent.trim();
                if (text.includes('k')) {
                    text = text.replace('.', ',').replace('k', 'К');
                    counter.textContent = text;
                }
            });
        }

        // Функция для перевода абсолютного времени во всплывающей подсказке, например:
        // «Feb 24, 2025, 3:09 PM GMT+3» → «24 февраля 2025, 15:09 по московскому времени»
        function translateAbsoluteTime(text) {
            // Маппирование месяцев берём из файла с переводами
            const monthMapping = translations.months || {
                Jan: 'января',
                Feb: 'февраля',
                Mar: 'марта',
                Apr: 'апреля',
                May: 'мая',
                Jun: 'июня',
                Jul: 'июля',
                Aug: 'августа',
                Sep: 'сентября',
                Oct: 'октября',
                Nov: 'ноября',
                Dec: 'декабря'
            };

            // Регулярное выражение для извлечения компонентов времени
            // Пример: Feb 24, 2025, 3:09 PM GMT+3
            const regex = /^([A-Z][a-z]{2}) (\d{1,2}), (\d{4}), (\d{1,2}):(\d{2})\s*(AM|PM)\s*GMT\+3$/;
            const match = text.match(regex);
            if (match) {
                const monthEn = match[1];
                const day = match[2];
                const year = match[3];
                let hour = parseInt(match[4], 10);
                const minute = match[5];
                const period = match[6];

                // Преобразование в 24-часовой формат
                if (period === 'PM' && hour !== 12) {
                    hour += 12;
                } else if (period === 'AM' && hour === 12) {
                    hour = 0;
                }
                // Форматирование часов с ведущим нулём
                const hourStr = hour < 10 ? '0' + hour : hour.toString();
                const monthRu = monthMapping[monthEn] || monthEn;

                // Используем перевод из файла переводов
                const byMoscowTime = translations.time?.by_moscow_time || "по московскому времени";
                return `${day} ${monthRu} ${year}, ${hourStr}:${minute} ${byMoscowTime}`;
            }
            return text;
        }

        // Функция для перевода элементов <relative-time>
        function translateRelativeTimes() {
            const timeElements = document.querySelectorAll('relative-time');
            timeElements.forEach(el => {
                // Если элемент уже переведён в основном DOM, проверяем его теневой DOM
                if (el.getAttribute('data-translated')) {
                    // Проверяем наличие теневого корня и пытаемся перевести его содержимое
                    if (el.shadowRoot) {
                        // Получаем текстовые узлы из теневого корня
                        const shadowTextNodes = [];

                        // Функция для рекурсивного обхода теневого DOM
                        function collectTextNodes(node) {
                            if (node.nodeType === Node.TEXT_NODE) {
                                shadowTextNodes.push(node);
                            } else if (node.childNodes) {
                                node.childNodes.forEach(childNode => {
                                    collectTextNodes(childNode);
                                });
                            }
                        }
                        // Обходим теневой DOM
                        collectTextNodes(el.shadowRoot);

                        // Переводим каждый текстовый узел
                        shadowTextNodes.forEach(textNode => {
                            const originalText = textNode.textContent.trim();
                            // Ищем паттерны времени
                            const hoursAgoMatch = originalText.match(/(\d+)\s+hours?\s+ago/);
                            const minutesAgoMatch = originalText.match(/(\d+)\s+minutes?\s+ago/);
                            const daysAgoMatch = originalText.match(/(\d+)\s+days?\s+ago/);
                            const weeksAgoMatch = originalText.match(/(\d+)\s+weeks?\s+ago/);
                            const lastWeekMatch = originalText.match(/last\s+week/i);
                            const yesterdayMatch = originalText.match(/yesterday/i);
                            const dateMatch = originalText.match(/([A-Za-z]+)\s+(\d{1,2}),\s+(\d{4})\s+(\d{1,2}):(\d{2})/i);
                            const shortDateMatch = originalText.match(/on\s+([A-Za-z]{3})\s+(\d{1,2})/i);

                            if (hoursAgoMatch) {
                                const hours = parseInt(hoursAgoMatch[1], 10);
                                let translatedText;

                                // Правильное склонение
                                if (hours === 1) {
                                    translatedText = translations.time?.hour_singular || "1 час назад";
                                } else if (hours >= 2 && hours <= 4) {
                                    translatedText = (translations.time?.hour_few || "{count} часа назад").replace("{count}", hours);
                                } else {
                                    translatedText = (translations.time?.hour_many || "{count} часов назад").replace("{count}", hours);
                                }

                                textNode.textContent = textNode.textContent.replace(
                                    /(\d+)\s+hours?\s+ago/,
                                    translatedText
                                );
                            } else if (daysAgoMatch) {
                                const days = parseInt(daysAgoMatch[1], 10);
                                let translatedText;

                                // Правильное склонение
                                if (days === 1) {
                                    translatedText = translations.time?.day_singular || "1 день назад";
                                } else if (days >= 2 && days <= 4) {
                                    translatedText = (translations.time?.day_few || "{count} дня назад").replace("{count}", days);
                                } else {
                                    translatedText = (translations.time?.day_many || "{count} дней назад").replace("{count}", days);
                                }

                                textNode.textContent = textNode.textContent.replace(
                                    /(\d+)\s+days?\s+ago/,
                                    translatedText
                                );
                            } else if (weeksAgoMatch) {
                                const weeks = parseInt(weeksAgoMatch[1], 10);
                                let translatedText;

                                // Правильное склонение
                                if (weeks === 1) {
                                    translatedText = translations.time?.week_singular || "1 неделю назад";
                                } else if (weeks >= 2 && weeks <= 4) {
                                    translatedText = (translations.time?.week_few || "{count} недели назад").replace("{count}", weeks);
                                } else {
                                    translatedText = (translations.time?.week_many || "{count} недель назад").replace("{count}", weeks);
                                }

                                textNode.textContent = textNode.textContent.replace(
                                    /(\d+)\s+weeks?\s+ago/,
                                    translatedText
                                );
                            } else if (lastWeekMatch) {
                                textNode.textContent = textNode.textContent.replace(
                                    /last\s+week/i,
                                    translations.time?.last_week || "на прошлой неделе"
                                );
                            } else if (yesterdayMatch) {
                                textNode.textContent = textNode.textContent.replace(
                                    /yesterday/i,
                                    translations.time?.yesterday || "вчера"
                                );
                            } else if (shortDateMatch) {
                                // Обработка формата «on Mar 13»
                                const monthEn = shortDateMatch[1];
                                const day = shortDateMatch[2];

                                // Используем словарь месяцев из файла переводов
                                const monthRu = translations.months?.[monthEn] || monthEn;
                                const translatedDate = `${day} ${monthRu}`;

                                textNode.textContent = textNode.textContent.replace(
                                    /on\s+[A-Za-z]{3}\s+\d{1,2}/i,
                                    `${day} ${monthRu}`
                                );
                            } else if (dateMatch) {
                                // Если у нас полная дата в формате «April 11, 2025 10:27»
                                const monthEn = dateMatch[1];
                                const day = dateMatch[2];
                                const year = dateMatch[3];
                                const hour = dateMatch[4];
                                const minute = dateMatch[5];

                                // Используем словарь месяцев из файла переводов
                                const monthRu = translations.months?.[monthEn] || monthEn;
                                const translatedDate = `${day} ${monthRu} ${year} ${hour}:${minute}`;

                                textNode.textContent = textNode.textContent.replace(
                                    /[A-Za-z]+\s+\d{1,2},\s+\d{4}\s+\d{1,2}:\d{2}/i,
                                    translatedDate
                                );
                            } else if (minutesAgoMatch) {
                                const minutes = parseInt(minutesAgoMatch[1], 10);
                                let translatedText;

                                // Правильное склонение
                                if (minutes === 1) {
                                    translatedText = translations.time?.minute_singular || "1 минуту назад";
                                } else if (minutes >= 2 && minutes <= 4) {
                                    translatedText = (translations.time?.minute_few || "{count} минуты назад").replace("{count}", minutes);
                                } else if (minutes >= 5 && minutes <= 20) {
                                    translatedText = (translations.time?.minute_many || "{count} минут назад").replace("{count}", minutes);
                                } else {
                                    // Для чисел 21, 31, 41… используем окончание как для 1
                                    const lastDigit = minutes % 10;
                                    const lastTwoDigits = minutes % 100;

                                    if (lastDigit === 1 && lastTwoDigits !== 11) {
                                        translatedText = (translations.time?.minute_singular || "1 минуту назад").replace("1", minutes);
                                    } else if (lastDigit >= 2 && lastDigit <= 4 && (lastTwoDigits < 10 || lastTwoDigits > 20)) {
                                        translatedText = (translations.time?.minute_few || "{count} минуты назад").replace("{count}", minutes);
                                    } else {
                                        translatedText = (translations.time?.minute_many || "{count} минут назад").replace("{count}", minutes);
                                    }
                                }

                                textNode.textContent = textNode.textContent.replace(
                                    /(\d+)\s+minutes?\s+ago/,
                                    translatedText
                                );
                            }
                        });
                    }
                    return;
                }

                // Перевод всплывающей подсказки, если атрибут title существует
                if (el.hasAttribute('title')) {
                    const originalTitle = el.getAttribute('title');
                    el.setAttribute('title', translateAbsoluteTime(originalTitle));
                }

                // Обработка текста даты/времени внутри элемента 
                const originalText = el.textContent.trim();

                // Обработка относительного времени «x hours ago», «x minutes ago» и т. д.
                const hoursAgoMatch = originalText.match(/(\d+)\s+hours?\s+ago/);
                const minutesAgoMatch = originalText.match(/(\d+)\s+minutes?\s+ago/);
                const onDateMatch = originalText.match(/on\s+([A-Za-z]+\s+\d+,\s+\d+)/);

                if (hoursAgoMatch) {
                    const hours = parseInt(hoursAgoMatch[1], 10);
                    let translatedText;

                    // Правильное склонение
                    if (hours === 1) {
                        translatedText = "1 час назад";
                    } else if (hours >= 2 && hours <= 4) {
                        translatedText = hours + " часа назад";
                    } else {
                        translatedText = hours + " часов назад";
                    }

                    el.textContent = translatedText;
                } else if (minutesAgoMatch) {
                    const minutes = parseInt(minutesAgoMatch[1], 10);
                    let translatedText;

                    // Правильное склонение
                    if (minutes === 1) {
                        translatedText = "1 минуту назад";
                    } else if (minutes >= 2 && minutes <= 4) {
                        translatedText = minutes + " минуты назад";
                    } else if (minutes >= 5 && minutes <= 20) {
                        translatedText = minutes + " минут назад";
                    } else {
                        // Для чисел 21, 31, 41… используем окончание как для 1
                        const lastDigit = minutes % 10;
                        const lastTwoDigits = minutes % 100;

                        if (lastDigit === 1 && lastTwoDigits !== 11) {
                            translatedText = minutes + " минуту назад";
                        } else if (lastDigit >= 2 && lastDigit <= 4 && (lastTwoDigits < 10 || lastTwoDigits > 20)) {
                            translatedText = minutes + " минуты назад";
                        } else {
                            translatedText = minutes + " минут назад";
                        }
                    }

                    el.textContent = translatedText;
                } else if (onDateMatch) {
                    // Обрабатываем формат «on Apr 12, 2025»
                    // Переводим английское название месяца на русский
                    const dateText = onDateMatch[1];
                    const monthMapping = {
                        Jan: 'января',
                        Feb: 'февраля',
                        Mar: 'марта',
                        Apr: 'апреля',
                        May: 'мая',
                        Jun: 'июня',
                        Jul: 'июля',
                        Aug: 'августа',
                        Sep: 'сентября',
                        Oct: 'октября',
                        Nov: 'ноября',
                        Dec: 'декабря'
                    };

                    // Регулярное выражение для поиска и замены месяца в строке даты
                    const monthRegex = /([A-Za-z]{3})\s+(\d{1,2}),\s+(\d{4})/;
                    const dateMatch = dateText.match(monthRegex);

                    if (dateMatch) {
                        const monthEn = dateMatch[1];
                        const day = dateMatch[2];
                        const year = dateMatch[3];
                        const monthRu = monthMapping[monthEn] || monthEn;

                        el.textContent = `в ${day} ${monthRu} ${year}`;
                    } else {
                        el.textContent = "в " + dateText;
                    }
                }

                // Пробуем перехватывать события мутации теневого корня
                if (window.ShadowRoot && !el.getAttribute('data-shadow-observed')) {
                    try {
                        // Пытаемся получить доступ к теневому DOM
                        const shadowRoot = el.shadowRoot;
                        if (shadowRoot) {
                            // Добавляем наблюдатель за изменениями в теневом DOM
                            const shadowObserver = new MutationObserver((mutations) => {
                                mutations.forEach(mutation => {
                                    if (mutation.type === 'childList' || mutation.type === 'characterData') {
                                        // Проходим по всем текстовым узлам в теневом DOM
                                        const textNodes = [];
                                        const walk = document.createTreeWalker(shadowRoot, NodeFilter.SHOW_TEXT);

                                        let node;
                                        while (node = walk.nextNode()) {
                                            textNodes.push(node);
                                        }

                                        textNodes.forEach(textNode => {
                                            const originalText = textNode.textContent;
                                            // Ищем паттерны времени
                                            if (originalText.match(/(\d+)\s+hours?\s+ago/)) {
                                                const hours = parseInt(originalText.match(/(\d+)\s+hours?\s+ago/)[1], 10);
                                                let translatedText;
                                                // Правильное склонение
                                                if (hours === 1) {
                                                    translatedText = translations.time?.hour_singular || "1 час назад";
                                                } else if (hours >= 2 && hours <= 4) {
                                                    translatedText = (translations.time?.hour_few || "{count} часа назад").replace("{count}", hours);
                                                } else {
                                                    translatedText = (translations.time?.hour_many || "{count} часов назад").replace("{count}", hours);
                                                }

                                                textNode.textContent = translatedText;
                                            } else if (originalText.match(/(\d+)\s+days?\s+ago/)) {
                                                const days = parseInt(originalText.match(/(\d+)\s+days?\s+ago/)[1], 10);
                                                let translatedText;

                                                // Правильное склонение для русского языка
                                                if (days === 1) {
                                                    translatedText = translations.time?.day_singular || "1 день назад";
                                                } else if (days >= 2 && days <= 4) {
                                                    translatedText = (translations.time?.day_few || "{count} дня назад").replace("{count}", days);
                                                } else {
                                                    translatedText = (translations.time?.day_many || "{count} дней назад").replace("{count}", days);
                                                }

                                                textNode.textContent = translatedText;
                                            } else if (originalText.match(/yesterday/i)) {
                                                textNode.textContent = translations.time?.yesterday || "вчера";
                                            } else if (originalText.match(/([A-Za-z]+)\s+(\d{1,2}),\s+(\d{4})\s+(\d{1,2}):(\d{2})/i)) {
                                                const match = originalText.match(/([A-Za-z]+)\s+(\d{1,2}),\s+(\d{4})\s+(\d{1,2}):(\d{2})/i); const monthEn = match[1];
                                                const day = match[2];
                                                const year = match[3];
                                                const hour = match[4];
                                                const minute = match[5];

                                                // Используем словарь месяцев из файла переводов
                                                const monthRu = translations.months?.[monthEn] || monthEn;
                                                textNode.textContent = `${day} ${monthRu} ${year} ${hour}:${minute}`;
                                            } else if (originalText.match(/(\d+)\s+minutes?\s+ago/)) {
                                                const minutes = parseInt(originalText.match(/(\d+)\s+minutes?\s+ago/)[1], 10);
                                                let translatedText;

                                                // Правильное склонение
                                                if (minutes === 1) {
                                                    translatedText = translations.time?.minute_singular || "1 минуту назад";
                                                } else if (minutes >= 2 && minutes <= 4) {
                                                    translatedText = (translations.time?.minute_few || "{count} минуты назад").replace("{count}", minutes);
                                                } else {
                                                    translatedText = (translations.time?.minute_many || "{count} минут назад").replace("{count}", minutes);
                                                }

                                                textNode.textContent = translatedText;
                                            }
                                        });
                                    }
                                });
                            });

                            shadowObserver.observe(shadowRoot, {
                                childList: true,
                                characterData: true,
                                subtree: true
                            });

                            el.setAttribute('data-shadow-observed', 'true');
                        }
                    } catch (error) {
                        console.error('[Русификатор Гитхаба] Ошибка при работе с теневым DOM:', error);
                    }
                }

                // Отмечаем элемент как переведённый
                el.setAttribute('data-translated', 'true');
            });
        }

        // функция для проверки, находится ли элемент в контейнере, где перевод нежелателен
        function isExcludedElement(el) {
            // Если элемент находится внутри заголовков Markdown, то не переводим
            if (el.closest('.markdown-heading')) return true;
            // Если элемент находится внутри ячейки с названием каталога, то не переводим
            if (el.closest('.react-directory-filename-column')) return true;
            return false;
        }

        // Функция для перевода блока GitHub Education
        function translateEducationExperience() {
            document.querySelectorAll('.experience__action-item h3').forEach(el => {
                if (el.textContent.includes('Add') && el.textContent.includes('repositories to a list')) {
                    el.innerHTML = el.innerHTML.replace(
                        /Add .* repositories to a list/,
                        'Добавьте репозитории, на которые поставили звезду, в список'
                    );
                }
            });

            document.querySelectorAll('.experience__action-item p.mt-4.f4').forEach(el => {
                if (el.textContent.includes('To complete this task, create a list with at least 3')) {
                    el.innerHTML = el.innerHTML.replace(
                        /To complete this task, create a list with at least 3 .* repos with the list name 'My Repo Watchlist'./,
                        'Чтобы выполнить это задание, создайте список, содержащий не менее трёх репозиториев, на которые вы поставили звезду, с названием «My Repo Watchlist».'
                    );
                }
            });

            document.querySelectorAll('.experience__actions-items-labels .Label--attention').forEach(el => {
                if (el.textContent.trim() === 'Incomplete') {
                    el.textContent = 'Не выполнено';
                }
            });

            document.querySelectorAll('.experience__actions-items-labels .Label--secondary').forEach(el => {
                if (el.textContent.trim() === 'List') {
                    el.textContent = 'Список';
                }
            });

            document.querySelectorAll('.experience__cta .Button--primary .Button-label').forEach(el => {
                if (el.textContent.trim() === 'See detailed instructions') {
                    el.textContent = 'Подробные инструкции';
                }
            });

            document.querySelectorAll('.experience__cta .Button--secondary .Button-label').forEach(el => {
                if (el.textContent.trim() === 'Mark complete') {
                    el.textContent = 'Отметить как выполненное';
                }
            });
        }

        function translateTextContent() {
            const elements = document.querySelectorAll(
                '.ActionList-sectionDivider-title, .ActionListItem-label, span[data-content], ' +
                '.AppHeader-context-item-label, #qb-input-query, .Truncate-text, h2, button, ' +
                '.Label, a, img[alt], .Box-title, .post__content p, .post__content li, .Button-label, ' +
                '.prc-ActionList-GroupHeading-eahp0'
            );

            elements.forEach(el => {
                // Если элемент подпадает под исключения, пропускаем его
                if (isExcludedElement(el)) return;

                if (el.tagName === 'IMG' && el.alt.trim() in translations) {
                    el.alt = translations[el.alt.trim()];
                } else if (el.childElementCount === 0) {
                    const text = el.textContent.trim();
                    if (translations[text]) {
                        el.textContent = translations[text];
                    } else {
                        const match = text.match(/^(\d+) repositories$/);
                        if (match) {
                            const count = parseInt(match[1], 10);
                            el.textContent = getRepositoriesTranslation(count);
                        } else if (text === 'added a repository to') {
                            el.textContent = translations['added a repository to'];
                        }
                    }
                } else {
                    // Часть функции translateTextContent(), отвечающая за обработку элементов с дочерними элементами
                    if (el.childElementCount > 0) {
                        // Сборка текстового содержания с учётом дочерних элементов
                        let text = '';
                        el.childNodes.forEach(node => {
                            if (node.nodeType === Node.TEXT_NODE) {
                                text += node.textContent;
                            } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'KBD') {
                                text += '/'; // Добавление символа «/» из <kbd>
                            }
                        });
                        text = text.trim();
                        if (translations[text]) {
                            // Создание нового фрагмента с переводом и сохранение тега <kbd>, если перевод соответствует шаблону
                            const newFragment = document.createDocumentFragment();
                            const parts = translations[text].split('<kbd class="AppHeader-search-kbd">/</kbd>');
                            newFragment.append(document.createTextNode(parts[0]));
                            // Добавлять тег <kbd> только если есть вторая часть перевода
                            if (parts.length > 1 && parts[1] !== undefined) {
                                const kbd = document.createElement('kbd');
                                kbd.className = 'AppHeader-search-kbd';
                                kbd.textContent = '/';
                                newFragment.append(kbd);
                                newFragment.append(document.createTextNode(parts[1]));
                            }
                            // Очистка элемента и вставка нового контента
                            el.innerHTML = '';
                            el.appendChild(newFragment);
                        }
                        el.childNodes.forEach(node => {
                            if (node.nodeType === Node.TEXT_NODE) {
                                const originalText = node.textContent;
                                const trimmed = originalText.trim();
                                if (translations[trimmed]) {
                                    node.textContent = translations[trimmed];
                                } else if (originalText.includes("starred")) {
                                    node.textContent = originalText.replace("starred", translations["starred"]);
                                } else if (originalText.includes("added a repository to")) {
                                    node.textContent = originalText.replace("added a repository to", translations['added a repository to']);
                                } else if (originalText.includes("Notifications")) {
                                    node.textContent = originalText.replace("Notifications", translations['Notifications']);
                                }
                            }
                        });
                        // Последный выхват строчек
                        if (/\bstarred\b/.test(el.innerHTML) && !el.innerHTML.includes('starred-button-icon')) {
                            el.innerHTML = el.innerHTML.replace(/\bstarred\b/g, function (match) {
                                return translations["starred"];
                            });
                        }
                        if (/\badded a repository to\b/.test(el.innerHTML)) {
                            el.innerHTML = el.innerHTML.replace(/\badded a repository to\b/g, translations['added a repository to']);
                        }
                        if (/\bNotifications\b/.test(el.innerHTML)) {
                            el.innerHTML = el.innerHTML.replace(/\bNotifications\b/g, translations['Notifications']);
                        }
                    } else {
                        // Сначала каждый узел
                        el.childNodes.forEach(node => {
                            if (node.nodeType === Node.TEXT_NODE) {
                                let originalText = node.textContent;
                                // Переводы
                                originalText = originalText.replace(/\bstarred\b/g, translations["starred"]);
                                originalText = originalText.replace(/\badded a repository to\b/g, translations['added a repository to']);
                                originalText = originalText.replace(/\bNotifications\b/g, translations['Notifications']);
                                node.textContent = originalText;
                            }
                        });

                        // Если всё ещё остаётся, заменить внутренний HTML
                        if (/\bstarred\b/.test(el.innerHTML)) {
                            el.innerHTML = el.innerHTML.replace(/\bstarred\b/g, translations["starred"]);
                        }
                        if (/\badded a repository to\b/.test(el.innerHTML)) {
                            el.innerHTML = el.innerHTML.replace(/\badded a repository to\b/g, translations['added a repository to']);
                        }
                        if (/\bNotifications\b/.test(el.innerHTML)) {
                            el.innerHTML = el.innerHTML.replace(/\bNotifications\b/g, translations['Notifications']);
                        }
                    }
                }
            });
            formatStarCount();
            translateRelativeTimes();

            document.querySelectorAll('.Button-label').forEach(btn => {
                if (btn.textContent.trim() === "New") {
                    btn.textContent = "Создать";
                }
            });
        }

        function translateCopilotPreview() {
            const askCopilotPlaceholder = document.querySelector('.copilotPreview__input[placeholder="Ask Copilot"]');
            if (askCopilotPlaceholder && translations['Ask Copilot']) {
                askCopilotPlaceholder.setAttribute('placeholder', translations['Ask Copilot']);
            }
            document.querySelectorAll('.copilotPreview__suggestionButton div').forEach(div => {
                const text = div.textContent.trim();
                if (translations[text]) {
                    div.innerHTML = translations[text];
                }
            });
        }

        function translateAttributes() {
            // Перевод placeholder
            document.querySelectorAll('input[placeholder]').forEach(el => {
                const text = el.getAttribute('placeholder');
                if (translations[text]) {
                    el.setAttribute('placeholder', translations[text]);
                }
            });
            // Перевод aria-label
            document.querySelectorAll('[aria-label]').forEach(el => {
                const text = el.getAttribute('aria-label');
                if (translations[text]) {
                    el.setAttribute('aria-label', translations[text]);
                }
            });
        }

        function translateTooltips() {
            const copilotChatTooltip = document.querySelector('tool-tip[for="copilot-chat-header-button"]');
            if (copilotChatTooltip && copilotChatTooltip.textContent.trim() === 'Chat with Copilot') {
                // Используем перевод из dashboard, сохранённый ранее
                copilotChatTooltip.textContent = window.dashboardCopilotTranslation;
            }

            document.querySelectorAll('tool-tip[role="tooltip"]').forEach(tooltip => {
                const text = tooltip.textContent.trim();
                if (translations[text]) {
                    tooltip.textContent = translations[text];
                }
            });

            document.querySelectorAll('.prc-TooltipV2-Tooltip-cYMVY').forEach(tooltip => {
                const text = tooltip.textContent.trim();
                if (translations[text]) {
                    tooltip.textContent = translations[text];
                }
            });
        }

        function translateGitHubEducation() {
            const noticeForms = document.querySelectorAll('div.js-notice form.js-notice-dismiss');

            noticeForms.forEach(form => {
                const heading = form.querySelector('h3.h4');
                if (heading && heading.textContent.trim() === 'Learn. Collaborate. Grow.') {
                    heading.textContent = 'Учитесь. Кооперируйтесь. Развивайтесь.';
                }

                const desc = form.querySelector('p.my-3.text-small');
                if (desc && desc.textContent.includes('GitHub Education gives you the tools')) {
                    desc.textContent = 'GitHub Education предоставляет инструменты и поддержку сообщества, чтобы вы могли принимать технологические вызовы и превращать их в возможности. Ваше технологическое будущее начинается здесь!';
                }

                const link = form.querySelector('.Button-label');
                if (link && link.textContent.trim() === 'Go to GitHub Education') {
                    link.textContent = 'Перейти в GitHub Education';
                }
            });

            document.querySelectorAll('.h5.color-fg-on-emphasis.text-mono').forEach(el => {
                if (el.textContent.trim() === 'LaunchPad') {
                    el.textContent = translations['LaunchPad'];
                }
            });

            document.querySelectorAll('.experience__gradient.experience__title').forEach(el => {
                if (el.textContent.trim() === 'Intro to GitHub') {
                    el.textContent = translations['Intro to GitHub'];
                }
            });

            // Шрифт Inter
            const educationNavBlock = document.querySelector('.d-flex.flex-justify-center.flex-md-justify-start.pb-5.pb-sm-7');
            if (educationNavBlock) {
                educationNavBlock.style.fontFamily = 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif';
            }

            document.querySelectorAll('header h1.mb-5').forEach(el => {
                el.style.fontFamily = 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif';
            });
        }

        function translateExperienceHeaderItems() {
            // Перевод заголовков и текста в блоке experience__header__items
            document.querySelectorAll('.experience__header__item .experience__hero__heading').forEach(el => {
                const text = el.textContent.trim();
                if (translations[text]) {
                    el.textContent = translations[text];
                }
            });

            // Перевод основного текста в блоке experience__header__items
            document.querySelectorAll('.experience__header__item p.color-fg-on-emphasis').forEach(el => {
                const text = el.textContent.trim();
                if (translations[text]) {
                    el.textContent = translations[text];
                }
            });

            // Шрифт Inter ко всему блоку для лучшей поддержки кириллицы
            document.querySelectorAll('.color-fg-on-emphasis').forEach(el => {
                el.style.fontFamily = 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif';
            });
        }

        function translateFilterMenu() {
            const filterTranslations = {
                "Filter": "Фильтр",
                "Events": "События",
                "Activity you want to see on your feed": "Деятельность, которую вы хотите видеть в своей ленте",
                "Announcements": "Объявления",
                "Special discussion posts from repositories": "Особые обсуждения из репозиториев",
                "Releases": "Выпуски",
                "Update posts from repositories": "Новые обновления в репозиториях",
                "Sponsors": "Спонсоры",
                "Relevant projects or people that are being sponsored": "Проекты или люди, которых кто-то начинает спонсировать",
                "Stars": "Звёзды",
                "Repositories being starred by people": "Репозитории, которые получают звёзды от людей",
                "Repositories": "Репозитории",
                "Repositories that are created or forked by people": "Репозитории, созданные или разветвлённые пользователями",
                "Repository activity": "Деятельность в репозиториях",
                "Issues and pull requests from repositories": "Новые темы и запросы на слияние в репозиториях",
                "Follows": "Подписки",
                "Who people are following": "На кого подписываются пользователи",
                "Recommendations": "Рекомендации",
                "Repositories and people you may like": "Репозитории и пользователи, которые могут вам понравиться",
                "Include events from starred repositories": "Включать события из репозиториев, на которые вы поставили звезду",
                "By default, the feed shows events from repositories you sponsor or watch, and people you follow.": "По умолчанию лента отображает события из репозиториев, которые вы спонсируете или за которыми следите, а также от людей, на которых подписаны.",
                "Reset to default": "Сбросить до настроек по умолчанию",
                "Save": "Сохранить"
            };

            const elements = document.querySelectorAll(
                '.SelectMenu-title, .SelectMenu-item h5, .SelectMenu-item span, .px-3.mt-2 h5, .px-3.mt-2 p'
            );

            elements.forEach(el => {
                const text = el.textContent.trim();
                if (filterTranslations[text]) {
                    el.textContent = filterTranslations[text];
                }
            });
        }

        function translateOpenCopilotMenu() {
            document.querySelectorAll('span.prc-ActionList-ItemLabel-TmBhn').forEach(el => {
                const originalText = el.textContent.trim();
                if (translations[originalText]) {
                    el.textContent = translations[originalText];
                }
            });
        }

        function translateStarButtons() {
            // Находим все span с классом d-inline внутри кнопок
            document.querySelectorAll('.BtnGroup-item .d-inline').forEach(span => {
                const text = span.textContent.trim();
                if (text === 'Star') {
                    span.textContent = translations["Star"] || 'Поставить звезду';
                } else if (text === 'Starred') {
                    span.textContent = translations["Starred"] || 'Звезда поставлена';
                }
            });

            // Переводим заголовок в диалоговом окне отмены звезды
            document.querySelectorAll('.Box-title').forEach(title => {
                if (title.textContent.trim() === 'Unstar this repository?') {
                    title.textContent = translations["Unstar this repository?"] || 'Убрать звезду с этого репозитория?';
                }
            });

            // Переводим кнопку Unstar в диалоговом окне
            document.querySelectorAll('.btn-danger.btn').forEach(btn => {
                if (btn.textContent.trim() === 'Unstar') {
                    btn.textContent = translations["Unstar"] || 'Убрать звезду';
                }
            });
        }

        function translateRepositoryButtons() {
            // Перевод кнопки Sponsor
            document.querySelectorAll('.Button-label .v-align-middle').forEach(span => {
                const text = span.textContent.trim();
                if (text === 'Sponsor') {
                    span.textContent = translations["Sponsor"] || 'Спонсировать';
                }
            });

            // Перевод кнопки Watch
            document.querySelectorAll('.prc-Button-Label-pTQ3x').forEach(span => {
                if (span.textContent.trim().startsWith('Watch')) {
                    // Сохраняем счётчик
                    const counter = span.querySelector('.Counter');
                    const counterHTML = counter ? counter.outerHTML : '';

                    // Новый текст с сохранённым счетчиком
                    span.innerHTML = (translations["Watch"] || 'Следить') +
                        (counterHTML ? ' ' + counterHTML : '');
                }
            });

            // Перевод кнопки Fork
            document.querySelectorAll('.BtnGroup.d-flex').forEach(btnGroup => {
                // Проверяем, что это непереведенная кнопка Fork
                if (btnGroup.textContent.includes('Fork') && !btnGroup.hasAttribute('data-translated-fork')) {
                    // Сначала сохраним все важные элементы
                    const counter = btnGroup.querySelector('#repo-network-counter');
                    const details = btnGroup.querySelector('details');

                    // Создаём функцию для глубокого обхода DOM-дерева
                    function translateNode(node) {
                        if (node.nodeType === Node.TEXT_NODE) {
                            // Регулярное выражение для поиска слова «Fork» с сохранением пробелов
                            const regex = /(\s*)Fork(\s*)/g;
                            node.textContent = node.textContent.replace(regex,
                                (match, before, after) => before + (translations["Fork"] || 'Разветвить') + after);
                        } else {
                            // Рекурсивный обход всех дочерних узлов
                            for (let i = 0; i < node.childNodes.length; i++) {
                                translateNode(node.childNodes[i]);
                            }
                        }
                    }

                    // Запускаем перевод с корневого элемента
                    translateNode(btnGroup);

                    // Отмечаем элемент как обработанный
                    btnGroup.setAttribute('data-translated-fork', 'true');
                }
            });
        }

        function translateLabelElements() {
            document.querySelectorAll('.prc-Label-Label--LG6X').forEach(el => {
                if (el.textContent.trim() === 'Free' && translations['Free']) {
                    el.textContent = translations['Free'];
                }
            });
        }

        // Функция для перевода статусов тем, кнопок редактирования и создания тем
        function translateIssueElements() {
            // Перевод статуса «Open» в темах
            document.querySelectorAll('span[data-testid="header-state"]').forEach(el => {
                if (el.textContent.trim() === 'Open' && translations['Open']) {
                    // Сохраняем SVG-значок
                    const svg = el.querySelector('svg');
                    const svgHTML = svg ? svg.outerHTML : '';

                    // Заменяем текст, сохраняя значок
                    el.innerHTML = svgHTML + translations['Open'];
                }
                // Перевод статуса «Closed» в темах
                else if (el.textContent.trim() === 'Closed' && translations['Closed']) {
                    // Сохраняем SVG-значок
                    const svg = el.querySelector('svg');
                    const svgHTML = svg ? svg.outerHTML : '';

                    // Заменяем текст, сохраняя значок
                    el.innerHTML = svgHTML + translations['Closed'];
                }
            });

            // Перевод кнопки «Edit»
            document.querySelectorAll('.prc-Button-ButtonBase-c50BI .prc-Button-Label-pTQ3x').forEach(el => {
                if (el.textContent.trim() === 'Edit' && translations['Edit']) {
                    el.textContent = translations['Edit'];
                }
            });

            // Перевод кнопки «New issue»
            document.querySelectorAll('.prc-Button-ButtonBase-c50BI .prc-Button-Label-pTQ3x').forEach(el => {
                if (el.textContent.trim() === 'New issue' && translations['New issue']) {
                    el.textContent = translations['New issue'];
                }
            });
            // Трансформация строки вида «Пользователь opened 2 hours ago» в «Открыта Пользователь 2 часа назад»
            document.querySelectorAll('.Box-sc-g0xbh4-0.dqmClk, [data-testid="issue-body-header-author"]').forEach(authorEl => {
                // Ищем ближайший родительский контейнер, который содержит также подвал с «opened»
                const container = authorEl.closest('.ActivityHeader-module__narrowViewportWrapper--Hjl75, .Box-sc-g0xbh4-0.koxHLL');
                if (!container) return;

                // Находим подвал с текстом «opened»
                const footer = container.querySelector('.ActivityHeader-module__footer--FVHp7, .Box-sc-g0xbh4-0.bJQcYY');
                if (!footer) return;

                // Находим span с «opened» и автором
                const openedSpan = footer.querySelector('span');
                const authorLink = authorEl.querySelector('a[data-testid="issue-body-header-author"], a[href*="/users/"]') || authorEl;

                // Проверяем, что span содержит «opened»
                if (!openedSpan || !openedSpan.textContent.includes('opened')) return;

                // Получаем ссылку на время с relative-time
                const timeLink = footer.querySelector('a[data-testid="issue-body-header-link"]');
                if (!timeLink) return;

                // Находим элемент relative-time внутри ссылки
                const relativeTime = timeLink.querySelector('relative-time');
                if (!relativeTime) return;

                try {
                    // Если уже трансформировано, пропускаем
                    if (footer.getAttribute('data-ru-transformed')) return;

                    // Отмечаем как трансформированное
                    footer.setAttribute('data-ru-transformed', 'true');

                    // Создаём новую структуру
                    // 1. Сохраняем автора
                    const authorClone = authorLink.cloneNode(true);

                    // 2. Меняем текст в span на перевод «opened» из файла локализации
                    openedSpan.textContent = translations["opened"] ? translations["opened"] + ' ' : 'Открыта ';

                    // 3. Вставляем автора после слова «Открыта»
                    openedSpan.after(authorClone);

                    // 4. Добавляем пробел между автором и временем
                    authorClone.after(document.createTextNode(' '));

                    // 5. Трансформируем текст времени
                    const originalTimeText = relativeTime.textContent;

                    // Проверяем, содержит ли текст паттерн времени (например, «3 hours ago» или «on Apr 12, 2025»)
                    const hoursAgoMatch = originalTimeText.match(/(\d+)\s+hours?\s+ago/);
                    const minutesAgoMatch = originalTimeText.match(/(\d+)\s+minutes?\s+ago/);
                    const onDateMatch = originalTimeText.match(/on\s+([A-Za-z]+\s+\d+,\s+\d+)/);

                    if (hoursAgoMatch) {
                        const hours = parseInt(hoursAgoMatch[1], 10);
                        let translatedText;

                        // Правильное склонение
                        if (hours === 1) {
                            translatedText = "Час назад";
                        } else if (hours >= 2 && hours <= 4) {
                            translatedText = hours + " часа назад";
                        } else {
                            translatedText = hours + " часов назад";
                        }

                        relativeTime.textContent = translatedText;
                    } else if (minutesAgoMatch) {
                        const minutes = parseInt(minutesAgoMatch[1], 10);
                        let translatedText;

                        // Правильное склонение
                        if (minutes === 1) {
                            translatedText = "1 минуту назад";
                        } else if (minutes >= 2 && minutes <= 4) {
                            translatedText = minutes + " минуты назад";
                        } else if (minutes >= 5 && minutes <= 20) {
                            translatedText = minutes + " минут назад";
                        } else {
                            // Для чисел 21, 31, 41… используем окончание как для 1
                            const lastDigit = minutes % 10;
                            const lastTwoDigits = minutes % 100;

                            if (lastDigit === 1 && lastTwoDigits !== 11) {
                                translatedText = minutes + " минуту назад";
                            } else if (lastDigit >= 2 && lastDigit <= 4 && (lastTwoDigits < 10 || lastTwoDigits > 20)) {
                                translatedText = minutes + " минуты назад";
                            } else {
                                translatedText = minutes + " минут назад";
                            }
                        }

                        relativeTime.textContent = translatedText;
                    } else if (onDateMatch) {
                        // Обрабатываем формат «on Apr 12, 2025»
                        // Переводим английское название месяца на русский
                        const dateText = onDateMatch[1];
                        const monthMapping = {
                            Jan: 'января',
                            Feb: 'февраля',
                            Mar: 'марта',
                            Apr: 'апреля',
                            May: 'мая',
                            Jun: 'июня',
                            Jul: 'июля',
                            Aug: 'августа',
                            Sep: 'сентября',
                            Oct: 'октября',
                            Nov: 'ноября',
                            Dec: 'декабря'
                        };

                        // Регулярное выражение для поиска и замены месяца в строке даты
                        const monthRegex = /([A-Za-z]{3})\s+(\d{1,2}),\s+(\d{4})/;
                        const dateMatch = dateText.match(monthRegex);

                        if (dateMatch) {
                            const monthEn = dateMatch[1];
                            const day = dateMatch[2];
                            const year = dateMatch[3];
                            const monthRu = monthMapping[monthEn] || monthEn;

                            relativeTime.textContent = `в ${day} ${monthRu} ${year}`;
                        } else {
                            relativeTime.textContent = "в " + dateText;
                        }
                    }

                    // 6. Скрываем оригинальный контейнер с автором
                    if (authorEl !== authorLink) {
                        authorEl.style.cssText = 'display: none !important;';
                    }

                    console.log('[Русификатор Гитхаба] Cтрока с автором темы трансформирована');
                } catch (error) {
                    console.error('[Русификатор Гитхаба] Ошибка при трансформации строки с автором:', error);
                }
            });
        }

        const feedTitleEl = document.querySelector('[data-target="feed-container.feedTitle"]');
        if (feedTitleEl && window.dashboardHomeTranslation) {
            feedTitleEl.textContent = window.dashboardHomeTranslation;
        }

        document.querySelectorAll('#feed-filter-menu summary').forEach(summary => {
            summary.innerHTML = summary.innerHTML.replace('Filter', translations["Filter"]);
        });

        const observer = new MutationObserver(() => {
            translateTextContent();
            translateAttributes();
            translateCopilotPreview();
            translateTooltips();
            translateGitHubEducation();
            translateExperienceHeaderItems();
            translateEducationExperience();
            translateFilterMenu();
            translateOpenCopilotMenu();
            translateStarButtons();
            translateRepositoryButtons();
            translateLabelElements();
            translateIssueElements();

            // Перевод подвала
            document.querySelectorAll('p.color-fg-subtle.text-small.text-light').forEach(node => {
                if (node.textContent.trim() === '© 2025 GitHub, Inc.') {
                    node.textContent = translations['© 2025 GitHub, Inc.'];
                }
            });

            document.querySelectorAll('a.Link').forEach(link => {
                const text = link.textContent.trim();
                if (text === 'About') link.textContent = 'О нас';
                if (text === 'Blog') link.textContent = 'Блог';
                if (text === 'Terms') link.textContent = 'Условия';
                if (text === 'Privacy') link.textContent = 'Конфиденциальность';
                if (text === 'Security') link.textContent = 'Безопасность';
                if (text === 'Status') link.textContent = 'Статус';
            });

            document.querySelectorAll('.Button-label').forEach(btn => {
                if (btn.textContent.trim() === 'Do not share my personal information') {
                    btn.textContent = 'Не передавать мои личные данные';
                }
                if (btn.textContent.trim() === 'Manage Cookies') {
                    btn.textContent = 'Управление куки';
                }
            });

            // Владельцы и перейти
            document.querySelectorAll('h3.ActionList-sectionDivider-title').forEach(node => {
                if (node.textContent.trim() === 'Owners') {
                    node.textContent = translations['Owners'];
                }
            });

            document.querySelectorAll('.ActionListItem-description.QueryBuilder-ListItem-trailing').forEach(span => {
                if (span.textContent.trim() === 'Jump to') {
                    span.textContent = translations['Jump to'];
                }
            });

            // Перевод для кнопки «Chat with Copilot»
            document.querySelectorAll('.ActionListItem-label').forEach(el => {
                if (el.textContent.trim() === "Chat with Copilot" && translations["Chat with Copilot"]) {
                    el.textContent = translations["Chat with Copilot"];
                }
            });

            // Перевод описания «Start a new Copilot thread»
            document.querySelectorAll('.QueryBuilder-ListItem-trailing').forEach(el => {
                if (el.textContent.trim() === "Start a new Copilot thread" && translations["Start a new Copilot thread"]) {
                    el.textContent = translations["Start a new Copilot thread"];
                }
            });

            // Замена «added a repository to»
            document.querySelectorAll('h3.h5.text-normal.color-fg-muted.d-flex.flex-items-center.flex-row.flex-nowrap.width-fit span.flex-1 span.flex-shrink-0').forEach(span => {
                if (span.textContent.trim() === 'added a repository to') {
                    span.textContent = translations['added a repository to'];
                }
            });
        });

        // Наблюдение за всем документом, включая изменения атрибутов
        observer.observe(document, {
            childList: true,
            subtree: true,
            attributes: true
        });

        // Будущие изменения
        const observerStarred = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => replaceAllStarred(node));
            });
        });

        // Запуск
        observerStarred.observe(document.body, { childList: true, subtree: true });

        // Начальное прохождение
        replaceAllStarred(document.body);

        translateTextContent();
        translateAttributes();
        translateCopilotPreview();
        translateTooltips();
        translateGitHubEducation();
        translateExperienceHeaderItems();
        translateEducationExperience();
        translateFilterMenu();
        translateOpenCopilotMenu();
        translateStarButtons();
        translateRepositoryButtons();
        translateIssueElements();

        // Замена «Filter»
        document.querySelectorAll('summary .octicon-filter').forEach(icon => {
            const summary = icon.parentElement;
            if (summary) {
                summary.childNodes.forEach(node => {
                    if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === 'Filter') {
                        node.textContent = translations["Filter"];
                    }
                });
            }
        });

        // Добавляем текст «Фильтр» в кнопку, если его нет
        document.querySelectorAll('summary').forEach(summary => {
            if (summary.querySelector('.octicon-filter')) {
                let hasFilterText = false;
                summary.childNodes.forEach(node => {
                    if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === 'Фильтр') {
                        hasFilterText = true;
                    }
                });
                if (!hasFilterText) {
                    const svgIcon = summary.querySelector('.octicon-filter');
                    const textNode = document.createTextNode('Фильтр');
                    if (svgIcon && svgIcon.nextSibling) {
                        summary.insertBefore(textNode, svgIcon.nextSibling);
                    } else {
                        summary.appendChild(textNode);
                    }
                }
            }
        });

        translateFilterMenu();
    }
})();