From 49a2e41dc806570cc717e7189afb9d9918bf0129 Mon Sep 17 00:00:00 2001 From: kaankacar Date: Tue, 27 Jan 2026 17:49:22 +0300 Subject: [PATCH 1/3] Add no-translate plugin to prevent Google Translate from translating proper nouns (#2191) --- docusaurus.config.ts | 1 + src/plugins/no-translate/index.ts | 96 +++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/plugins/no-translate/index.ts diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 3f7570a17..e7b3a457f 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -84,6 +84,7 @@ const config: Config = { ], './src/plugins/route-export/index.ts', './src/plugins/analytics-module/index.ts', + './src/plugins/no-translate/index.ts', ], markdown: { mermaid: true, diff --git a/src/plugins/no-translate/index.ts b/src/plugins/no-translate/index.ts new file mode 100644 index 000000000..06cb5e32d --- /dev/null +++ b/src/plugins/no-translate/index.ts @@ -0,0 +1,96 @@ +import type { LoadContext, Plugin } from "@docusaurus/types"; + +// Words and phrases that should not be translated by Google Translate. +// These are proper nouns, brand names, and technical terms that lose +// meaning when translated. Case-sensitive to avoid false positives +// (e.g. "Rust" the language vs "rust" the oxidation). +const NO_TRANSLATE_WORDS: string[] = [ + "Stellar", + "Soroban", + "Horizon", + "Rust", + "XLM", +]; + +export default function noTranslatePlugin(context: LoadContext): Plugin { + const wordsJSON = JSON.stringify(NO_TRANSLATE_WORDS); + + return { + name: 'stellar-docs-no-translate-plugin', + + injectHtmlTags() { + return { + headTags: [ + { + tagName: 'script', + innerHTML: ` +(function() { + var words = ${wordsJSON}; + var pattern = new RegExp("\\\\b(" + words.join("|") + ")\\\\b", "g"); + + function processTextNode(node) { + if (!pattern.test(node.nodeValue)) return; + + // Skip text inside code blocks or already-wrapped nodes + if (node.parentElement.closest("pre, code, .notranslate")) return; + + var fragment = document.createDocumentFragment(); + var text = node.nodeValue; + var lastIndex = 0; + + // Reset regex state since we already called .test() + pattern.lastIndex = 0; + + var match; + while ((match = pattern.exec(text)) !== null) { + // Append text before the match + if (match.index > lastIndex) { + fragment.appendChild(document.createTextNode(text.slice(lastIndex, match.index))); + } + + // Wrap the matched word + var span = document.createElement("span"); + span.className = "notranslate"; + span.textContent = match[0]; + fragment.appendChild(span); + + lastIndex = pattern.lastIndex; + } + + // Append remaining text + if (lastIndex < text.length) { + fragment.appendChild(document.createTextNode(text.slice(lastIndex))); + } + + node.parentNode.replaceChild(fragment, node); + } + + function processTree(root) { + var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); + var nodes = []; + while (walker.nextNode()) nodes.push(walker.currentNode); + nodes.forEach(processTextNode); + } + + document.addEventListener("DOMContentLoaded", function() { + processTree(document.body); + + // Re-process on SPA navigation (Docusaurus client-side routing) + new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + mutation.addedNodes.forEach(function(node) { + if (node.nodeType === Node.ELEMENT_NODE) { + processTree(node); + } + }); + }); + }).observe(document.body, { childList: true, subtree: true }); + }); +})(); + `.trim(), + }, + ], + }; + }, + }; +} From 4e89efe205d42227178917cc1a0610c2ac102041 Mon Sep 17 00:00:00 2001 From: kaankacar Date: Tue, 27 Jan 2026 18:09:25 +0300 Subject: [PATCH 2/3] Fix spacing around notranslate spans for Google Translate --- src/plugins/no-translate/index.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/plugins/no-translate/index.ts b/src/plugins/no-translate/index.ts index 06cb5e32d..13c03b847 100644 --- a/src/plugins/no-translate/index.ts +++ b/src/plugins/no-translate/index.ts @@ -43,18 +43,25 @@ export default function noTranslatePlugin(context: LoadContext): Plugin { var match; while ((match = pattern.exec(text)) !== null) { - // Append text before the match - if (match.index > lastIndex) { - fragment.appendChild(document.createTextNode(text.slice(lastIndex, match.index))); + var start = match.index; + var end = pattern.lastIndex; + + // Pull surrounding spaces inside the span so Google Translate + // cannot collapse them at the notranslate boundary + if (start > lastIndex && text[start - 1] === " ") start--; + if (end < text.length && text[end] === " ") end++; + + // Append text before the span + if (start > lastIndex) { + fragment.appendChild(document.createTextNode(text.slice(lastIndex, start))); } - // Wrap the matched word var span = document.createElement("span"); span.className = "notranslate"; - span.textContent = match[0]; + span.textContent = text.slice(start, end); fragment.appendChild(span); - lastIndex = pattern.lastIndex; + lastIndex = end; } // Append remaining text From 4894c91570392f6e51515a65c2e893799dd98215 Mon Sep 17 00:00:00 2001 From: kaankacar Date: Tue, 27 Jan 2026 18:40:53 +0300 Subject: [PATCH 3/3] Trim no-translate list to only Rust --- src/plugins/no-translate/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/plugins/no-translate/index.ts b/src/plugins/no-translate/index.ts index 13c03b847..d98c52513 100644 --- a/src/plugins/no-translate/index.ts +++ b/src/plugins/no-translate/index.ts @@ -5,11 +5,7 @@ import type { LoadContext, Plugin } from "@docusaurus/types"; // meaning when translated. Case-sensitive to avoid false positives // (e.g. "Rust" the language vs "rust" the oxidation). const NO_TRANSLATE_WORDS: string[] = [ - "Stellar", - "Soroban", - "Horizon", "Rust", - "XLM", ]; export default function noTranslatePlugin(context: LoadContext): Plugin {