While working on a browser-based utility project recently, I hit a surprisingly tricky problem: I needed to add Chinese Simplified ⇄ Traditional conversion to a tool. The catch? I didn't want to send users' text to a server, and I definitely didn't want to maintain a backend just for text transformation.
The obvious answer was to find a JavaScript library that could handle this entirely in the browser. But as I started researching, I realized this was more nuanced than I initially thought.
The Problem with Existing Solutions
Let me be honest: there are plenty of Chinese converter tools out there. The issue is that most of them either:
- Rely on server-side APIs – which means your text leaves the browser. For a privacy-focused tool, that's a non-starter.
- Use naive character mapping – where each character is mapped individually. This works for most cases but completely misses phrase-level conversion.
Here's what I mean by phrase-level conversion. Consider the word "软件" (software). A naive character-by-character conversion would give you "軟件" in Traditional Chinese. But that's wrong for Taiwan – they say "軟體". Similarly, "鼠标" (mouse) should become "滑鼠" in Taiwan, not "鼠標".
This is the classic trap of Chinese conversion. It's not just about character mapping; it's about regional vocabulary differences. And that's precisely where OpenCC shines.
Why OpenCC?
OpenCC (Open Chinese Convert) is the gold standard for Chinese conversion. It's been around for years, maintained by the open-source community, and used by major projects. The key features:
- Phrase-aware conversion – it understands context and converts whole phrases, not just characters
- Region-specific output – separate conversion rules for Taiwan vs. Hong Kong Traditional
- Two-way support – both Simplified → Traditional and Traditional → Simplified
The problem? OpenCC is primarily a Python/C++ library. Using it in the browser requires a JavaScript port. That's where opencc-js comes in – it's a direct JavaScript implementation that runs entirely client-side.
The Architecture Decision
I wanted this tool to work entirely in the browser. No server, no API calls, no text leaving the user's device. This meant:
- Load the conversion engine via CDN
- Package the dictionaries locally
- Cache converter instances to avoid re-initialization
The first decision was how to load the library. I went with the UMD build from a CDN:
<script src="https://fastly.jsdelivr.net/npm/opencc-js@1.4.1/dist/umd/full.js" onerror="window.__openccFailed=true"></script>
The onerror handler was crucial – if the CDN fails to load, we need to show a graceful error message instead of a broken tool. This is something I learned from experience: always plan for network failures, even in client-side tools.
The Core Implementation
The API is refreshingly simple. Once the library loads, creating a converter is straightforward:
const converter = OpenCC.Converter({ from: 'cn', to: 'tw' });
console.log(converter('汉字')); // "漢字"
The from and to parameters accept region codes: cn for mainland China, tw for Taiwan, hk for Hong Kong. This gives us four conversion directions:
- Simplified → Traditional (Taiwan):
{from: 'cn', to: 'tw'} - Simplified → Traditional (Hong Kong):
{from: 'cn', to: 'hk'} - Traditional (Taiwan) → Simplified:
{from: 'tw', to: 'cn'} - Traditional (Hong Kong) → Simplified:
{from: 'hk', to: 'cn'}
But there's a performance consideration. Creating a converter isn't free – it loads and initializes the dictionary data. Creating a new converter on every conversion would be wasteful. The solution is simple caching:
const converterCache = new Map();
function getConverter(from, to) {
const key = `${from},${to}`;
if (!converterCache.has(key)) {
converterCache.set(key, OpenCC.Converter({ from, to }));
}
return converterCache.get(key);
}
This way, the first conversion in each direction pays the initialization cost, but subsequent conversions are instant.
The AI-Assisted Development Experience
Here's where things got interesting. I built this tool with heavy assistance from Claude AI, and the experience was... educational.
What the AI Got Right
The initial scaffolding was surprisingly good. I described the requirements – a browser-based converter with four conversion directions, copy functionality, and dark mode support – and the AI produced a working skeleton within minutes. The CSS was particularly well-handled, including the prefers-color-scheme media query for dark mode.
What the AI Got Wrong
The first version had a critical flaw: it was trying to use opencc-js as an ES module, which doesn't work with the UMD build. The error was subtle – the import statement looked correct, but the module resolution failed at runtime.
The AI also initially tried to create a new converter on every conversion, which would have been a performance disaster. It took a specific prompt to add caching:
"Cache the converter instances so they're only created once per direction."
The Back-and-Forth
The real value of AI assistance came from iteration. For example, the AI initially handled the CDN failure case poorly – it just let the page break. I had to explicitly ask:
"What happens if the CDN fails to load? Add error handling."
That's when we added the onerror callback and a status message system. The AI wouldn't have thought about this on its own – it was focused on the happy path.
The Hard Problem: Regional Differences
The most interesting engineering challenge wasn't technical – it was linguistic. Consider these conversions:
- "软件" → Taiwan: "軟體" (not "軟件")
- "鼠标" → Taiwan: "滑鼠" (not "鼠標")
- "网络" → Taiwan: "網路" (not "網絡")
These aren't character-level transformations. They require word-level knowledge. OpenCC handles this through its extensive dictionary system, but it means the conversion isn't always predictable. This is actually a feature – it's what makes the output sound natural to native speakers.
For the UI, this meant I couldn't just label the options generically. Users need to know that "Simplified → Traditional" isn't enough – they need to choose between Taiwan and Hong Kong variants. The UI needed to be explicit about these regional differences.
Performance Considerations
The full.js build of opencc-js is about 2MB – it includes all the dictionaries. That's a significant download, but it's a one-time cost. Once loaded, the conversion itself is fast:
- First conversion after load: ~50-100ms (dictionary initialization)
- Subsequent conversions: <5ms
The caching strategy was crucial. Without it, every conversion would pay the initialization cost. With it, the tool feels instant after the first use.
What I Learned
1. Client-side libraries have hidden costs. The 2MB dictionary download is worth it for privacy, but it's a real trade-off. If I were building this for a high-traffic site, I'd consider lazy-loading the library only when the user actually needs conversion.
2. Regional variants are a UX problem, not just a technical one. Users often don't know whether they need Taiwan or Hong Kong Traditional. The tool needs to make this distinction clear without overwhelming them.
3. AI assistance works best with specific prompts. The AI didn't anticipate edge cases like CDN failures or performance issues. But when I asked for specific improvements, it implemented them correctly. The collaboration worked because I knew what questions to ask.
The Result
During this process, I built a small browser-based tool to make this workflow easier. It handles all four conversion directions, works entirely client-side, and respects user privacy by never sending text to a server.
The tool is part of a larger collection of browser-based developer utilities I've been building. If you're curious, you can find it at Craftvo. But more importantly, if you're dealing with Chinese text conversion in your own projects, OpenCC is worth a serious look – it's the difference between output that looks machine-translated and output that reads naturally.
Tags: javascript, chinese, opensource, webdev, i18n
Top comments (0)