From a0b9a8de3c6c68ce2cc09ed8dada2560d1db773d Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 30 Jun 2026 14:55:17 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0]=20=EC=B4=88=EA=B8=B0=20=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=EC=8B=9C=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20DOM=20?= =?UTF-8?q?=ED=83=90=EC=83=89=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 3 +++ i18n.js | 54 +++++++++++++++++++++++++++++--------------------- test_i18n.html | 21 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 23 deletions(-) create mode 100644 test_i18n.html diff --git a/.jules/bolt.md b/.jules/bolt.md index f74ef2f..be55af1 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,6 @@ ## 2024-06-20 - Unnecessary initial DOM updates for default language **Learning:** The simple static i18n implementation runs `node.textContent = dict[node.dataset.i18n]` for every translatable node on the initial script load, even when the HTML is already written in the target language (Korean). This creates unnecessary layout/paint operations and blocking time on the main thread for elements that don't need text changes. **Action:** Always check if the current value matches the desired value before updating the DOM (`node.textContent !== newText`), and add early exits when setting state to the same value to avoid redundant DOM traversal and writes. +## 2024-06-25 - Prevent redundant initial DOM updates +**Learning:** Initializing the app triggers an unnecessary `querySelectorAll` and textContent update loop for the default language, causing layout thrashing even when the HTML is already rendered correctly. +**Action:** Add early exit logic to skip expensive DOM lookups and writes if the requested language matches the pre-rendered default language, while ensuring essential state (like `aria-pressed`) still initializes. diff --git a/i18n.js b/i18n.js index 2393774..9bdbe46 100644 --- a/i18n.js +++ b/i18n.js @@ -315,12 +315,8 @@ function setLanguage(lang) { const dict = messages[lang] || messages.ko; - if (!i18nNodes) { - i18nNodes = document.querySelectorAll("[data-i18n]"); + if (!langButtons) { langButtons = document.querySelectorAll("[data-lang]"); - metaDesc = document.querySelector('meta[name="description"]'); - ogDesc = document.querySelector('meta[property="og:description"]'); - footerLogo = document.querySelector("#footer-logo"); } if (document.documentElement.lang !== lang) { @@ -330,29 +326,41 @@ function setLanguage(lang) { document.title = dict.metaTitle; } - if (metaDesc && metaDesc.getAttribute("content") !== dict.metaDescription) { - metaDesc.setAttribute("content", dict.metaDescription); - } - if (ogDesc && ogDesc.getAttribute("content") !== dict.metaDescription) { - ogDesc.setAttribute("content", dict.metaDescription); - } + // ⚡ Bolt: Skip expensive DOM queries and updates if default language (ko) is requested on initial load + const isInitialKo = currentLang === null && lang === 'ko'; - if (footerLogo) { - if (footerLogo.getAttribute("src") !== dict.logoSrc) { - footerLogo.setAttribute("src", dict.logoSrc); + if (!isInitialKo) { + if (!i18nNodes) { + i18nNodes = document.querySelectorAll("[data-i18n]"); + metaDesc = document.querySelector('meta[name="description"]'); + ogDesc = document.querySelector('meta[property="og:description"]'); + footerLogo = document.querySelector("#footer-logo"); } - if (footerLogo.getAttribute("alt") !== dict.logoAlt) { - footerLogo.setAttribute("alt", dict.logoAlt); + + if (metaDesc && metaDesc.getAttribute("content") !== dict.metaDescription) { + metaDesc.setAttribute("content", dict.metaDescription); + } + if (ogDesc && ogDesc.getAttribute("content") !== dict.metaDescription) { + ogDesc.setAttribute("content", dict.metaDescription); } - } - // Only update textContent if it actually changed to avoid layout recalculations - i18nNodes.forEach((node) => { - const newText = dict[node.dataset.i18n]; - if (newText && node.textContent !== newText) { - node.textContent = newText; + if (footerLogo) { + if (footerLogo.getAttribute("src") !== dict.logoSrc) { + footerLogo.setAttribute("src", dict.logoSrc); + } + if (footerLogo.getAttribute("alt") !== dict.logoAlt) { + footerLogo.setAttribute("alt", dict.logoAlt); + } } - }); + + // Only update textContent if it actually changed to avoid layout recalculations + i18nNodes.forEach((node) => { + const newText = dict[node.dataset.i18n]; + if (newText && node.textContent !== newText) { + node.textContent = newText; + } + }); + } langButtons.forEach((button) => { const pressed = String(button.dataset.lang === lang); diff --git a/test_i18n.html b/test_i18n.html new file mode 100644 index 0000000..15f8891 --- /dev/null +++ b/test_i18n.html @@ -0,0 +1,21 @@ + + +
+ +구슬이 서 말이어도 꿰어야 보배이듯, 문서, 메일, 로그, 회의록을 맥락 안에서 엮어 사람이 무엇을 결정하고 무엇을 실행할지 보이게 하는 AI 의사결정 지원 시스템을 연구하고 만듭니다.
+ +