Gh translate button
Skill aymandakirgh/ghclaudetoolkit/skills/gh-translate-button
Use when the user wants to add a language switcher, a translate button, an i18n toggle, or multi-language support to a page or component. Triggers on "translation button", "language switcher", "language selector", "i18n toggle", "add languages to this page", "multi-language", "localize this", "RTL support", "Arabic support", "lang switcher", "translate widget". Specs a reusable component supporting EN, IT, AR, NL, ZH-CN, ES, FR, DE, PT-BR — with Arabic as RTL and the active language driven by a ?lang= URL param (never localStorage) — and ships a drop-in vanilla HTML/JS snippet.From its SKILL.md
npx -y skills add aymandakirgh/ghclaudetoolkit --skill gh-translate-buttonAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.2 KB, as published. Nobody here has run it
gh-translate-button
A reusable, dependency-free language switcher. Drop it into any page; it switches UI strings,
flips direction for RTL, and persists the choice only through the ?lang= URL parameter.
Requirements (non-negotiable)
- Languages (9):
en,it,ar,nl,zh-CN,es,fr,de,pt-BR. - Arabic is RTL. When
aris active, setdir="rtl"on<html>; all others areltr. - State lives in
?lang=only. The active language is read from and written to the URL query param. Never uselocalStorage/cookies — the URL is the single source of truth, so a shared link reproduces the exact language, and there's no hidden persisted state. - No reload on switch. Update the URL with
history.replaceStateand re-render in place. - Graceful fallback. Unknown/missing
?lang=→ defaulten. Missing key in a dictionary → fall back to theenstring. - Accessible. The control is a labelled
<select>(or button group) with anaria-label.
How content is marked up
Tag any translatable element with data-i18n="<key>"; the script swaps its textContent per the
active language's dictionary. Set <html lang> and dir automatically.
Drop-in snippet (vanilla, no build step)
<!-- 1) Place the switcher anywhere -->
<div id="lang-switcher" aria-label="Language"></div>
<!-- 2) Mark translatable text -->
<h1 data-i18n="hero_title">We handle it</h1>
<a href="#" data-i18n="cta">Get started</a>
<!-- 3) Drop-in logic -->
<script>
(function () {
// Supported languages. `rtl: true` only for Arabic.
const LANGS = [
{ code: 'en', label: 'English', rtl: false },
{ code: 'it', label: 'Italiano', rtl: false },
{ code: 'ar', label: 'العربية', rtl: true },
{ code: 'nl', label: 'Nederlands', rtl: false },
{ code: 'zh-CN', label: '简体中文', rtl: false },
{ code: 'es', label: 'Español', rtl: false },
{ code: 'fr', label: 'Français', rtl: false },
{ code: 'de', label: 'Deutsch', rtl: false },
{ code: 'pt-BR', label: 'Português', rtl: false },
];
const DEFAULT = 'en';
// String table: I18N[code][key]. Fill in your real copy; en is the fallback.
const I18N = {
'en': { hero_title: 'We handle it', cta: 'Get started' },
'it': { hero_title: 'Lo gestiamo noi', cta: 'Inizia' },
'ar': { hero_title: 'نحن نتولى الأمر', cta: 'ابدأ الآن' },
'nl': { hero_title: 'Wij regelen het', cta: 'Begin nu' },
'zh-CN': { hero_title: '交给我们', cta: '开始使用' },
'es': { hero_title: 'Nos encargamos', cta: 'Empezar' },
'fr': { hero_title: 'On s’en occupe', cta: 'Commencer' },
'de': { hero_title: 'Wir kümmern uns', cta: 'Loslegen' },
'pt-BR': { hero_title: 'A gente cuida', cta: 'Começar' },
};
const known = c => LANGS.some(l => l.code === c);
// Active language = ?lang= param, else default. No storage, ever.
function currentLang() {
const p = new URLSearchParams(location.search).get('lang');
return known(p) ? p : DEFAULT;
}
function apply(code) {
const lang = LANGS.find(l => l.code === code) || LANGS.find(l => l.code === DEFAULT);
const dict = I18N[lang.code] || {};
const fallback = I18N[DEFAULT] || {};
document.documentElement.lang = lang.code;
document.documentElement.dir = lang.rtl ? 'rtl' : 'ltr';
document.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
const val = dict[key] != null ? dict[key] : fallback[key];
if (val != null) el.textContent = val;
});
}
function setLang(code) {
const url = new URL(location.href);
url.searchParams.set('lang', code); // the ONLY place state is kept
history.replaceState(null, '', url); // no reload, no localStorage
apply(code);
render(code);
}
function render(active) {
const host = document.getElementById('lang-switcher');
if (!host) return;
host.innerHTML = '';
const sel = document.createElement('select');
sel.setAttribute('aria-label', 'Select language');
LANGS.forEach(l => {
const opt = document.createElement('option');
opt.value = l.code;
opt.textContent = l.label;
if (l.code === active) opt.selected = true;
sel.appendChild(opt);
});
sel.addEventListener('change', e => setLang(e.target.value));
host.appendChild(sel);
}
function init() { const c = currentLang(); apply(c); render(c); }
window.addEventListener('popstate', init); // back/forward keeps URL ↔ UI in sync
if (document.readyState !== 'loading') init();
else document.addEventListener('DOMContentLoaded', init);
})();
</script>
Adapting it
- Add a string: add the key to every
I18N[code]; mark the element withdata-i18n="key". - Add a language: append to
LANGSand add itsI18Nentry. Onlyaris RTL today. - Framework port: the same three pieces (read
?lang=, write viahistory.replaceState, re-render) port directly to React/Vue — keep?lang=as the source of truth; do not introducelocalStorage. - SEO (optional): emit
<link rel="alternate" hreflang="<code>" href="...?lang=<code>">per language so each variant is discoverable.
Acceptance check
- All 9 languages selectable; switching updates text without reload.
-
?lang=arsetsdir="rtl"; othersltr. - State is in the URL only — no
localStorage/cookies anywhere. - Unknown
?lang=and missing keys fall back toen. - Shared link with
?lang=reproduces the exact language.