This is the repository for a Japanese-English learner's dictionary delivered as a static website. Its production is being supervised by Tom Gally. All of the entry-writing and coding is being done by Claude in Claude Code for the Web, with some bug-hunting and improvement-suggesting by ChatGPT and Gemini.
Live site: https://www.tkgje.jp/
This dictionary is licensed under Creative Commons Zero v1.0 Universal, and anyone is free to copy the data and code for whatever purpose they like, including commercial uses.
This dictionary is designed for learners of Japanese as a second language. It uses an original three-tier vocabulary classification system:
Dictionary features include:
Audio readings for example sentences will be added in the future.
entries_index.json for current entries and candidate_words.json for future additionsprompts/routine2.md) — scheduled Claude sessions that rotate between polishing, entry creation, cross-model accuracy review (via OpenRouter, budget-capped), systemic fixes, and knowledge-base maintenance; each run verifies its own entry changes with an independent model, records quality metrics, and merges its own PRIntermediate learners of Japanese who:
The dictionary is built as a completely static website:
index.html directly in your browserThe dictionary is built as a static HTML site at docs/:
Entry pages are organized by numeric ID ranges (500 entries per directory):
docs/
├── index.html # Home page
├── search.html # Search interface
├── browse.html # Browse by kana row
├── recent.html # Recently modified entries
├── random.html # Random word cloud
├── entries/
│ ├── 00000/ # Entries 00000-00499
│ │ ├── 00396_taberu.html
│ │ └── 00499_sakana.html
│ ├── 00500/ # Entries 00500-00999
│ ├── 01000/ # Entries 01000-01499
│ └── ...
└── kanji/ # Kanji index pages
├── 00001_teki_mato_target.html
├── 00009_jin_hito_person.html
└── ... # One page per kanji
This numeric range structure allows the dictionary to scale to 10,000+ entries while staying within GitHub's 1,000 files per directory limit.
The dictionary provides four different ways to explore entries:
Quick lookup by Japanese, romaji, or English. The traditional dictionary experience for users who know what word they're looking for.
Explore entries organized by:
The dictionary uses an original three-tier classification system instead of JLPT levels. All new entries are added to the general tier.
View the most recently added or revised entries (up to 250). Each entry shows:
Useful for tracking dictionary updates and discovering new content.
A word cloud of randomly selected entries. Click any word to view its full entry. Great for serendipitous discovery and vocabulary review.
The dictionary uses a custom notation for furigana (reading annotations above kanji):
{kanji|reading}
Examples:
{食|た}べる → 食べる with た above 食{友達|ともだち} → 友達 with ともだち above it{日本語|にほんご}が{分|わ}かる → Multiple annotations in one sentence<ruby> tagsExample sentences can contain inline word links that allow users to click any word to navigate to its dictionary entry.
In JSON source files, links use special Unicode delimiters:
⟦{surface|reading}→baseform:entry_id⟧
Components:
⟦ (U+27E6) - Opening bracketsurface - The word as it appears in the sentence (may include furigana notation)→ (U+2192) - Arrow separatorbaseform - The dictionary form of the word (displayed in tooltip): (U+FF1A) - Fullwidth colon separatorentry_id - The target dictionary entry ID (e.g., 00111_hon)⟧ (U+27E7) - Closing bracketExample:
"japanese": "⟦{本|ほん}→本:00111_hon⟧⟦を→を:00422_wo⟧⟦{読|よ}む→読む:00426_yomu⟧。"
noentry: Use for words without dictionary entries: ⟦{矍鑠|かくしゃく}→矍鑠:noentry⟧The kanji index allows users to find all dictionary entries containing a specific kanji character.
Each kanji is identified by a unique ID:
{5-digit number}_{onyomi}_{kunyomi}_{gloss}
Examples:
00009_jin_hito_person (人)00116_kou_taka_tall (高)00431_yo_ama_surplus (余)The ID uses none when a kanji lacks on'yomi or kun'yomi.
kanji/docs/kanji/ during site buildje-dict-1/
├── entries/ # Dictionary entries (one JSON file per word)
│ ├── 00000/ # Entries 00000-00499
│ ├── 00500/ # Entries 00500-00999
│ ├── 01000/ # Entries 01000-01499
│ └── ... # (500 entries per directory)
├── kanji/ # Kanji index data
│ ├── kanji_list.json # Master list mapping kanji to IDs
│ └── {kanji_id}.json # Entry lists for each kanji
├── build/ # Build and management scripts
│ ├── schema.json # JSON schema for entries
│ ├── validate.py # Entry validation (schema, cross-refs)
│ ├── validate_tags.py # Tag taxonomy validation
│ ├── build_flat.py # Static HTML site generator (atomic builds)
│ ├── entry_renderer.py # Entry page HTML generation
│ ├── page_generators.py # Navigation page generation
│ ├── search_index_builder.py # Search index and JS generation
│ ├── report.py # Dictionary health dashboard
│ ├── generate_word_lookup.py # Builds word_id_lookup.json
│ ├── path_utils.py # Shared path/prefix utilities
│ ├── japanese_utils.py # Hiragana/romaji/furigana utilities
│ ├── constants.py # Centralized cross-reference type definitions
│ ├── update_indexes.py # Updates entries_index.json and candidate list
│ ├── manage_candidates.py # Manages candidate_words.json
│ ├── get_entry_path.py # Computes correct path for new entries
│ ├── get_timestamp.py # Generates UTC timestamp for metadata
│ ├── check_duplicate.py # Checks for duplicate entries
│ ├── verify_furigana.py # Verifies furigana coverage
│ ├── templates/ # CSS and JS templates (styles.css, search.js, etc.)
│ ├── tests/ # Unit tests (pytest)
│ ├── archive/ # One-time migration scripts (no longer used)
│ └── requirements.txt # Python 3.10+ dependencies
├── pipeline/ # Automated task pipeline
│ ├── run-pipeline.sh # Task runner (reads config, invokes claude, validates)
│ ├── validate-task.sh # Task-specific validation gates
│ ├── update-status.py # Pipeline status tracking and reporting
│ ├── update-brief.py # Regenerates PROJECT_CONTEXT_BRIEF.md
│ ├── recommend-tasks.py # Task scheduler recommendations
│ ├── routine_next.py # Unified Routine mode selector (weighted rotation + health nudges)
│ ├── metrics_snapshot.py # Per-run quality metrics → metrics-history.jsonl
│ ├── openrouter-ledger.json # Daily OpenRouter spend ledger ($5/day cap)
│ └── pipeline-config.json # Active pipeline configuration
├── polishing/ # Progress tracking for polishing tasks
├── prompts/ # Task prompts (interactive and batch/)
├── docs/ # Generated output (served as static site)
│ ├── entries/ # Individual entry HTML files
│ │ ├── 00000/ # (same numeric range structure as entries/)
│ │ └── ...
│ └── kanji/ # Kanji index HTML pages
├── .claude/ # Claude Code configuration
│ ├── skills/ # Agent skills for entry guidelines (auto-loaded)
│ └── settings.json
├── .github/workflows/ # GitHub Actions (validate.yml, pipeline.yml)
├── Makefile # Build runner (make validate, make build, etc.)
├── entries_index.json # Index of all dictionary entries
├── candidate_words.json # Words to potentially add in future
├── PROJECT_CONTEXT_BRIEF.md # Quick-reference counts for session start
└── PROJECT_STATUS.md # Session continuity and recent change log
Each entry is a JSON file with the following structure:
{
"id": "00396_taberu",
"headword": "{食|た}べる",
"reading": "たべる",
"part_of_speech": "verb (ichidan)",
"gloss": "to eat",
"definitions": [
{
"sense_number": 1,
"gloss": "to eat",
"explanation": "The most common verb for eating..."
}
],
"examples": [
{
"id": "00396_taberu_ex1",
"japanese": "{朝|あさ}ごはんを{食|た}べましたか。",
"english": "Did you eat breakfast?",
"notes": null,
"sense_numbers": [1]
}
],
"notes": "Usage notes, grammar notes, etc.",
"cross_references": [
{
"type": "pair",
"reading": "たべもの",
"headword": "{食|た}べ{物|もの}"
}
],
"metadata": {
"created": "2026-01-05T10:00:00Z",
"modified": "2026-01-05T10:00:00Z",
"ai_model": "claude-opus-4-5",
"vocabulary_tier": "basic"
}
}
The sense_numbers field on example sentences links each example to one or more definition senses:
sense_number values in definitions (e.g., [1], [2], or [1, 2])[1] for all examples[1, 2]This enables future features like filtering examples by sense and helps learners understand which meaning each example demonstrates.
Files use the format: {id}_{romanized_reading}.json
toukyou, not tokyoarubaito)Files go in directories based on the numeric ID range (500 entries per directory):
entries/00000/00396_taberu.jsonentries/00500/00538_aruku.jsonentries/01000/01186_mukau.jsonThe directory name is determined by rounding down to the nearest 500:
entries/00000/entries/00500/entries/01000/entries_index.json, candidate_words.json)The following skills are available in .claude/skills/ and will be automatically invoked by Claude Code when relevant:
| Skill | Purpose |
|---|---|
entry-guidelines | General quality standards for all entries |
verb-entry | Requirements for verb entries (transitivity, aspect, collocations) |
adjective-entry | Requirements for adjective entries (forms, conjugations) |
particle-entry | Requirements for particle entries (predicate lists, contrasts) |
other-entries | Requirements for nouns, counters, adverbs, expressions |
example-sentences | Requirements for example sentences (counts, length, vocabulary) |
vocabulary-notes | Formatting guidelines for notes field |
vocabulary-tiers | Guidelines for the three-tier vocabulary system |
cross-reference-entry | Guidelines for adding cross-references between entries |
find-candidates | Guidelines for finding new candidate words to add |
revise-entries | Checklist for revising existing entries to v2 standards |
polish-entries | Systematic review and improvement of entries |
delete-entry | Guidelines for safely deleting entries |
resolve-duplicates | Guidelines for identifying and resolving duplicate entries |
kanji-index | Guidelines for maintaining the kanji index feature |
inline-word-links | Guidelines for adding inline cross-reference links to examples |
Skills are automatically loaded when Claude determines they're relevant to the current task.
make validatemake build (or make quick for incremental)Based on multi-model LLM evaluation, these are HIGH PRIORITY for all entries:
For Verbs:
For Particles:
For All Entries:
Entries are organized by numeric ID ranges (500 entries per directory):
| ID Range | Directory |
|---|---|
| 00000-00499 | entries/00000/ |
| 00500-00999 | entries/00500/ |
| 01000-01499 | entries/01000/ |
| ... | ... |
Use python3 build/get_entry_path.py <reading> <entry_id> to get the correct path for new entries.
Creative Commons Zero v1.0 Universal
Email suggestions for enhancements to Tom Gally.
Hacker News (1)
Python
90.8%
Shell
3.8%
CSS
3.1%
JavaScript
2.2%
This is the repository for a Japanese-English learner's dictionary delivered as a static website. Its production is being supervised by Tom Gally. All of the entry-writing and coding is being done by Claude in Claude Code for the Web, with some bug-hunting and improvement-suggesting by ChatGPT and Gemini.
Live site: https://www.tkgje.jp/
This dictionary is licensed under Creative Commons Zero v1.0 Universal, and anyone is free to copy the data and code for whatever purpose they like, including commercial uses.
This dictionary is designed for learners of Japanese as a second language. It uses an original three-tier vocabulary classification system:
Dictionary features include:
Audio readings for example sentences will be added in the future.
entries_index.json for current entries and candidate_words.json for future additionsprompts/routine2.md) — scheduled Claude sessions that rotate between polishing, entry creation, cross-model accuracy review (via OpenRouter, budget-capped), systemic fixes, and knowledge-base maintenance; each run verifies its own entry changes with an independent model, records quality metrics, and merges its own PRIntermediate learners of Japanese who:
The dictionary is built as a completely static website:
index.html directly in your browserThe dictionary is built as a static HTML site at docs/:
Entry pages are organized by numeric ID ranges (500 entries per directory):
docs/
├── index.html # Home page
├── search.html # Search interface
├── browse.html # Browse by kana row
├── recent.html # Recently modified entries
├── random.html # Random word cloud
├── entries/
│ ├── 00000/ # Entries 00000-00499
│ │ ├── 00396_taberu.html
│ │ └── 00499_sakana.html
│ ├── 00500/ # Entries 00500-00999
│ ├── 01000/ # Entries 01000-01499
│ └── ...
└── kanji/ # Kanji index pages
├── 00001_teki_mato_target.html
├── 00009_jin_hito_person.html
└── ... # One page per kanji
This numeric range structure allows the dictionary to scale to 10,000+ entries while staying within GitHub's 1,000 files per directory limit.
The dictionary provides four different ways to explore entries:
Quick lookup by Japanese, romaji, or English. The traditional dictionary experience for users who know what word they're looking for.
Explore entries organized by:
The dictionary uses an original three-tier classification system instead of JLPT levels. All new entries are added to the general tier.
View the most recently added or revised entries (up to 250). Each entry shows:
Useful for tracking dictionary updates and discovering new content.
A word cloud of randomly selected entries. Click any word to view its full entry. Great for serendipitous discovery and vocabulary review.
The dictionary uses a custom notation for furigana (reading annotations above kanji):
{kanji|reading}
Examples:
{食|た}べる → 食べる with た above 食{友達|ともだち} → 友達 with ともだち above it{日本語|にほんご}が{分|わ}かる → Multiple annotations in one sentence<ruby> tagsExample sentences can contain inline word links that allow users to click any word to navigate to its dictionary entry.
In JSON source files, links use special Unicode delimiters:
⟦{surface|reading}→baseform:entry_id⟧
Components:
⟦ (U+27E6) - Opening bracketsurface - The word as it appears in the sentence (may include furigana notation)→ (U+2192) - Arrow separatorbaseform - The dictionary form of the word (displayed in tooltip): (U+FF1A) - Fullwidth colon separatorentry_id - The target dictionary entry ID (e.g., 00111_hon)⟧ (U+27E7) - Closing bracketExample:
"japanese": "⟦{本|ほん}→本:00111_hon⟧⟦を→を:00422_wo⟧⟦{読|よ}む→読む:00426_yomu⟧。"
noentry: Use for words without dictionary entries: ⟦{矍鑠|かくしゃく}→矍鑠:noentry⟧The kanji index allows users to find all dictionary entries containing a specific kanji character.
Each kanji is identified by a unique ID:
{5-digit number}_{onyomi}_{kunyomi}_{gloss}
Examples:
00009_jin_hito_person (人)00116_kou_taka_tall (高)00431_yo_ama_surplus (余)The ID uses none when a kanji lacks on'yomi or kun'yomi.
kanji/docs/kanji/ during site buildje-dict-1/
├── entries/ # Dictionary entries (one JSON file per word)
│ ├── 00000/ # Entries 00000-00499
│ ├── 00500/ # Entries 00500-00999
│ ├── 01000/ # Entries 01000-01499
│ └── ... # (500 entries per directory)
├── kanji/ # Kanji index data
│ ├── kanji_list.json # Master list mapping kanji to IDs
│ └── {kanji_id}.json # Entry lists for each kanji
├── build/ # Build and management scripts
│ ├── schema.json # JSON schema for entries
│ ├── validate.py # Entry validation (schema, cross-refs)
│ ├── validate_tags.py # Tag taxonomy validation
│ ├── build_flat.py # Static HTML site generator (atomic builds)
│ ├── entry_renderer.py # Entry page HTML generation
│ ├── page_generators.py # Navigation page generation
│ ├── search_index_builder.py # Search index and JS generation
│ ├── report.py # Dictionary health dashboard
│ ├── generate_word_lookup.py # Builds word_id_lookup.json
│ ├── path_utils.py # Shared path/prefix utilities
│ ├── japanese_utils.py # Hiragana/romaji/furigana utilities
│ ├── constants.py # Centralized cross-reference type definitions
│ ├── update_indexes.py # Updates entries_index.json and candidate list
│ ├── manage_candidates.py # Manages candidate_words.json
│ ├── get_entry_path.py # Computes correct path for new entries
│ ├── get_timestamp.py # Generates UTC timestamp for metadata
│ ├── check_duplicate.py # Checks for duplicate entries
│ ├── verify_furigana.py # Verifies furigana coverage
│ ├── templates/ # CSS and JS templates (styles.css, search.js, etc.)
│ ├── tests/ # Unit tests (pytest)
│ ├── archive/ # One-time migration scripts (no longer used)
│ └── requirements.txt # Python 3.10+ dependencies
├── pipeline/ # Automated task pipeline
│ ├── run-pipeline.sh # Task runner (reads config, invokes claude, validates)
│ ├── validate-task.sh # Task-specific validation gates
│ ├── update-status.py # Pipeline status tracking and reporting
│ ├── update-brief.py # Regenerates PROJECT_CONTEXT_BRIEF.md
│ ├── recommend-tasks.py # Task scheduler recommendations
│ ├── routine_next.py # Unified Routine mode selector (weighted rotation + health nudges)
│ ├── metrics_snapshot.py # Per-run quality metrics → metrics-history.jsonl
│ ├── openrouter-ledger.json # Daily OpenRouter spend ledger ($5/day cap)
│ └── pipeline-config.json # Active pipeline configuration
├── polishing/ # Progress tracking for polishing tasks
├── prompts/ # Task prompts (interactive and batch/)
├── docs/ # Generated output (served as static site)
│ ├── entries/ # Individual entry HTML files
│ │ ├── 00000/ # (same numeric range structure as entries/)
│ │ └── ...
│ └── kanji/ # Kanji index HTML pages
├── .claude/ # Claude Code configuration
│ ├── skills/ # Agent skills for entry guidelines (auto-loaded)
│ └── settings.json
├── .github/workflows/ # GitHub Actions (validate.yml, pipeline.yml)
├── Makefile # Build runner (make validate, make build, etc.)
├── entries_index.json # Index of all dictionary entries
├── candidate_words.json # Words to potentially add in future
├── PROJECT_CONTEXT_BRIEF.md # Quick-reference counts for session start
└── PROJECT_STATUS.md # Session continuity and recent change log
Each entry is a JSON file with the following structure:
{
"id": "00396_taberu",
"headword": "{食|た}べる",
"reading": "たべる",
"part_of_speech": "verb (ichidan)",
"gloss": "to eat",
"definitions": [
{
"sense_number": 1,
"gloss": "to eat",
"explanation": "The most common verb for eating..."
}
],
"examples": [
{
"id": "00396_taberu_ex1",
"japanese": "{朝|あさ}ごはんを{食|た}べましたか。",
"english": "Did you eat breakfast?",
"notes": null,
"sense_numbers": [1]
}
],
"notes": "Usage notes, grammar notes, etc.",
"cross_references": [
{
"type": "pair",
"reading": "たべもの",
"headword": "{食|た}べ{物|もの}"
}
],
"metadata": {
"created": "2026-01-05T10:00:00Z",
"modified": "2026-01-05T10:00:00Z",
"ai_model": "claude-opus-4-5",
"vocabulary_tier": "basic"
}
}
The sense_numbers field on example sentences links each example to one or more definition senses:
sense_number values in definitions (e.g., [1], [2], or [1, 2])[1] for all examples[1, 2]This enables future features like filtering examples by sense and helps learners understand which meaning each example demonstrates.
Files use the format: {id}_{romanized_reading}.json
toukyou, not tokyoarubaito)Files go in directories based on the numeric ID range (500 entries per directory):
entries/00000/00396_taberu.jsonentries/00500/00538_aruku.jsonentries/01000/01186_mukau.jsonThe directory name is determined by rounding down to the nearest 500:
entries/00000/entries/00500/entries/01000/entries_index.json, candidate_words.json)The following skills are available in .claude/skills/ and will be automatically invoked by Claude Code when relevant:
| Skill | Purpose |
|---|---|
entry-guidelines | General quality standards for all entries |
verb-entry | Requirements for verb entries (transitivity, aspect, collocations) |
adjective-entry | Requirements for adjective entries (forms, conjugations) |
particle-entry | Requirements for particle entries (predicate lists, contrasts) |
other-entries | Requirements for nouns, counters, adverbs, expressions |
example-sentences | Requirements for example sentences (counts, length, vocabulary) |
vocabulary-notes | Formatting guidelines for notes field |
vocabulary-tiers | Guidelines for the three-tier vocabulary system |
cross-reference-entry | Guidelines for adding cross-references between entries |
find-candidates | Guidelines for finding new candidate words to add |
revise-entries | Checklist for revising existing entries to v2 standards |
polish-entries | Systematic review and improvement of entries |
delete-entry | Guidelines for safely deleting entries |
resolve-duplicates | Guidelines for identifying and resolving duplicate entries |
kanji-index | Guidelines for maintaining the kanji index feature |
inline-word-links | Guidelines for adding inline cross-reference links to examples |
Skills are automatically loaded when Claude determines they're relevant to the current task.
make validatemake build (or make quick for incremental)Based on multi-model LLM evaluation, these are HIGH PRIORITY for all entries:
For Verbs:
For Particles:
For All Entries:
Entries are organized by numeric ID ranges (500 entries per directory):
| ID Range | Directory |
|---|---|
| 00000-00499 | entries/00000/ |
| 00500-00999 | entries/00500/ |
| 01000-01499 | entries/01000/ |
| ... | ... |
Use python3 build/get_entry_path.py <reading> <entry_id> to get the correct path for new entries.
Creative Commons Zero v1.0 Universal
Email suggestions for enhancements to Tom Gally.
Hacker News (1)
Python
90.8%
Shell
3.8%
CSS
3.1%
JavaScript
2.2%