Anki Mining card template for Japanese.
Field | Value |
---|---|
Expression | {expression} |
ExpressionFurigana | {furigana-plain} |
ExpressionReading | {reading} |
ExpressionAudio | {audio} |
MainDefinition | {selection-text} |
Sentence | {cloze-prefix}<b>{cloze-body}</b>{cloze-suffix} |
SentenceFurigana | |
SentenceAudio | |
FullDefinition | {glossary} |
Image | |
Translation | |
PitchPosition | {pitch-accent-positions} |
Hint | |
Frequency | {frequencies} |
FreqSort | |
MiscInfo | {document-title} |
ExtraField | |
*IsSentenceCard |
Notes:
- When the *IsSentenceCard field is filled with any character, card is turned into a sentence card. When empty, it is turned into a vocab card.
- The MainDefinition field is for the displayed definition on the card. When empty, the template displays a definition from the FullDefinition field. The preferred dictionaries for the default definition can be changed in the back template.
- The PitchPosition field takes in
{pitch-accent-positions}
.{pitch-accents}
and{pitch-accent-graphs}
will not work. - The FreqSort field is for frequency sorting. (See freq)
- The Hint field is for a hint on the front of the card (See Animecards).
- The furigana fields only take in plain furigana. (e.g. 漢字[かんじ] not 漢字).
- The color scheme can be inherited from system by removing this rule from the styling template.
More info can be added using other applications.
-
For mpvacious, fields can be filled like the following. In
subs2srs.conf
:model_name=Mining-JP sentence_field=Sentence secondary_field=Translation audio_field=SentenceAudio image_field=Image miscinfo_field=MiscInfo # The tag(s) added to new notes. Spaces separate multiple tags. note_tag=subs2srs アニメ::%n
-
For jidoujisho, fields can be filled like the following:
Field Value Expression Term ExpressionFurigana Furigana ExpressionReading Reading ExpressionAudio Term Audio Sentence Sentence SentenceAudio Sentence Audio FullDefinition Meaning Image Image FreqSort Frequency MiscInfo Context - Include image/audio HTML tags on export
- Use line break tag instead of newline on export
- Prepend dictionary name in meaning
Custom Yomichan Handlebars templates.
-
{selection-text-modified}
- Basically the same as the built-in{selection-text}
but will ignore selected text if it is the same as the target vocab (for Yomichan search page). -
{grammar-pt}
- fills out a field when a grammar dictionary has an entry. -
{context}
- Context tag. e.g.ラノベ::また、同じ夢を見ていた
if in ttsu, etc. Used with Field To Tag or FieldReporter
Custom CSS for changing the Yomichan popup appearance.
Font download links:
- Noto Sans JP: https://fonts.google.com/noto/specimen/Noto+Sans+JP
- UD Digi Kyokasho N-R: https://learnjapanese.moe/font/#windows-10
Just copy and paste the following CSS into Yomichan's custom CSS.
body {
font-family: "Inter", "Noto Sans JP", sans-serif;
--background-color: #1e1e2a;
--input-background-color: color-mix(
in srgb,
var(--background-color),
#fff 5%
);
}
.headword {
font-family: "UD Digi Kyokasho N";
}
/* Disable furigana on search page */
rt.query-parser-segment-reading {
display: none;
}
/* Collapse lists of links */
.definition-item:not([data-dictionary="JMdict"])
.gloss-list:has(.gloss-content > a:only-child) {
list-style: none;
display: inline;
padding-left: 0;
}
.definition-item:not([data-dictionary="JMdict"])
.gloss-list:has(.gloss-content > a:only-child)
* {
display: inline;
}
.definition-item:not([data-dictionary="JMdict"])
.gloss-item:has(.gloss-content > a:only-child):not(:last-child)::after {
content: " | ";
}
/* Collapse JMnedict entries */
.definition-item[data-dictionary="JMnedict"] .gloss-list {
list-style: none;
display: inline;
padding-left: 0;
}
.definition-item[data-dictionary="JMnedict"] .gloss-list * {
display: inline;
}
.definition-item[data-dictionary="JMnedict"]
.gloss-list
> .gloss-item:not(:last-child)::after {
content: " | ";
}
/* Collapse Jitendex entries */
.definition-item[data-dictionary*="Jitendex"] .gloss-sc-ul {
list-style: none;
display: inline;
padding-left: 0;
}
.definition-item[data-dictionary*="Jitendex"] .gloss-sc-ul > .gloss-sc-li {
display: inline;
}
.definition-item[data-dictionary*="Jitendex"]
.gloss-sc-ul
> .gloss-sc-li:not(:last-child)::after {
content: "; ";
}
/* Make Jitendex example sentences smaller */
.definition-item[data-dictionary*="Jitendex"]
*[data-sc-content="example-sentence-a"] {
font-size: 1em !important;
}
/* Only show summary for Pixiv */
[data-sc-pixiv="children"],
[data-sc-pixiv="related-tags"],
[data-sc-pixiv="continue-reading"],
[data-sc-pixiv="nav-header"] {
display: none;
}
/* Only show the first 2 frequency lists */
span.frequency-group-item:nth-child(n + 3) {
display: none;
}
/* Show on hover */
span.frequency-group-item:first-child:has(.tag-label:hover) ~ * {
display: inline-block;
}
/* Only show the first pitch dictionary */
li.pronunciation-group:first-child ~ * {
display: none;
}
ol.pronunciation-group-list:not([data-count="1"]) {
list-style: none;
padding: 0;
}
/* Show on hover */
li.pronunciation-group:first-child:has(.tag:hover) ~ * {
display: inline-block;
}
/* Hide add duplicate */
button.action-button[title="Add duplicate expression (Alt + E)"] {
display: none;
}
Anki card template for the Kanken Deck.
Font download link: https://github.com/adobe-fonts/source-han-serif/raw/release/Variable/TTF/SourceHanSerif-VF.ttf
Just copy and paste the following templates into Anki.
-
Front Template
<div id="deck" deck_name="{{Deck}}"></div> <div id="content"> Kanken Level: ? <div lang="ja" class="sentence">{{SentenceFront}}</div> {{#Picture}} {{Picture}} <br /> {{/Picture}} {{KankenAudio}} <hr /> <div id="container"> <div id="diagram" style="opacity: 0">{{Diagram}}</div> </div> </div> <script> function makeGrid() { const TATEGAKI = true; /* toggle vertical writing */ const NUMOFBOXES = -1; /* specify # of boxes; -1 to change based on word, 0 to hide */ const MAXBOXSIZE = 140; /* specify max boxsize (in px) */ const boxSize = Math.min( document.documentElement.clientWidth / 4.5, MAXBOXSIZE ); const container = document.getElementById("container"); const diagram = document.getElementById("diagram"); for (child of diagram.children) { child.style.height = `${boxSize}px`; child.style.width = `${boxSize}px`; } const diagramHTML = container.innerHTML; let fullSize = NUMOFBOXES === -1 ? boxSize * diagram.childElementCount : boxSize * NUMOFBOXES; let currSize = boxSize / 2; container.innerHTML = ""; if (TATEGAKI) { container.style.height = `${fullSize}px`; container.style.width = `${boxSize}px`; container.innerHTML += `<div class="vert-line" style="height:${fullSize}px;width:${ boxSize / 2 }px"></div>`; while (currSize < fullSize) { container.innerHTML += `<div class="hori-line" style="height:${currSize}px;width:${boxSize}px;"></div>`; currSize += boxSize / 2; } container.innerHTML += diagramHTML; } else { container.style.height = `${boxSize}px`; container.style.width = `${fullSize}px`; container.innerHTML += `<div class="hori-line" style="height:${ boxSize / 2 }px;width:${fullSize}px"></div>`; while (currSize < fullSize) { container.innerHTML += `<div class="vert-line" style="height:${boxSize}px;width:${currSize}px;"></div>`; currSize += boxSize / 2; } container.innerHTML += diagramHTML; } if (NUMOFBOXES === 0) { container.style.border = "none"; for (child of container.children) { child.style.border = "none"; } } return; } makeGrid(); </script>
-
Back Template
<div id="deck" deck_name="{{Deck}}"></div> <div id="content"> Kanken Level: {{KankenLevel}} <div lang="ja" class="sentence">{{SentenceBack}}</div> {{#Picture}} {{Picture}} <br /> {{/Picture}} {{KankenAudio}} <hr /> <div id="container"> <div id="diagram">{{Diagram}}</div> </div> <div lang="ja" id="extra"> {{Kana}}【{{Kanji}}】 <br /> {{Meaning}} </div> </div> <script> function makeGrid() { const TATEGAKI = true; /* toggle vertical writing */ const NUMOFBOXES = -1; /* specify # of boxes; -1 to change based on word, 0 to hide */ const MAXBOXSIZE = 140; /* specify max boxsize (in px) */ const boxSize = Math.min( document.documentElement.clientWidth / 4.5, MAXBOXSIZE ); const container = document.getElementById("container"); const diagram = document.getElementById("diagram"); for (child of diagram.children) { child.style.height = `${boxSize}px`; child.style.width = `${boxSize}px`; } const diagramHTML = container.innerHTML; let fullSize = NUMOFBOXES === -1 ? boxSize * diagram.childElementCount : boxSize * NUMOFBOXES; let currSize = boxSize / 2; container.innerHTML = ""; if (TATEGAKI) { container.style.height = `${fullSize}px`; container.style.width = `${boxSize}px`; container.innerHTML += `<div class="vert-line" style="height:${fullSize}px;width:${ boxSize / 2 }px"></div>`; while (currSize < fullSize) { container.innerHTML += `<div class="hori-line" style="height:${currSize}px;width:${boxSize}px;"></div>`; currSize += boxSize / 2; } container.innerHTML += diagramHTML; } else { container.style.height = `${boxSize}px`; container.style.width = `${fullSize}px`; container.innerHTML += `<div class="hori-line" style="height:${ boxSize / 2 }px;width:${fullSize}px"></div>`; while (currSize < fullSize) { container.innerHTML += `<div class="vert-line" style="height:${boxSize}px;width:${currSize}px;"></div>`; currSize += boxSize / 2; } container.innerHTML += diagramHTML; } if (NUMOFBOXES === 0) { container.style.border = "none"; for (child of container.children) { child.style.border = "none"; } } return; } makeGrid(); </script>
-
Styling
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+JP&family=Noto+Serif+JP&display=swap"); html.win, html.mac, html.linux:not(.android) { --main-font: "Noto Serif JP", serif; } html.mobile { --main-font: "Noto Serif JP", serif; } .card.nightMode { --main-bg: #1c2127; --main-color: #dadada; --sub-color: #7d8590; --grey: rgba(128, 128, 128, 0.1); --accent: #2980f1; font-family: var(--main-font); background-color: var(--main-bg); color: var(--main-color); font-size: 20px; text-align: center; } #qa { display: flex; align-items: stretch; flex-direction: column; min-height: calc(100vh - 40px); } @font-face { font-family: "Noto Serif JP"; src: local("Noto Serif JP"), local("Noto Serif JP Regular"), url("_NotoSerifJP.otf"); } /* ----- Front elements ----- */ #content { margin-top: 24px; } .sentence { font-size: 28px; } /* PC replay button */ .replay-button { margin-top: -5px; } .replay-button svg { width: 30px; height: auto; } .replay-button svg path { fill: var(--main-color); transition: 0.2s; } .replay-button svg circle { fill: var(--main-bg); display: none; } .replay-button:hover svg path { fill: var(--sub-color); } /* Grid */ #container { margin: auto; border-style: solid; border-color: var(--grey); background-color: rgba(255, 255, 255, 0); } .vert-line { position: absolute; border-style: none; border-right-style: dotted; border-color: var(--grey); } .hori-line { position: absolute; border-style: none; border-bottom-style: dotted; border-color: var(--grey); } /* ----- Back elements ----- */ /* Stroke diagram */ #diagram { line-height: 0; } #diagram > img { height: 140px; width: 140px; position: relative; z-index: 100; } /* Extra info */ #extra { opacity: 0; } #extra:hover { opacity: 1; } /* ---------- Misc ---------- */ /* Remove default margins */ * { margin: 0px; padding: 0px; } /* Images */ img { height: 200px; width: auto; border-radius: 8px; } /* Underline CSS */ u { text-decoration: none; color: var(--accent); font-weight: 400; } /* Line margins */ hr { margin-top: 0.5em; margin-bottom: 0.5em; }