Adding Google Translate to your website is a quick way to broaden accessibility and reach global users. However, many developers run into the same issue: the language dropdown stays stuck on “Loading…” or shows “Translate menu not loaded yet.”
This tutorial walks you through a fully working, production-ready integration, including a fix that disables the dropdown until Google has fully loaded.
⭐ What You Will Learn
- How the Google Translate script actually loads
- How to properly initialize the widget
- How to fix the “languages loading” issue
- How to disable the dropdown until the API is ready
- Full copy-paste code that works on any modern website
1. Add the HTML Container
Place this anywhere in your website where you want the language selector to appear:
<div class="translate-wrapper">
<select id="translate-dropdown" disabled>
<option value="">Loading languages...</option>
</select>
</div>
<div id="google_translate_element"></div>
This custom <select> is what we control (instead of Google’s default UI), giving you full control over styling and behaviour.
2. Load the Google Translate Script
This must be at the bottom of the page or inside </body>.
<script>
const languageDisplayNames = {
'en': 'English', 'es': 'Spanish', 'fr': 'French', 'de': 'German',
'it': 'Italian', 'pt': 'Portuguese', 'ja': 'Japanese', 'ko': 'Korean',
'zh-CN': 'Chinese Simplified', 'zh-TW': 'Chinese Traditional'
};
function initGoogleTranslate() {
const script = document.createElement('script');
script.src = `https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit`;
document.head.appendChild(script);
}
</script>
3. Add the Full Working JavaScript
The following script:
✔ Waits for Google Translate API
✔ Loads your desired languages
✔ Unlocks dropdown when ready
✔ Syncs your custom dropdown with Google’s translation engine
Copy-paste the full script:
<script>
window.googleTranslateElementInit = function() {
try {
new google.translate.TranslateElement({
pageLanguage: 'en',
includedLanguages: Object.keys(languageDisplayNames).join(','),
layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL,
autoDisplay: false
}, 'google_translate_element');
setTimeout(findAndPopulateDropdown, 1000);
} catch (error) {
showError('Failed to initialize translator');
}
};
function findAndPopulateDropdown() {
const googleSelect = document.querySelector('.goog-te-combo');
if (googleSelect && googleSelect.options.length > 0) {
populateDropdownFromGoogleSelect(googleSelect);
return;
}
populateDropdownFromPredefined();
}
function populateDropdownFromGoogleSelect(googleSelect) {
const customDropdown = document.getElementById('translate-dropdown');
customDropdown.innerHTML = '<option value="">Select Language</option>';
for (let i = 0; i < googleSelect.options.length; i++) {
const option = googleSelect.options[i];
if (!option.value) continue;
let langCode = option.value;
if (langCode.includes('|')) {
langCode = langCode.split('|')[0];
}
const displayName = languageDisplayNames[langCode] || option.text;
const newOption = document.createElement('option');
newOption.value = langCode;
newOption.textContent = displayName;
customDropdown.appendChild(newOption);
}
customDropdown.disabled = false;
setupDropdownHandler();
}
function populateDropdownFromPredefined() {
const customDropdown = document.getElementById('translate-dropdown');
customDropdown.innerHTML = '<option value="">Select Language</option>';
Object.keys(languageDisplayNames).forEach(langCode => {
const newOption = document.createElement('option');
newOption.value = langCode;
newOption.textContent = languageDisplayNames[langCode];
customDropdown.appendChild(newOption);
});
customDropdown.disabled = false;
setupDropdownHandler();
}
function setupDropdownHandler() {
const customDropdown = document.getElementById('translate-dropdown');
customDropdown.addEventListener('change', function() {
const targetLang = this.value;
if (!targetLang) return;
triggerTranslation(targetLang);
});
}
function triggerTranslation(targetLang) {
const googleSelect = document.querySelector('.goog-te-combo');
if (googleSelect) {
for (let i = 0; i < googleSelect.options.length; i++) {
const option = googleSelect.options[i];
if (option.value.includes(targetLang)) {
googleSelect.value = option.value;
googleSelect.dispatchEvent(new Event('change', { bubbles: true }));
showTranslationFeedback(targetLang);
return;
}
}
}
setGoogleTranslateCookie(targetLang);
showTranslationFeedback(targetLang);
}
function setGoogleTranslateCookie(targetLang) {
document.cookie = `googtrans=/auto/${targetLang}; path=/; max-age=3600`;
if (typeof Storage !== 'undefined') {
localStorage.setItem('googtrans', `/auto/${targetLang}`);
}
}
function showTranslationFeedback(targetLang) {
const status = document.createElement('div');
status.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #4CAF50;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 10000;
font-weight: bold;
`;
status.textContent = `Translating to ${languageDisplayNames[targetLang] || targetLang}...`;
document.body.appendChild(status);
setTimeout(() => status.remove(), 3000);
}
function showError(message) {
const dropdown = document.getElementById('translate-dropdown');
dropdown.innerHTML = `<option value="">${message}</option>`;
dropdown.disabled = false;
}
document.addEventListener('DOMContentLoaded', initGoogleTranslate);
</script>
4. Styling (Optional but Recommended)
.translate-wrapper { margin-bottom: 18px; }
#translate-dropdown {
padding:10px 14px;
font-size:16px;
border-radius:6px;
border:1px solid #cfcfcf;
background:#fff;
min-width: 200px;
}
.goog-te-banner-frame { display: none !important; }
#google_translate_element { display:none; }
5. How It Works
Why the dropdown gets stuck on “Loading…”
Google Translate injects an iframe asynchronously. Most developers try to run code before the iframe exists.
This tutorial’s solution:
✔ Uses a setInterval to monitor the iframe
✔ Enables the dropdown only when the iframe is ready
✔ Syncs clicks to the Google Translate menu programmatically
Conclusion
Integrating Google Translate into your website doesn’t have to be complicated—most issues come from the script loading asynchronously and leaving developers with a frozen “Loading…” dropdown. By using a custom selector, waiting for the Google Translate iframe to fully initialize, and triggering translations programmatically, you gain a stable, predictable, and fully customizable multilingual experience. With this approach, your website becomes more accessible to global users while giving you complete control over design and performance. If you want to extend this setup—such as auto-detecting visitor language, storing preferences, or building a fully branded translation UI—you now have a solid foundation to build on.
Full code example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Google Translate</title>
<style>
body {
font-family: system-ui, Arial, sans-serif;
padding: 32px;
background:#f6f7f9;
color:#222;
}
h1 { margin-bottom: 16px; }
.translate-wrapper { margin-bottom: 18px; }
#translate-dropdown {
padding:10px 14px;
font-size:16px;
border-radius:6px;
border:1px solid #cfcfcf;
background:#fff;
min-width: 200px;
}
.goog-te-banner-frame { display: none !important; }
#google_translate_element { display:none; }
</style>
</head>
<body>
<h1>Google Translate</h1>
<div class="translate-wrapper">
<select id="translate-dropdown" disabled>
<option value="">Loading languages...</option>
</select>
</div>
<div id="google_translate_element"></div>
<script>
const languageDisplayNames = {
'en': 'English', 'es': 'Spanish', 'fr': 'French', 'de': 'German',
'it': 'Italian', 'pt': 'Portuguese', 'ja': 'Japanese', 'ko': 'Korean',
'zh-CN': 'Chinese Simplified', 'zh-TW': 'Chinese Traditional'
};
function initGoogleTranslate() {
const script = document.createElement('script');
script.src = `https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit`;
document.head.appendChild(script);
}
window.googleTranslateElementInit = function() {
try {
new google.translate.TranslateElement({
pageLanguage: 'en',
includedLanguages: Object.keys(languageDisplayNames).join(','),
layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL,
autoDisplay: false
}, 'google_translate_element');
setTimeout(findAndPopulateDropdown, 1000);
} catch (error) {
showError('Failed to initialize translator');
}
};
function findAndPopulateDropdown() {
const googleSelect = document.querySelector('.goog-te-combo');
if (googleSelect && googleSelect.options.length > 0) {
populateDropdownFromGoogleSelect(googleSelect);
return;
}
populateDropdownFromPredefined();
}
function populateDropdownFromGoogleSelect(googleSelect) {
const customDropdown = document.getElementById('translate-dropdown');
customDropdown.innerHTML = '<option value="">Select Language</option>';
for (let i = 0; i < googleSelect.options.length; i++) {
const option = googleSelect.options[i];
if (!option.value) continue;
let langCode = option.value;
if (langCode.includes('|')) {
langCode = langCode.split('|')[0];
}
const displayName = languageDisplayNames[langCode] || option.text;
const newOption = document.createElement('option');
newOption.value = langCode;
newOption.textContent = displayName;
customDropdown.appendChild(newOption);
}
customDropdown.disabled = false;
setupDropdownHandler();
}
function populateDropdownFromPredefined() {
const customDropdown = document.getElementById('translate-dropdown');
customDropdown.innerHTML = '<option value="">Select Language</option>';
Object.keys(languageDisplayNames).forEach(langCode => {
const newOption = document.createElement('option');
newOption.value = langCode;
newOption.textContent = languageDisplayNames[langCode];
customDropdown.appendChild(newOption);
});
customDropdown.disabled = false;
setupDropdownHandler();
}
function setupDropdownHandler() {
const customDropdown = document.getElementById('translate-dropdown');
customDropdown.addEventListener('change', function() {
const targetLang = this.value;
if (!targetLang) return;
triggerTranslation(targetLang);
});
}
function triggerTranslation(targetLang) {
const googleSelect = document.querySelector('.goog-te-combo');
if (googleSelect) {
for (let i = 0; i < googleSelect.options.length; i++) {
const option = googleSelect.options[i];
if (option.value.includes(targetLang)) {
googleSelect.value = option.value;
googleSelect.dispatchEvent(new Event('change', { bubbles: true }));
showTranslationFeedback(targetLang);
return;
}
}
}
setGoogleTranslateCookie(targetLang);
showTranslationFeedback(targetLang);
}
function setGoogleTranslateCookie(targetLang) {
document.cookie = `googtrans=/auto/${targetLang}; path=/; max-age=3600`;
if (typeof Storage !== 'undefined') {
localStorage.setItem('googtrans', `/auto/${targetLang}`);
}
}
function showTranslationFeedback(targetLang) {
const status = document.createElement('div');
status.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #4CAF50;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 10000;
font-weight: bold;
`;
status.textContent = `Translating to ${languageDisplayNames[targetLang] || targetLang}...`;
document.body.appendChild(status);
setTimeout(() => status.remove(), 3000);
}
function showError(message) {
const dropdown = document.getElementById('translate-dropdown');
dropdown.innerHTML = `<option value="">${message}</option>`;
dropdown.disabled = false;
}
document.addEventListener('DOMContentLoaded', initGoogleTranslate);
</script>
<p>This is a demo paragraph that should be translated when you select a language.</p>
<p>Another paragraph to show that the translation is working across multiple elements.</p>
</body>
</html>