younesEver tried typing in Arabic, Thai, or Georgian on a standard QWERTY keyboard? It's painful. You...
Ever tried typing in Arabic, Thai, or Georgian on a standard QWERTY keyboard? It's painful. You either install system-level keyboard packs, use clunky OS settings, or hunt for sketchy websites covered in ads.
I built AnyKeyboard — a free online virtual keyboard supporting 100+ languages, from Arabic to Zulu. Here's how.
The existing virtual keyboard landscape is dominated by a few players:
None of them felt modern. None were built with Next.js or had proper SEO for every language. There was room for something better.
Stack:
The key architectural decision was one page per keyboard. Each language gets its own URL:
/languages/middle-eastern/arabic-keyboard
/languages/east-asian/japanese-keyboard
/languages/south-asian/hindi-keyboard
/languages/european/german-keyboard
This is critical for SEO — someone searching "arabic keyboard online" lands directly on the Arabic keyboard page.
Each keyboard layout is a JSON file:
{
"language": "Arabic",
"direction": "rtl",
"rows": [
["ض", "ص", "ث", "ق", "ف", "غ", "ع", "ه", "خ", "ح", "ج", "د"],
["ش", "س", "ي", "ب", "ل", "ا", "ت", "ن", "م", "ك", "ط"],
["ئ", "ء", "ؤ", "ر", "لا", "ى", "ة", "و", "ز", "ظ"]
],
"shiftRows": [...]
}
The rendering engine handles:
Right-to-left languages were the trickiest part. It's not just about flipping the text direction — the entire UX needs to feel natural:
/* RTL keyboard layout */
.keyboard-container[dir="rtl"] {
direction: rtl;
}
.keyboard-container[dir="rtl"] .output-area {
text-align: right;
direction: rtl;
unicode-bidi: bidi-override;
}
The cursor behavior, text selection, and copy/paste all needed special handling for RTL scripts.
With 100+ language pages, SEO was the primary growth strategy:
Results after 3 months:
Some keyboards need special input methods:
// Dead key support (for diacritics)
let pendingDeadKey = null;
function handleKeyPress(key) {
if (isDeadKey(key)) {
pendingDeadKey = key;
return;
}
if (pendingDeadKey) {
const combined = combineWithDeadKey(pendingDeadKey, key);
pendingDeadKey = null;
return insertText(combined);
}
insertText(key);
}
// Example: dead key ˆ + a = â
function combineWithDeadKey(dead, base) {
const combinations = {
'ˆ': { 'a': 'â', 'e': 'ê', 'i': 'î', 'o': 'ô', 'u': 'û' },
'¨': { 'a': 'ä', 'e': 'ë', 'i': 'ï', 'o': 'ö', 'u': 'ü' },
// ... more combinations
};
return combinations[dead]?.[base] || base;
}
The revenue model is simple:
For example, the Arabic keyboard page shows Arabic keyboard stickers on Amazon. The Hindi page shows Hindi stickers. Each recommendation is contextual.
🌐 AnyKeyboard.io — Free virtual keyboards for 100+ languages
No account needed. Pick a language, start typing, copy your text. That's it.
Some popular keyboards:
Working with non-Latin scripts in your projects? What's been your biggest Unicode headache? I'd love to hear your war stories in the comments.