The Tingdu wordset format
Version 1 · for your own words, phrases, pictures and sounds
A Tingdu wordset is one JSON file. Everything an item needs travels inside it: the text, its pronunciation, its translation and, if you like, a picture and a sound, written as data URLs (the binary file encoded as text). One file is all you share, back up or import: open Tingdu, tap Import, and choose the file or paste its text.
Prefer a spreadsheet? Tingdu also imports a simple CSV table. Want to start from something that works? Download the example wordset (nine words for a TV episode, with pictures).
The file at a glance
{
"format": "tingdu-wordset",
"version": 1,
"wordset": {
"id": "my-first-set",
"name": "My first set",
"language": "zh-CN",
"items": [
{ "id": "w1", "text": "你好", "phonetic": "nǐ hǎo", "translation": "hello" }
]
}
}
formatis always the string"tingdu-wordset".-
versionis the number1(not the string"1"). -
Then exactly one of:
wordset, a single wordset object, orwordsets, an array of 1 to 500 wordset objects (this is what Tingdu writes when you export a whole collection). - Other top-level fields are ignored.
Wordset fields
| Field | Required | Rules |
|---|---|---|
id |
yes |
1 to 64 characters, only letters, digits, ., _ and
- (the pattern ^[A-Za-z0-9._-]{1,64}$). It identifies the
wordset: importing a file with an id you already have asks whether to replace it
(its records stay) or keep both.
|
name |
yes | Not empty, at most 80 characters. Shown in the library. |
language |
yes |
The language of text as a language tag: a lowercase code of 2 or 3
letters, optionally followed by - parts of 2 to 8 letters or digits, at
most 35 characters. Examples: zh-CN (Mandarin, simplified),
zh-TW, ja-JP, en-GB, fr. It
chooses the text-to-speech voice, so be precise.
|
collection |
no | At most 80 characters. Wordsets with the same collection are grouped together in the library, for example one collection per TV series. |
items |
yes | An array of 1 to 2000 items. |
Item fields
| Field | Required | Rules |
|---|---|---|
id |
yes | Not empty, at most 64 characters, unique within the wordset. Your records follow it, so keep it when you fix a typo in the text. |
text |
yes |
The word or phrase being learnt, for example 咖啡. Not empty, at most
200 characters.
|
phonetic |
no |
How to say it, for example pinyin with tone marks kāfēi. At most 200
characters. Shown under the text when “Show phonetic” is on in Settings.
|
translation |
yes |
The meaning in your language, for example coffee. At most 200
characters.
|
picture |
no |
A data URL of an image: image/svg+xml, image/png,
image/jpeg or image/webp; the whole data URL at most 5 MB
(5,242,880 characters). See Pictures.
|
sound |
no |
A data URL of a recording with any audio/… type; the whole data URL at
most 10 MB (10,485,760 characters). See Sounds.
|
extra |
no | An object for your own notes. Tingdu checks that it is an object and does not use or keep it. |
A whole file may be up to 200 MB; above 20 MB Tingdu asks before importing, because it can be slow. Fields Tingdu does not know are ignored.
Pictures and sounds inside the JSON: data URLs
A data URL is a whole file written as text: data:, the file type, and the
content. For a binary file (PNG, JPEG, WebP, MP3…) the content is base64:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…
data:audio/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4…
Make one from a file with a single command and paste the result into the JSON:
Windows (PowerShell)
"data:image/png;base64," + [Convert]::ToBase64String([IO.File]::ReadAllBytes("$PWD\cat.png")) | Set-Clipboard
Linux
echo "data:audio/mpeg;base64,$(base64 -w0 cat.mp3)" > cat.txt
macOS
echo "data:audio/mpeg;base64,$(base64 -i cat.mp3)" | pbcopy
Or skip the commands: add pictures and recordings in Tingdu’s own editor and use Export. The exported file is this format, with every picture and sound already inside.
Pictures
-
Allowed types: SVG (
image/svg+xml), PNG, JPEG and WebP. A link such ashttps://…/cat.pngis not accepted: the picture must be inside the file, so the wordset works offline. - PNG, JPEG and WebP pictures larger than 256 px on their longer side are scaled down to 256 px (and stored as WebP) when you import, so big photos do not fill your device. SVG is kept as it is.
-
SVG is the best choice for simple pictograms: tiny, sharp at every size, and easy to write
by hand (or to ask an AI chat for). Use
viewBox="0 0 64 64"and draw lines withstroke="currentColor", so they turn light in the dark theme. Tingdu removes<style>,style="…",<image>,<foreignObject>, links and scripts from an SVG before showing it; use plain attributes such asfill="#e4573d". -
An SVG can go in as base64, or as readable text with
;utf8,. In the text form use single quotes inside the SVG (the JSON string uses double quotes) and write#as%23:
"picture": "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><circle cx='32' cy='36' r='20' fill='%23e4573d' stroke='currentColor' stroke-width='3'/><path d='M32 16c0-6 4-8 8-8' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round'/></svg>"
The same idea in base64 (a water drop):
"picture": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PHBhdGggZD0iTTMyIDhDMjIgMjQgMTYgMzIgMTYgNDBhMTYgMTYgMCAwIDAgMzIgMGMwLTgtNi0xNi0xNi0zMnoiIGZpbGw9IiMzYTg2ZDQiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4="
The picture games need enough pictures to choose from: at least 4 items with different pictures for Choose and Pairs, 6 for Rush.
Sounds (optional)
-
You do not need sounds. An item without a
soundis spoken by the device’s text-to-speech voice for the wordset’slanguage. Add a recording when you want a real voice, a particular accent, or a line from a scene. -
Any
audio/…type is accepted; what plays depends on the device. MP3 (audio/mpeg) plays almost everywhere;audio/ogg,audio/webmandaudio/mp4are fine in current browsers and on Android. - Keep recordings short: a word or a phrase, not a whole scene.
A complete example
Two pictured items and one without a picture. Copy it, paste it into the box on Tingdu’s Import page, tap Check pasted text, and it imports as it is. The example file is a longer one.
{
"format": "tingdu-wordset",
"version": 1,
"wordset": {
"id": "fruit-and-water",
"name": "Fruit and water",
"language": "zh-CN",
"collection": "My first sets",
"items": [
{
"id": "pingguo",
"text": "苹果",
"phonetic": "píngguǒ",
"translation": "apple",
"picture": "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><circle cx='32' cy='36' r='20' fill='%23e4573d' stroke='currentColor' stroke-width='3'/><path d='M32 16c0-6 4-8 8-8' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round'/></svg>"
},
{
"id": "shui",
"text": "水",
"phonetic": "shuǐ",
"translation": "water",
"picture": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PHBhdGggZD0iTTMyIDhDMjIgMjQgMTYgMzIgMTYgNDBhMTYgMTYgMCAwIDAgMzIgMGMwLTgtNi0xNi0xNi0zMnoiIGZpbGw9IiMzYTg2ZDQiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4="
},
{
"id": "ni-hao",
"text": "你好",
"phonetic": "nǐ hǎo",
"translation": "hello",
"extra": { "note": "the first word of every series" }
}
]
}
}
An item with its own recording looks the same, with one more field (shortened here):
{ "id": "xie-xie", "text": "谢谢", "phonetic": "xièxie", "translation": "thank you",
"sound": "data:audio/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4…" }
The CSV alternative
A table saved as CSV from a spreadsheet works too, one item per row. The first row names the columns:
text,phonetic,translation
咖啡,kāfēi,coffee
手机,shǒujī,mobile phone
"没关系",méi guānxi,"it's fine, no problem"
-
Columns:
textandtranslationare required;phonetic,pictureandsoundare optional. Names are not case-sensitive, any order works, and other columns are ignored. -
Standard CSV quoting: a cell with a comma, a quote or a line break goes in double quotes,
and a quote inside it is doubled (
""). A data URL always contains a comma, so apictureorsoundcell must be quoted. - Spaces around a cell are trimmed; empty rows are skipped; a byte order mark is fine; line ends may be LF or CR LF.
-
The wordset’s name is the file name (or “My wordset” for pasted text) and its language is
guessed: Chinese (
zh-CN) when at least half of the texts contain Chinese characters, otherwise English (en-US). You can change both before saving. Item ids are numbered for you. - Errors name the row, counting the header as row 1:
row 4.text.
When the import says “The file is not valid”
Tingdu checks the whole file before it saves anything and lists the first ten problems, each
with where it is: wordset.items[3].text is the text of the fourth
item (counting from 0).
| Message | What it means |
|---|---|
(file): not valid JSON (…) |
The text is not JSON: a missing comma or bracket, a comma after the last item,
comments, curly “smart” quotes, or a sentence of an AI chat before or after the
JSON. Paste only the part from { to the last }; a
surrounding ```json code block is removed for you.
|
format: must be "tingdu-wordset" |
The format field is missing or misspelt. |
version: must be 1 |
Missing, or written as a string "1". |
(file): must hold either "wordset" or "wordsets" |
Neither or both of them are there. |
wordset.id: may contain only letters, digits, ".", "_" and "-" |
Spaces, Chinese characters or other symbols in the wordset id. |
wordset.language: must be a language tag such as zh-CN |
A name such as "Chinese", or an uppercase code "ZH".
|
….text: must be a non-empty string |
An item without text, or with an empty one. |
….translation: is required |
An item without a translation. |
….id: duplicates the id "w3" |
Two items share an id; give each its own. |
….picture: must be a data URL of an SVG, PNG, JPEG or WebP image |
A link instead of a data URL, a missing data:image/… start, or another
type such as GIF.
|
….sound: must be a data URL of an audio file |
A link, or a type that does not start with audio/. |
…: must be at most 200 characters |
A text, phonetic or translation that is too long (80 for names, 64 for ids). |
wordset.items: must hold at least one item |
An empty list. |
header: needs a "translation" column |
A CSV whose first row lacks a required column name. |
Making a list for a TV series with an AI chat? The blog post Learn Chinese with the TV series you love has a ready-to-copy prompt that asks for exactly this format.