I’m using GSAP with SplitType and a custom language switcher (en.json, es.json) to translate my website dynamically.
Everything translates correctly except the .scroll-fade-text section — which uses SplitType for scroll-triggered word-by-word animation.
On language change, the translated text briefly appears, then reverts back to the original language’s content (probably due to SplitType or GSAP misfiring).
What I’ve Tried:
Verified the translation keys load and update correctly via data-i18n
Used requestAnimationFrame() and setTimeout() to delay SplitType initialization
Used .revert() and .kill() to clean up previous animation
HTML
<div class="scroll-lock-section">
<div class="scroll-text-wrap scroll-fade-text" data-i18n="scrollLine">
MY LONG TEXT IN ENGLISH HERE.
</div>
</div>
JS
document.addEventListener('DOMContentLoaded', () => {
const langSelect = document.getElementById('langSelect');
const storedLang = localStorage.getItem('lang') || 'en';
langSelect.value = storedLang;
applyLanguage(storedLang);
langSelect.addEventListener('change', () => {
const lang = langSelect.value;
localStorage.setItem('lang', lang);
applyLanguage(lang);
});
});
function applyLanguage(lang) {
fetch(`/lang/${lang}.json`)
.then(res => res.json())
.then(data => {
document.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
const allowHtml = el.getAttribute('data-i18n-html') === 'true';
if (data[key]) {
if (allowHtml) {
el.innerHTML = data[key];
} else {
el.textContent = data[key];
}
}
});
// Attempt delayed animation init
requestAnimationFrame(() => {
setTimeout(() => {
initScrollAnimation();
}, 100);
});
});
}
let scrollSplit;
function initScrollAnimation() {
if (scrollSplit) scrollSplit.revert();
ScrollTrigger.getAll().forEach(trigger => trigger.kill());
gsap.killTweensOf("*");
scrollSplit = new SplitType(".scroll-fade-text", {
types: "words",
wordClass: "word"
});
gsap.to(scrollSplit.words, {
color: "white",
stagger: 0.3,
scrollTrigger: {
trigger: ".scroll-lock-section",
start: "top top",
end: "+=1000",
scrub: true,
pin: true,
invalidateOnRefresh: true
}
});
}
CSS
.scroll-lock-section {
height: 85vh;
display: flex;
align-items: center;
justify-content: center;
padding: 0 20px;
background-color: #0c1624;
}
.scroll-text-wrap {
font-size: 48px;
font-family: 'Manrope', sans-serif;
font-weight: bold;
max-width: 1000px;
text-align: center;
line-height: 1.5;
margin-bottom: -150px;
}
.scroll-text-wrap .word {
color: gray;
transition: color 0.4s ease;
}
When I switch the language from English to Spanish:
All other data-i18n elements update correctly.
scroll-fade-text briefly changes, then reverts back to English.
The GSAP animation continues working, but with the old content.
How can I properly re-split and animate translated content inside .scroll-fade-text using SplitType + GSAP without it resetting or reverting?
All translation is client-side using data-i18n and a JSON map.
Working example…
HTML
<a class="contact-button" href="mycontact" target="_blank" rel="noopener" data-i18n="navContact">Contact us</a>
JSON
en.json
"navContact": "Contact us",
/* more below */
es.json
"navContact": "Contáctanos",
/* more below */