Library / SDK
WaniKani/WanaKana avatar
WaniKani/WanaKana

WanaKana: kana detection and romaji conversion for the browser

Javascript library for detecting and transforming between Hiragana, Katakana, and Romaji

944 stars88 forksJavaScriptMIT

At a glance

What is it?
WanaKana is a JavaScript library for detecting and converting between Hiragana, Katakana and Romaji, with an IME style input binding, custom kana mappings and okurigana handling. It runs in the browser without a build step, ships as a CommonJS and ES module package, and has community ports in seven other languages.
Who is it for?
WanaKana fits a JavaScript project that needs to detect Japanese text, offer romaji input in a form, convert names between scripts or strip okurigana, and it fits teams that want the same behaviour in the browser and in Node without a build step. It does not fit a project that needs to read or write kanji, since the library detects kanji but does not transliterate them, and it does not fit a codebase that has to live on a release cadence newer than 5.3.1 without pinning.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 116 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Detection is separate from conversion, and kanji is out of scope

WanaKana is a utility library for detecting and transliterating Hiragana, Katakana and Romaji, and the library keeps two ideas apart that are often confused: whether a string is Japanese, and what a string sounds like.

The detection functions are predicates, and they are specific. isJapanese, isKana, isHiragana and isKatakana each answer for their own script, and isKanji answers whether the string contains kanji. The README is clear that isKanji is a containment check rather than a statement about the whole string, since the example returns true for a string of kanji and false for another that mixes kanji with kana.

That distinction is the scope of the library. WanaKana does not transliterate kanji, and it does not read them. The package description says it converts between Kanji, Hiragana, Katakana and Romaji, but the conversion functions operate on kana and romaji, and the kanji in a string passes through untouched. A name like a person's name written in kanji stays as kanji when you call toKana on the romaji reading of it, and the readings you would need to make that conversion are the part you supply.

The audience is therefore developers building Japanese language features: a romaji input field that shows kana as you type, a search box that accepts either script, a form that normalises what the user typed, or a dictionary of readings for a language-learning product. The project is sponsored by Tofugu and WaniKani and authored by Mims H. Wright and Duncan Bay, which matches that audience.

toKana has options that decide what the user sees

The conversion functions share a shape, and toKana is the one with the most interesting options.

toKana takes romaji and returns kana. The plain form converts the way you would expect, turning ONAJI buttsuuji into katakana with a hiragana okurigana, and handling a long vowel mark as expected. The quoted form is where it gets more than a character map: the library converts the single quotes around a word into the Japanese quotation marks, so a romaji string that quotes a word comes back with proper Japanese brackets.

The options are the real API. A customKanaMapping object overrides individual readings, and the example shows a mapping that turns one syllable into one kana and another into a literal string, so a product that accepts a non-standard reading can accept it without patching the library. A passRomaji option leaves romaji sequences in place while converting the rest, which is what you want in mixed text where the latin part is not a reading. A useObsoleteKana option switches on the old kana forms, converting wi to the archaic form rather than the modern one.

The same three options recur across the other direction functions. toHiragana and toKatakana both accept passRomaji and useObsoleteKana, so the same mixed-text rule applies whether you are normalising to hiragana or to katakana. The IME mode is a separate concern, mentioned in the browser section, and it is what makes a bound input field behave like a Japanese keyboard.

toRomaji and the custom mapping in the other direction

Going back to latin script has its own conventions, and WanaKana exposes them as options rather than hardcoding them.

toRomaji converts kana to romaji, and by default it produces lowercase output with a space between words. An upcaseKatakana option uppercases the romaji that came from katakana, so a string in kana with katakana in it comes back with the katakana part in capitals, which matches the convention of romanising a name.

The customRomajiMapping option is the one to look at closely. The example replaces three kana with their alternative romanisations, and the result is that the same kana produce a different reading. Reading systems differ: a school in Japan, a subtitle file and a fan translation group may each romanise the same kana differently, and a product that has to match a specific reading table cannot rely on a single default. Overriding the mapping at the call site is how you keep that decision in your codebase rather than in a fork of the library.

Note the direction of the option. customKanaMapping applies when converting romaji to kana, and customRomajiMapping applies when converting kana to romaji. They are not symmetric, because the two directions have different ambiguity problems, and a product that converts in both directions needs to decide both tables.

stripOkurigana and tokenize handle the awkward parts of Japanese text

Two utility groups exist for text that is not cleanly one script, and they are the parts most likely to be useful in a real product.

stripOkurigana removes the kana that accompany a kanji stem, leaving the stem and the reading out. The default behaviour removes the trailing kana, and a leading option removes kana at the start instead, which handles the opposite case where the reading precedes the stem. A matchKanji option takes a reference word and strips only the kana that follow that specific kanji sequence, so you are not removing kana that belongs to a different word in the same string. The examples show the difference: with a leading option the result is the stem alone, and with matchKanji set to a different word the kana of the original word is kept.

tokenize splits a string into runs of the same script, and the output keeps the spaces and the word boundaries, so a mixed sentence comes back as separate pieces for the latin text, the space, the kanji and the kana. A compact option changes that behaviour, and the example shows a sentence with the japanese part merged into a single token while the english part stays separate.

Between them these two cover the case that breaks naive regex approaches, where a string contains several scripts and the developer needs to know where one ends and the next begins.

The unpkg build is for pages with no build step, and it carries the polyfills

The browser usage section is a single script tag, and it is the path for a page that has no bundler.

The library is loaded from unpkg, the UMD bundle with browser polyfills, and the input element is bound with a call that sets the IME mode with toKana as the default converter:

html
<head>
  <meta charset="UTF-8">
  <script src="https://unpkg.com/wanakana"></script>
</head>
<body>
  <input type="text" id="wanakana-input" />
  <script>
    var textInput = document.getElementById('wanakana-input');
    wanakana.bind(textInput, /* options */); // uses IMEMode with toKana() as default
    // to remove event listeners: wanakana.unbind(textInput);
  </script>
</body>

Two details in that snippet are the whole browser story. bind attaches an event listener on the input that converts the text as it is typed, and unbind removes that listener, which matters for a single page application that mounts and unmounts views.

There is a caveat that the README puts under its own heading, and it is easy to miss. Only the browser build via unpkg or the root wanakana.min.js includes polyfills for older browsers. If you bundle the package yourself with a modern build tool, the polyfills are not there, and you are responsible for any older browser support. The .browserslistrc file at the repository root is the place to check what the project targets.

The extended API reference is published on the project website, and the demo on the same site shows the library in action.

Installing the package for Node and modern bundlers

Everything else goes through npm. The package is published as wanakana and the install step is a single command.

shell
npm install wanakana

For a project that uses ES modules, the import is either the whole namespace or the named functions you actually need:

javascript
import * as wanakana from 'wanakana';
// or
import { toKana, isRomaji } from 'wanakana';

For a CommonJS project on Node 12 or later, it is a require call:

javascript
const wanakana = require('wanakana');

The named import is the better default for a bundle, because the library is large enough that pulling the whole namespace into a page you only use two functions from is wasteful, and tree shaking handles the named form cleanly.

The package metadata adds two details worth knowing. The version is 5.3.1 and the licence is MIT, and the devEngines field asks for node >=16, which is a constraint on the tooling that builds the project rather than on the published package. The keywords list includes ime, which is the input method editor term, and that is the feature the bind helper exposes.

A first exercise is short: take a form field, bind it, and type romaji into it to see kana appear. From there, toHiragana with passRomaji on mixed text, then stripOkurigana on a name, are the natural next steps.

Where WanaKana is the wrong tool

There are clear cases where this library does not solve the problem.

The first is kanji. The library detects kanji and leaves it alone, and it does not convert a kanji reading into romaji or the reverse. Any feature that needs readings for kanji text, such as a flashcard system, a dictionary lookup or furigana generation, needs a dictionary and a reading engine as well, and the kanji string itself will not be handled by toRomaji.

The second is the JavaScript-only assumption. The README lists community ports for Python, published on PyPI as wanakana-python, and for Java, Rust, Swift, Kotlin, C# and Go. These are the real alternative for a non-JavaScript stack, and the difference in approach is that a port is a reimplementation rather than a binding, so the options and behaviours of a port may not match the JavaScript version exactly. If your service is Python and the port lags a release, you are maintaining a divergence.

The third is the release cadence. The published versions are 5.3.1, 5.3.0 and 5.2.0, all from 2023-11-20, while the last push to the repository was on 2026-06-29. The source tree is being worked on but the published package has not moved in that time, so a team that needs a fix or a new option in the npm package has to wait for a release, pin a commit and build it, or port the change themselves.

The fourth is the polyfill boundary. The browser build carries polyfills for older browsers, and a bundled build does not, so a project that still supports legacy browsers has to take the unpkg path or add its own polyfills, which is a decision to make early rather than after a support ticket.

Editorial conclusion

WanaKana fits a JavaScript project that needs to detect Japanese text, offer romaji input in a form, convert names between scripts or strip okurigana, and it fits teams that want the same behaviour in the browser and in Node without a build step. It does not fit a project that needs to read or write kanji, since the library detects kanji but does not transliterate them, and it does not fit a codebase that has to live on a release cadence newer than 5.3.1 without pinning. Before adopting it, decide whether you need the toKana, toHiragana, toKatakana or toRomaji direction, check the customKanaMapping and customRomajiMapping options for the readings your product accepts, and if you load it from a script tag, remember that only the browser build carries the polyfills for older browsers.

Frequently asked questions

What does WanaKana convert and what does it leave alone?

WanaKana detects and converts Hiragana, Katakana and Romaji. Its conversion functions work on kana and romaji, and kanji inside a string is passed through untouched, so the library does not transliterate kanji even though it can detect it with isKanji.

How do I install WanaKana and use it in Node?

Install the package with npm install wanakana. On Node 12 or later you can then require it with const wanakana = require('wanakana'), while an ES module project uses import * as wanakana from 'wanakana' or imports the named functions such as toKana and isRomaji directly.

How do I let a user type romaji and see kana in a text field?

Bind the element with wanakana.bind(domElement, options), which attaches an input listener that converts as the user types, and the default sets the IME mode with toKana as the converter. You can remove the listener again with wanakana.unbind(domElement).

Can I change how WanaKana romanises or transliterates a specific reading?

Yes. A customKanaMapping option overrides readings when converting romaji to kana, and a customRomajiMapping option overrides the romanisation when converting kana to romaji, which matters when your product has to follow a particular reading table rather than the default one.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. WaniKani/WanaKana on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wanikani-wanakana.svg)](https://hysenlabs.com/projects/wanikani-wanakana)

Was this useful?