DEV Community

mrzitoun
mrzitoun

Posted on

I built a free, 7-language symbol copy-paste tool, no framework, no backend, no database

Every time I needed to type a °, an é, or an in a document, I ended up doing the same annoying dance: open a search engine, click into some ad-heavy "symbol copy" site, hunt for the right character between banner ads, copy it, come back. I finally got tired of it and built AltKeyHub, a free tool to search, browse, and copy 300+ symbols, accented letters, and special characters in one click, plus a lookup for Windows Alt codes, Mac shortcuts, and HTML entities. No account, no install, no backend.

A few things about how it's built turned out more interesting than I expected, so here's the write-up.

No framework, on purpose

The whole site is static HTML/CSS/vanilla JS, generated at build time by a small Python + Jinja2 pipeline. No React, no client-side router, no server. The reasoning: this is fundamentally a content + interaction tool (search, click, copy), not an app with real state, a static site generator gets me fast page loads, dead-simple hosting (it's just files on Vercel), and crucially content that's fully present in the HTML for crawlers and AI answer engines, not something that only exists after JS runs.

The symbol dataset (300+ entries: character, Unicode name, keywords, Windows Alt code, Mac shortcut, HTML entity) lives in a plain Python list and gets compiled into both the pre-rendered HTML grid and a JS data file for client-side search, one source of truth, two consumers.

Going multilingual without a framework

The tool is available in 7 languages (French, English, German, Spanish, Portuguese, Arabic, Chinese). The architecture is simple: one locale folder per language (/en/, /de/, …, with French at the root), each fed by a JSON dictionary of UI strings generated from a single Python source. Those same dictionaries get injected into each page as a small window.ALTKEYHUB_I18N object for the handful of strings that need to be set dynamically in JS (search result counts, toast messages).

Every page carries hreflang alternates linking all 7 versions together, plus x-default. Arabic gets dir="rtl" and a couple of targeted CSS overrides; Arabic and Chinese pull in Noto Sans Arabic / Noto Sans SC alongside the main typeface so accented and CJK glyphs render properly.

FAQPage schema for GEO

Beyond the core tool, I added a small set of guide pages (e.g. "how to type the degree symbol"), each with a 4-question FAQ block marked up with schema.org's FAQPage JSON-LD. That's the structured-data type AI answer engines lean on to cite a specific answer directly, worth doing deliberately now that "does an LLM cite this page" is as relevant as "does it rank in Google."

The bug: relative paths and folder depth

This is the part worth sharing. Once I added the guide pages, they lived one directory level deeper than everything else (/guides/<slug>.html, or /en/guides/<slug>.html). My original path logic only accounted for the locale-folder depth ("" for French at the root, "../" for every other locale) — so on French guide pages, asset references like assets/js/common.js resolved to a non-existent guides/assets/js/common.js. Copy-to-clipboard silently broke because window.AltKeyHub was undefined — the script had 404'd.

The fix was to stop special-casing depth and just compute it properly with posixpath.relpath:

\python
def relative_link(from_code, from_route, to_code, to_route):
"""Relative href from (from_code, from_route) to (to_code, to_route).
Correct regardless of nesting depth — handles the plain locale-folder
case as well as the extra guides/ subfolder."""
from_full = full_path(from_code, from_route)
to_full = full_path(to_code, to_route)
from_dir = posixpath.dirname(from_full) or "."
return posixpath.relpath(to_full, start=from_dir)
\
\

One general-purpose function instead of ad-hoc string concatenation, and it turned out to fix a second, pre-existing bug for free: the brand logo link on non-French pages had been quietly pointing back to the French homepage all along.

Try it / feedback welcome

If you ever need to type a special character and don't want to deal with ads: altkeyhub.com. The 6 non-French translations were done by me with AI assistance and are solid for a first launch, but if you're a native speaker (especially Arabic or Chinese) and spot something off, I'd genuinely appreciate a comment or a message.

Top comments (0)