feat(app): Anki's bracket furigana becomes readings where its templates show them

A field a note type shows through Anki's furigana filters
({{furigana:Reading}}, kanji:, kana:) holds 漢字[かんじ]; the import now
turns it into readings over the text before the brackets, as Anki's own
filters read it (back to a space, which goes). Other fields stay as
written; a sound stays a sound. The note types' templates and field
names are read from both package formats (Anki's unicase collation
registered so their tables can be read at all). New fixtures exported
by Anki itself (tool/anki/make_fixtures.py) test both formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-09 13:26:37 +02:00
commit dd36424917
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -15,6 +15,9 @@ The collection holds, on purpose:
- a Basic note with a list and HTML entities ("Japanese::Animals");
- a manual reschedule in the review log (no answer).
A second deck (`anki_furigana*.apkg`) has a note type that shows its
Reading through Anki's furigana filter, and the same text in its Expression.
Run from the repository root, in a virtual environment with Anki's package:
python3 -m venv /tmp/anki && /tmp/anki/bin/pip install anki==26.9.3
@ -125,5 +128,43 @@ def main():
(OUT / "anki_before_2_1_50.apkg").write_bytes(out.getvalue())
def furigana():
"""A deck whose note type shows a field through Anki's furigana filter,
in the latest format and the legacy one: the import turns that field's
漢字[かんじ] into readings, and leaves the same text in another field."""
with tempfile.TemporaryDirectory() as work:
col = Collection(os.path.join(work, "collection.anki2"))
models = col.models
model = models.new("Japanese (furigana)")
for name in ("Expression", "Reading", "Meaning"):
models.add_field(model, models.new_field(name))
template = models.new_template("Recognition")
template["qfmt"] = "{{Expression}}"
template["afmt"] = "{{FrontSide}}<hr id=answer>{{furigana:Reading}}<br>{{Meaning}}"
models.add_template(model, template)
models.add(model)
note = col.new_note(col.models.by_name("Japanese (furigana)"))
note.fields[0] = "日本語[にほんご]"
note.fields[1] = "日本語[にほんご]を 勉強[べんきょう]する"
note.fields[2] = "to study Japanese"
col.add_note(note, DeckId(col.decks.id("Furigana")))
for legacy, name in [
(False, "anki_furigana.apkg"),
(True, "anki_furigana_legacy.apkg"),
]:
col.export_anki_package(
out_path=str(OUT / name),
options=ExportAnkiPackageOptions(
with_scheduling=False,
with_deck_configs=False,
with_media=False,
legacy=legacy,
),
limit=None,
)
col.close()
if __name__ == "__main__":
main()
furigana()