Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -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.
54 changes: 31 additions & 23 deletions i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Optimization condition needs clarification

  • Location: i18n.js:330
  • Problem: The isInitialKo condition combines two concepts (initial load + Korean) in one variable, making future maintenance harder
  • Root cause: The condition 'currentLang === null && lang === 'ko'' mixes state initialization check with language check
  • Fix: Separate the initial load check from the language check for better maintainability
  • Regression test: Verify Korean content still loads correctly without DOM updates on initial load

Suggested diff

@@ -327,7 +327,8 @@ function setLanguage(lang) {
     document.title = dict.metaTitle;
   }
 
-  // ⚡ Bolt: Skip expensive DOM queries and updates if default language (ko) is requested on initial load
-  const isInitialKo = currentLang === null && lang === 'ko';
+  // ⚡ Bolt: Skip expensive DOM queries on initial Korean load
+  const isInitialLoad = currentLang === null;
+  const isDefaultLanguage = lang === 'ko';
 
-  if (!isInitialKo) {
+  if (!(isInitialLoad && isDefaultLanguage)) {
     if (!i18nNodes) {


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);
Expand Down
21 changes: 21 additions & 0 deletions test_i18n.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>i18n Test</title>
<meta name="description" content="맥락지혜 연구실은 흩어진 기업 자료를 맥락 안에서 판단 가능한 구조로 바꾸는 AI 의사결정 지원 시스템을 연구하고 만듭니다.">
<meta property="og:description" content="맥락지혜 연구실은 흩어진 기업 자료를 맥락 안에서 판단 가능한 구조로 바꾸는 AI 의사결정 지원 시스템을 연구하고 만듭니다.">
<script src="i18n.js" defer></script>
</head>
<body>
<div class="language-switch">
<button type="button" data-lang="ko" aria-pressed="true">KO</button>
<button type="button" data-lang="en" aria-pressed="false">EN</button>
</div>

<h1 data-i18n="hero.title">맥락지혜 연구실</h1>
<p data-i18n="hero.thesis">구슬이 서 말이어도 꿰어야 보배이듯, 문서, 메일, 로그, 회의록을 맥락 안에서 엮어 사람이 무엇을 결정하고 무엇을 실행할지 보이게 하는 AI 의사결정 지원 시스템을 연구하고 만듭니다.</p>

<img id="footer-logo" src="assets/context-wisdom-lab-logo.svg" alt="맥락지혜 연구실 · Contextual Wisdom Lab">
</body>
</html>