Hello, I want to share a bit of how I exported my Wanikani progress to Anki.
Why Anki?
Well, it’s fast and it works offline on my iPhone, no typing and no loading time. I can use it while I’m waiting in line in McDonalds, while walking to groceries, etc.
Prerequisite
- Some knowledge on JavaScript, SQL (SQLite).
- Having localhost server.
- Having SQLite browser, I use this software https://sqlitebrowser.org/.
Notice
- Please test the result in a sandbox Anki profile to make sure that everything works correctly and does not mess with your main Anki profile.
The Guide
So, here’s how I did it:
-
Download all JSONs of all levels using Wanikani API.
https://www.wanikani.com/api/user/YOUR_API_KEY/kanji/1 https://www.wanikani.com/api/user/YOUR_API_KEY/kanji/2 etc... https://www.wanikani.com/api/user/YOUR_API_KEY/vocabulary/1 https://www.wanikani.com/api/user/YOUR_API_KEY/vocabulary/2 etc...
-
Put the files in a localhost server, with the folder structure like this:
/WK/kanji/1.json /WK/kanji/2.json /WK/kanji/3.json etc... /WK/vocab/1.json /WK/vocab/2.json /WK/vocab/3.json etc...
-
Now, we’ll move to the Anki package. Rename your desired Anki package extension to zip. (
.apkg
to.zip
) and extract the contents. I used this deck Jp Yomi - Words by Yomi Frequency w/ Kanji Info, Stats - AnkiWeb. -
Open
collection.anki2
in the SQLite browser. -
Navigate to Browse Data > Table: Col. Save this creation date value (
crt
column) in a note. -
Open http://localhost/ in your web browser. Copy the following script in the console window. Change the
deckCreationTime
variable to yourcrt
value (in my case, it’s 1536170400) and themaxLevel
to your last Wanikani level. And then, run the script. (You might need to adapt other things in the script depending to your Anki deck)var maxLevel = 25; var deckCreationTime = 1536170400; var allSQL = ""; function kanjisql (i) { fetch('/WK/kanji/' + i + '.json') .then(function (response) { return response.json(); }) .then(function (response) { var SQL = ""; response.requested_information.forEach(function(el) { if(el.user_specific) { reps = (el.user_specific.meaning_correct + el.user_specific.meaning_incorrect) > (el.user_specific.reading_correct + el.user_specific.reading_incorrect) ? (el.user_specific.meaning_correct + el.user_specific.meaning_incorrect) : (el.user_specific.reading_correct + el.user_specific.reading_incorrect); lapses = el.user_specific.meaning_incorrect > el.user_specific.reading_incorrect ? el.user_specific.meaning_incorrect : el.user_specific.reading_incorrect; var ivl = 1; switch(el.user_specific.srs_numeric) { case 3: ivl = 1; break; case 4: ivl = 2; break; case 5: ivl = 6; break; case 6: ivl = 17; break; case 7: ivl = 45; break; case 8: ivl = 118; break; case 9: ivl = 306; break; } var due = 1; switch(el.user_specific.srs_numeric) { case 3: case 4: case 5: case 6: case 7: case 8: due = Math.floor((el.user_specific.available_date - deckCreationTime)/86400) ; break; case 9: due = Math.floor((el.user_specific.burned_date + 26438400 - deckCreationTime)/86400); break; } SQL += `UPDATE cards SET type = 2, queue = 2, due = ${due}, ivl = ${ivl}, factor = 2500, reps = ${reps}, lapses = ${lapses} WHERE nid IN (SELECT id FROM notes WHERE flds like "%\u001f${el.character}\u001f%");\n`; } }); return SQL; }).then(function(SQL) { allSQL += SQL; }); } for (var i = 1; i <= maxLevel; i++) { var num = i; kanjisql(num); }
-
Print
allSQL
variable usingconsole.log(allSQL)
. It will print the SQL commands. Save it into a txt file.UPDATE cards SET type = 2, queue = 2, due = 122, ivl = 306, factor = 2500, reps = 8, lapses = 0 WHERE nid IN (SELECT id FROM notes WHERE flds like "%一%"); UPDATE cards SET type = 2, queue = 2, due = 122, ivl = 306, factor = 2500, reps = 8, lapses = 0 WHERE nid IN (SELECT id FROM notes WHERE flds like "%二%"); UPDATE cards SET type = 2, queue = 2, due = 288, ivl = 306, factor = 2500, reps = 12, lapses = 1 WHERE nid IN (SELECT id FROM notes WHERE flds like "%九%"); UPDATE cards SET type = 2, queue = 2, due = 122, ivl = 306, factor = 2500, reps = 8, lapses = 0 WHERE nid IN (SELECT id FROM notes WHERE flds like "%七%"); UPDATE cards SET type = 2, queue = 2, due = 122, ivl = 306, factor = 2500, reps = 8, lapses = 0 WHERE nid IN (SELECT id FROM notes WHERE flds like "%人%"); UPDATE cards SET type = 2, queue = 2, due = 242, ivl = 306, factor = 2500, reps = 31, lapses = 5 WHERE nid IN (SELECT id FROM notes WHERE flds like "%入%"); UPDATE cards SET type = 2, queue = 2, due = 122, ivl = 306, factor = 2500, reps = 8, lapses = 0 WHERE nid IN (SELECT id FROM notes WHERE flds like "%八%"); UPDATE cards SET type = 2, queue = 2, due = 213, ivl = 306, factor = 2500, reps = 30, lapses = 5 WHERE nid IN (SELECT id FROM notes WHERE flds like "%力%"); UPDATE cards SET type = 2, queue = 2, due = 122, ivl = 306, factor = 2500, reps = 8, lapses = 0 WHERE nid IN (SELECT id FROM notes WHERE flds like "%十%"); UPDATE cards SET type = 2, queue = 2, due = 122, ivl = 306, factor = 2500, reps = 8, lapses = 0 WHERE nid IN (SELECT id FROM notes WHERE flds like "%三%"); etc...
-
You’ll need to do the same for the vocabularies. In step 6, change the
fetch('/WK/kanji/' + i + '.json')
part intofetch('/WK/vocab/' + i + '.json')
and run again. Save the SQL commands to your txt file. -
All SQL commands are ready. Now, we’ll modify the Anki database.
-
Open
collection.anki2
in the SQLite browser, put the SQL commands in the Execute SQL window. -
Execute them!
-
Save the database and quit the SQLite browser.
-
Compress both
collection.anki2
andmedia
into a zip file. Change the extension to.apkg
(See Archive.apkg) -
Double click it to import to Anki.
-
Voila! Now the due, interval, lapses, and reviews are imported to Anki
-
To make sure that everything went correctly, please check the due date of some cards and compare them to the due date in the Wanikani website.
Notes
- The deck doesn’t have to be the same as Wanikani deck. I use this gigantic 25k+ cards deck.
- There are some words in Wanikani that might not exist in the Anki deck. So, the progress of those words will be discarded.
- This is a one-way conversion. Once you move to Anki and make more progress on Anki, I guess there’s no way back to move your progress from Anki to Wanikani.
- The main downside is, my Wanikani level will be stuck in level 26 forever, haha.