diff --git a/.gitignore b/.gitignore index b009cb5..4c7985d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ webcache/ output/ notes/ +monokakido/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 88d0f2b..3cf5ddd 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,19 @@ Jitenbot is a program for scraping Japanese dictionary websites and compiling the scraped data into compact dictionary file formats. ### Supported Dictionaries -* Online - * [国語辞典オンライン](https://kokugo.jitenon.jp/) (Jitenon Kokugo) - * [四字熟語辞典オンライン](https://yoji.jitenon.jp/) (Jitenon Yoji) - * [故事・ことわざ・慣用句オンライン](https://kotowaza.jitenon.jp/) (Jitenon Kotowaza) -* Offline - * [新明解国語辞典 第八版](https://www.monokakido.jp/ja/dictionaries/smk8/index.html) (Shinmeikai 8e) - * [大辞林 第四版](https://www.monokakido.jp/ja/dictionaries/daijirin2/index.html) (Daijirin 4e) +* Web Dictionaries + * [国語辞典オンライン](https://kokugo.jitenon.jp/) (`jitenon-kokugo`) + * [四字熟語辞典オンライン](https://yoji.jitenon.jp/) (`jitenon-yoji`) + * [故事・ことわざ・慣用句オンライン](https://kotowaza.jitenon.jp/) (`jitenon-kotowaza`) +* Monokakido + * [新明解国語辞典 第八版](https://www.monokakido.jp/ja/dictionaries/smk8/index.html) (`smk8`) + * [大辞林 第四版](https://www.monokakido.jp/ja/dictionaries/daijirin2/index.html) (`daijirin2`) + * [三省堂国語辞典 第八版](https://www.monokakido.jp/ja/dictionaries/sankoku8/index.html) (`sankoku8`) ### Supported Output Formats * [Yomichan](https://github.com/foosoft/yomichan) +* MDict (.MDX & .MDD) # Examples @@ -47,26 +49,50 @@ compiling the scraped data into compact dictionary file formats. ![daijirin2](https://user-images.githubusercontent.com/8003332/235578700-9dbf4fb0-0154-48b5-817c-8fe75e442afc.png) +
+ Sanseidō 8e (print | yomichan) + + ![sankoku8](https://github.com/stephenmk/jitenbot/assets/8003332/0358b3fc-71fb-4557-977c-1976a12229ec) +
+ +
+ Various (GoldenDict) + + ![goldendict](https://github.com/stephenmk/jitenbot/assets/8003332/76104cbf-845d-4e18-8b78-3ee3ebbf4da6) +
+ # Usage ``` -usage: jitenbot [-h] [-p PAGE_DIR] [-i IMAGE_DIR] - {jitenon-kokugo,jitenon-yoji,jitenon-kotowaza,smk8,daijirin2} +usage: jitenbot [-h] [-p PAGE_DIR] [-m MEDIA_DIR] [-i MDICT_ICON] + [--no-mdict-export] [--no-yomichan-export] + [--validate-yomichan-terms] + {jitenon-kokugo,jitenon-yoji,jitenon-kotowaza,smk8,daijirin2,sankoku8} Convert Japanese dictionary files to new formats. positional arguments: - {jitenon-kokugo,jitenon-yoji,jitenon-kotowaza,smk8,daijirin2} + {jitenon-kokugo,jitenon-yoji,jitenon-kotowaza,smk8,daijirin2,sankoku8} name of dictionary to convert options: -h, --help show this help message and exit -p PAGE_DIR, --page-dir PAGE_DIR path to directory containing XML page files - -i IMAGE_DIR, --image-dir IMAGE_DIR - path to directory containing image folders (gaiji, - graphics, etc.) + -m MEDIA_DIR, --media-dir MEDIA_DIR + path to directory containing media folders (gaiji, + graphics, audio, etc.) + -i MDICT_ICON, --mdict-icon MDICT_ICON + path to icon file to be used with MDict + --no-mdict-export skip export of dictionary data to MDict format + --no-yomichan-export skip export of dictionary data to Yomichan format + --validate-yomichan-terms + validate JSON structure of exported Yomichan + dictionary terms + +See README.md for details regarding media directory structures + ``` -### Online Targets +### Web Targets Jitenbot will scrape the target website and save the pages to the [user cache directory](https://pypi.org/project/platformdirs/). As a courtesy to the website owners, jitenbot is configured to pause for 10 seconds between each page request. Consequently, a complete crawl of a target website may take several days. @@ -74,9 +100,113 @@ a complete crawl of a target website may take several days. HTTP request headers (user agent string, etc.) may be customized by editing the `config.json` file created in the [user config directory](https://pypi.org/project/platformdirs/). -### Offline Targets -Page data and image data must be procured by the user -and passed to jitenbot via the appropriate command line flags. +### Monokakido Targets +These digital dictionaries are available for purchase through the [Monokakido Dictionaries app](https://www.monokakido.jp/ja/dictionaries/app/) on MacOS/iOS. Under ideal circumstances, Jitenbot would be able to automatically fetch all the data it needs from this app's data directory[^1] on your system. In its current state of development, Jitenbot unfortunately requires you to find and assemble the necessary data yourself. The files must be organized into a particular folder structure (defined below) and then passed to Jitenbot via the corresponding command line arguments. + +Some of the folders in the app's data directory[^1] contain encoded files that must be unencoded using [golddranks' monokakido tool](https://github.com/golddranks/monokakido/). These folders are indicated by a reference mark (※) in the notes below. + +[^1]: `/Library/Application Support/AppStoreContent/jp.monokakido.Dictionaries/Products/` + +
+ smk8 files + +Since Yomichan does not support audio files from imported dictionaries, the `audio/` directory may be omitted to save filesize space in the output ZIP file if desired. + +``` +. +├── media +│   ├── audio (※) +│   │   ├── 00001.aac +│   │   ├── 00002.aac +│   │   ├── 00003.aac +│   │   ├── ... +│   │   └── 82682.aac +│   ├── Audio.png +│   └── gaiji +│   ├── 1d110.svg +│   ├── 1d15d.svg +│   ├── 1d15e.svg +│   ├── ... +│   └── xbunnoa.svg +└── pages (※) + ├── 0000000000.xml + ├── 0000000001.xml + ├── 0000000002.xml + ├── ... + └── 0000064581.xml +``` +
+ +
+ daijirin2 files + +The `graphics/` directory may be omitted to save space if desired. + +``` +. +├── media +│   ├── gaiji +│   │   ├── 1D10B.svg +│   │   ├── 1D110.svg +│   │   ├── 1D12A.svg +│   │   ├── ... +│   │   └── vectorOB.svg +│   └── graphics (※) +│   ├── 3djr_0002.png +│   ├── 3djr_0004.png +│   ├── 3djr_0005.png +│   ├── ... +│   └── 4djr_yahazu.png +└── pages (※) + ├── 0000000001.xml + ├── 0000000002.xml + ├── 0000000003.xml + ├── ... + └── 0000182633.xml +``` +
+ +
+ sankoku8 files + +``` +. +├── media +│   ├── graphics +│   │   ├── 000chouchou.png +│   │   ├── ... +│   │   └── 888udatsu.png +│   ├── svg-accent +│   │   ├── アクセント.svg +│   │   └── 平板.svg +│   ├── svg-frac +│   │   ├── frac-1-2.svg +│   │   ├── ... +│   │   └── frac-a-b.svg +│   ├── svg-gaiji +│   │   ├── aiaigasa.svg +│   │   ├── ... +│   │   └── 異体字_西.svg +│   ├── svg-intonation +│   │   ├── 上昇下降.svg +│   │   ├── ... +│   │   └── 長.svg +│   ├── svg-logo +│   │   ├── denshi.svg +│   │   ├── ... +│   │   └── 重要語.svg +│   └── svg-special +│   └── 区切り線.svg +└── pages (※) + ├── 0000000001.xml + ├── ... + └── 0000065457.xml +``` +
# Attribution `Adobe-Japan1_sequences.txt` is provided by [The Adobe-Japan1-7 Character Collection](https://github.com/adobe-type-tools/Adobe-Japan1). + +The Yomichan term-bank schema definition `dictionary-term-bank-v3-schema.json` is provided by the [Yomichan](https://github.com/foosoft/yomichan) project. + +Many thanks to [epistularum](https://github.com/epistularum) for providing thoughtful feedback regarding the implementation of the MDict export functionality. diff --git a/TODO.md b/TODO.md index 30c860d..4aaadee 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,14 @@ ### Todo +- [x] Add factory classes to reduce the amount of class import statements +- [x] Add dynamic import functionality to factory classes to reduce boilerplate +- [x] Support exporting to MDict (.MDX) dictionary format +- [x] Validate JSON schema of Yomichan terms during export +- [ ] Add support for monokakido search keys from index files +- [ ] Delete unneeded media from temp build directory before final export - [ ] Add test suite - [ ] Add documentation (docstrings, etc.) -- [ ] Validate JSON schema of Yomichan terms during export -- [ ] Add factory classes to reduce the amount of class import statements - [ ] Add build scripts for producing program binaries -- [ ] Support exporting to MDict (.MDX) dictionary format - [ ] Validate scraped webpages after downloading - [ ] Log non-fatal failures to a log file instead of raising exceptions - [ ] Support more dictionary websites @@ -13,7 +16,7 @@ - [ ] [Yoji-Jukugo.com](https://yoji-jukugo.com/) - [ ] [実用日本語表現辞典](https://www.weblio.jp/cat/dictionary/jtnhj) - [ ] Support more Monokakido dictionaries - - [ ] 三省堂国語辞典 第8版 (SANKOKU8) + - [x] 三省堂国語辞典 第8版 (SANKOKU8) - [ ] 精選版 日本国語大辞典 (NDS) - [ ] 大辞泉 第2版 (DAIJISEN2) - [ ] 明鏡国語辞典 第3版 (MK3) diff --git a/bot/crawlers/base/crawler.py b/bot/crawlers/base/crawler.py new file mode 100644 index 0000000..bbbcb9b --- /dev/null +++ b/bot/crawlers/base/crawler.py @@ -0,0 +1,54 @@ +import re +from abc import ABC, abstractmethod + +from bot.factory import new_entry +from bot.factory import new_yomichan_exporter +from bot.factory import new_mdict_exporter + + +class BaseCrawler(ABC): + def __init__(self, target): + self._target = target + self._page_map = {} + self._entries = [] + self._page_id_pattern = None + + @abstractmethod + def collect_pages(self, page_dir): + raise NotImplementedError + + def read_pages(self): + pages_len = len(self._page_map) + items = self._page_map.items() + for idx, (page_id, page_path) in enumerate(items): + update = f"\tReading page {idx+1}/{pages_len}" + print(update, end='\r', flush=True) + entry = new_entry(self._target, page_id) + with open(page_path, "r", encoding="utf-8") as f: + page = f.read() + try: + entry.set_page(page) + except ValueError as err: + print(err) + print("Try deleting and redownloading file:") + print(f"\t{page_path}\n") + continue + self._entries.append(entry) + print() + + def make_yomichan_dictionary(self, media_dir, validate): + exporter = new_yomichan_exporter(self._target) + exporter.export(self._entries, media_dir, validate) + + def make_mdict_dictionary(self, media_dir, icon_file): + exporter = new_mdict_exporter(self._target) + exporter.export(self._entries, media_dir, icon_file) + + def _parse_page_id(self, page_link): + m = re.search(self._page_id_pattern, page_link) + if m is None: + return None + page_id = int(m.group(1)) + if page_id in self._page_map: + return None + return page_id diff --git a/bot/crawlers/base/jitenon.py b/bot/crawlers/base/jitenon.py new file mode 100644 index 0000000..49e4626 --- /dev/null +++ b/bot/crawlers/base/jitenon.py @@ -0,0 +1,30 @@ +from bs4 import BeautifulSoup + +from bot.time import timestamp +from bot.crawlers.scrapers.jitenon import Jitenon as JitenonScraper +from bot.crawlers.base.crawler import BaseCrawler + + +class JitenonCrawler(BaseCrawler): + def __init__(self, target): + super().__init__(target) + self._gojuon_url = None + + def collect_pages(self, page_dir): + print(f"{timestamp()} Scraping {self._gojuon_url}") + jitenon = JitenonScraper() + gojuon_doc, _ = jitenon.scrape(self._gojuon_url) + gojuon_soup = BeautifulSoup(gojuon_doc, features="html.parser") + for gojuon_a in gojuon_soup.select(".kana_area a", href=True): + gojuon_href = gojuon_a['href'] + kana_doc, _ = jitenon.scrape(gojuon_href) + kana_soup = BeautifulSoup(kana_doc, features="html.parser") + for kana_a in kana_soup.select(".word_box a", href=True): + page_link = kana_a['href'] + page_id = self._parse_page_id(page_link) + if page_id is None: + continue + _, page_path = jitenon.scrape(page_link) + self._page_map[page_id] = page_path + pages_len = len(self._page_map) + print(f"\n{timestamp()} Found {pages_len} entry pages") diff --git a/bot/crawlers/base/monokakido.py b/bot/crawlers/base/monokakido.py new file mode 100644 index 0000000..ca98545 --- /dev/null +++ b/bot/crawlers/base/monokakido.py @@ -0,0 +1,20 @@ +import os +from bot.time import timestamp +from bot.crawlers.base.crawler import BaseCrawler + + +class MonokakidoCrawler(BaseCrawler): + def __init__(self, target): + super().__init__(target) + self._page_id_pattern = r"^([0-9]+)\.xml$" + + def collect_pages(self, page_dir): + print(f"{timestamp()} Searching for page files in `{page_dir}`") + for pagefile in os.listdir(page_dir): + page_id = self._parse_page_id(pagefile) + if page_id is None or page_id == 0: + continue + path = os.path.join(page_dir, pagefile) + self._page_map[page_id] = path + pages_len = len(self._page_map) + print(f"{timestamp()} Found {pages_len} page files for processing") diff --git a/bot/crawlers/crawlers.py b/bot/crawlers/crawlers.py deleted file mode 100644 index c7bf8ea..0000000 --- a/bot/crawlers/crawlers.py +++ /dev/null @@ -1,151 +0,0 @@ -import os -import re -from abc import ABC, abstractmethod -from bs4 import BeautifulSoup - -import bot.scraper as Scraper -from bot.entries.factory import new_entry -from bot.yomichan.exporters.factory import new_exporter - - -class Crawler(ABC): - def __init__(self, target): - self._target = target - self._page_map = {} - self._entries = [] - self._page_id_pattern = None - - @abstractmethod - def collect_pages(self, page_dir): - pass - - def read_pages(self): - pages_len = len(self._page_map) - items = self._page_map.items() - for idx, (page_id, page_path) in enumerate(items): - update = f"Reading page {idx+1}/{pages_len}" - print(update, end='\r', flush=True) - entry = new_entry(self._target, page_id) - with open(page_path, "r", encoding="utf-8") as f: - page = f.read() - try: - entry.set_page(page) - except ValueError as err: - print(err) - print("Try deleting and redownloading file:") - print(f"\t{page_path}\n") - continue - self._entries.append(entry) - print() - - def make_yomichan_dictionary(self, image_dir): - exporter = new_exporter(self._target) - exporter.export(self._entries, image_dir) - - def _parse_page_id(self, page_link): - m = re.search(self._page_id_pattern, page_link) - if m is None: - return None - page_id = int(m.group(1)) - if page_id in self._page_map: - return None - return page_id - - -class JitenonKokugoCrawler(Crawler): - def __init__(self, target): - super().__init__(target) - self._gojuon_url = "https://kokugo.jitenon.jp/cat/gojuonindex.php" - self._page_id_pattern = r"word/p([0-9]+)$" - - def collect_pages(self, page_dir): - jitenon = Scraper.Jitenon() - gojuon_doc, _ = jitenon.scrape(self._gojuon_url) - gojuon_soup = BeautifulSoup(gojuon_doc, features="html.parser") - for gojuon_a in gojuon_soup.select(".kana_area a", href=True): - gojuon_href = gojuon_a['href'] - max_kana_page = 1 - current_kana_page = 1 - while current_kana_page <= max_kana_page: - kana_doc, _ = jitenon.scrape(f"{gojuon_href}&page={current_kana_page}") - current_kana_page += 1 - kana_soup = BeautifulSoup(kana_doc, features="html.parser") - page_total = kana_soup.find(class_="page_total").text - m = re.search(r"全([0-9]+)件", page_total) - if m: - max_kana_page = int(m.group(1)) - for kana_a in kana_soup.select(".word_box a", href=True): - page_link = kana_a['href'] - page_id = self._parse_page_id(page_link) - if page_id is None: - continue - _, page_path = jitenon.scrape(page_link) - self._page_map[page_id] = page_path - pages_len = len(self._page_map) - print(f"Finished scraping {pages_len} pages") - - -class _JitenonCrawler(Crawler): - def __init__(self, target): - super().__init__(target) - self._gojuon_url = None - - def collect_pages(self, page_dir): - print("Scraping jitenon.jp") - jitenon = Scraper.Jitenon() - gojuon_doc, _ = jitenon.scrape(self._gojuon_url) - gojuon_soup = BeautifulSoup(gojuon_doc, features="html.parser") - for gojuon_a in gojuon_soup.select(".kana_area a", href=True): - gojuon_href = gojuon_a['href'] - kana_doc, _ = jitenon.scrape(gojuon_href) - kana_soup = BeautifulSoup(kana_doc, features="html.parser") - for kana_a in kana_soup.select(".word_box a", href=True): - page_link = kana_a['href'] - page_id = self._parse_page_id(page_link) - if page_id is None: - continue - _, page_path = jitenon.scrape(page_link) - self._page_map[page_id] = page_path - pages_len = len(self._page_map) - print(f"Finished scraping {pages_len} pages") - - -class JitenonYojiCrawler(_JitenonCrawler): - def __init__(self, target): - super().__init__(target) - self._gojuon_url = "https://yoji.jitenon.jp/cat/gojuon.html" - self._page_id_pattern = r"([0-9]+)\.html$" - - -class JitenonKotowazaCrawler(_JitenonCrawler): - def __init__(self, target): - super().__init__(target) - self._gojuon_url = "https://kotowaza.jitenon.jp/cat/gojuon.php" - self._page_id_pattern = r"([0-9]+)\.php$" - - -class _MonokakidoCrawler(Crawler): - def __init__(self, target): - super().__init__(target) - self._page_id_pattern = r"^([0-9]+)\.xml$" - - def collect_pages(self, page_dir): - print(f"Searching for page files in `{page_dir}`") - for pagefile in os.listdir(page_dir): - page_id = self._parse_page_id(pagefile) - if page_id is None or page_id == 0: - continue - path = os.path.join(page_dir, pagefile) - self._page_map[page_id] = path - pages_len = len(self._page_map) - print(f"Found {pages_len} page files for processing") - - -class Smk8Crawler(_MonokakidoCrawler): - def __init__(self, target): - super().__init__(target) - - -class Daijirin2Crawler(_MonokakidoCrawler): - def __init__(self, target): - super().__init__(target) diff --git a/bot/crawlers/daijirin2.py b/bot/crawlers/daijirin2.py new file mode 100644 index 0000000..a9c711b --- /dev/null +++ b/bot/crawlers/daijirin2.py @@ -0,0 +1,5 @@ +from bot.crawlers.base.monokakido import MonokakidoCrawler + + +class Crawler(MonokakidoCrawler): + pass diff --git a/bot/crawlers/factory.py b/bot/crawlers/factory.py deleted file mode 100644 index 081567b..0000000 --- a/bot/crawlers/factory.py +++ /dev/null @@ -1,18 +0,0 @@ -from bot.targets import Targets - -from bot.crawlers.crawlers import JitenonKokugoCrawler -from bot.crawlers.crawlers import JitenonYojiCrawler -from bot.crawlers.crawlers import JitenonKotowazaCrawler -from bot.crawlers.crawlers import Smk8Crawler -from bot.crawlers.crawlers import Daijirin2Crawler - - -def new_crawler(target): - crawler_map = { - Targets.JITENON_KOKUGO: JitenonKokugoCrawler, - Targets.JITENON_YOJI: JitenonYojiCrawler, - Targets.JITENON_KOTOWAZA: JitenonKotowazaCrawler, - Targets.SMK8: Smk8Crawler, - Targets.DAIJIRIN2: Daijirin2Crawler, - } - return crawler_map[target](target) diff --git a/bot/crawlers/jitenon_kokugo.py b/bot/crawlers/jitenon_kokugo.py new file mode 100644 index 0000000..e748ea1 --- /dev/null +++ b/bot/crawlers/jitenon_kokugo.py @@ -0,0 +1,40 @@ +import re +from bs4 import BeautifulSoup + +from bot.time import timestamp +from bot.crawlers.base.crawler import BaseCrawler +from bot.crawlers.scrapers.jitenon import Jitenon as JitenonScraper + + +class Crawler(BaseCrawler): + def __init__(self, target): + super().__init__(target) + self._gojuon_url = "https://kokugo.jitenon.jp/cat/gojuonindex.php" + self._page_id_pattern = r"word/p([0-9]+)$" + + def collect_pages(self, page_dir): + print(f"{timestamp()} Scraping {self._gojuon_url}") + jitenon = JitenonScraper() + gojuon_doc, _ = jitenon.scrape(self._gojuon_url) + gojuon_soup = BeautifulSoup(gojuon_doc, features="html.parser") + for gojuon_a in gojuon_soup.select(".kana_area a", href=True): + gojuon_href = gojuon_a['href'] + max_kana_page = 1 + current_kana_page = 1 + while current_kana_page <= max_kana_page: + kana_doc, _ = jitenon.scrape(f"{gojuon_href}&page={current_kana_page}") + current_kana_page += 1 + kana_soup = BeautifulSoup(kana_doc, features="html.parser") + page_total = kana_soup.find(class_="page_total").text + m = re.search(r"全([0-9]+)件", page_total) + if m: + max_kana_page = int(m.group(1)) + for kana_a in kana_soup.select(".word_box a", href=True): + page_link = kana_a['href'] + page_id = self._parse_page_id(page_link) + if page_id is None: + continue + _, page_path = jitenon.scrape(page_link) + self._page_map[page_id] = page_path + pages_len = len(self._page_map) + print(f"\n{timestamp()} Found {pages_len} entry pages") diff --git a/bot/crawlers/jitenon_kotowaza.py b/bot/crawlers/jitenon_kotowaza.py new file mode 100644 index 0000000..693fa52 --- /dev/null +++ b/bot/crawlers/jitenon_kotowaza.py @@ -0,0 +1,8 @@ +from bot.crawlers.base.jitenon import JitenonCrawler + + +class Crawler(JitenonCrawler): + def __init__(self, target): + super().__init__(target) + self._gojuon_url = "https://kotowaza.jitenon.jp/cat/gojuon.php" + self._page_id_pattern = r"([0-9]+)\.php$" diff --git a/bot/crawlers/jitenon_yoji.py b/bot/crawlers/jitenon_yoji.py new file mode 100644 index 0000000..5b89875 --- /dev/null +++ b/bot/crawlers/jitenon_yoji.py @@ -0,0 +1,8 @@ +from bot.crawlers.base.jitenon import JitenonCrawler + + +class Crawler(JitenonCrawler): + def __init__(self, target): + super().__init__(target) + self._gojuon_url = "https://yoji.jitenon.jp/cat/gojuon.html" + self._page_id_pattern = r"([0-9]+)\.html$" diff --git a/bot/crawlers/sankoku8.py b/bot/crawlers/sankoku8.py new file mode 100644 index 0000000..a9c711b --- /dev/null +++ b/bot/crawlers/sankoku8.py @@ -0,0 +1,5 @@ +from bot.crawlers.base.monokakido import MonokakidoCrawler + + +class Crawler(MonokakidoCrawler): + pass diff --git a/bot/crawlers/scrapers/jitenon.py b/bot/crawlers/scrapers/jitenon.py new file mode 100644 index 0000000..e4163d9 --- /dev/null +++ b/bot/crawlers/scrapers/jitenon.py @@ -0,0 +1,10 @@ +import re +from bot.crawlers.scrapers.scraper import BaseScraper + + +class Jitenon(BaseScraper): + def _get_netloc_re(self): + domain = r"jitenon\.jp" + pattern = r"^(?:([A-Za-z0-9.\-]+)\.)?" + domain + r"$" + netloc_re = re.compile(pattern) + return netloc_re diff --git a/bot/scraper.py b/bot/crawlers/scrapers/scraper.py similarity index 82% rename from bot/scraper.py rename to bot/crawlers/scrapers/scraper.py index 577f602..eeb9534 100644 --- a/bot/scraper.py +++ b/bot/crawlers/scrapers/scraper.py @@ -1,24 +1,28 @@ import time -import requests import re import os import hashlib +import random +import math from datetime import datetime -from pathlib import Path - -from platformdirs import user_cache_dir from urllib.parse import urlparse +from pathlib import Path +from abc import ABC, abstractmethod + +import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry +from platformdirs import user_cache_dir +from bot.time import timestamp from bot.data import load_config -class Scraper(): +class BaseScraper(ABC): def __init__(self): + self.cache_count = 0 self._config = load_config() - pattern = r"^(?:([A-Za-z0-9.\-]+)\.)?" + self.domain + r"$" - self.netloc_re = re.compile(pattern) + self.netloc_re = self._get_netloc_re() self.__set_session() def scrape(self, urlstring): @@ -31,9 +35,14 @@ class Scraper(): with open(cache_path, "w", encoding="utf-8") as f: f.write(html) else: - print("Discovering cached files...", end='\r', flush=True) + self.cache_count += 1 + print(f"\tDiscovering cached file {self.cache_count}", end='\r', flush=True) return html, cache_path + @abstractmethod + def _get_netloc_re(self): + raise NotImplementedError + def __set_session(self): retry_strategy = Retry( total=3, @@ -87,21 +96,14 @@ class Scraper(): def __get(self, urlstring): delay = 10 time.sleep(delay) - now = datetime.now().strftime("%H:%M:%S") - print(f"{now} scraping {urlstring} ...", end='') + print(f"{timestamp()} Scraping {urlstring} ...", end='') try: response = self.session.get(urlstring, timeout=10) - print("OK") + print(f"{timestamp()} OK") return response.text - except Exception: - print("failed") - print("resetting session and trying again") + except Exception as ex: + print(f"\tFailed: {str(ex)}") + print(f"{timestamp()} Resetting session and trying again") self.__set_session() response = self.session.get(urlstring, timeout=10) return response.text - - -class Jitenon(Scraper): - def __init__(self): - self.domain = r"jitenon\.jp" - super().__init__() diff --git a/bot/crawlers/smk8.py b/bot/crawlers/smk8.py new file mode 100644 index 0000000..a9c711b --- /dev/null +++ b/bot/crawlers/smk8.py @@ -0,0 +1,5 @@ +from bot.crawlers.base.monokakido import MonokakidoCrawler + + +class Crawler(MonokakidoCrawler): + pass diff --git a/bot/data.py b/bot/data.py index 5d68769..95349db 100644 --- a/bot/data.py +++ b/bot/data.py @@ -37,14 +37,16 @@ def load_config(): @cache def load_yomichan_inflection_categories(): - file_name = "yomichan_inflection_categories.json" + file_name = os.path.join( + "yomichan", "inflection_categories.json") data = __load_json(file_name) return data @cache def load_yomichan_metadata(): - file_name = "yomichan_metadata.json" + file_name = os.path.join( + "yomichan", "index.json") data = __load_json(file_name) return data @@ -53,31 +55,21 @@ def load_yomichan_metadata(): def load_variant_kanji(): def loader(data, row): data[row[0]] = row[1] - file_name = "variant_kanji.csv" + file_name = os.path.join( + "entries", "variant_kanji.csv") data = {} __load_csv(file_name, loader, data) return data @cache -def load_smk8_phrase_readings(): +def load_phrase_readings(target): def loader(data, row): entry_id = (int(row[0]), int(row[1])) reading = row[2] data[entry_id] = reading - file_name = os.path.join("smk8", "phrase_readings.csv") - data = {} - __load_csv(file_name, loader, data) - return data - - -@cache -def load_daijirin2_phrase_readings(): - def loader(data, row): - entry_id = (int(row[0]), int(row[1])) - reading = row[2] - data[entry_id] = reading - file_name = os.path.join("daijirin2", "phrase_readings.csv") + file_name = os.path.join( + "entries", target.value, "phrase_readings.csv") data = {} __load_csv(file_name, loader, data) return data @@ -92,22 +84,33 @@ def load_daijirin2_kana_abbreviations(): if abbr.strip() != "": abbreviations.append(abbr) data[entry_id] = abbreviations - file_name = os.path.join("daijirin2", "kana_abbreviations.csv") + file_name = os.path.join( + "entries", "daijirin2", "kana_abbreviations.csv") data = {} __load_csv(file_name, loader, data) return data @cache -def load_smk8_yomichan_name_conversion(): - file_name = os.path.join("smk8", "yomichan_name_conversion.json") +def load_yomichan_name_conversion(target): + file_name = os.path.join( + "yomichan", "name_conversion", f"{target.value}.json") data = __load_json(file_name) return data @cache -def load_daijirin2_yomichan_name_conversion(): - file_name = os.path.join("daijirin2", "yomichan_name_conversion.json") +def load_yomichan_term_schema(): + file_name = os.path.join( + "yomichan", "dictionary-term-bank-v3-schema.json") + schema = __load_json(file_name) + return schema + + +@cache +def load_mdict_name_conversion(target): + file_name = os.path.join( + "mdict", "name_conversion", f"{target.value}.json") data = __load_json(file_name) return data @@ -131,7 +134,8 @@ def __load_adobe_glyphs(): data[code].append(character) else: data[code] = [character] - file_name = os.path.join("adobe", "Adobe-Japan1_sequences.txt") + file_name = os.path.join( + "entries", "adobe", "Adobe-Japan1_sequences.txt") data = {} __load_csv(file_name, loader, data, delim=';') return data @@ -139,7 +143,8 @@ def __load_adobe_glyphs(): @cache def __load_override_adobe_glyphs(): - file_name = os.path.join("adobe", "override_glyphs.json") + file_name = os.path.join( + "entries", "adobe", "override_glyphs.json") json_data = __load_json(file_name) data = {} for key, val in json_data.items(): diff --git a/bot/entries/base/entry.py b/bot/entries/base/entry.py new file mode 100644 index 0000000..60d4f16 --- /dev/null +++ b/bot/entries/base/entry.py @@ -0,0 +1,68 @@ +from abc import ABC, abstractmethod + + +class Entry(ABC): + ID_TO_ENTRY = {} + SUBENTRY_ID_TO_ENTRY_ID = {} + + def __init__(self, target, entry_id): + if entry_id not in self.ID_TO_ENTRY: + self.ID_TO_ENTRY[entry_id] = self + else: + raise Exception(f"Duplicate entry ID: {entry_id}") + self.target = target + self.entry_id = entry_id + self._page = None + self._headwords = None + self._part_of_speech_tags = None + + @abstractmethod + def get_global_identifier(self): + raise NotImplementedError + + @abstractmethod + def set_page(self, page): + raise NotImplementedError + + @abstractmethod + def get_page_soup(self): + raise NotImplementedError + + def get_headwords(self): + if self._headwords is not None: + return self._headwords + headwords = self._get_headwords() + self._add_variant_expressions(headwords) + self._headwords = headwords + return headwords + + @abstractmethod + def _get_headwords(self): + raise NotImplementedError + + @abstractmethod + def _add_variant_expressions(self, headwords): + raise NotImplementedError + + @abstractmethod + def get_part_of_speech_tags(self): + raise NotImplementedError + + def get_parent(self): + if self.entry_id in self.SUBENTRY_ID_TO_ENTRY_ID: + parent_id = self.SUBENTRY_ID_TO_ENTRY_ID[self.entry_id] + parent = self.ID_TO_ENTRY[parent_id] + else: + parent = None + return parent + + def get_first_expression(self): + headwords = self.get_headwords() + expressions = next(iter(headwords.values())) + expression = expressions[0] + return expression + + def get_first_reading(self): + headwords = self.get_headwords() + reading = next(iter(headwords.keys())) + return reading diff --git a/bot/entries/expressions.py b/bot/entries/base/expressions.py similarity index 63% rename from bot/entries/expressions.py rename to bot/entries/base/expressions.py index 687a325..8049a99 100644 --- a/bot/entries/expressions.py +++ b/bot/entries/base/expressions.py @@ -31,11 +31,14 @@ def add_fullwidth(expressions): def add_variant_kanji(expressions): variant_kanji = load_variant_kanji() - for old_kanji, new_kanji in variant_kanji.items(): + for kyuuji, shinji in variant_kanji.items(): new_exps = [] for expression in expressions: - if old_kanji in expression: - new_exp = expression.replace(old_kanji, new_kanji) + if kyuuji in expression: + new_exp = expression.replace(kyuuji, shinji) + new_exps.append(new_exp) + if shinji in expression: + new_exp = expression.replace(shinji, kyuuji) new_exps.append(new_exp) for new_exp in new_exps: if new_exp not in expressions: @@ -85,40 +88,3 @@ def expand_abbreviation_list(expressions): if new_exp not in new_exps: new_exps.append(new_exp) return new_exps - - -def expand_smk_alternatives(text): - """Return a list of strings described by △ notation.""" - m = re.search(r"△([^(]+)(([^(]+))", text) - if m is None: - return [text] - alt_parts = [m.group(1)] - for alt_part in m.group(2).split("・"): - alt_parts.append(alt_part) - alts = [] - for alt_part in alt_parts: - alt_exp = re.sub(r"△[^(]+([^(]+)", alt_part, text) - alts.append(alt_exp) - return alts - - -def expand_daijirin_alternatives(text): - """Return a list of strings described by = notation.""" - group_pattern = r"([^=]+)(=([^(]+)(=([^(]+)))?" - groups = re.findall(group_pattern, text) - expressions = [""] - for group in groups: - new_exps = [] - for expression in expressions: - new_exps.append(expression + group[0]) - expressions = new_exps.copy() - if group[1] == "": - continue - new_exps = [] - for expression in expressions: - new_exps.append(expression + group[2]) - for expression in expressions: - for alt in group[3].split("・"): - new_exps.append(expression + alt) - expressions = new_exps.copy() - return expressions diff --git a/bot/entries/base/jitenon_entry.py b/bot/entries/base/jitenon_entry.py new file mode 100644 index 0000000..43d6005 --- /dev/null +++ b/bot/entries/base/jitenon_entry.py @@ -0,0 +1,142 @@ +import re +from abc import abstractmethod +from datetime import datetime, date +from bs4 import BeautifulSoup + +from bot.entries.base.entry import Entry +import bot.entries.base.expressions as Expressions + + +class JitenonEntry(Entry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.expression = "" + self.yomikata = "" + self.definition = "" + self.other_forms = [] + self.modified_date = date(1970, 1, 1) + self.attribution = "" + + def get_global_identifier(self): + return f"@{self.target.value}-{format(self.entry_id, '06')}" + + def set_page(self, page): + soup = BeautifulSoup(page, features="html5lib") + self.__set_modified_date(page) + self.__set_attribution(soup) + table = soup.find(class_="kanjirighttb") + if table is None: + raise ValueError("Error: table data not found in page.") + rows = table.find("tbody").find_all("tr") + colname = "" + for row in rows: + colname = row.th.text if row.th is not None else colname + colval = self.__clean_text(row.td.text) + self.__set_column(colname, colval) + self._page = table.decode() + + def get_page_soup(self): + soup = BeautifulSoup(self._page, "html5lib") + return soup + + def get_part_of_speech_tags(self): + # Jitenon doesn't have any + return [] + + def _get_headwords(self): + headwords = {} + for reading in self._get_readings(): + headwords[reading] = [self.expression] + other_form_headwords = self._other_form_headwords() + for reading, expressions in other_form_headwords.items(): + if reading not in headwords: + headwords[reading] = [] + for expression in expressions: + if expression not in headwords[reading]: + headwords[reading].append(expression) + return headwords + + @abstractmethod + def _get_column_map(self): + raise NotImplementedError + + def __set_modified_date(self, page): + m = re.search(r"\"dateModified\": \"(\d{4}-\d{2}-\d{2})", page) + if m is None: + return + modified_date = datetime.strptime(m.group(1), '%Y-%m-%d').date() + self.modified_date = modified_date + + def __set_attribution(self, soup): + attribution = soup.find(class_="copyright") + if attribution is not None: + self.attribution = soup.find(class_="copyright").text + else: + self.attribution = "" + + def __set_column(self, colname, colval): + column_map = self._get_column_map() + attr_name = column_map[colname] + attr_value = getattr(self, attr_name) + if isinstance(attr_value, str): + setattr(self, attr_name, colval) + elif isinstance(attr_value, list): + if len(attr_value) == 0: + setattr(self, attr_name, [colval]) + else: + attr_value.append(colval) + + def _get_readings(self): + yomikata = self.yomikata + m = re.search(r"^[ぁ-ヿ、]+$", yomikata) + if m: + return [yomikata] + m = re.search(r"^([ぁ-ヿ、]+)※", yomikata) + if m: + return [m.group(1)] + m = re.search(r"^[ぁ-ヿ、]+([ぁ-ヿ、])[ぁ-ヿ、]+$", yomikata) + if m: + return Expressions.expand_abbreviation(yomikata) + m = re.search(r"^([ぁ-ヿ、]+)(([ぁ-ヿ/\s、]+))$", yomikata) + if m: + yomikatas = [m.group(1)] + alts = m.group(2).split("/") + for alt in alts: + yomikatas.append(alt.strip()) + return yomikatas + print(f"Invalid 読み方 format: {self.yomikata}\n{self}\n") + return [""] + + def _other_form_headwords(self): + other_form_headwords = {} + for val in self.other_forms: + m = re.search(r"^([^(]+)(([ぁ-ヿ、]+))$", val) + if not m: + print(f"Invalid 異形 format: {val}\n{self}\n") + continue + expression = m.group(1) + reading = m.group(2) + if reading not in other_form_headwords: + other_form_headwords[reading] = [] + if expression not in other_form_headwords[reading]: + other_form_headwords[reading].append(expression) + return other_form_headwords + + @staticmethod + def __clean_text(text): + text = text.replace("\n", "") + text = text.replace(",", "、") + text = text.replace(" ", "") + text = text.strip() + return text + + def __str__(self): + column_map = self._get_column_map() + colvals = [str(self.entry_id)] + for attr_name in column_map.values(): + attr_val = getattr(self, attr_name) + if isinstance(attr_val, str): + colvals.append(attr_val) + elif isinstance(attr_val, list): + colvals.append(";".join(attr_val)) + return ",".join(colvals) diff --git a/bot/entries/base/sanseido_entry.py b/bot/entries/base/sanseido_entry.py new file mode 100644 index 0000000..bb52431 --- /dev/null +++ b/bot/entries/base/sanseido_entry.py @@ -0,0 +1,60 @@ +from abc import abstractmethod +from bs4 import BeautifulSoup + +from bot.entries.base.entry import Entry +import bot.entries.base.expressions as Expressions + + +class SanseidoEntry(Entry): + def set_page(self, page): + page = self._decompose_subentries(page) + self._page = page + + def get_page_soup(self): + soup = BeautifulSoup(self._page, "xml") + return soup + + def get_global_identifier(self): + parent_part = format(self.entry_id[0], '06') + child_part = hex(self.entry_id[1]).lstrip('0x').zfill(4).upper() + return f"@{self.target.value}-{parent_part}-{child_part}" + + def _decompose_subentries(self, page): + soup = BeautifulSoup(page, features="xml") + for x in self._get_subentry_parameters(): + subentry_class, tags, subentry_list = x + for tag in tags: + tag_soup = soup.find(tag) + while tag_soup is not None: + tag_soup.name = "項目" + subentry_id = self.id_string_to_entry_id(tag_soup.attrs["id"]) + self.SUBENTRY_ID_TO_ENTRY_ID[subentry_id] = self.entry_id + subentry = subentry_class(self.target, subentry_id) + page = tag_soup.decode() + subentry.set_page(page) + subentry_list.append(subentry) + tag_soup.decompose() + tag_soup = soup.find(tag) + return soup.decode() + + @abstractmethod + def _get_subentry_parameters(self): + raise NotImplementedError + + def _add_variant_expressions(self, headwords): + for expressions in headwords.values(): + Expressions.add_variant_kanji(expressions) + Expressions.add_fullwidth(expressions) + Expressions.remove_iteration_mark(expressions) + Expressions.add_iteration_mark(expressions) + + @staticmethod + def id_string_to_entry_id(id_string): + parts = id_string.split("-") + if len(parts) == 1: + return (int(parts[0]), 0) + elif len(parts) == 2: + # subentries have a hexadecimal part + return (int(parts[0]), int(parts[1], 16)) + else: + raise Exception(f"Invalid entry ID: {id_string}") diff --git a/bot/entries/daijirin2.py b/bot/entries/daijirin2.py deleted file mode 100644 index 1463442..0000000 --- a/bot/entries/daijirin2.py +++ /dev/null @@ -1,240 +0,0 @@ -import re -from bs4 import BeautifulSoup - -import bot.entries.expressions as Expressions -import bot.soup as Soup -from bot.data import load_daijirin2_phrase_readings -from bot.data import load_daijirin2_kana_abbreviations -from bot.entries.entry import Entry -from bot.entries.daijirin2_preprocess import preprocess_page - - -class _BaseDaijirin2Entry(Entry): - ID_TO_ENTRY = {} - SUBENTRY_ID_TO_ENTRY_ID = {} - - def __init__(self, entry_id): - super().__init__(entry_id) - if entry_id not in self.ID_TO_ENTRY: - self.ID_TO_ENTRY[entry_id] = self - else: - raise Exception(f"Duplicate entry ID: {entry_id}") - self.children = [] - self.phrases = [] - self._kana_abbreviations = load_daijirin2_kana_abbreviations() - - def set_page(self, page): - page = self.__decompose_subentries(page) - self._page = page - - def get_page_soup(self): - soup = BeautifulSoup(self._page, "xml") - return soup - - def get_part_of_speech_tags(self): - if self._part_of_speech_tags is not None: - return self._part_of_speech_tags - self._part_of_speech_tags = [] - soup = self.get_page_soup() - for pos_group in soup.find_all("品詞G"): - if pos_group.parent.name == "大語義": - self._set_part_of_speech_tags(pos_group) - return self._part_of_speech_tags - - def _set_part_of_speech_tags(self, el): - pos_names = ["品詞", "品詞活用", "品詞行", "用法"] - for child in el.children: - if child.name is not None: - self._set_part_of_speech_tags(child) - continue - pos = str(child) - if el.name not in pos_names: - continue - elif pos in ["[", "]"]: - continue - elif pos in self._part_of_speech_tags: - continue - else: - self._part_of_speech_tags.append(pos) - - def get_headwords(self): - if self._headwords is not None: - return self._headwords - self._set_headwords() - self._set_variant_headwords() - return self._headwords - - def _set_regular_headwords(self, soup): - self._fill_alts(soup) - reading = soup.find("見出仮名").text - expressions = [] - for el in soup.find_all("標準表記"): - expression = self._clean_expression(el.text) - if "—" in expression: - kana_abbrs = self._kana_abbreviations[self.entry_id] - for abbr in kana_abbrs: - expression = expression.replace("—", abbr, 1) - expressions.append(expression) - expressions = Expressions.expand_abbreviation_list(expressions) - if len(expressions) == 0: - expressions.append(reading) - self._headwords = {reading: expressions} - - def _set_variant_headwords(self): - for expressions in self._headwords.values(): - Expressions.add_variant_kanji(expressions) - Expressions.add_fullwidth(expressions) - Expressions.remove_iteration_mark(expressions) - Expressions.add_iteration_mark(expressions) - - def __decompose_subentries(self, page): - soup = BeautifulSoup(page, features="xml") - subentry_parameters = [ - [Daijirin2ChildEntry, ["子項目"], self.children], - [Daijirin2PhraseEntry, ["句項目"], self.phrases], - ] - for x in subentry_parameters: - subentry_class, tags, subentry_list = x - for tag in tags: - tag_soup = soup.find(tag) - while tag_soup is not None: - tag_soup.name = "項目" - subentry_id = self.id_string_to_entry_id(tag_soup.attrs["id"]) - self.SUBENTRY_ID_TO_ENTRY_ID[subentry_id] = self.entry_id - subentry = subentry_class(subentry_id) - page = tag_soup.decode() - subentry.set_page(page) - subentry_list.append(subentry) - tag_soup.decompose() - tag_soup = soup.find(tag) - return soup.decode() - - @staticmethod - def id_string_to_entry_id(id_string): - parts = id_string.split("-") - if len(parts) == 1: - return (int(parts[0]), 0) - elif len(parts) == 2: - # subentries have a hexadecimal part - return (int(parts[0]), int(parts[1], 16)) - else: - raise Exception(f"Invalid entry ID: {id_string}") - - @staticmethod - def _delete_unused_nodes(soup): - unused_nodes = [ - "漢字音logo", "活用分節", "連語句活用分節", "語構成", - "表外字マーク", "表外字マーク", "ルビG" - ] - for name in unused_nodes: - Soup.delete_soup_nodes(soup, name) - - @staticmethod - def _clean_expression(expression): - for x in ["〈", "〉", "《", "》", " "]: - expression = expression.replace(x, "") - return expression - - @staticmethod - def _fill_alts(soup): - for gaiji in soup.find_all(class_="gaiji"): - if gaiji.name == "img" and gaiji.has_attr("alt"): - gaiji.name = "span" - gaiji.string = gaiji.attrs["alt"] - - -class Daijirin2Entry(_BaseDaijirin2Entry): - def __init__(self, page_id): - entry_id = (page_id, 0) - super().__init__(entry_id) - - def set_page(self, page): - page = preprocess_page(page) - super().set_page(page) - - def _set_headwords(self): - soup = self.get_page_soup() - self._delete_unused_nodes(soup) - if soup.find("漢字見出") is not None: - self._set_kanji_headwords(soup) - elif soup.find("略語G") is not None: - self._set_acronym_headwords(soup) - else: - self._set_regular_headwords(soup) - - def _set_kanji_headwords(self, soup): - readings = [] - for el in soup.find_all("漢字音"): - hira = Expressions.kata_to_hira(el.text) - readings.append(hira) - if soup.find("漢字音") is None: - readings.append("") - expressions = [] - for el in soup.find_all("漢字見出"): - expressions.append(el.text) - self._headwords = {} - for reading in readings: - self._headwords[reading] = expressions - - def _set_acronym_headwords(self, soup): - expressions = [] - for el in soup.find_all("略語"): - expression_parts = [] - for part in el.find_all(["欧字", "和字"]): - expression_parts.append(part.text) - expression = "".join(expression_parts) - expressions.append(expression) - self._headwords = {"": expressions} - - -class Daijirin2ChildEntry(_BaseDaijirin2Entry): - def __init__(self, entry_id): - super().__init__(entry_id) - - def _set_headwords(self): - soup = self.get_page_soup() - self._delete_unused_nodes(soup) - self._set_regular_headwords(soup) - - -class Daijirin2PhraseEntry(_BaseDaijirin2Entry): - def __init__(self, entry_id): - super().__init__(entry_id) - self.__phrase_readings = load_daijirin2_phrase_readings() - - def get_part_of_speech_tags(self): - # phrases do not contain these tags - return [] - - def _set_headwords(self): - soup = self.get_page_soup() - headwords = {} - expressions = self._find_expressions(soup) - readings = self._find_readings() - for idx, expression in enumerate(expressions): - reading = readings[idx] - if reading in headwords: - headwords[reading].append(expression) - else: - headwords[reading] = [expression] - self._headwords = headwords - - def _find_expressions(self, soup): - self._delete_unused_nodes(soup) - text = soup.find("句表記").text - text = self._clean_expression(text) - alternatives = Expressions.expand_daijirin_alternatives(text) - expressions = [] - for alt in alternatives: - for exp in Expressions.expand_abbreviation(alt): - expressions.append(exp) - return expressions - - def _find_readings(self): - text = self.__phrase_readings[self.entry_id] - alternatives = Expressions.expand_daijirin_alternatives(text) - readings = [] - for alt in alternatives: - for reading in Expressions.expand_abbreviation(alt): - readings.append(reading) - return readings diff --git a/bot/entries/daijirin2/base_entry.py b/bot/entries/daijirin2/base_entry.py new file mode 100644 index 0000000..1113404 --- /dev/null +++ b/bot/entries/daijirin2/base_entry.py @@ -0,0 +1,88 @@ +import bot.soup as Soup +from bot.data import load_daijirin2_kana_abbreviations +from bot.entries.base.sanseido_entry import SanseidoEntry +import bot.entries.base.expressions as Expressions + + +class BaseEntry(SanseidoEntry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.children = [] + self.phrases = [] + self._kana_abbreviations = load_daijirin2_kana_abbreviations() + + def get_part_of_speech_tags(self): + if self._part_of_speech_tags is not None: + return self._part_of_speech_tags + self._part_of_speech_tags = [] + soup = self.get_page_soup() + for pos_group in soup.find_all("品詞G"): + if pos_group.parent.name == "大語義": + self._set_part_of_speech_tags(pos_group) + return self._part_of_speech_tags + + def _set_part_of_speech_tags(self, el): + pos_names = ["品詞", "品詞活用", "品詞行", "用法"] + for child in el.children: + if child.name is not None: + self._set_part_of_speech_tags(child) + continue + pos = str(child) + if el.name not in pos_names: + continue + elif pos in ["[", "]"]: + continue + elif pos in self._part_of_speech_tags: + continue + else: + self._part_of_speech_tags.append(pos) + + def _get_regular_headwords(self, soup): + self._fill_alts(soup) + reading = soup.find("見出仮名").text + expressions = [] + for el in soup.find_all("標準表記"): + expression = self._clean_expression(el.text) + if "—" in expression: + kana_abbrs = self._kana_abbreviations[self.entry_id] + for abbr in kana_abbrs: + expression = expression.replace("—", abbr, 1) + expressions.append(expression) + expressions = Expressions.expand_abbreviation_list(expressions) + if len(expressions) == 0: + expressions.append(reading) + headwords = {reading: expressions} + return headwords + + def _get_subentry_parameters(self): + from bot.entries.daijirin2.child_entry import ChildEntry + from bot.entries.daijirin2.phrase_entry import PhraseEntry + subentry_parameters = [ + [ChildEntry, ["子項目"], self.children], + [PhraseEntry, ["句項目"], self.phrases], + ] + return subentry_parameters + + @staticmethod + def _delete_unused_nodes(soup): + """Remove extra markup elements that appear in the entry + headword line which are not part of the entry headword""" + unused_nodes = [ + "漢字音logo", "活用分節", "連語句活用分節", "語構成", + "表外字マーク", "表外字マーク", "ルビG" + ] + for name in unused_nodes: + Soup.delete_soup_nodes(soup, name) + + @staticmethod + def _clean_expression(expression): + for x in ["〈", "〉", "《", "》", " "]: + expression = expression.replace(x, "") + return expression + + @staticmethod + def _fill_alts(soup): + for gaiji in soup.find_all(class_="gaiji"): + if gaiji.name == "img" and gaiji.has_attr("alt"): + gaiji.name = "span" + gaiji.string = gaiji.attrs["alt"] diff --git a/bot/entries/daijirin2/child_entry.py b/bot/entries/daijirin2/child_entry.py new file mode 100644 index 0000000..42685a0 --- /dev/null +++ b/bot/entries/daijirin2/child_entry.py @@ -0,0 +1,9 @@ +from bot.entries.daijirin2.base_entry import BaseEntry + + +class ChildEntry(BaseEntry): + def _get_headwords(self): + soup = self.get_page_soup() + self._delete_unused_nodes(soup) + headwords = self._get_regular_headwords(soup) + return headwords diff --git a/bot/entries/daijirin2/entry.py b/bot/entries/daijirin2/entry.py new file mode 100644 index 0000000..0b6970f --- /dev/null +++ b/bot/entries/daijirin2/entry.py @@ -0,0 +1,50 @@ +import bot.entries.base.expressions as Expressions +from bot.entries.daijirin2.base_entry import BaseEntry +from bot.entries.daijirin2.preprocess import preprocess_page + + +class Entry(BaseEntry): + def __init__(self, target, page_id): + entry_id = (page_id, 0) + super().__init__(target, entry_id) + + def set_page(self, page): + page = preprocess_page(page) + super().set_page(page) + + def _get_headwords(self): + soup = self.get_page_soup() + self._delete_unused_nodes(soup) + if soup.find("漢字見出") is not None: + headwords = self._get_kanji_headwords(soup) + elif soup.find("略語G") is not None: + headwords = self._get_acronym_headwords(soup) + else: + headwords = self._get_regular_headwords(soup) + return headwords + + def _get_kanji_headwords(self, soup): + readings = [] + for el in soup.find_all("漢字音"): + hira = Expressions.kata_to_hira(el.text) + readings.append(hira) + if soup.find("漢字音") is None: + readings.append("") + expressions = [] + for el in soup.find_all("漢字見出"): + expressions.append(el.text) + headwords = {} + for reading in readings: + headwords[reading] = expressions + return headwords + + def _get_acronym_headwords(self, soup): + expressions = [] + for el in soup.find_all("略語"): + expression_parts = [] + for part in el.find_all(["欧字", "和字"]): + expression_parts.append(part.text) + expression = "".join(expression_parts) + expressions.append(expression) + headwords = {"": expressions} + return headwords diff --git a/bot/entries/daijirin2/phrase_entry.py b/bot/entries/daijirin2/phrase_entry.py new file mode 100644 index 0000000..0470d7d --- /dev/null +++ b/bot/entries/daijirin2/phrase_entry.py @@ -0,0 +1,67 @@ +import re + +import bot.entries.base.expressions as Expressions +from bot.data import load_phrase_readings +from bot.entries.daijirin2.base_entry import BaseEntry + + +class PhraseEntry(BaseEntry): + def get_part_of_speech_tags(self): + # phrases do not contain these tags + return [] + + def _get_headwords(self): + soup = self.get_page_soup() + headwords = {} + expressions = self._find_expressions(soup) + readings = self._find_readings() + for idx, expression in enumerate(expressions): + reading = readings[idx] + if reading in headwords: + headwords[reading].append(expression) + else: + headwords[reading] = [expression] + return headwords + + def _find_expressions(self, soup): + self._delete_unused_nodes(soup) + text = soup.find("句表記").text + text = self._clean_expression(text) + alternatives = parse_phrase(text) + expressions = [] + for alt in alternatives: + for exp in Expressions.expand_abbreviation(alt): + expressions.append(exp) + return expressions + + def _find_readings(self): + phrase_readings = load_phrase_readings(self.target) + text = phrase_readings[self.entry_id] + alternatives = parse_phrase(text) + readings = [] + for alt in alternatives: + for reading in Expressions.expand_abbreviation(alt): + readings.append(reading) + return readings + + +def parse_phrase(text): + """Return a list of strings described by = notation.""" + group_pattern = r"([^=]+)(=([^(]+)(=([^(]+)))?" + groups = re.findall(group_pattern, text) + expressions = [""] + for group in groups: + new_exps = [] + for expression in expressions: + new_exps.append(expression + group[0]) + expressions = new_exps.copy() + if group[1] == "": + continue + new_exps = [] + for expression in expressions: + new_exps.append(expression + group[2]) + for expression in expressions: + for alt in group[3].split("・"): + new_exps.append(expression + alt) + expressions = new_exps.copy() + return expressions diff --git a/bot/entries/daijirin2_preprocess.py b/bot/entries/daijirin2/preprocess.py similarity index 100% rename from bot/entries/daijirin2_preprocess.py rename to bot/entries/daijirin2/preprocess.py diff --git a/bot/entries/entry.py b/bot/entries/entry.py deleted file mode 100644 index 57316f6..0000000 --- a/bot/entries/entry.py +++ /dev/null @@ -1,36 +0,0 @@ -from abc import ABC, abstractmethod - - -class Entry(ABC): - def __init__(self, entry_id): - self.entry_id = entry_id - self._page = None - self._headwords = None - self._part_of_speech_tags = None - - @abstractmethod - def set_page(self, page): - pass - - @abstractmethod - def get_page_soup(self): - pass - - @abstractmethod - def get_headwords(self): - pass - - @abstractmethod - def get_part_of_speech_tags(self): - pass - - def get_first_expression(self): - headwords = self.get_headwords() - expressions = next(iter(headwords.values())) - expression = expressions[0] - return expression - - def get_first_reading(self): - headwords = self.get_headwords() - reading = next(iter(headwords.keys())) - return reading diff --git a/bot/entries/factory.py b/bot/entries/factory.py deleted file mode 100644 index 23ca066..0000000 --- a/bot/entries/factory.py +++ /dev/null @@ -1,18 +0,0 @@ -from bot.targets import Targets - -from bot.entries.jitenon import JitenonKokugoEntry -from bot.entries.jitenon import JitenonYojiEntry -from bot.entries.jitenon import JitenonKotowazaEntry -from bot.entries.smk8 import Smk8Entry -from bot.entries.daijirin2 import Daijirin2Entry - - -def new_entry(target, page_id): - entry_map = { - Targets.JITENON_KOKUGO: JitenonKokugoEntry, - Targets.JITENON_YOJI: JitenonYojiEntry, - Targets.JITENON_KOTOWAZA: JitenonKotowazaEntry, - Targets.SMK8: Smk8Entry, - Targets.DAIJIRIN2: Daijirin2Entry, - } - return entry_map[target](page_id) diff --git a/bot/entries/jitenon.py b/bot/entries/jitenon.py deleted file mode 100644 index fd9fcd2..0000000 --- a/bot/entries/jitenon.py +++ /dev/null @@ -1,227 +0,0 @@ -import re -from datetime import datetime, date -from bs4 import BeautifulSoup - -from bot.entries.entry import Entry -import bot.entries.expressions as Expressions - - -class _JitenonEntry(Entry): - ID_TO_ENTRY = {} - - def __init__(self, entry_id): - super().__init__(entry_id) - if entry_id not in self.ID_TO_ENTRY: - self.ID_TO_ENTRY[entry_id] = self - else: - raise Exception(f"Duplicate entry ID: {entry_id}") - self.modified_date = date(1970, 1, 1) - self.attribution = "" - for column in self._COLUMNS.values(): - setattr(self, column[0], column[1]) - - def set_page(self, page): - soup = BeautifulSoup(page, features="html5lib") - self.__set_modified_date(page) - self.__set_attribution(soup) - table = soup.find(class_="kanjirighttb") - if table is None: - raise ValueError("Error: table data not found in page.") - rows = table.find("tbody").find_all("tr") - colname = "" - for row in rows: - colname = row.th.text if row.th is not None else colname - colval = self.__clean_text(row.td.text) - self.__set_column(colname, colval) - self._page = table.decode() - - def get_page_soup(self): - soup = BeautifulSoup(self._page, "html5lib") - return soup - - def get_headwords(self): - if self._headwords is not None: - return self._headwords - self._set_headwords() - self._set_variant_headwords() - return self._headwords - - def get_part_of_speech_tags(self): - # Jitenon doesn't have any - return [] - - def _set_headwords(self): - headwords = {} - for yomikata in self._yomikatas(): - headwords[yomikata] = [self.expression] - ikei_headwords = self._ikei_headwords() - for reading, expressions in ikei_headwords.items(): - if reading not in headwords: - headwords[reading] = [] - for expression in expressions: - if expression not in headwords[reading]: - headwords[reading].append(expression) - self._headwords = headwords - - def __set_modified_date(self, page): - m = re.search(r"\"dateModified\": \"(\d{4}-\d{2}-\d{2})", page) - if m is None: - return - date = datetime.strptime(m.group(1), '%Y-%m-%d').date() - self.modified_date = date - - def __set_attribution(self, soup): - attribution = soup.find(class_="copyright") - if attribution is not None: - self.attribution = soup.find(class_="copyright").text - else: - self.attribution = "" - - def __set_column(self, colname, colval): - attr_name = self._COLUMNS[colname][0] - attr_value = getattr(self, attr_name) - if isinstance(attr_value, str): - setattr(self, attr_name, colval) - elif isinstance(attr_value, list): - if len(attr_value) == 0: - setattr(self, attr_name, [colval]) - else: - attr_value.append(colval) - - def _yomikatas(self): - yomikata = self.yomikata - m = re.search(r"^[ぁ-ヿ、]+$", yomikata) - if m: - return [yomikata] - m = re.search(r"^([ぁ-ヿ、]+)※", yomikata) - if m: - return [m.group(1)] - m = re.search(r"^[ぁ-ヿ、]+([ぁ-ヿ、])[ぁ-ヿ、]+$", yomikata) - if m: - return Expressions.expand_abbreviation(yomikata) - m = re.search(r"^([ぁ-ヿ、]+)(([ぁ-ヿ/\s、]+))$", yomikata) - if m: - yomikatas = [m.group(1)] - alts = m.group(2).split("/") - for alt in alts: - yomikatas.append(alt.strip()) - return yomikatas - print(f"Invalid 読み方 format: {self.yomikata}\n{self}\n") - return [""] - - def _ikei_headwords(self): - ikei_headwords = {} - for val in self.ikei: - m = re.search(r"^([^(]+)(([ぁ-ヿ、]+))$", val) - if not m: - print(f"Invalid 異形 format: {val}\n{self}\n") - continue - expression = m.group(1) - reading = m.group(2) - if reading not in ikei_headwords: - ikei_headwords[reading] = [] - if expression not in ikei_headwords[reading]: - ikei_headwords[reading].append(expression) - return ikei_headwords - - @staticmethod - def __clean_text(text): - text = text.replace("\n", "") - text = text.replace(",", "、") - text = text.replace(" ", "") - text = text.strip() - return text - - def __str__(self): - colvals = [str(self.entry_id)] - for attr in self._COLUMNS.values(): - attr_val = getattr(self, attr[0]) - if isinstance(attr_val, str): - colvals.append(attr_val) - elif isinstance(attr_val, list): - colvals.append(";".join(attr_val)) - return ",".join(colvals) - - -class JitenonYojiEntry(_JitenonEntry): - _COLUMNS = { - "四字熟語": ["expression", ""], - "読み方": ["yomikata", ""], - "意味": ["imi", ""], - "出典": ["shutten", ""], - "漢検級": ["kankenkyuu", ""], - "場面用途": ["bamenyouto", ""], - "異形": ["ikei", []], - "類義語": ["ruigigo", []], - } - - def __init__(self, entry_id): - super().__init__(entry_id) - - def _set_variant_headwords(self): - for expressions in self._headwords.values(): - Expressions.add_variant_kanji(expressions) - - -class JitenonKotowazaEntry(_JitenonEntry): - _COLUMNS = { - "言葉": ["expression", ""], - "読み方": ["yomikata", ""], - "意味": ["imi", ""], - "出典": ["shutten", ""], - "例文": ["reibun", ""], - "異形": ["ikei", []], - "類句": ["ruiku", []], - } - - def __init__(self, entry_id): - super().__init__(entry_id) - - def _set_headwords(self): - if self.expression == "金棒引き・鉄棒引き": - self._headwords = { - "かなぼうひき": ["金棒引き", "鉄棒引き"] - } - else: - super()._set_headwords() - - def _set_variant_headwords(self): - for expressions in self._headwords.values(): - Expressions.add_variant_kanji(expressions) - Expressions.add_fullwidth(expressions) - - -class JitenonKokugoEntry(_JitenonEntry): - _COLUMNS = { - "言葉": ["expression", ""], - "読み方": ["yomikata", ""], - "意味": ["imi", ""], - "例文": ["reibun", ""], - "別表記": ["betsuhyouki", ""], - "対義語": ["taigigo", ""], - "活用": ["katsuyou", ""], - "用例": ["yourei", ""], - "類語": ["ruigo", ""], - } - - def __init__(self, entry_id): - super().__init__(entry_id) - - def _set_headwords(self): - headwords = {} - for reading in self.yomikata.split("・"): - if reading not in headwords: - headwords[reading] = [] - for expression in self.expression.split("・"): - headwords[reading].append(expression) - if self.betsuhyouki.strip() != "": - for expression in self.betsuhyouki.split("・"): - headwords[reading].append(expression) - self._headwords = headwords - - def _set_variant_headwords(self): - for expressions in self._headwords.values(): - Expressions.add_variant_kanji(expressions) - Expressions.add_fullwidth(expressions) - Expressions.remove_iteration_mark(expressions) - Expressions.add_iteration_mark(expressions) diff --git a/bot/entries/jitenon_kokugo/entry.py b/bot/entries/jitenon_kokugo/entry.py new file mode 100644 index 0000000..523ac63 --- /dev/null +++ b/bot/entries/jitenon_kokugo/entry.py @@ -0,0 +1,45 @@ +from bot.entries.base.jitenon_entry import JitenonEntry +import bot.entries.base.expressions as Expressions + + +class Entry(JitenonEntry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.example = "" + self.alt_expression = "" + self.antonym = "" + self.attachments = "" + self.compounds = "" + self.related_words = "" + + def _get_column_map(self): + return { + "言葉": "expression", + "読み方": "yomikata", + "意味": "definition", + "例文": "example", + "別表記": "alt_expression", + "対義語": "antonym", + "活用": "attachments", + "用例": "compounds", + "類語": "related_words", + } + + def _get_headwords(self): + headwords = {} + for reading in self.yomikata.split("・"): + if reading not in headwords: + headwords[reading] = [] + for expression in self.expression.split("・"): + headwords[reading].append(expression) + if self.alt_expression.strip() != "": + for expression in self.alt_expression.split("・"): + headwords[reading].append(expression) + return headwords + + def _add_variant_expressions(self, headwords): + for expressions in headwords.values(): + Expressions.add_variant_kanji(expressions) + Expressions.add_fullwidth(expressions) + Expressions.remove_iteration_mark(expressions) + Expressions.add_iteration_mark(expressions) diff --git a/bot/entries/jitenon_kotowaza/entry.py b/bot/entries/jitenon_kotowaza/entry.py new file mode 100644 index 0000000..71dc35f --- /dev/null +++ b/bot/entries/jitenon_kotowaza/entry.py @@ -0,0 +1,35 @@ +from bot.entries.base.jitenon_entry import JitenonEntry +import bot.entries.base.expressions as Expressions + + +class Entry(JitenonEntry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.origin = "" + self.example = "" + self.related_expressions = [] + + def _get_column_map(self): + return { + "言葉": "expression", + "読み方": "yomikata", + "意味": "definition", + "異形": "other_forms", + "出典": "origin", + "例文": "example", + "類句": "related_expressions", + } + + def _get_headwords(self): + if self.expression == "金棒引き・鉄棒引き": + headwords = { + "かなぼうひき": ["金棒引き", "鉄棒引き"] + } + else: + headwords = super()._get_headwords() + return headwords + + def _add_variant_expressions(self, headwords): + for expressions in headwords.values(): + Expressions.add_variant_kanji(expressions) + Expressions.add_fullwidth(expressions) diff --git a/bot/entries/jitenon_yoji/entry.py b/bot/entries/jitenon_yoji/entry.py new file mode 100644 index 0000000..e0e8b13 --- /dev/null +++ b/bot/entries/jitenon_yoji/entry.py @@ -0,0 +1,27 @@ +import bot.entries.base.expressions as Expressions +from bot.entries.base.jitenon_entry import JitenonEntry + + +class Entry(JitenonEntry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.origin = "" + self.kanken_level = "" + self.category = "" + self.related_expressions = [] + + def _get_column_map(self): + return { + "四字熟語": "expression", + "読み方": "yomikata", + "意味": "definition", + "異形": "other_forms", + "出典": "origin", + "漢検級": "kanken_level", + "場面用途": "category", + "類義語": "related_expressions", + } + + def _add_variant_expressions(self, headwords): + for expressions in headwords.values(): + Expressions.add_variant_kanji(expressions) diff --git a/bot/entries/sankoku8/base_entry.py b/bot/entries/sankoku8/base_entry.py new file mode 100644 index 0000000..8d7a394 --- /dev/null +++ b/bot/entries/sankoku8/base_entry.py @@ -0,0 +1,104 @@ +import bot.soup as Soup +from bot.entries.base.sanseido_entry import SanseidoEntry +from bot.entries.sankoku8.parse import parse_hyouki_soup + + +class BaseEntry(SanseidoEntry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.children = [] + self.phrases = [] + self._hyouki_name = "表記" + self._midashi_name = None + self._midashi_kana_name = None + + def _get_headwords(self): + soup = self.get_page_soup() + self._delete_unused_nodes(soup) + readings = self._find_readings(soup) + expressions = self._find_expressions(soup) + headwords = {} + for reading in readings: + headwords[reading] = [] + if len(readings) == 1: + reading = readings[0] + if soup.find(self._midashi_name).find(self._hyouki_name) is None: + headwords[reading].append(reading) + for exp in expressions: + if exp not in headwords[reading]: + headwords[reading].append(exp) + elif len(readings) > 1 and len(expressions) == 0: + for reading in readings: + headwords[reading].append(reading) + elif len(readings) > 1 and len(expressions) == 1: + if soup.find(self._midashi_name).find(self._hyouki_name) is None: + for reading in readings: + headwords[reading].append(reading) + expression = expressions[0] + for reading in readings: + if expression not in headwords[reading]: + headwords[reading].append(expression) + elif len(readings) > 1 and len(expressions) == len(readings): + if soup.find(self._midashi_name).find(self._hyouki_name) is None: + for reading in readings: + headwords[reading].append(reading) + for idx, reading in enumerate(readings): + exp = expressions[idx] + if exp not in headwords[reading]: + headwords[reading].append(exp) + else: + raise Exception() # shouldn't happen + return headwords + + def get_part_of_speech_tags(self): + if self._part_of_speech_tags is not None: + return self._part_of_speech_tags + self._part_of_speech_tags = [] + soup = self.get_page_soup() + for midashi in soup.find_all([self._midashi_name, "見出部要素"]): + pos_group = midashi.find("品詞G") + if pos_group is None: + continue + for tag in pos_group.find_all("a"): + if tag.text not in self._part_of_speech_tags: + self._part_of_speech_tags.append(tag.text) + return self._part_of_speech_tags + + def _find_expressions(self, soup): + expressions = [] + for hyouki in soup.find_all(self._hyouki_name): + self._fill_alts(hyouki) + for expression in parse_hyouki_soup(hyouki, [""]): + expressions.append(expression) + return expressions + + def _find_readings(self, soup): + midasi_kana = soup.find(self._midashi_kana_name) + readings = parse_hyouki_soup(midasi_kana, [""]) + return readings + + def _get_subentry_parameters(self): + from bot.entries.sankoku8.child_entry import ChildEntry + from bot.entries.sankoku8.phrase_entry import PhraseEntry + subentry_parameters = [ + [ChildEntry, ["子項目"], self.children], + [PhraseEntry, ["句項目"], self.phrases], + ] + return subentry_parameters + + @staticmethod + def _delete_unused_nodes(soup): + """Remove extra markup elements that appear in the entry + headword line which are not part of the entry headword""" + unused_nodes = [ + "語構成", "平板", "アクセント", "表外字マーク", "表外音訓マーク", + "アクセント分節", "活用分節", "ルビG", "分書" + ] + for name in unused_nodes: + Soup.delete_soup_nodes(soup, name) + + @staticmethod + def _fill_alts(soup): + for img in soup.find_all("img"): + if img.has_attr("alt"): + img.string = img.attrs["alt"] diff --git a/bot/entries/sankoku8/child_entry.py b/bot/entries/sankoku8/child_entry.py new file mode 100644 index 0000000..9f6b1c1 --- /dev/null +++ b/bot/entries/sankoku8/child_entry.py @@ -0,0 +1,8 @@ +from bot.entries.sankoku8.base_entry import BaseEntry + + +class ChildEntry(BaseEntry): + def __init__(self, target, page_id): + super().__init__(target, page_id) + self._midashi_name = "子見出部" + self._midashi_kana_name = "子見出仮名" diff --git a/bot/entries/sankoku8/entry.py b/bot/entries/sankoku8/entry.py new file mode 100644 index 0000000..533ac66 --- /dev/null +++ b/bot/entries/sankoku8/entry.py @@ -0,0 +1,14 @@ +from bot.entries.sankoku8.base_entry import BaseEntry +from bot.entries.sankoku8.preprocess import preprocess_page + + +class Entry(BaseEntry): + def __init__(self, target, page_id): + entry_id = (page_id, 0) + super().__init__(target, entry_id) + self._midashi_name = "見出部" + self._midashi_kana_name = "見出仮名" + + def set_page(self, page): + page = preprocess_page(page) + super().set_page(page) diff --git a/bot/entries/sankoku8/parse.py b/bot/entries/sankoku8/parse.py new file mode 100644 index 0000000..a57574b --- /dev/null +++ b/bot/entries/sankoku8/parse.py @@ -0,0 +1,65 @@ +from bs4 import BeautifulSoup + + +def parse_hyouki_soup(soup, base_exps): + omitted_characters = [ + "/", "〈", "〉", "(", ")", "⦅", "⦆", ":", "…" + ] + exps = base_exps.copy() + for child in soup.children: + new_exps = [] + if child.name == "言換G": + for alt in child.find_all("言換"): + parts = parse_hyouki_soup(alt, [""]) + for exp in exps: + for part in parts: + new_exps.append(exp + part) + elif child.name == "補足表記": + alt1 = child.find("表記対象") + alt2 = child.find("表記内容G") + parts1 = parse_hyouki_soup(alt1, [""]) + parts2 = parse_hyouki_soup(alt2, [""]) + for exp in exps: + for part in parts1: + new_exps.append(exp + part) + for part in parts2: + new_exps.append(exp + part) + elif child.name == "省略": + parts = parse_hyouki_soup(child, [""]) + for exp in exps: + new_exps.append(exp) + for part in parts: + new_exps.append(exp + part) + elif child.name is not None: + new_exps = parse_hyouki_soup(child, exps) + else: + text = child.text + for char in omitted_characters: + text = text.replace(char, "") + for exp in exps: + new_exps.append(exp + text) + exps = new_exps.copy() + return exps + + +def parse_hyouki_pattern(pattern): + replacements = { + "(": "<省略>(", + ")": ")", + "{": "<補足表記><表記対象>", + "・": "<表記内容G>(<表記内容>", + "}": "", + "〈": "<言換G>〈<言換>", + "/": "/<言換>", + "〉": "", + "⦅": "<補足表記><表記対象>", + "\": "<表記内容G>⦅<表記内容>", + "⦆": "", + } + markup = f"{pattern}" + for key, val in replacements.items(): + markup = markup.replace(key, val) + soup = BeautifulSoup(markup, "xml") + hyouki_soup = soup.find("span") + exps = parse_hyouki_soup(hyouki_soup, [""]) + return exps diff --git a/bot/entries/sankoku8/phrase_entry.py b/bot/entries/sankoku8/phrase_entry.py new file mode 100644 index 0000000..e5da208 --- /dev/null +++ b/bot/entries/sankoku8/phrase_entry.py @@ -0,0 +1,37 @@ +from bot.data import load_phrase_readings +from bot.entries.sankoku8.base_entry import BaseEntry +from bot.entries.sankoku8.parse import parse_hyouki_soup +from bot.entries.sankoku8.parse import parse_hyouki_pattern + + +class PhraseEntry(BaseEntry): + def get_part_of_speech_tags(self): + # phrases do not contain these tags + return [] + + def _get_headwords(self): + soup = self.get_page_soup() + self._delete_unused_nodes(soup) + expressions = self._find_expressions(soup) + readings = self._find_readings(soup) + headwords = {} + if len(expressions) != len(readings): + raise Exception(f"{self.entry_id[0]}-{self.entry_id[1]}") + for idx, expression in enumerate(expressions): + reading = readings[idx] + if reading in headwords: + headwords[reading].append(expression) + else: + headwords[reading] = [expression] + return headwords + + def _find_expressions(self, soup): + phrase_soup = soup.find("句表記") + expressions = parse_hyouki_soup(phrase_soup, [""]) + return expressions + + def _find_readings(self, soup): + reading_patterns = load_phrase_readings(self.target) + reading_pattern = reading_patterns[self.entry_id] + readings = parse_hyouki_pattern(reading_pattern) + return readings diff --git a/bot/entries/sankoku8/preprocess.py b/bot/entries/sankoku8/preprocess.py new file mode 100644 index 0000000..1eee32d --- /dev/null +++ b/bot/entries/sankoku8/preprocess.py @@ -0,0 +1,51 @@ +import re +from bs4 import BeautifulSoup + +from bot.data import get_adobe_glyph + + +__GAIJI = { + "svg-gaiji/byan.svg": "𰻞", + "svg-gaiji/G16EF.svg": "篡", +} + + +def preprocess_page(page): + soup = BeautifulSoup(page, features="xml") + __replace_glyph_codes(soup) + __add_image_alt_text(soup) + __replace_tatehyphen(soup) + page = __strip_page(soup) + return page + + +def __replace_glyph_codes(soup): + for el in soup.find_all("glyph"): + m = re.search(r"^glyph:([0-9]+);?$", el.attrs["style"]) + code = int(m.group(1)) + for geta in el.find_all(string="〓"): + glyph = get_adobe_glyph(code) + geta.replace_with(glyph) + + +def __add_image_alt_text(soup): + for img in soup.find_all("img"): + if not img.has_attr("src"): + continue + src = img.attrs["src"] + if src in __GAIJI: + img.attrs["alt"] = __GAIJI[src] + + +def __replace_tatehyphen(soup): + for img in soup.find_all("img", {"src": "svg-gaiji/tatehyphen.svg"}): + img.string = "−" + img.unwrap() + + +def __strip_page(soup): + koumoku = soup.find(["項目"]) + if koumoku is not None: + return koumoku.decode() + else: + raise Exception(f"Primary 項目 not found in page:\n{soup.prettify()}") diff --git a/bot/entries/smk8.py b/bot/entries/smk8.py deleted file mode 100644 index 11ef7e6..0000000 --- a/bot/entries/smk8.py +++ /dev/null @@ -1,223 +0,0 @@ -import re -from bs4 import BeautifulSoup - -import bot.entries.expressions as Expressions -import bot.soup as Soup -from bot.data import load_smk8_phrase_readings -from bot.entries.entry import Entry -from bot.entries.smk8_preprocess import preprocess_page - - -class _BaseSmk8Entry(Entry): - ID_TO_ENTRY = {} - SUBENTRY_ID_TO_ENTRY_ID = {} - - def __init__(self, entry_id): - super().__init__(entry_id) - if entry_id not in self.ID_TO_ENTRY: - self.ID_TO_ENTRY[entry_id] = self - else: - raise Exception(f"Duplicate entry ID: {entry_id}") - self.children = [] - self.phrases = [] - self.kanjis = [] - - def set_page(self, page): - page = self.__decompose_subentries(page) - self._page = page - - def get_page_soup(self): - soup = BeautifulSoup(self._page, "xml") - return soup - - def get_headwords(self): - if self._headwords is not None: - return self._headwords - self._set_headwords() - self._set_variant_headwords() - return self._headwords - - def get_part_of_speech_tags(self): - if self._part_of_speech_tags is not None: - return self._part_of_speech_tags - self._part_of_speech_tags = [] - soup = self.get_page_soup() - headword_info = soup.find("見出要素") - if headword_info is None: - return self._part_of_speech_tags - for tag in headword_info.find_all("品詞M"): - if tag.text not in self._part_of_speech_tags: - self._part_of_speech_tags.append(tag.text) - return self._part_of_speech_tags - - def _set_variant_headwords(self): - for expressions in self._headwords.values(): - Expressions.add_variant_kanji(expressions) - Expressions.add_fullwidth(expressions) - Expressions.remove_iteration_mark(expressions) - Expressions.add_iteration_mark(expressions) - - def _find_reading(self, soup): - midasi_kana = soup.find("見出仮名") - reading = midasi_kana.text - for x in [" ", "・"]: - reading = reading.replace(x, "") - return reading - - def _find_expressions(self, soup): - clean_expressions = [] - for expression in soup.find_all("標準表記"): - clean_expression = self._clean_expression(expression.text) - clean_expressions.append(clean_expression) - expressions = Expressions.expand_abbreviation_list(clean_expressions) - return expressions - - def __decompose_subentries(self, page): - soup = BeautifulSoup(page, features="xml") - subentry_parameters = [ - [Smk8ChildEntry, ["子項目F", "子項目"], self.children], - [Smk8PhraseEntry, ["句項目F", "句項目"], self.phrases], - [Smk8KanjiEntry, ["造語成分項目"], self.kanjis], - ] - for x in subentry_parameters: - subentry_class, tags, subentry_list = x - for tag in tags: - tag_soup = soup.find(tag) - while tag_soup is not None: - tag_soup.name = "項目" - subentry_id = self.id_string_to_entry_id(tag_soup.attrs["id"]) - self.SUBENTRY_ID_TO_ENTRY_ID[subentry_id] = self.entry_id - subentry = subentry_class(subentry_id) - page = tag_soup.decode() - subentry.set_page(page) - subentry_list.append(subentry) - tag_soup.decompose() - tag_soup = soup.find(tag) - return soup.decode() - - @staticmethod - def id_string_to_entry_id(id_string): - parts = id_string.split("-") - if len(parts) == 1: - return (int(parts[0]), 0) - elif len(parts) == 2: - # subentries have a hexadecimal part - return (int(parts[0]), int(parts[1], 16)) - else: - raise Exception(f"Invalid entry ID: {id_string}") - - @staticmethod - def _clean_expression(expression): - for x in ["〈", "〉", "{", "}", "…", " "]: - expression = expression.replace(x, "") - return expression - - @staticmethod - def _fill_alts(soup): - for e in soup.find_all(["親見出仮名", "親見出表記"]): - e.string = e.attrs["alt"] - for gaiji in soup.find_all("外字"): - gaiji.string = gaiji.img.attrs["alt"] - - -class Smk8Entry(_BaseSmk8Entry): - def __init__(self, page_id): - entry_id = (page_id, 0) - super().__init__(entry_id) - - def set_page(self, page): - page = preprocess_page(page) - super().set_page(page) - - def _set_headwords(self): - soup = self.get_page_soup() - Soup.delete_soup_nodes(soup, "表音表記") - self._fill_alts(soup) - reading = self._find_reading(soup) - expressions = [] - if soup.find("見出部").find("標準表記") is None: - expressions.append(reading) - for expression in self._find_expressions(soup): - if expression not in expressions: - expressions.append(expression) - self._headwords = {reading: expressions} - - -class Smk8ChildEntry(_BaseSmk8Entry): - def __init__(self, entry_id): - super().__init__(entry_id) - - def _set_headwords(self): - soup = self.get_page_soup() - Soup.delete_soup_nodes(soup, "表音表記") - self._fill_alts(soup) - reading = self._find_reading(soup) - expressions = [] - if soup.find("子見出部").find("標準表記") is None: - expressions.append(reading) - for expression in self._find_expressions(soup): - if expression not in expressions: - expressions.append(expression) - self._headwords = {reading: expressions} - - -class Smk8PhraseEntry(_BaseSmk8Entry): - def __init__(self, entry_id): - super().__init__(entry_id) - self.__phrase_readings = load_smk8_phrase_readings() - - def get_part_of_speech_tags(self): - # phrases do not contain these tags - return [] - - def _set_headwords(self): - soup = self.get_page_soup() - headwords = {} - expressions = self._find_expressions(soup) - readings = self._find_readings() - for idx, expression in enumerate(expressions): - reading = readings[idx] - if reading in headwords: - headwords[reading].append(expression) - else: - headwords[reading] = [expression] - self._headwords = headwords - - def _find_expressions(self, soup): - Soup.delete_soup_nodes(soup, "ルビG") - self._fill_alts(soup) - text = soup.find("標準表記").text - text = self._clean_expression(text) - alternatives = Expressions.expand_smk_alternatives(text) - expressions = [] - for alt in alternatives: - for exp in Expressions.expand_abbreviation(alt): - expressions.append(exp) - return expressions - - def _find_readings(self): - text = self.__phrase_readings[self.entry_id] - alternatives = Expressions.expand_smk_alternatives(text) - readings = [] - for alt in alternatives: - for reading in Expressions.expand_abbreviation(alt): - readings.append(reading) - return readings - - -class Smk8KanjiEntry(_BaseSmk8Entry): - def __init__(self, entry_id): - super().__init__(entry_id) - - def _set_headwords(self): - soup = self.get_page_soup() - self._fill_alts(soup) - reading = self.__get_parent_reading() - expressions = self._find_expressions(soup) - self._headwords = {reading: expressions} - - def __get_parent_reading(self): - parent_id = self.SUBENTRY_ID_TO_ENTRY_ID[self.entry_id] - parent = self.ID_TO_ENTRY[parent_id] - reading = parent.get_first_reading() - return reading diff --git a/bot/entries/smk8/base_entry.py b/bot/entries/smk8/base_entry.py new file mode 100644 index 0000000..7bf32c2 --- /dev/null +++ b/bot/entries/smk8/base_entry.py @@ -0,0 +1,73 @@ +import bot.soup as Soup +import bot.entries.base.expressions as Expressions +from bot.entries.base.sanseido_entry import SanseidoEntry + + +class BaseEntry(SanseidoEntry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.children = [] + self.phrases = [] + self.kanjis = [] + + def get_part_of_speech_tags(self): + if self._part_of_speech_tags is not None: + return self._part_of_speech_tags + self._part_of_speech_tags = [] + soup = self.get_page_soup() + headword_info = soup.find("見出要素") + if headword_info is None: + return self._part_of_speech_tags + for tag in headword_info.find_all("品詞M"): + if tag.text not in self._part_of_speech_tags: + self._part_of_speech_tags.append(tag.text) + return self._part_of_speech_tags + + def _find_reading(self, soup): + midasi_kana = soup.find("見出仮名") + reading = midasi_kana.text + for x in [" ", "・"]: + reading = reading.replace(x, "") + return reading + + def _find_expressions(self, soup): + clean_expressions = [] + for expression in soup.find_all("標準表記"): + clean_expression = self._clean_expression(expression.text) + clean_expressions.append(clean_expression) + expressions = Expressions.expand_abbreviation_list(clean_expressions) + return expressions + + def _get_subentry_parameters(self): + from bot.entries.smk8.child_entry import ChildEntry + from bot.entries.smk8.phrase_entry import PhraseEntry + from bot.entries.smk8.kanji_entry import KanjiEntry + subentry_parameters = [ + [ChildEntry, ["子項目F", "子項目"], self.children], + [PhraseEntry, ["句項目F", "句項目"], self.phrases], + [KanjiEntry, ["造語成分項目"], self.kanjis], + ] + return subentry_parameters + + @staticmethod + def _delete_unused_nodes(soup): + """Remove extra markup elements that appear in the entry + headword line which are not part of the entry headword""" + unused_nodes = [ + "表音表記", "表外音訓マーク", "表外字マーク", "ルビG" + ] + for name in unused_nodes: + Soup.delete_soup_nodes(soup, name) + + @staticmethod + def _clean_expression(expression): + for x in ["〈", "〉", "{", "}", "…", " "]: + expression = expression.replace(x, "") + return expression + + @staticmethod + def _fill_alts(soup): + for elm in soup.find_all(["親見出仮名", "親見出表記"]): + elm.string = elm.attrs["alt"] + for gaiji in soup.find_all("外字"): + gaiji.string = gaiji.img.attrs["alt"] diff --git a/bot/entries/smk8/child_entry.py b/bot/entries/smk8/child_entry.py new file mode 100644 index 0000000..0dbe375 --- /dev/null +++ b/bot/entries/smk8/child_entry.py @@ -0,0 +1,17 @@ +from bot.entries.smk8.base_entry import BaseEntry + + +class ChildEntry(BaseEntry): + def _get_headwords(self): + soup = self.get_page_soup() + self._delete_unused_nodes(soup) + self._fill_alts(soup) + reading = self._find_reading(soup) + expressions = [] + if soup.find("子見出部").find("標準表記") is None: + expressions.append(reading) + for expression in self._find_expressions(soup): + if expression not in expressions: + expressions.append(expression) + headwords = {reading: expressions} + return headwords diff --git a/bot/entries/smk8/entry.py b/bot/entries/smk8/entry.py new file mode 100644 index 0000000..4baed42 --- /dev/null +++ b/bot/entries/smk8/entry.py @@ -0,0 +1,26 @@ +from bot.entries.smk8.base_entry import BaseEntry +from bot.entries.smk8.preprocess import preprocess_page + + +class Entry(BaseEntry): + def __init__(self, target, page_id): + entry_id = (page_id, 0) + super().__init__(target, entry_id) + + def set_page(self, page): + page = preprocess_page(page) + super().set_page(page) + + def _get_headwords(self): + soup = self.get_page_soup() + self._delete_unused_nodes(soup) + self._fill_alts(soup) + reading = self._find_reading(soup) + expressions = [] + if soup.find("見出部").find("標準表記") is None: + expressions.append(reading) + for expression in self._find_expressions(soup): + if expression not in expressions: + expressions.append(expression) + headwords = {reading: expressions} + return headwords diff --git a/bot/entries/smk8/kanji_entry.py b/bot/entries/smk8/kanji_entry.py new file mode 100644 index 0000000..3e77faf --- /dev/null +++ b/bot/entries/smk8/kanji_entry.py @@ -0,0 +1,22 @@ +from bot.entries.smk8.base_entry import BaseEntry + + +class KanjiEntry(BaseEntry): + def get_part_of_speech_tags(self): + # kanji entries do not contain these tags + return [] + + def _get_headwords(self): + soup = self.get_page_soup() + self._delete_unused_nodes(soup) + self._fill_alts(soup) + reading = self.__get_parent_reading() + expressions = self._find_expressions(soup) + headwords = {reading: expressions} + return headwords + + def __get_parent_reading(self): + parent_id = self.SUBENTRY_ID_TO_ENTRY_ID[self.entry_id] + parent = self.ID_TO_ENTRY[parent_id] + reading = parent.get_first_reading() + return reading diff --git a/bot/entries/smk8/phrase_entry.py b/bot/entries/smk8/phrase_entry.py new file mode 100644 index 0000000..aac9b84 --- /dev/null +++ b/bot/entries/smk8/phrase_entry.py @@ -0,0 +1,64 @@ +import re + +import bot.entries.base.expressions as Expressions +from bot.data import load_phrase_readings +from bot.entries.smk8.base_entry import BaseEntry + + +class PhraseEntry(BaseEntry): + def __init__(self, target, entry_id): + super().__init__(target, entry_id) + self.__phrase_readings = load_phrase_readings(self.target) + + def get_part_of_speech_tags(self): + # phrase entries do not contain these tags + return [] + + def _get_headwords(self): + soup = self.get_page_soup() + headwords = {} + expressions = self._find_expressions(soup) + readings = self._find_readings() + for idx, expression in enumerate(expressions): + reading = readings[idx] + if reading in headwords: + headwords[reading].append(expression) + else: + headwords[reading] = [expression] + return headwords + + def _find_expressions(self, soup): + self._delete_unused_nodes(soup) + self._fill_alts(soup) + text = soup.find("標準表記").text + text = self._clean_expression(text) + alternatives = parse_phrase(text) + expressions = [] + for alt in alternatives: + for exp in Expressions.expand_abbreviation(alt): + expressions.append(exp) + return expressions + + def _find_readings(self): + text = self.__phrase_readings[self.entry_id] + alternatives = parse_phrase(text) + readings = [] + for alt in alternatives: + for reading in Expressions.expand_abbreviation(alt): + readings.append(reading) + return readings + + +def parse_phrase(text): + """Return a list of strings described by △ notation.""" + match = re.search(r"△([^(]+)(([^(]+))", text) + if match is None: + return [text] + alt_parts = [match.group(1)] + for alt_part in match.group(2).split("・"): + alt_parts.append(alt_part) + alts = [] + for alt_part in alt_parts: + alt_exp = re.sub(r"△[^(]+([^(]+)", alt_part, text) + alts.append(alt_exp) + return alts diff --git a/bot/entries/smk8_preprocess.py b/bot/entries/smk8/preprocess.py similarity index 82% rename from bot/entries/smk8_preprocess.py rename to bot/entries/smk8/preprocess.py index 2e480a8..ebda252 100644 --- a/bot/entries/smk8_preprocess.py +++ b/bot/entries/smk8/preprocess.py @@ -6,8 +6,8 @@ from bot.data import get_adobe_glyph __GAIJI = { "gaiji/5350.svg": "卐", - "gaiji/62cb.svg": "抛", - "gaiji/7be1.svg": "簒", + "gaiji/62cb.svg": "拋", + "gaiji/7be1.svg": "篡", } @@ -15,6 +15,7 @@ def preprocess_page(page): page = __strip_page(page) page = __replace_glyph_codes(page) page = __format_hyougai_marks(page) + page = __remove_pronunciation_parentheses(page) return page @@ -64,6 +65,7 @@ def __format_hyougai_marks(page): for x in ["\n", "\t", " "]: text = text.replace(x, "") text = re.sub(r"〈([^〈]+)〉", r"\1", text) + page = re.sub(r"〈([^〈]+)〉", r"␂\1␃", page) for mark in re.findall(r"《.", text): if mark[1] == "〓": @@ -79,13 +81,29 @@ def __format_hyougai_marks(page): page = re.sub(f"〈([^{mark[1]}]*)({mark[1]})", r"\1<表外字>\2", page, count=1) + page = page.replace("␂", "〈") page = page.replace("␃", "〉") soup = BeautifulSoup(page, features="xml") + for el in soup.find_all("表外音訓"): if el.text == "": el.append(el.next_sibling) + mark_xml = "<表外音訓マーク>︽" + mark_soup = BeautifulSoup(mark_xml, "xml") + el.append(mark_soup.表外音訓マーク) + for el in soup.find_all("表外字"): if el.text == "": el.append(el.next_sibling) + mark_xml = "<表外字マーク>︿" + mark_soup = BeautifulSoup(mark_xml, "xml") + el.append(mark_soup.表外字マーク) + return soup.decode() + + +def __remove_pronunciation_parentheses(page): + page = page.replace("<表音表記>(", "<表音表記>") + page = page.replace(")", "") + return page diff --git a/bot/factory.py b/bot/factory.py new file mode 100644 index 0000000..7b025d4 --- /dev/null +++ b/bot/factory.py @@ -0,0 +1,37 @@ +import importlib + + +def new_crawler(target): + module_path = f"bot.crawlers.{target.name.lower()}" + module = importlib.import_module(module_path) + return module.Crawler(target) + + +def new_entry(target, page_id): + module_path = f"bot.entries.{target.name.lower()}.entry" + module = importlib.import_module(module_path) + return module.Entry(target, page_id) + + +def new_yomichan_exporter(target): + module_path = f"bot.yomichan.exporters.{target.name.lower()}" + module = importlib.import_module(module_path) + return module.Exporter(target) + + +def new_yomichan_terminator(target): + module_path = f"bot.yomichan.terms.{target.name.lower()}" + module = importlib.import_module(module_path) + return module.Terminator(target) + + +def new_mdict_exporter(target): + module_path = f"bot.mdict.exporters.{target.name.lower()}" + module = importlib.import_module(module_path) + return module.Exporter(target) + + +def new_mdict_terminator(target): + module_path = f"bot.mdict.terms.{target.name.lower()}" + module = importlib.import_module(module_path) + return module.Terminator(target) diff --git a/bot/mdict/exporters/base/exporter.py b/bot/mdict/exporters/base/exporter.py new file mode 100644 index 0000000..37ed376 --- /dev/null +++ b/bot/mdict/exporters/base/exporter.py @@ -0,0 +1,174 @@ +import os +import shutil +import subprocess +from abc import ABC, abstractmethod +from pathlib import Path + +from platformdirs import user_documents_dir, user_cache_dir + +from bot.time import timestamp +from bot.factory import new_mdict_terminator + + +class BaseExporter(ABC): + def __init__(self, target): + self._target = target + self._terminator = new_mdict_terminator(target) + self._build_dir = None + self._build_media_dir = None + self._description_file = None + self._out_dir = None + + def export(self, entries, media_dir, icon_file): + self._init_build_media_dir(media_dir) + self._init_description_file(entries) + self._write_mdx_file(entries) + self._write_mdd_file() + self._write_icon_file(icon_file) + self._write_css_file() + self._rm_build_dir() + + def _get_build_dir(self): + if self._build_dir is not None: + return self._build_dir + cache_dir = user_cache_dir("jitenbot") + build_directory = os.path.join(cache_dir, "mdict_build") + print(f"{timestamp()} Initializing build directory `{build_directory}`") + if Path(build_directory).is_dir(): + shutil.rmtree(build_directory) + os.makedirs(build_directory) + self._build_dir = build_directory + return self._build_dir + + def _init_build_media_dir(self, media_dir): + build_dir = self._get_build_dir() + build_media_dir = os.path.join(build_dir, self._target.value) + if media_dir is not None: + print(f"{timestamp()} Copying media files to build directory...") + shutil.copytree(media_dir, build_media_dir) + else: + os.makedirs(build_media_dir) + css_file = self._get_css_file() + shutil.copy(css_file, build_media_dir) + self._terminator.set_media_dir(build_media_dir) + self._build_media_dir = build_media_dir + + def _init_description_file(self, entries): + description_template_file = self._get_description_template_file() + with open(description_template_file, "r", encoding="utf8") as f: + description = f.read() + description = description.replace( + "{{revision}}", self._get_revision(entries)) + description = description.replace( + "{{attribution}}", self._get_attribution(entries)) + build_dir = self._get_build_dir() + description_file = os.path.join( + build_dir, f"{self._target.value}.mdx.description.html") + with open(description_file, "w", encoding="utf8") as f: + f.write(description) + self._description_file = description_file + + def _write_mdx_file(self, entries): + terms = self._get_terms(entries) + print(f"{timestamp()} Exporting {len(terms)} Mdict keys...") + out_dir = self._get_out_dir() + out_file = os.path.join(out_dir, f"{self._target.value}.mdx") + params = [ + "mdict", + "-a", self._get_term_file(terms), + "--title", self._get_title_file(), + "--description", self._description_file, + out_file + ] + subprocess.run(params, check=True) + + def _get_terms(self, entries): + terms = [] + entries_len = len(entries) + for idx, entry in enumerate(entries): + update = f"\tCreating MDict terms for entry {idx+1}/{entries_len}" + print(update, end='\r', flush=True) + new_terms = self._terminator.make_terms(entry) + for term in new_terms: + terms.append(term) + print() + return terms + + def _write_mdd_file(self): + out_dir = self._get_out_dir() + out_file = os.path.join(out_dir, f"{self._target.value}.mdd") + params = [ + "mdict", + "-a", self._build_media_dir, + "--title", self._get_title_file(), + "--description", self._description_file, + out_file + ] + subprocess.run(params, check=True) + + def _write_icon_file(self, icon_file): + premade_icon_file = self._get_premade_icon_file() + out_dir = self._get_out_dir() + out_file = os.path.join(out_dir, f"{self._target.value}.png") + if icon_file is not None and Path(icon_file).is_file(): + shutil.copy(icon_file, out_file) + elif Path(premade_icon_file).is_file(): + shutil.copy(premade_icon_file, out_file) + + def _write_css_file(self): + css_file = self._get_css_file() + out_dir = self._get_out_dir() + shutil.copy(css_file, out_dir) + + def _get_out_dir(self): + if self._out_dir is not None: + return self._out_dir + out_dir = os.path.join( + user_documents_dir(), "jitenbot", "mdict", self._target.value) + print(f"{timestamp()} Initializing output directory `{out_dir}`") + if Path(out_dir).is_dir(): + shutil.rmtree(out_dir) + os.makedirs(out_dir) + self._out_dir = out_dir + return out_dir + + def _get_term_file(self, terms): + build_dir = self._get_build_dir() + term_file = os.path.join(build_dir, f"{self._target.value}.mdx.txt") + with open(term_file, "w", encoding="utf8") as f: + for term in terms: + f.write("\n".join(term)) + f.write("\n\n") + return term_file + + def _get_title_file(self): + return os.path.join( + "data", "mdict", "title", + f"{self._target.value}.mdx.title.html") + + def _get_css_file(self): + return os.path.join( + "data", "mdict", "css", + f"{self._target.value}.css") + + def _get_premade_icon_file(self): + return os.path.join( + "data", "mdict", "icon", + f"{self._target.value}.png") + + def _get_description_template_file(self): + return os.path.join( + "data", "mdict", "description", + f"{self._target.value}.mdx.description.html") + + def _rm_build_dir(self): + build_dir = self._get_build_dir() + shutil.rmtree(build_dir) + + @abstractmethod + def _get_revision(self, entries): + raise NotImplementedError + + @abstractmethod + def _get_attribution(self, entries): + raise NotImplementedError diff --git a/bot/mdict/exporters/base/jitenon.py b/bot/mdict/exporters/base/jitenon.py new file mode 100644 index 0000000..2e6b1df --- /dev/null +++ b/bot/mdict/exporters/base/jitenon.py @@ -0,0 +1,18 @@ +from bot.mdict.exporters.base.exporter import BaseExporter + + +class JitenonExporter(BaseExporter): + def _get_revision(self, entries): + modified_date = None + for entry in entries: + if modified_date is None or entry.modified_date > modified_date: + modified_date = entry.modified_date + revision = modified_date.strftime("%Y年%m月%d日閲覧") + return revision + + def _get_attribution(self, entries): + modified_date = None + for entry in entries: + if modified_date is None or entry.modified_date > modified_date: + attribution = entry.attribution + return attribution diff --git a/bot/mdict/exporters/base/monokakido.py b/bot/mdict/exporters/base/monokakido.py new file mode 100644 index 0000000..b9b9629 --- /dev/null +++ b/bot/mdict/exporters/base/monokakido.py @@ -0,0 +1,8 @@ +from datetime import datetime +from bot.mdict.exporters.base.exporter import BaseExporter + + +class MonokakidoExporter(BaseExporter): + def _get_revision(self, entries): + timestamp = datetime.now().strftime("%Y年%m月%d日作成") + return timestamp diff --git a/bot/mdict/exporters/daijirin2.py b/bot/mdict/exporters/daijirin2.py new file mode 100644 index 0000000..4692470 --- /dev/null +++ b/bot/mdict/exporters/daijirin2.py @@ -0,0 +1,6 @@ +from bot.mdict.exporters.base.monokakido import MonokakidoExporter + + +class Exporter(MonokakidoExporter): + def _get_attribution(self, entries): + return "© Sanseido Co., LTD. 2019" diff --git a/bot/mdict/exporters/jitenon_kokugo.py b/bot/mdict/exporters/jitenon_kokugo.py new file mode 100644 index 0000000..5689fa8 --- /dev/null +++ b/bot/mdict/exporters/jitenon_kokugo.py @@ -0,0 +1,5 @@ +from bot.mdict.exporters.base.jitenon import JitenonExporter + + +class Exporter(JitenonExporter): + pass diff --git a/bot/mdict/exporters/jitenon_kotowaza.py b/bot/mdict/exporters/jitenon_kotowaza.py new file mode 100644 index 0000000..5689fa8 --- /dev/null +++ b/bot/mdict/exporters/jitenon_kotowaza.py @@ -0,0 +1,5 @@ +from bot.mdict.exporters.base.jitenon import JitenonExporter + + +class Exporter(JitenonExporter): + pass diff --git a/bot/mdict/exporters/jitenon_yoji.py b/bot/mdict/exporters/jitenon_yoji.py new file mode 100644 index 0000000..5689fa8 --- /dev/null +++ b/bot/mdict/exporters/jitenon_yoji.py @@ -0,0 +1,5 @@ +from bot.mdict.exporters.base.jitenon import JitenonExporter + + +class Exporter(JitenonExporter): + pass diff --git a/bot/mdict/exporters/sankoku8.py b/bot/mdict/exporters/sankoku8.py new file mode 100644 index 0000000..6063864 --- /dev/null +++ b/bot/mdict/exporters/sankoku8.py @@ -0,0 +1,6 @@ +from bot.mdict.exporters.base.monokakido import MonokakidoExporter + + +class Exporter(MonokakidoExporter): + def _get_attribution(self, entries): + return "© Sanseido Co., LTD. 2021" diff --git a/bot/mdict/exporters/smk8.py b/bot/mdict/exporters/smk8.py new file mode 100644 index 0000000..a030b4b --- /dev/null +++ b/bot/mdict/exporters/smk8.py @@ -0,0 +1,6 @@ +from bot.mdict.exporters.base.monokakido import MonokakidoExporter + + +class Exporter(MonokakidoExporter): + def _get_attribution(self, entries): + return "© Sanseido Co., LTD. 2020" diff --git a/bot/mdict/glossary/daijirin2.py b/bot/mdict/glossary/daijirin2.py new file mode 100644 index 0000000..1a8b0d5 --- /dev/null +++ b/bot/mdict/glossary/daijirin2.py @@ -0,0 +1,77 @@ +import re +import os +from functools import cache +from pathlib import Path + +from bot.soup import delete_soup_nodes +from bot.data import load_mdict_name_conversion +from bot.name_conversion import convert_names + + +def make_glossary(entry, media_dir): + soup = entry.get_page_soup() + __add_rubies(soup) + __hyperlink_parent_expression(soup, entry) + __delete_unused_nodes(soup, media_dir) + __convert_links(soup, entry) + + name_conversion = load_mdict_name_conversion(entry.target) + convert_names(soup, name_conversion) + + glossary = soup.span.decode() + return glossary + + +def __add_rubies(soup): + for name in ["表外音訓", "表外字"]: + for ruby in soup.find_all(name): + ruby.name = "ruby" + rt = ruby.find("表外字マーク") + rt.name = "rt" + ruby.append(rt) # needs to positioned after the text + + +def __hyperlink_parent_expression(soup, entry): + if soup.find("親表記") is None: + return + parent_entry = entry.get_parent() + gid = parent_entry.get_global_identifier() + for el in soup.find_all("親表記"): + el.name = "a" + el.attrs["href"] = f"entry://{gid}" + + +def __delete_unused_nodes(soup, media_dir): + if not __graphics_directory_exists(media_dir): + delete_soup_nodes(soup, "カットG") + for el in soup.find_all("logo"): + next_sibling = el.next_sibling + if next_sibling is None: + continue + elif next_sibling.name in ["漢字見出G", "漢字音G"]: + el.decompose() + for el in soup.find_all("漢字音G"): + for child in el.find_all(string="・"): + child.replace_with("") + + +@cache +def __graphics_directory_exists(media_dir): + path = os.path.join(media_dir, "graphics") + return Path(path).is_dir() + + +def __convert_links(soup, entry): + for el in soup.find_all("a"): + href = el.attrs["href"] + if re.match(r"^[0-9]+(?:-[0-9A-F]{4})?$", href): + ref_entry_id = entry.id_string_to_entry_id(href) + ref_entry = entry.ID_TO_ENTRY[ref_entry_id] + gid = ref_entry.get_global_identifier() + el.attrs["href"] = f"entry://{gid}" + elif re.match(r"^entry:", href): + pass + elif re.match(r"^https?:[\w\W]*", href): + pass + else: + raise Exception(f"Invalid href format: {href}") diff --git a/bot/mdict/glossary/jitenon.py b/bot/mdict/glossary/jitenon.py new file mode 100644 index 0000000..737ea59 --- /dev/null +++ b/bot/mdict/glossary/jitenon.py @@ -0,0 +1,141 @@ +# pylint: disable=too-few-public-methods + +import re + + +class JitenonGlossary(): + def __init__(self): + self._id_pattern = None + self._expression_header = None + + def _replace_punctuation(self, soup): + punctuation = { + "/": "/", + ",": "、", + } + for el in soup.find_all(string=True): + text = el.text + for old, new in punctuation.items(): + text = text.replace(old, new) + el.replace_with(text) + + def _add_internal_links(self, soup, entry): + for el in soup.find_all("a"): + href = el.attrs["href"] + m = re.search(self._id_pattern, href) + if m is not None: + ref_entry_id = int(m.group(1)) + ref_entry = entry.ID_TO_ENTRY[ref_entry_id] + gid = ref_entry.get_global_identifier() + el.attrs["href"] = f"entry://{gid}" + elif re.match(r"^(?:https?:|\?)[\w\W]*", href): + pass + else: + raise Exception(f"Invalid href format: {href}") + + def _decompose_table_rows(self, soup, entry): + for tr in soup.find_all("tr"): + if tr.find("th") is None: + continue + elif tr.th.text == self._expression_header: + tr.decompose() + elif tr.th.text == "読み方": + if self._do_display_yomikata_in_headword(entry): + tr.decompose() + elif tr.th.text == "意味": + definition = tr.td + definition.name = "div" + definition.attrs["class"] = "意味" + soup.body.insert(0, definition) + tr.decompose() + if soup.find("tr") is None: + soup.table.decompose() + + def _insert_headword_line(self, soup, entry): + headword_line = soup.new_tag("div") + headword_line.attrs["class"] = "見出し" + if self._do_display_yomikata_in_headword(entry): + reading = soup.new_tag("span") + reading.attrs["class"] = "読み方" + reading.string = entry.yomikata + headword_line.append(reading) + expression = soup.new_tag("span") + expression.attrs["class"] = self._expression_header + expression.string = f"【{entry.expression}】" + headword_line.append(expression) + soup.body.insert(0, headword_line) + + def _do_display_yomikata_in_headword(self, entry): + if not re.match(r"^[ぁ-ヿ、]+$", entry.yomikata): + return False + elif len(entry.yomikata) > 10: + return False + else: + return True + + +class JitenonKokugoGlossary(JitenonGlossary): + def __init__(self): + super().__init__() + self._expression_header = "言葉" + self._id_pattern = r"kokugo.jitenon.jp/word/p([0-9]+)$" + + def make_glossary(self, entry, media_dir): + soup = entry.get_page_soup() + self._remove_antonym_list_item(soup) + self._replace_number_icons(soup, media_dir) + self._replace_punctuation(soup) + self._add_internal_links(soup, entry) + self._decompose_table_rows(soup, entry) + self._insert_headword_line(soup, entry) + glossary = soup.body.prettify() + return glossary + + def _remove_antonym_list_item(self, soup): + for el in soup.find_all("li"): + if el.text == "対義語辞典": + el.decompose() + + def _replace_number_icons(self, soup, media_dir): + for el in soup.find_all("img"): + alt = el.attrs["alt"] + text = re.search(r"[0-9]+", alt).group(0) + el.name = "span" + el.string = text + del el.attrs["src"] + del el.attrs["alt"] + + def _do_display_yomikata_in_headword(self, entry): + return len(entry.yomikata) <= 10 + + +class JitenonYojiGlossary(JitenonGlossary): + def __init__(self): + super().__init__() + self._expression_header = "四字熟語" + self._id_pattern = r"yoji.jitenon.jp/yoji.?/([0-9]+)\.html$" + + def make_glossary(self, entry, media_dir): + soup = entry.get_page_soup() + self._replace_punctuation(soup) + self._add_internal_links(soup, entry) + self._decompose_table_rows(soup, entry) + self._insert_headword_line(soup, entry) + glossary = soup.body.prettify() + return glossary + + +class JitenonKotowazaGlossary(JitenonGlossary): + def __init__(self): + super().__init__() + self._expression_header = "言葉" + self._id_pattern = r"kotowaza.jitenon.jp/kotowaza/([0-9]+)\.php$" + + def make_glossary(self, entry, media_dir): + soup = entry.get_page_soup() + self._replace_punctuation(soup) + self._add_internal_links(soup, entry) + self._decompose_table_rows(soup, entry) + self._insert_headword_line(soup, entry) + glossary = soup.body.prettify() + return glossary diff --git a/bot/mdict/glossary/sankoku8.py b/bot/mdict/glossary/sankoku8.py new file mode 100644 index 0000000..8ee16f0 --- /dev/null +++ b/bot/mdict/glossary/sankoku8.py @@ -0,0 +1,137 @@ +import re +from bs4 import BeautifulSoup +from bot.data import load_mdict_name_conversion +from bot.name_conversion import convert_names + + +def make_glossary(entry, media_dir): + soup = entry.get_page_soup() + __reposition_marks(soup) + __remove_appendix_links(soup) + __convert_images(soup) + __remove_links_without_href(soup) + __convert_links(soup, entry) + __add_parent_link(soup, entry) + __add_homophone_links(soup, entry) + + name_conversion = load_mdict_name_conversion(entry.target) + convert_names(soup, name_conversion) + + glossary = soup.span.decode() + return glossary + + +def __reposition_marks(soup): + """These 表外字マーク symbols will be converted to rubies later, so they need to + be positioned after the corresponding text in order to appear correctly""" + for elm in soup.find_all("表外字"): + mark = elm.find("表外字マーク") + elm.append(mark) + for elm in soup.find_all("表外音訓"): + mark = elm.find("表外音訓マーク") + elm.append(mark) + + +def __remove_appendix_links(soup): + """This info would be useful and nice to have, but jitenbot currently + isn't designed to fetch and process these appendix files. It probably + wouldn't be possible to include them in Yomichan, but it would definitely + be possible for Mdict.""" + for elm in soup.find_all("a"): + if not elm.has_attr("href"): + continue + if elm.attrs["href"].startswith("appendix"): + elm.attrs["data-name"] = "a" + elm.attrs["data-href"] = elm.attrs["href"] + elm.name = "span" + del elm.attrs["href"] + + +def __convert_images(soup): + conversions = [ + ["svg-logo/重要語.svg", "*"], + ["svg-logo/最重要語.svg", "**"], + ["svg-logo/一般常識語.svg", "☆☆"], + ["svg-logo/追い込み.svg", ""], + ["svg-special/区切り線.svg", "|"], + ["svg-accent/平板.svg", "⎺"], + ["svg-accent/アクセント.svg", "⌝"], + ["svg-logo/アク.svg", "アク"], + ["svg-logo/丁寧.svg", "丁寧"], + ["svg-logo/可能.svg", "可能"], + ["svg-logo/尊敬.svg", "尊敬"], + ["svg-logo/接尾.svg", "接尾"], + ["svg-logo/接頭.svg", "接頭"], + ["svg-logo/表記.svg", "表記"], + ["svg-logo/謙譲.svg", "謙譲"], + ["svg-logo/区別.svg", "区別"], + ["svg-logo/由来.svg", "由来"], + ] + for conversion in conversions: + filename, text = conversion + for elm in soup.find_all("img", attrs={"src": filename}): + elm.attrs["data-name"] = elm.name + elm.attrs["data-src"] = elm.attrs["src"] + elm.name = "span" + elm.string = text + del elm.attrs["src"] + + +def __remove_links_without_href(soup): + for elm in soup.find_all("a"): + if elm.has_attr("href"): + continue + elm.attrs["data-name"] = elm.name + elm.name = "span" + + +def __convert_links(soup, entry): + for elm in soup.find_all("a"): + href = elm.attrs["href"].split(" ")[0] + if re.match(r"^#?[0-9]+(?:-[0-9A-F]{4})?$", href): + href = href.removeprefix("#") + ref_entry_id = entry.id_string_to_entry_id(href) + if ref_entry_id in entry.ID_TO_ENTRY: + ref_entry = entry.ID_TO_ENTRY[ref_entry_id] + else: + ref_entry = entry.ID_TO_ENTRY[(ref_entry_id[0], 0)] + gid = ref_entry.get_global_identifier() + elm.attrs["href"] = f"entry://{gid}" + elif re.match(r"^entry:", href): + pass + elif re.match(r"^https?:[\w\W]*", href): + pass + else: + raise Exception(f"Invalid href format: {href}") + + +def __add_parent_link(soup, entry): + elm = soup.find("親見出相当部") + if elm is not None: + parent_entry = entry.get_parent() + gid = parent_entry.get_global_identifier() + elm.attrs["href"] = f"entry://{gid}" + elm.attrs["data-name"] = elm.name + elm.name = "a" + + +def __add_homophone_links(soup, entry): + forward_link = ["←", entry.entry_id[0] + 1] + backward_link = ["→", entry.entry_id[0] - 1] + homophone_info_list = [ + ["svg-logo/homophone1.svg", [forward_link]], + ["svg-logo/homophone2.svg", [forward_link, backward_link]], + ["svg-logo/homophone3.svg", [backward_link]], + ] + for homophone_info in homophone_info_list: + filename, link_info = homophone_info + for elm in soup.find_all("img", attrs={"src": filename}): + for info in link_info: + text, link_id = info + link_entry = entry.ID_TO_ENTRY[(link_id, 0)] + gid = link_entry.get_global_identifier() + link = BeautifulSoup("", "xml").a + link.string = text + link.attrs["href"] = f"entry://{gid}" + elm.append(link) + elm.unwrap() diff --git a/bot/mdict/glossary/smk8.py b/bot/mdict/glossary/smk8.py new file mode 100644 index 0000000..613fc1b --- /dev/null +++ b/bot/mdict/glossary/smk8.py @@ -0,0 +1,67 @@ +import re + +from bot.soup import delete_soup_nodes +from bot.data import load_mdict_name_conversion +from bot.name_conversion import convert_names + + +def make_glossary(entry, media_dir): + soup = entry.get_page_soup() + __fill_alts(soup, entry) + __delete_unused_nodes(soup) + __convert_links(soup, entry) + __convert_priority_markers(soup) + + name_conversion = load_mdict_name_conversion(entry.target) + convert_names(soup, name_conversion) + + glossary = soup.span.decode() + return glossary + + +def __fill_alts(soup, entry): + names = ["親見出仮名", "親見出表記"] + if soup.find(names) is None: + return + parent_entry = entry.get_parent() + gid = parent_entry.get_global_identifier() + for el in soup.find_all(names): + el.name = "a" + alt = el.attrs["alt"] + el.string = alt + el.attrs["href"] = f"entry://{gid}" + del el.attrs["alt"] + + +def __delete_unused_nodes(soup): + for name in ["連濁"]: + delete_soup_nodes(soup, name) + + +def __convert_links(soup, entry): + for el in soup.find_all("a"): + href = el.attrs["href"] + if href.startswith("$"): + el.unwrap() + elif re.match(r"^[0-9]+(?:-[0-9A-F]{4})?$", href): + ref_entry_id = entry.id_string_to_entry_id(href) + ref_entry = entry.ID_TO_ENTRY[ref_entry_id] + gid = ref_entry.get_global_identifier() + el.attrs["href"] = f"entry://{gid}" + elif re.match(r"^[0-9]+[ab]?\.aac$", href): + el.attrs["href"] = f"sound://audio/{href}" + elif re.match(r"^entry:", href): + pass + elif re.match(r"^https?:[\w\W]*", href): + pass + else: + raise Exception(f"Invalid href format: {href}") + + +def __convert_priority_markers(soup): + for el in soup.find_all("img", attrs={"alt": "*"}): + el.name = "span" + el.string = "*" + for el in soup.find_all("img", attrs={"alt": "⁑"}): + el.name = "span" + el.string = "**" diff --git a/bot/mdict/terms/base/jitenon.py b/bot/mdict/terms/base/jitenon.py new file mode 100644 index 0000000..4f255bf --- /dev/null +++ b/bot/mdict/terms/base/jitenon.py @@ -0,0 +1,20 @@ +from bot.mdict.terms.base.terminator import BaseTerminator + + +class JitenonTerminator(BaseTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = None + + def _glossary(self, entry): + if entry.entry_id in self._glossary_cache: + return self._glossary_cache[entry.entry_id] + glossary = self._glossary_maker.make_glossary(entry, self._media_dir) + self._glossary_cache[entry.entry_id] = glossary + return glossary + + def _link_glossary_parameters(self, entry): + return [] + + def _subentry_lists(self, entry): + return [] diff --git a/bot/mdict/terms/base/terminator.py b/bot/mdict/terms/base/terminator.py new file mode 100644 index 0000000..945a65b --- /dev/null +++ b/bot/mdict/terms/base/terminator.py @@ -0,0 +1,83 @@ +import re +from abc import abstractmethod, ABC + + +class BaseTerminator(ABC): + def __init__(self, target): + self._target = target + self._glossary_cache = {} + self._media_dir = None + + def set_media_dir(self, media_dir): + self._media_dir = media_dir + + def make_terms(self, entry): + gid = entry.get_global_identifier() + glossary = self.__get_full_glossary(entry) + terms = [[gid, glossary]] + keys = self.__get_keys(entry) + link = f"@@@LINK={gid}" + for key in keys: + if key.strip() != "": + terms.append([key, link]) + for subentry_list in self._subentry_lists(entry): + for subentry in subentry_list: + for term in self.make_terms(subentry): + terms.append(term) + return terms + + def __get_full_glossary(self, entry): + glossary = [] + style_link = f"" + glossary.append(style_link) + glossary.append(self._glossary(entry)) + + for x in self._link_glossary_parameters(entry): + (subentries, list_title) = x + if len(subentries) == 0: + continue + items = [] + for subentry in subentries: + exp = subentry.get_first_expression() + gid = subentry.get_global_identifier() + item = f"
  • {exp}
  • " + items.append(item) + link_glossary = f"
    {list_title}
    " + glossary.append(link_glossary) + return "\n".join(glossary) + + def __get_keys(self, entry): + keys = set() + headwords = entry.get_headwords() + for reading, expressions in headwords.items(): + stripped_reading = reading.strip() + keys.add(stripped_reading) + if re.match(r"^[ぁ-ヿ、]+$", stripped_reading): + kana_only_key = f"{stripped_reading}【∅】" + else: + kana_only_key = "" + if len(expressions) == 0: + keys.add(kana_only_key) + for expression in expressions: + stripped_expression = expression.strip() + keys.add(stripped_expression) + if stripped_expression == "": + keys.add(kana_only_key) + elif stripped_expression == stripped_reading: + keys.add(kana_only_key) + else: + combo_key = f"{stripped_reading}【{stripped_expression}】" + keys.add(combo_key) + return keys + + @abstractmethod + def _glossary(self, entry): + raise NotImplementedError + + @abstractmethod + def _link_glossary_parameters(self, entry): + raise NotImplementedError + + @abstractmethod + def _subentry_lists(self, entry): + raise NotImplementedError diff --git a/bot/mdict/terms/daijirin2.py b/bot/mdict/terms/daijirin2.py new file mode 100644 index 0000000..640b520 --- /dev/null +++ b/bot/mdict/terms/daijirin2.py @@ -0,0 +1,23 @@ +from bot.mdict.terms.base.terminator import BaseTerminator +from bot.mdict.glossary.daijirin2 import make_glossary + + +class Terminator(BaseTerminator): + def _glossary(self, entry): + if entry.entry_id in self._glossary_cache: + return self._glossary_cache[entry.entry_id] + glossary = make_glossary(entry, self._media_dir) + self._glossary_cache[entry.entry_id] = glossary + return glossary + + def _link_glossary_parameters(self, entry): + return [ + [entry.children, "子項目"], + [entry.phrases, "句項目"], + ] + + def _subentry_lists(self, entry): + return [ + entry.children, + entry.phrases, + ] diff --git a/bot/mdict/terms/jitenon_kokugo.py b/bot/mdict/terms/jitenon_kokugo.py new file mode 100644 index 0000000..2a44b7b --- /dev/null +++ b/bot/mdict/terms/jitenon_kokugo.py @@ -0,0 +1,8 @@ +from bot.mdict.terms.base.jitenon import JitenonTerminator +from bot.mdict.glossary.jitenon import JitenonKokugoGlossary + + +class Terminator(JitenonTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = JitenonKokugoGlossary() diff --git a/bot/mdict/terms/jitenon_kotowaza.py b/bot/mdict/terms/jitenon_kotowaza.py new file mode 100644 index 0000000..3492a49 --- /dev/null +++ b/bot/mdict/terms/jitenon_kotowaza.py @@ -0,0 +1,8 @@ +from bot.mdict.terms.base.jitenon import JitenonTerminator +from bot.mdict.glossary.jitenon import JitenonKotowazaGlossary + + +class Terminator(JitenonTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = JitenonKotowazaGlossary() diff --git a/bot/mdict/terms/jitenon_yoji.py b/bot/mdict/terms/jitenon_yoji.py new file mode 100644 index 0000000..a4175a1 --- /dev/null +++ b/bot/mdict/terms/jitenon_yoji.py @@ -0,0 +1,8 @@ +from bot.mdict.terms.base.jitenon import JitenonTerminator +from bot.mdict.glossary.jitenon import JitenonYojiGlossary + + +class Terminator(JitenonTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = JitenonYojiGlossary() diff --git a/bot/mdict/terms/sankoku8.py b/bot/mdict/terms/sankoku8.py new file mode 100644 index 0000000..71a3b8f --- /dev/null +++ b/bot/mdict/terms/sankoku8.py @@ -0,0 +1,23 @@ +from bot.mdict.terms.base.terminator import BaseTerminator +from bot.mdict.glossary.sankoku8 import make_glossary + + +class Terminator(BaseTerminator): + def _glossary(self, entry): + if entry.entry_id in self._glossary_cache: + return self._glossary_cache[entry.entry_id] + glossary = make_glossary(entry, self._media_dir) + self._glossary_cache[entry.entry_id] = glossary + return glossary + + def _link_glossary_parameters(self, entry): + return [ + [entry.children, "子項目"], + [entry.phrases, "句項目"], + ] + + def _subentry_lists(self, entry): + return [ + entry.children, + entry.phrases, + ] diff --git a/bot/mdict/terms/smk8.py b/bot/mdict/terms/smk8.py new file mode 100644 index 0000000..ef2b7a2 --- /dev/null +++ b/bot/mdict/terms/smk8.py @@ -0,0 +1,24 @@ +from bot.mdict.terms.base.terminator import BaseTerminator +from bot.mdict.glossary.smk8 import make_glossary + + +class Terminator(BaseTerminator): + def _glossary(self, entry): + if entry.entry_id in self._glossary_cache: + return self._glossary_cache[entry.entry_id] + glossary = make_glossary(entry, self._media_dir) + self._glossary_cache[entry.entry_id] = glossary + return glossary + + def _link_glossary_parameters(self, entry): + return [ + [entry.children, "子項目"], + [entry.phrases, "句項目"], + ] + + def _subentry_lists(self, entry): + return [ + entry.children, + entry.phrases, + entry.kanjis, + ] diff --git a/bot/yomichan/glossary/name_conversion.py b/bot/name_conversion.py similarity index 88% rename from bot/yomichan/glossary/name_conversion.py rename to bot/name_conversion.py index 776d65e..2c9b808 100644 --- a/bot/yomichan/glossary/name_conversion.py +++ b/bot/name_conversion.py @@ -30,7 +30,7 @@ def __apply_name_conversion_procedures(soup, procedures): "has_previous_sibling": __has_previous_sibling, "replace": __replace, "wrap": __wrap, - "add_ruby_text": __add_ruby_text, + "insert_span": __insert_span, } for procedure in procedures: function = functions[procedure["procedure_name"]] @@ -92,10 +92,9 @@ def __wrap(soup, l_wrap, r_wrap): soup.string = f"{l_wrap}{soup.text}{r_wrap}" -def __add_ruby_text(soup, mark, style): - if style.strip() != "": - markup = f"{mark}" - else: - markup = f"{mark}" - rt_soup = BeautifulSoup(markup, "xml") - soup.append(rt_soup.rt) +def __insert_span(soup, attr_name, attr_val): + span_markup = f"" + span_soup = BeautifulSoup(span_markup, "xml") + for content in reversed(soup.contents): + span_soup.span.insert(0, content.extract()) + soup.append(span_soup.span) diff --git a/bot/targets.py b/bot/targets.py index 3c4c571..157786f 100644 --- a/bot/targets.py +++ b/bot/targets.py @@ -7,3 +7,4 @@ class Targets(Enum): JITENON_KOTOWAZA = "jitenon-kotowaza" SMK8 = "smk8" DAIJIRIN2 = "daijirin2" + SANKOKU8 = "sankoku8" diff --git a/bot/time.py b/bot/time.py new file mode 100644 index 0000000..f8dae94 --- /dev/null +++ b/bot/time.py @@ -0,0 +1,5 @@ +import time + + +def timestamp(): + return time.strftime('%X') diff --git a/bot/yomichan/exporters/export.py b/bot/yomichan/exporters/base/exporter.py similarity index 57% rename from bot/yomichan/exporters/export.py rename to bot/yomichan/exporters/base/exporter.py index 4658030..5e4e870 100644 --- a/bot/yomichan/exporters/export.py +++ b/bot/yomichan/exporters/base/exporter.py @@ -1,22 +1,27 @@ import json import os import shutil +import copy from pathlib import Path -from datetime import datetime +from abc import ABC, abstractmethod + +import fastjsonschema from platformdirs import user_documents_dir, user_cache_dir +from bot.time import timestamp from bot.data import load_yomichan_metadata -from bot.yomichan.terms.factory import new_terminator +from bot.data import load_yomichan_term_schema +from bot.factory import new_yomichan_terminator -class Exporter: +class BaseExporter(ABC): def __init__(self, target): self._target = target - self._terminator = new_terminator(target) + self._terminator = new_yomichan_terminator(target) self._build_dir = None self._terms_per_file = 2000 - def export(self, entries, image_dir): + def export(self, entries, image_dir, validate): self.__init_build_image_dir(image_dir) meta = load_yomichan_metadata() index = meta[self._target.value]["index"] @@ -24,25 +29,45 @@ class Exporter: index["attribution"] = self._get_attribution(entries) tags = meta[self._target.value]["tags"] terms = self.__get_terms(entries) + if validate: + self.__validate_terms(terms) self.__make_dictionary(terms, index, tags) + @abstractmethod + def _get_revision(self, entries): + raise NotImplementedError + + @abstractmethod + def _get_attribution(self, entries): + raise NotImplementedError + def _get_build_dir(self): if self._build_dir is not None: return self._build_dir cache_dir = user_cache_dir("jitenbot") build_directory = os.path.join(cache_dir, "yomichan_build") + print(f"{timestamp()} Initializing build directory `{build_directory}`") if Path(build_directory).is_dir(): shutil.rmtree(build_directory) os.makedirs(build_directory) self._build_dir = build_directory return self._build_dir + def __get_invalid_term_dir(self): + cache_dir = user_cache_dir("jitenbot") + log_dir = os.path.join(cache_dir, "invalid_yomichan_terms") + if Path(log_dir).is_dir(): + shutil.rmtree(log_dir) + os.makedirs(log_dir) + return log_dir + def __init_build_image_dir(self, image_dir): build_dir = self._get_build_dir() build_img_dir = os.path.join(build_dir, self._target.value) if image_dir is not None: - print("Copying image files to build directory...") + print(f"{timestamp()} Copying media files to build directory...") shutil.copytree(image_dir, build_img_dir) + print(f"{timestamp()} Finished copying files") else: os.makedirs(build_img_dir) self._terminator.set_image_dir(build_img_dir) @@ -51,7 +76,7 @@ class Exporter: terms = [] entries_len = len(entries) for idx, entry in enumerate(entries): - update = f"Creating Yomichan terms for entry {idx+1}/{entries_len}" + update = f"\tCreating Yomichan terms for entry {idx+1}/{entries_len}" print(update, end='\r', flush=True) new_terms = self._terminator.make_terms(entry) for term in new_terms: @@ -59,8 +84,29 @@ class Exporter: print() return terms + def __validate_terms(self, terms): + print(f"{timestamp()} Making a copy of term data for validation...") + terms_copy = copy.deepcopy(terms) # because validator will alter data! + term_count = len(terms_copy) + log_dir = self.__get_invalid_term_dir() + schema = load_yomichan_term_schema() + validator = fastjsonschema.compile(schema) + failure_count = 0 + for idx, term in enumerate(terms_copy): + update = f"\tValidating term {idx+1}/{term_count}" + print(update, end='\r', flush=True) + try: + validator([term]) + except fastjsonschema.JsonSchemaException: + failure_count += 1 + term_file = os.path.join(log_dir, f"{idx}.json") + with open(term_file, "w", encoding='utf8') as f: + json.dump([term], f, indent=4, ensure_ascii=False) + print(f"\n{timestamp()} Finished validating with {failure_count} error{'' if failure_count == 1 else 's'}") + if failure_count > 0: + print(f"{timestamp()} Invalid terms saved to `{log_dir}` for debugging") + def __make_dictionary(self, terms, index, tags): - print(f"Exporting {len(terms)} Yomichan terms...") self.__write_term_banks(terms) self.__write_index(index) self.__write_tag_bank(tags) @@ -68,14 +114,18 @@ class Exporter: self.__rm_build_dir() def __write_term_banks(self, terms): + print(f"{timestamp()} Exporting {len(terms)} JSON terms") build_dir = self._get_build_dir() max_i = int(len(terms) / self._terms_per_file) + 1 for i in range(max_i): + update = f"\tWriting terms to term bank {i+1}/{max_i}" + print(update, end='\r', flush=True) + start = self._terms_per_file * i + end = self._terms_per_file * (i + 1) term_file = os.path.join(build_dir, f"term_bank_{i+1}.json") with open(term_file, "w", encoding='utf8') as f: - start = self._terms_per_file * i - end = self._terms_per_file * (i + 1) json.dump(terms[start:end], f, indent=4, ensure_ascii=False) + print() def __write_index(self, index): build_dir = self._get_build_dir() @@ -93,7 +143,8 @@ class Exporter: def __write_archive(self, filename): archive_format = "zip" - out_dir = os.path.join(user_documents_dir(), "jitenbot") + print(f"{timestamp()} Archiving data to {archive_format.upper()} file...") + out_dir = os.path.join(user_documents_dir(), "jitenbot", "yomichan") if not Path(out_dir).is_dir(): os.makedirs(out_dir) out_file = f"{filename}.{archive_format}" @@ -103,67 +154,8 @@ class Exporter: base_filename = os.path.join(out_dir, filename) build_dir = self._get_build_dir() shutil.make_archive(base_filename, archive_format, build_dir) - print(f"Dictionary file saved to {out_filepath}") + print(f"{timestamp()} Dictionary file saved to `{out_filepath}`") def __rm_build_dir(self): build_dir = self._get_build_dir() shutil.rmtree(build_dir) - - -class JitenonExporter(Exporter): - def __init__(self, target): - super().__init__(target) - - def _get_revision(self, entries): - modified_date = None - for entry in entries: - if modified_date is None or entry.modified_date > modified_date: - modified_date = entry.modified_date - revision = f"{self._target.value};{modified_date}" - return revision - - def _get_attribution(self, entries): - modified_date = None - for entry in entries: - if modified_date is None or entry.modified_date > modified_date: - attribution = entry.attribution - return attribution - - -class JitenonKokugoExporter(JitenonExporter): - def __init__(self, target): - super().__init__(target) - - -class JitenonYojiExporter(JitenonExporter): - def __init__(self, target): - super().__init__(target) - - -class JitenonKotowazaExporter(JitenonExporter): - def __init__(self, target): - super().__init__(target) - - -class Smk8Exporter(Exporter): - def __init__(self, target): - super().__init__(target) - - def _get_revision(self, entries): - timestamp = datetime.now().strftime("%Y-%m-%d") - return f"{self._target.value};{timestamp}" - - def _get_attribution(self, entries): - return "© Sanseido Co., LTD. 2020" - - -class Daijirin2Exporter(Exporter): - def __init__(self, target): - super().__init__(target) - - def _get_revision(self, entries): - timestamp = datetime.now().strftime("%Y-%m-%d") - return f"{self._target.value};{timestamp}" - - def _get_attribution(self, entries): - return "© Sanseido Co., LTD. 2019" diff --git a/bot/yomichan/exporters/base/jitenon.py b/bot/yomichan/exporters/base/jitenon.py new file mode 100644 index 0000000..80f0175 --- /dev/null +++ b/bot/yomichan/exporters/base/jitenon.py @@ -0,0 +1,18 @@ +from bot.yomichan.exporters.base.exporter import BaseExporter + + +class JitenonExporter(BaseExporter): + def _get_revision(self, entries): + modified_date = None + for entry in entries: + if modified_date is None or entry.modified_date > modified_date: + modified_date = entry.modified_date + revision = f"{self._target.value};{modified_date}" + return revision + + def _get_attribution(self, entries): + modified_date = None + for entry in entries: + if modified_date is None or entry.modified_date > modified_date: + attribution = entry.attribution + return attribution diff --git a/bot/yomichan/exporters/base/monokakido.py b/bot/yomichan/exporters/base/monokakido.py new file mode 100644 index 0000000..5c5f3fa --- /dev/null +++ b/bot/yomichan/exporters/base/monokakido.py @@ -0,0 +1,8 @@ +from datetime import datetime +from bot.yomichan.exporters.base.exporter import BaseExporter + + +class MonokakidoExporter(BaseExporter): + def _get_revision(self, entries): + timestamp = datetime.now().strftime("%Y-%m-%d") + return f"{self._target.value};{timestamp}" diff --git a/bot/yomichan/exporters/daijirin2.py b/bot/yomichan/exporters/daijirin2.py new file mode 100644 index 0000000..7115342 --- /dev/null +++ b/bot/yomichan/exporters/daijirin2.py @@ -0,0 +1,6 @@ +from bot.yomichan.exporters.base.monokakido import MonokakidoExporter + + +class Exporter(MonokakidoExporter): + def _get_attribution(self, entries): + return "© Sanseido Co., LTD. 2019" diff --git a/bot/yomichan/exporters/factory.py b/bot/yomichan/exporters/factory.py deleted file mode 100644 index 5ab9a6a..0000000 --- a/bot/yomichan/exporters/factory.py +++ /dev/null @@ -1,18 +0,0 @@ -from bot.targets import Targets - -from bot.yomichan.exporters.export import JitenonKokugoExporter -from bot.yomichan.exporters.export import JitenonYojiExporter -from bot.yomichan.exporters.export import JitenonKotowazaExporter -from bot.yomichan.exporters.export import Smk8Exporter -from bot.yomichan.exporters.export import Daijirin2Exporter - - -def new_exporter(target): - exporter_map = { - Targets.JITENON_KOKUGO: JitenonKokugoExporter, - Targets.JITENON_YOJI: JitenonYojiExporter, - Targets.JITENON_KOTOWAZA: JitenonKotowazaExporter, - Targets.SMK8: Smk8Exporter, - Targets.DAIJIRIN2: Daijirin2Exporter, - } - return exporter_map[target](target) diff --git a/bot/yomichan/exporters/jitenon_kokugo.py b/bot/yomichan/exporters/jitenon_kokugo.py new file mode 100644 index 0000000..0a3ef7a --- /dev/null +++ b/bot/yomichan/exporters/jitenon_kokugo.py @@ -0,0 +1,5 @@ +from bot.yomichan.exporters.base.jitenon import JitenonExporter + + +class Exporter(JitenonExporter): + pass diff --git a/bot/yomichan/exporters/jitenon_kotowaza.py b/bot/yomichan/exporters/jitenon_kotowaza.py new file mode 100644 index 0000000..0a3ef7a --- /dev/null +++ b/bot/yomichan/exporters/jitenon_kotowaza.py @@ -0,0 +1,5 @@ +from bot.yomichan.exporters.base.jitenon import JitenonExporter + + +class Exporter(JitenonExporter): + pass diff --git a/bot/yomichan/exporters/jitenon_yoji.py b/bot/yomichan/exporters/jitenon_yoji.py new file mode 100644 index 0000000..0a3ef7a --- /dev/null +++ b/bot/yomichan/exporters/jitenon_yoji.py @@ -0,0 +1,5 @@ +from bot.yomichan.exporters.base.jitenon import JitenonExporter + + +class Exporter(JitenonExporter): + pass diff --git a/bot/yomichan/exporters/sankoku8.py b/bot/yomichan/exporters/sankoku8.py new file mode 100644 index 0000000..b33c389 --- /dev/null +++ b/bot/yomichan/exporters/sankoku8.py @@ -0,0 +1,6 @@ +from bot.yomichan.exporters.base.monokakido import MonokakidoExporter + + +class Exporter(MonokakidoExporter): + def _get_attribution(self, entries): + return "© Sanseido Co., LTD. 2021" diff --git a/bot/yomichan/exporters/smk8.py b/bot/yomichan/exporters/smk8.py new file mode 100644 index 0000000..7f71aa3 --- /dev/null +++ b/bot/yomichan/exporters/smk8.py @@ -0,0 +1,6 @@ +from bot.yomichan.exporters.base.monokakido import MonokakidoExporter + + +class Exporter(MonokakidoExporter): + def _get_attribution(self, entries): + return "© Sanseido Co., LTD. 2020" diff --git a/bot/yomichan/glossary/daijirin2.py b/bot/yomichan/glossary/daijirin2.py index f2b6f2c..178de00 100644 --- a/bot/yomichan/glossary/daijirin2.py +++ b/bot/yomichan/glossary/daijirin2.py @@ -1,14 +1,15 @@ import re import os -from bs4 import BeautifulSoup from functools import cache from pathlib import Path -import bot.icons as Icons +from bs4 import BeautifulSoup + +import bot.yomichan.glossary.icons as Icons from bot.soup import delete_soup_nodes -from bot.data import load_daijirin2_yomichan_name_conversion +from bot.data import load_yomichan_name_conversion from bot.yomichan.glossary.gloss import make_gloss -from bot.yomichan.glossary.name_conversion import convert_names +from bot.name_conversion import convert_names def make_glossary(entry, image_dir): @@ -26,7 +27,7 @@ def make_glossary(entry, image_dir): __convert_daigoginum(soup, image_dir) __convert_jundaigoginum(soup, image_dir) - name_conversion = load_daijirin2_yomichan_name_conversion() + name_conversion = load_yomichan_name_conversion(entry.target) convert_names(soup, name_conversion) gloss = make_gloss(soup.span) @@ -111,8 +112,8 @@ def __convert_gaiji(soup, image_dir): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, @@ -150,8 +151,8 @@ def __convert_logos(soup, image_dir): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, @@ -174,8 +175,8 @@ def __convert_kanjion_logos(soup, image_dir): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, @@ -198,8 +199,8 @@ def __convert_daigoginum(soup, image_dir): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, @@ -222,8 +223,8 @@ def __convert_jundaigoginum(soup, image_dir): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, diff --git a/bot/yomichan/glossary/gloss.py b/bot/yomichan/glossary/gloss.py index c94e5aa..705cd7e 100644 --- a/bot/yomichan/glossary/gloss.py +++ b/bot/yomichan/glossary/gloss.py @@ -76,6 +76,7 @@ def __get_attributes(attrs): def __get_style(inline_style_string): + # pylint: disable=no-member style = {} parsed_style = parseStyle(inline_style_string) if parsed_style.fontStyle != "": @@ -100,7 +101,7 @@ def __get_style(inline_style_string): "marginLeft": parsed_style.marginLeft, } for key, val in margins.items(): - m = re.search(r"(\d+(\.\d*)?|\.\d+)em", val) + m = re.search(r"(-?\d+(\.\d*)?|-?\.\d+)em", val) if m: style[key] = float(m.group(1)) diff --git a/bot/icons.py b/bot/yomichan/glossary/icons.py similarity index 69% rename from bot/icons.py rename to bot/yomichan/glossary/icons.py index 2e83821..4cf46c2 100644 --- a/bot/icons.py +++ b/bot/yomichan/glossary/icons.py @@ -26,6 +26,27 @@ def make_monochrome_fill_rectangle(path, text): f.write(svg) +@cache +def make_accent(path): + svg = __svg_accent() + with open(path, "w", encoding="utf-8") as f: + f.write(svg) + + +@cache +def make_heiban(path): + svg = __svg_heiban() + with open(path, "w", encoding="utf-8") as f: + f.write(svg) + + +@cache +def make_red_char(path, char): + svg = __svg_red_character(char) + with open(path, "w", encoding="utf-8") as f: + f.write(svg) + + def __calculate_svg_ratio(path): with open(path, "r", encoding="utf-8") as f: xml = f.read() @@ -82,3 +103,30 @@ def __svg_masked_rectangle(text): fill='black' mask='url(#a)'/> """ return svg.strip() + + +def __svg_heiban(): + svg = f""" + + +""" + return svg.strip() + + +def __svg_accent(): + svg = f""" + + + +""" + return svg.strip() + + +def __svg_red_character(char): + svg = f""" + + {char} +""" + return svg.strip() diff --git a/bot/yomichan/glossary/jitenon.py b/bot/yomichan/glossary/jitenon.py index 6e3a192..61111b4 100644 --- a/bot/yomichan/glossary/jitenon.py +++ b/bot/yomichan/glossary/jitenon.py @@ -2,7 +2,7 @@ import re import os from bs4 import BeautifulSoup -import bot.icons as Icons +import bot.yomichan.glossary.icons as Icons from bot.yomichan.glossary.gloss import make_gloss @@ -58,9 +58,9 @@ class JitenonGlossary(): if self._do_display_yomikata_in_headword(entry): tr.decompose() elif tr.th.text == "意味": - imi = tr.td - imi.name = "div" - soup.body.insert(0, imi) + definition = tr.td + definition.name = "div" + soup.body.insert(0, definition) tr.decompose() if soup.find("tr") is None: soup.table.decompose() @@ -118,8 +118,8 @@ class JitenonKokugoGlossary(JitenonGlossary): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, diff --git a/bot/yomichan/glossary/sankoku8.py b/bot/yomichan/glossary/sankoku8.py new file mode 100644 index 0000000..ceca5d7 --- /dev/null +++ b/bot/yomichan/glossary/sankoku8.py @@ -0,0 +1,344 @@ +import re +import os +from bs4 import BeautifulSoup + +import bot.yomichan.glossary.icons as Icons +from bot.data import load_yomichan_name_conversion +from bot.yomichan.glossary.gloss import make_gloss +from bot.name_conversion import convert_names + + +def make_glossary(entry, media_dir): + soup = entry.get_page_soup() + __remove_glyph_styles(soup) + __reposition_marks(soup) + __remove_links_without_href(soup) + __remove_appendix_links(soup) + __convert_links(soup, entry) + __add_parent_link(soup, entry) + __add_homophone_links(soup, entry) + __convert_images_to_text(soup) + __text_parens_to_images(soup, media_dir) + __replace_icons(soup, media_dir) + __replace_accent_symbols(soup, media_dir) + __convert_gaiji(soup, media_dir) + __convert_graphics(soup, media_dir) + __convert_number_icons(soup, media_dir) + + name_conversion = load_yomichan_name_conversion(entry.target) + convert_names(soup, name_conversion) + + gloss = make_gloss(soup.span) + glossary = [gloss] + return glossary + + +def __remove_glyph_styles(soup): + """The css_parser library will emit annoying warning messages + later if it sees these glyph character styles""" + for elm in soup.find_all("glyph"): + if elm.has_attr("style"): + elm["data-style"] = elm.attrs["style"] + del elm.attrs["style"] + + +def __reposition_marks(soup): + """These マーク symbols will be converted to rubies later, so they need to + be positioned after the corresponding text in order to appear correctly""" + for elm in soup.find_all("表外字"): + mark = elm.find("表外字マーク") + elm.append(mark) + for elm in soup.find_all("表外音訓"): + mark = elm.find("表外音訓マーク") + elm.append(mark) + + +def __remove_links_without_href(soup): + for elm in soup.find_all("a"): + if elm.has_attr("href"): + continue + elm.attrs["data-name"] = elm.name + elm.name = "span" + + +def __remove_appendix_links(soup): + for elm in soup.find_all("a"): + if elm.attrs["href"].startswith("appendix"): + elm.unwrap() + + +def __convert_links(soup, entry): + for elm in soup.find_all("a"): + href = elm.attrs["href"].split(" ")[0] + href = href.removeprefix("#") + if not re.match(r"^[0-9]+(?:-[0-9A-F]{4})?$", href): + raise Exception(f"Invalid href format: {href}") + ref_entry_id = entry.id_string_to_entry_id(href) + if ref_entry_id in entry.ID_TO_ENTRY: + ref_entry = entry.ID_TO_ENTRY[ref_entry_id] + else: + ref_entry = entry.ID_TO_ENTRY[(ref_entry_id[0], 0)] + expression = ref_entry.get_first_expression() + elm.attrs["href"] = f"?query={expression}&wildcards=off" + + +def __add_parent_link(soup, entry): + elm = soup.find("親見出相当部") + if elm is not None: + parent_entry = entry.get_parent() + expression = parent_entry.get_first_expression() + elm.attrs["href"] = f"?query={expression}&wildcards=off" + elm.name = "a" + + +def __add_homophone_links(soup, entry): + forward_link = ["←", entry.entry_id[0] + 1] + backward_link = ["→", entry.entry_id[0] - 1] + homophone_info_list = [ + ["svg-logo/homophone1.svg", [forward_link]], + ["svg-logo/homophone2.svg", [forward_link, backward_link]], + ["svg-logo/homophone3.svg", [backward_link]], + ] + for homophone_info in homophone_info_list: + filename, link_info = homophone_info + for elm in soup.find_all("img", attrs={"src": filename}): + for info in link_info: + text, link_id = info + link_entry = entry.ID_TO_ENTRY[(link_id, 0)] + expression = link_entry.get_first_expression() + link = BeautifulSoup("", "xml").a + link.string = text + link.attrs["href"] = f"?query={expression}&wildcards=off" + elm.append(link) + elm.unwrap() + + +def __convert_images_to_text(soup): + conversions = [ + ["svg-logo/重要語.svg", "*", "vertical-align: super; font-size: 0.6em"], + ["svg-logo/最重要語.svg", "**", "vertical-align: super; font-size: 0.6em"], + ["svg-logo/一般常識語.svg", "☆☆", "vertical-align: super; font-size: 0.6em"], + ["svg-logo/追い込み.svg", "", ""], + ["svg-special/区切り線.svg", "|", ""], + ] + for conversion in conversions: + filename, text, style = conversion + for elm in soup.find_all("img", attrs={"src": filename}): + if text == "": + elm.unwrap() + continue + if style != "": + elm.attrs["style"] = style + elm.attrs["data-name"] = elm.name + elm.attrs["data-src"] = elm.attrs["src"] + elm.name = "span" + elm.string = text + del elm.attrs["src"] + + +def __text_parens_to_images(soup, media_dir): + for elm in soup.find_all("red"): + char = elm.text + if char not in ["(", ")"]: + continue + filename = f"red_{char}.svg" + path = os.path.join(media_dir, filename) + Icons.make_red_char(path, char) + ratio = Icons.calculate_ratio(path) + img = BeautifulSoup("", "xml").img + img.attrs = { + "height": 1.0, + "width": ratio, + "sizeUnits": "em", + "collapsible": False, + "collapsed": False, + "background": False, + "appearance": "auto", + "path": f"{os.path.basename(media_dir)}/{filename}", + } + elm.attrs["data-name"] = elm.name + elm.name = "span" + elm.string = "" + elm.append(img) + elm.attrs["style"] = "vertical-align: text-bottom;" + + +def __replace_icons(soup, media_dir): + cls_to_appearance = { + "default": "monochrome", + "fill": "monochrome", + "red": "auto", + "redfill": "auto", + "none": "monochrome", + } + icon_info_list = [ + ["svg-logo/アク.svg", "アク", "default"], + ["svg-logo/丁寧.svg", "丁寧", "default"], + ["svg-logo/可能.svg", "可能", "default"], + ["svg-logo/尊敬.svg", "尊敬", "default"], + ["svg-logo/接尾.svg", "接尾", "default"], + ["svg-logo/接頭.svg", "接頭", "default"], + ["svg-logo/表記.svg", "表記", "default"], + ["svg-logo/謙譲.svg", "謙譲", "default"], + ["svg-logo/区別.svg", "区別", "redfill"], + ["svg-logo/由来.svg", "由来", "redfill"], + ["svg-logo/人.svg", "", "none"], + ["svg-logo/他.svg", "", "none"], + ["svg-logo/動.svg", "", "none"], + ["svg-logo/名.svg", "", "none"], + ["svg-logo/句.svg", "", "none"], + ["svg-logo/派.svg", "", "none"], + ["svg-logo/自.svg", "", "none"], + ["svg-logo/連.svg", "", "none"], + ["svg-logo/造.svg", "", "none"], + ["svg-logo/造2.svg", "", "none"], + ["svg-logo/造3.svg", "", "none"], + ["svg-logo/百科.svg", "", "none"], + ] + for icon_info in icon_info_list: + src, text, cls = icon_info + for elm in soup.find_all("img", attrs={"src": src}): + path = media_dir + for part in src.split("/"): + path = os.path.join(path, part) + __make_rectangle(path, text, cls) + ratio = Icons.calculate_ratio(path) + img = BeautifulSoup("", "xml").img + img.attrs = { + "height": 1.0, + "width": ratio, + "sizeUnits": "em", + "collapsible": False, + "collapsed": False, + "background": False, + "appearance": cls_to_appearance[cls], + "title": elm.attrs["alt"] if elm.has_attr("alt") else "", + "path": f"{os.path.basename(media_dir)}/{src}", + } + elm.name = "span" + elm.clear() + elm.append(img) + elm.attrs["style"] = "vertical-align: text-bottom; margin-right: 0.25em;" + + +def __replace_accent_symbols(soup, media_dir): + accent_info_list = [ + ["svg-accent/平板.svg", Icons.make_heiban], + ["svg-accent/アクセント.svg", Icons.make_accent], + ] + for info in accent_info_list: + src, write_svg_function = info + for elm in soup.find_all("img", attrs={"src": src}): + path = media_dir + for part in src.split("/"): + path = os.path.join(path, part) + write_svg_function(path) + ratio = Icons.calculate_ratio(path) + img = BeautifulSoup("", "xml").img + img.attrs = { + "height": 1.0, + "width": ratio, + "sizeUnits": "em", + "collapsible": False, + "collapsed": False, + "background": False, + "appearance": "auto", + "path": f"{os.path.basename(media_dir)}/{src}", + } + elm.name = "span" + elm.clear() + elm.append(img) + elm.attrs["style"] = "vertical-align: super; margin-left: -0.25em;" + + +def __convert_gaiji(soup, media_dir): + for elm in soup.find_all("img"): + if not elm.has_attr("src"): + continue + src = elm.attrs["src"] + if src.startswith("graphics"): + continue + path = media_dir + for part in src.split("/"): + if part.strip() == "": + continue + path = os.path.join(path, part) + ratio = Icons.calculate_ratio(path) + img = BeautifulSoup("", "xml").img + img.attrs = { + "height": 1.0, + "width": ratio, + "sizeUnits": "em", + "collapsible": False, + "collapsed": False, + "background": False, + "appearance": "monochrome", + "title": elm.attrs["alt"] if elm.has_attr("alt") else "", + "path": f"{os.path.basename(media_dir)}/{src}", + } + elm.name = "span" + elm.clear() + elm.append(img) + elm.attrs["style"] = "vertical-align: text-bottom;" + + +def __convert_graphics(soup, media_dir): + for elm in soup.find_all("img"): + if not elm.has_attr("src"): + continue + src = elm.attrs["src"] + if not src.startswith("graphics"): + continue + elm.attrs = { + "collapsible": True, + "collapsed": True, + "title": elm.attrs["alt"] if elm.has_attr("alt") else "", + "path": f"{os.path.basename(media_dir)}/{src}", + "src": src, + } + + +def __convert_number_icons(soup, media_dir): + for elm in soup.find_all("大語義番号"): + if elm.find_parent("a") is None: + filename = f"{elm.text}-fill.svg" + appearance = "monochrome" + path = os.path.join(media_dir, filename) + __make_rectangle(path, elm.text, "fill") + else: + filename = f"{elm.text}-bluefill.svg" + appearance = "auto" + path = os.path.join(media_dir, filename) + __make_rectangle(path, elm.text, "bluefill") + ratio = Icons.calculate_ratio(path) + img = BeautifulSoup("", "xml").img + img.attrs = { + "height": 1.0, + "width": ratio, + "sizeUnits": "em", + "collapsible": False, + "collapsed": False, + "background": False, + "appearance": appearance, + "title": elm.text, + "path": f"{os.path.basename(media_dir)}/{filename}", + } + elm.name = "span" + elm.clear() + elm.append(img) + elm.attrs["style"] = "vertical-align: text-bottom; margin-right: 0.25em;" + + +def __make_rectangle(path, text, cls): + if cls == "none": + pass + elif cls == "fill": + Icons.make_monochrome_fill_rectangle(path, text) + elif cls == "red": + Icons.make_rectangle(path, text, "red", "white", "red") + elif cls == "redfill": + Icons.make_rectangle(path, text, "red", "red", "white") + elif cls == "bluefill": + Icons.make_rectangle(path, text, "blue", "blue", "white") + else: + Icons.make_rectangle(path, text, "black", "transparent", "black") diff --git a/bot/yomichan/glossary/smk8.py b/bot/yomichan/glossary/smk8.py index 870c3fc..f17ee42 100644 --- a/bot/yomichan/glossary/smk8.py +++ b/bot/yomichan/glossary/smk8.py @@ -2,11 +2,11 @@ import re import os from bs4 import BeautifulSoup -import bot.icons as Icons +import bot.yomichan.glossary.icons as Icons from bot.soup import delete_soup_nodes -from bot.data import load_smk8_yomichan_name_conversion +from bot.data import load_yomichan_name_conversion from bot.yomichan.glossary.gloss import make_gloss -from bot.yomichan.glossary.name_conversion import convert_names +from bot.name_conversion import convert_names def make_glossary(entry, image_dir): @@ -20,7 +20,7 @@ def make_glossary(entry, image_dir): __convert_gaiji(soup, image_dir) __convert_rectangles(soup, image_dir) - name_conversion = load_smk8_yomichan_name_conversion() + name_conversion = load_yomichan_name_conversion(entry.target) convert_names(soup, name_conversion) gloss = make_gloss(soup.span) @@ -92,8 +92,8 @@ def __convert_gaiji(soup, image_dir): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, @@ -124,8 +124,8 @@ def __convert_rectangles(soup, image_dir): ratio = Icons.calculate_ratio(path) img = BeautifulSoup("", "xml").img img.attrs = { - "height": 1.0 if ratio > 1.0 else ratio, - "width": ratio if ratio > 1.0 else 1.0, + "height": 1.0, + "width": ratio, "sizeUnits": "em", "collapsible": False, "collapsed": False, diff --git a/bot/yomichan/terms/base/jitenon.py b/bot/yomichan/terms/base/jitenon.py new file mode 100644 index 0000000..d0d5388 --- /dev/null +++ b/bot/yomichan/terms/base/jitenon.py @@ -0,0 +1,26 @@ +from bot.yomichan.terms.base.terminator import BaseTerminator + + +class JitenonTerminator(BaseTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = None + + def _definition_tags(self, entry): + return None + + def _glossary(self, entry): + if entry.entry_id in self._glossary_cache: + return self._glossary_cache[entry.entry_id] + glossary = self._glossary_maker.make_glossary(entry, self._image_dir) + self._glossary_cache[entry.entry_id] = glossary + return glossary + + def _sequence(self, entry): + return entry.entry_id + + def _link_glossary_parameters(self, entry): + return [] + + def _subentry_lists(self, entry): + return [] diff --git a/bot/yomichan/terms/terminator.py b/bot/yomichan/terms/base/terminator.py similarity index 76% rename from bot/yomichan/terms/terminator.py rename to bot/yomichan/terms/base/terminator.py index d41a50a..f57c4cc 100644 --- a/bot/yomichan/terms/terminator.py +++ b/bot/yomichan/terms/base/terminator.py @@ -1,7 +1,8 @@ +from abc import abstractmethod, ABC from bot.data import load_yomichan_inflection_categories -class Terminator: +class BaseTerminator(ABC): def __init__(self, target): self._target = target self._glossary_cache = {} @@ -62,3 +63,31 @@ class Terminator: } glossary.append(gloss) return glossary + + @abstractmethod + def _definition_tags(self, entry): + raise NotImplementedError + + @abstractmethod + def _inflection_rules(self, entry, expression): + raise NotImplementedError + + @abstractmethod + def _glossary(self, entry): + raise NotImplementedError + + @abstractmethod + def _sequence(self, entry): + raise NotImplementedError + + @abstractmethod + def _term_tags(self, entry): + raise NotImplementedError + + @abstractmethod + def _link_glossary_parameters(self, entry): + raise NotImplementedError + + @abstractmethod + def _subentry_lists(self, entry): + raise NotImplementedError diff --git a/bot/yomichan/terms/daijirin2.py b/bot/yomichan/terms/daijirin2.py index 10aaa76..7cf06fb 100644 --- a/bot/yomichan/terms/daijirin2.py +++ b/bot/yomichan/terms/daijirin2.py @@ -1,14 +1,10 @@ -from bot.entries.daijirin2 import Daijirin2PhraseEntry as PhraseEntry - -from bot.yomichan.terms.terminator import Terminator +from bot.entries.daijirin2.phrase_entry import PhraseEntry +from bot.yomichan.terms.base.terminator import BaseTerminator from bot.yomichan.glossary.daijirin2 import make_glossary from bot.yomichan.grammar import sudachi_rules, tags_to_rules -class Daijirin2Terminator(Terminator): - def __init__(self, target): - super().__init__(target) - +class Terminator(BaseTerminator): def _definition_tags(self, entry): return "" diff --git a/bot/yomichan/terms/factory.py b/bot/yomichan/terms/factory.py deleted file mode 100644 index d3fc199..0000000 --- a/bot/yomichan/terms/factory.py +++ /dev/null @@ -1,18 +0,0 @@ -from bot.targets import Targets - -from bot.yomichan.terms.jitenon import JitenonKokugoTerminator -from bot.yomichan.terms.jitenon import JitenonYojiTerminator -from bot.yomichan.terms.jitenon import JitenonKotowazaTerminator -from bot.yomichan.terms.smk8 import Smk8Terminator -from bot.yomichan.terms.daijirin2 import Daijirin2Terminator - - -def new_terminator(target): - terminator_map = { - Targets.JITENON_KOKUGO: JitenonKokugoTerminator, - Targets.JITENON_YOJI: JitenonYojiTerminator, - Targets.JITENON_KOTOWAZA: JitenonKotowazaTerminator, - Targets.SMK8: Smk8Terminator, - Targets.DAIJIRIN2: Daijirin2Terminator, - } - return terminator_map[target](target) diff --git a/bot/yomichan/terms/jitenon.py b/bot/yomichan/terms/jitenon.py deleted file mode 100644 index f74abaa..0000000 --- a/bot/yomichan/terms/jitenon.py +++ /dev/null @@ -1,67 +0,0 @@ -from bot.yomichan.grammar import sudachi_rules -from bot.yomichan.terms.terminator import Terminator - -from bot.yomichan.glossary.jitenon import JitenonKokugoGlossary -from bot.yomichan.glossary.jitenon import JitenonYojiGlossary -from bot.yomichan.glossary.jitenon import JitenonKotowazaGlossary - - -class JitenonTerminator(Terminator): - def __init__(self, target): - super().__init__(target) - - def _definition_tags(self, entry): - return None - - def _glossary(self, entry): - if entry.entry_id in self._glossary_cache: - return self._glossary_cache[entry.entry_id] - glossary = self._glossary_maker.make_glossary(entry, self._image_dir) - self._glossary_cache[entry.entry_id] = glossary - return glossary - - def _sequence(self, entry): - return entry.entry_id - - def _link_glossary_parameters(self, entry): - return [] - - def _subentry_lists(self, entry): - return [] - - -class JitenonKokugoTerminator(JitenonTerminator): - def __init__(self, target): - super().__init__(target) - self._glossary_maker = JitenonKokugoGlossary() - - def _inflection_rules(self, entry, expression): - return sudachi_rules(expression) - - def _term_tags(self, entry): - return "" - - -class JitenonYojiTerminator(JitenonTerminator): - def __init__(self, target): - super().__init__(target) - self._glossary_maker = JitenonYojiGlossary() - - def _inflection_rules(self, entry, expression): - return "" - - def _term_tags(self, entry): - tags = entry.kankenkyuu.split("/") - return " ".join(tags) - - -class JitenonKotowazaTerminator(JitenonTerminator): - def __init__(self, target): - super().__init__(target) - self._glossary_maker = JitenonKotowazaGlossary() - - def _inflection_rules(self, entry, expression): - return sudachi_rules(expression) - - def _term_tags(self, entry): - return "" diff --git a/bot/yomichan/terms/jitenon_kokugo.py b/bot/yomichan/terms/jitenon_kokugo.py new file mode 100644 index 0000000..3e33b77 --- /dev/null +++ b/bot/yomichan/terms/jitenon_kokugo.py @@ -0,0 +1,15 @@ +from bot.yomichan.grammar import sudachi_rules +from bot.yomichan.glossary.jitenon import JitenonKokugoGlossary +from bot.yomichan.terms.base.jitenon import JitenonTerminator + + +class Terminator(JitenonTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = JitenonKokugoGlossary() + + def _inflection_rules(self, entry, expression): + return sudachi_rules(expression) + + def _term_tags(self, entry): + return "" diff --git a/bot/yomichan/terms/jitenon_kotowaza.py b/bot/yomichan/terms/jitenon_kotowaza.py new file mode 100644 index 0000000..a0651b9 --- /dev/null +++ b/bot/yomichan/terms/jitenon_kotowaza.py @@ -0,0 +1,15 @@ +from bot.yomichan.grammar import sudachi_rules +from bot.yomichan.glossary.jitenon import JitenonKotowazaGlossary +from bot.yomichan.terms.base.jitenon import JitenonTerminator + + +class Terminator(JitenonTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = JitenonKotowazaGlossary() + + def _inflection_rules(self, entry, expression): + return sudachi_rules(expression) + + def _term_tags(self, entry): + return "" diff --git a/bot/yomichan/terms/jitenon_yoji.py b/bot/yomichan/terms/jitenon_yoji.py new file mode 100644 index 0000000..5087539 --- /dev/null +++ b/bot/yomichan/terms/jitenon_yoji.py @@ -0,0 +1,15 @@ +from bot.yomichan.glossary.jitenon import JitenonYojiGlossary +from bot.yomichan.terms.base.jitenon import JitenonTerminator + + +class Terminator(JitenonTerminator): + def __init__(self, target): + super().__init__(target) + self._glossary_maker = JitenonYojiGlossary() + + def _inflection_rules(self, entry, expression): + return "" + + def _term_tags(self, entry): + tags = entry.kanken_level.split("/") + return " ".join(tags) diff --git a/bot/yomichan/terms/sankoku8.py b/bot/yomichan/terms/sankoku8.py new file mode 100644 index 0000000..d6e6afd --- /dev/null +++ b/bot/yomichan/terms/sankoku8.py @@ -0,0 +1,43 @@ +from bot.entries.sankoku8.phrase_entry import PhraseEntry +from bot.yomichan.terms.base.terminator import BaseTerminator +from bot.yomichan.glossary.sankoku8 import make_glossary +from bot.yomichan.grammar import sudachi_rules, tags_to_rules + + +class Terminator(BaseTerminator): + def _definition_tags(self, entry): + return "" + + def _inflection_rules(self, entry, expression): + if isinstance(entry, PhraseEntry): + return sudachi_rules(expression) + pos_tags = entry.get_part_of_speech_tags() + if len(pos_tags) == 0: + return sudachi_rules(expression) + else: + return tags_to_rules(expression, pos_tags, self._inflection_categories) + + def _glossary(self, entry): + if entry.entry_id in self._glossary_cache: + return self._glossary_cache[entry.entry_id] + glossary = make_glossary(entry, self._image_dir) + self._glossary_cache[entry.entry_id] = glossary + return glossary + + def _sequence(self, entry): + return entry.entry_id[0] * 100000 + entry.entry_id[1] + + def _term_tags(self, entry): + return "" + + def _link_glossary_parameters(self, entry): + return [ + [entry.children, "子"], + [entry.phrases, "句"] + ] + + def _subentry_lists(self, entry): + return [ + entry.children, + entry.phrases, + ] diff --git a/bot/yomichan/terms/smk8.py b/bot/yomichan/terms/smk8.py index d1e3ca7..9e85c17 100644 --- a/bot/yomichan/terms/smk8.py +++ b/bot/yomichan/terms/smk8.py @@ -1,12 +1,11 @@ -from bot.entries.smk8 import Smk8KanjiEntry as KanjiEntry -from bot.entries.smk8 import Smk8PhraseEntry as PhraseEntry - -from bot.yomichan.terms.terminator import Terminator +from bot.entries.smk8.kanji_entry import KanjiEntry +from bot.entries.smk8.phrase_entry import PhraseEntry +from bot.yomichan.terms.base.terminator import BaseTerminator from bot.yomichan.glossary.smk8 import make_glossary from bot.yomichan.grammar import sudachi_rules, tags_to_rules -class Smk8Terminator(Terminator): +class Terminator(BaseTerminator): def __init__(self, target): super().__init__(target) diff --git a/data/adobe/Adobe-Japan1_sequences.txt b/data/entries/adobe/Adobe-Japan1_sequences.txt similarity index 100% rename from data/adobe/Adobe-Japan1_sequences.txt rename to data/entries/adobe/Adobe-Japan1_sequences.txt diff --git a/data/adobe/override_glyphs.json b/data/entries/adobe/override_glyphs.json similarity index 100% rename from data/adobe/override_glyphs.json rename to data/entries/adobe/override_glyphs.json diff --git a/data/daijirin2/kana_abbreviations.csv b/data/entries/daijirin2/kana_abbreviations.csv similarity index 100% rename from data/daijirin2/kana_abbreviations.csv rename to data/entries/daijirin2/kana_abbreviations.csv diff --git a/data/daijirin2/phrase_readings.csv b/data/entries/daijirin2/phrase_readings.csv similarity index 99% rename from data/daijirin2/phrase_readings.csv rename to data/entries/daijirin2/phrase_readings.csv index 97022fa..dab7e7a 100644 --- a/data/daijirin2/phrase_readings.csv +++ b/data/entries/daijirin2/phrase_readings.csv @@ -1391,7 +1391,7 @@ 22544,16385,おもいこしをあげる 22634,16385,おもいたったがきちにち 22634,16386,おもいたつひがきちじつ -22728,16385,おもうゆえに +22728,16385,おもうえに 22728,16386,おもうこころ 22728,16387,おもうこといわねばはらふくる 22728,16388,おもうそら @@ -5224,7 +5224,7 @@ 111520,16385,てんちょうにたっする 111583,16385,てんどうぜかひか 111583,16386,てんどうひとをころさず -111645,16385,てんばくうをいく +111645,16385,てんばくうをゆく 111695,16385,てんびんにかける 111790,16385,てんめいをしる 111801,16385,てんもうかいかいそにしてもらさず @@ -5713,7 +5713,7 @@ 119456,16385,なまきにくぎ 119456,16386,なまきをさく 119472,16385,なまけもののあしからとりがたつ -119472,16386,なまけもののせっくはたらき +119472,16386,なまけもののせっくばたらき 119503,16385,なますにたたく 119503,16386,なますをふく 119507,16385,なまずをひょうたんでおさえる @@ -7215,7 +7215,7 @@ 154782,16388,みずがはいる 154782,16389,みずがひく 154782,16390,みずかる -154782,16391,みずきょければうおすまず +154782,16391,みずきよければうおすまず 154782,16392,みずすむ 154782,16393,みずでわる 154782,16394,みずとあぶら diff --git a/data/entries/sankoku8/phrase_readings.csv b/data/entries/sankoku8/phrase_readings.csv new file mode 100644 index 0000000..b9756f4 --- /dev/null +++ b/data/entries/sankoku8/phrase_readings.csv @@ -0,0 +1,3573 @@ +4,16385,ああいえばこういう +16,16385,アーチをかける +87,16385,あいくちがわるい +139,16385,あいそがつきる +139,16386,あいそも{こそ・こそ}もつきはてる +149,16385,あいだにたつ +161,16385,あいてにする +161,16386,あいてになる +214,16385,あいまをぬう +262,16385,あおはあいよりいでてあいよりあおし +293,16385,あおすじをたてる +302,16385,あおなにしお +342,16385,あがきがとれない +351,16385,あかごのてをひねるよう +365,16385,あかしんごうみんなでわたればこわくない +426,16385,あかるみにでる +435,16385,あきたつ +435,16386,あきのひはつるべおとし +440,16385,あきかぜがたつ +458,16385,あきないはうしのよだれ +460,16385,あきなすはよめにくわすな +485,16385,あくがつよい +489,16385,あいたくちがふさがらない +496,16385,あくうんがつよい +508,16385,あくじせんりをはしる +521,16385,あくじょのふかなさけ +536,16385,あくせんみにつかず +578,16385,あくむからさめる +587,16385,あぐらをかく +602,16385,あけにそまる +607,16385,あげあしをとる +664,16385,あごがひあがる +664,16386,あごでつかう +664,16387,あごをだす +664,16388,あごを{な・な}でる +664,16389,あごをはずす +674,16385,あさのごとくみだれる +746,16385,あしがある +746,16386,あしがちにつかない +746,16387,あしがつく +746,16388,あしがでる +746,16389,あしがとおのく +746,16390,あしがながい +746,16391,あしがはやい +746,16392,あしがはやい +746,16393,あしでかく +746,16394,あしでかせぐ +746,16395,あしをあらう +746,16396,あしをいれる +746,16397,あしを{すく・すく}われる +746,16398,あしをつかう +746,16399,あしをとられる +746,16400,あしをのばす +746,16401,あしをはこぶ +746,16402,あしをひっぱる +746,16403,あしをぼうにする +746,16404,あしをむけてねられない +746,16405,あしをむける +749,16385,あじもしゃしゃりもない +749,16386,あじもそっけもない +749,16387,あじをしめる +774,16385,あしげにする +790,16385,あしたはあしたのかぜがふく +821,16385,あしもとからとりがたつ +821,16386,あしもとがわるい +821,16387,あしもとにつけこむ +821,16388,あしもとにひがつく +821,16389,あしもとにもおよばない +821,16390,あしもとのあかるいうちに +821,16391,あしもとへもよりつけない +821,16392,あしもとを{すく・すく}われる +821,16393,あしもとをみる +837,16385,あすはわがみ +841,16385,あずかってちからがある +866,16385,あせになる +866,16386,あせをかく +866,16387,あせをながす +913,16385,あたいをとらせる +921,16385,あたかも{よ・よ}し +947,16385,あたまがあがらない +947,16386,あたまがいたい +947,16387,あたまかくしてしりかくさず +947,16388,あたまがさがる +947,16389,あたまがしろくなる +947,16390,あたまがひくい +947,16391,あたまがまわる +947,16392,あたまからゆげを〈たてる/だす〉 +947,16393,あたまがわく +947,16394,あたまがわれるよう +947,16395,あたまにくる +947,16396,あたまのうえの{はえ・はえ}もおえない +947,16397,あたまのくろい{ねずみ・ねずみ} +947,16398,あたま(のなか)がまっしろになる +947,16399,あたまひとつぬける +947,16400,あたまをいためる +947,16401,あたまをおさえる +947,16402,あたまをかかえる +947,16403,あたまをかく +947,16404,あたまをさげる +947,16405,あたまをつかう +947,16406,あたまをつっこむ +947,16407,あたまをはねる +947,16408,あたまをひねる +947,16409,あたまをひやす +947,16410,あたまをまるめる +947,16411,あたまを{もた・もた}げる +953,16385,あたらしいさけをふるいかわぶくろにもる +953,16386,あたらしいページをくわえる +959,16385,あたりをはらう +960,16385,あたりをつける +964,16385,あたらずといえどもとおからず +964,16386,あたるもはっけあたらぬもはっけ +964,16387,あたるをさいわい +973,16385,あちらたてればこちらがたたず +974,16385,あっといわせる +982,16385,あついものがこみあげる +985,16385,あっかはりょうかをくちくする +1000,16385,あっけにとられる +1006,16385,あつささむさもひがんまで +1049,16385,あつものにこりて{なます・なます}をふく +1098,16385,あとがない +1098,16386,あとにする +1098,16387,あとにもさきにも +1098,16388,あとの{かり・かり}がさきになる +1098,16389,あとはのとなれやまとなれ +1098,16390,あとをおう +1098,16391,あとをたたない +1098,16392,あとをひく +1100,16385,あとをおう +1100,16386,あとをたつ +1100,16387,あとをつける +1102,16385,あとあしですなをかける +1141,16385,アドバルーンをあげる +1164,16385,あながあく +1164,16386,あながあくほど +1164,16387,あながあったらはいりたい +1234,16385,あばたもえくぼ +1256,16385,あびるほどのむ +1271,16385,あぶないはしをわたる +1278,16385,あぶらがつかれる +1278,16386,あぶらをうる +1278,16387,あぶらをしぼる +1279,16385,あぶらがのる +1332,16385,あまいしるをすう +1332,16386,あまくみる +1367,16385,あまだれいしを{うが・うが}つ +1410,16385,あみをはる +1441,16385,あめがふろうが{やり・やり}がふろうが +1441,16386,あめのふるひはてんきがわるい +1441,16387,あめふってじかたまる +1442,16385,アメと{ムチ・むち} +1442,16386,あめをしゃぶらせる +1460,16385,あめだまをしゃぶらせる +1462,16385,あめつゆをしのぐ +1476,16385,あやをつける +1498,16385,あやまちてはあらたむるにはばかることなかれ +1578,16385,あらねつをとる +1623,16385,ありのはいでるすきもない +1673,16385,あったもので(は)ない +1673,16386,あってなきがごとし +1761,16385,あわをくう +1778,16385,あわせるかおがない +1868,16385,あんしょうにのりあげる +1877,16385,あんずるよりうむが{やす・やす}し +1905,16385,アンテナをはる +1974,16385,いはじんじゅつ +1976,16385,いをはる +1979,16385,いをたてる +1980,16385,いあまってことばたらず +1980,16386,いとする +1980,16387,いにかいする +1980,16388,いに{かな・かな}う +1980,16389,いにそう +1980,16390,いにそまない +1980,16391,いにみたない +1980,16392,いのあるところ +1980,16393,いをうける +1980,16394,いをえる +1980,16395,いを{く・く}む +1980,16396,いをつうじる +1980,16397,いをつくす +1980,16398,いをつよくする +1980,16399,いをむかえる +1980,16400,いをもちいる +1994,16385,いいいみで +2018,16385,いいかげんにしろ +2111,16385,いいえてみょう +2111,16386,いうことなし +2111,16387,いうことをきかない +2111,16388,いうにおよばず +2111,16389,いうにこと(を)かいて +2111,16390,いうにやおよぶ +2111,16391,いうは{やす・やす}くおこなうは{かた・かた}し +2111,16392,いうまでもない +2111,16393,いうもさらなり +2111,16394,いうを{ま・ま}たない +2111,16395,いったいわない +2111,16396,いわないことではない +2111,16397,いわぬがはな +2117,16385,いえをたたむ +2208,16385,いかりしんとうにはっする +2221,16385,いかんのいをひょうする +2230,16385,いきがあがる +2230,16386,いきがかかる +2230,16387,いきがきれる +2230,16388,いきがつまる +2230,16389,いきがながい +2230,16390,いきのかよった +2230,16391,いきのしたから +2230,16392,いきもつかせず +2230,16393,いきをいれる +2230,16394,いきをこらす +2230,16395,いきをころす +2230,16396,いきをつく +2230,16397,いきをぬく +2230,16398,いきを{の・の}む +2230,16399,いきをひきとる +2230,16400,いきをひそめる +2230,16401,いきをふきかえす +2232,16385,いきてんをつく +2248,16385,いきうまのめをぬく +2315,16385,いきのねをとめる +2439,16385,いざかまくら +2442,16385,いさいをはなつ +2469,16385,いしがながれてこのはがしずむ +2469,16386,いしにかじりついても +2469,16387,いしにたつや +2469,16388,いしにまくらしながれにくちすすぐ +2469,16389,いしのうえにもさんねん +2469,16390,いしもておわれる +2478,16385,いじがわるい +2478,16386,いじをはる +2489,16385,いしきがたかい +2514,16385,いしばしをたたいてわたる +2558,16385,いしょくたりてれいせつをしる +2587,16385,いずまいをただす +2595,16385,いずれ{あやめ・あやめ}か{かきつばた・かきつばた} +2632,16385,いそがばまわれ +2648,16385,いたにつく +2652,16385,いたいめをみる +2652,16386,いたくもかゆくもない +2652,16387,いたくもないはらをさぐられる +2670,16385,いたごいちまいしたはじごく +2685,16385,いたちのみちをきったよう +2723,16385,いたれりつくせり +2728,16385,いちかばちか +2728,16386,いちからじゅうまで +2728,16387,いちにもににも +2728,16388,いちぬけた +2728,16389,いちひめにたろう +2728,16390,いちふじにたかさん{なすび・なすび} +2728,16391,いちもにもなく +2728,16392,いちをきいてじゅうをしる +2752,16385,いちぎにおよばず +2764,16385,いちごにつきる +2768,16385,いちごんなかるべからず +2768,16386,いちごんのもとに +2768,16387,いちごんもない +2772,16385,いちじがばんじ +2777,16385,いちじつのちょうがある +2778,16385,いちじゅのかげいちがのながれもたしょうのえん +2811,16385,いちにちのちょうがある +2841,16385,いちまいかむ +2845,16385,いちみゃくあいつうじる +2853,16385,いちもく(を)おく +2874,16385,いちようおちててんかのあきをしる +2905,16385,いっかをなす +2941,16385,いっきょうをきっする +2953,16385,いっけんきょに{ほ・ほ}ゆればばんけんじつをつたう +2965,16385,いっこくをあらそう +2984,16385,いっし(を)むくいる +2986,16385,いっしまとわぬ +2986,16386,いっしみだれず +3002,16385,いっしゅうまわって +3011,16385,いっしょうこうなり(て)ばんこつかる +3012,16385,いっしょうにふす(る) +3028,16385,いっすんさきはやみ +3028,16386,いっすんのこういんかろんずべからず +3028,16387,いっすんのむしにもごぶのたましい +3030,16385,いっせいを{ふうび・ふうび}する +3035,16385,いっせきをとうじる +3036,16385,いっせきぶつ +3036,16386,いっせきもうける +3042,16385,いっせんをかくす +3042,16386,いっせんをこえる +3067,16385,いっちゅうをゆする +3069,16385,いっちょうあがり +3104,16385,いっとうちをぬく +3116,16385,いっぱいくわす +3117,16385,いっぱい、ちに{まみ・まみ}れる +3123,16385,いっぱんを〈みて/もって〉ぜんぴょうを〈ぼくす/おす〉 +3137,16385,いっぷくいれる +3137,16386,いっぷくつける +3137,16387,いっぷくもる +3148,16385,いっぽぬけでる +3148,16386,いっぽまちがえば +3148,16387,いっぽをゆずる +3178,16385,いとのきれた{たこ・たこ} +3178,16386,いとをたれる +3178,16387,いとをひく +3270,16385,いぬとさる +3270,16386,いぬもあるけばぼうにあたる +3270,16387,いぬもくわない +3300,16385,いのちあってのものだね +3300,16386,いのちからにばんめ +3300,16387,いのちながければはじおおし +3300,16388,いのちをおとす +3300,16389,いのちをけずる +3300,16390,いのちをちぢめる +3347,16385,いぶくろをつかむ +3347,16386,いぶくろをみたす +3377,16385,いまこそわかれめ +3377,16386,いまないた{からす・からす}がもうわらう +3377,16387,いまにはじまったことで(は)ない +3377,16388,いまはこれまで +3377,16389,いまはむかし +3377,16390,いまをときめく +3408,16385,いまやおそし +3435,16385,いも(のこ)をあらうよう +3435,16386,いものにえたもごぞんじない +3456,16385,いやもへちまもない +3473,16385,いやけがさす +3545,16385,いりまめにはながさく +3558,16385,いてもたってもいられない +3592,16385,いろのしろいはしちなんかくす +3592,16386,いろをうしなう +3592,16387,いろをうる +3592,16388,いろをこのむ +3592,16389,いろをそえる +3592,16390,いろをただす +3592,16391,いろをつける +3592,16392,いろをなす +3654,16385,いわしのあたまもしんじんから +3685,16385,いんに{こ・こ}もる +3685,16386,いんにように +3707,16385,いんかんとおからず +3730,16385,いんこうをやくする +3852,16385,いんどうをわたす +3853,16385,いんとくあればようほうあり +3928,16385,{う・う}のまねをする{からす・からす} +3928,16386,{う・う}のめ{たか・たか}のめ +3980,16385,うえからものをいう +3980,16386,うえにはうえがある +3980,16387,うえをしたへのおおさわぎ +3989,16385,ウエイトをおく +4026,16385,うおがみずをえたよう +4039,16385,うおごころあればみずごころ(あり) +4102,16385,うきみをやつす +4109,16385,うぐいすなかせたこともある +4143,16385,うけてたつ +4148,16385,うごの{たけのこ・たけのこ}のよう +4152,16385,うごきがとれない +4153,16385,うごかぬしょうこ +4167,16385,うしにひかれてぜんこうじまいり +4167,16386,うしをうまにのりかえる +4169,16385,うじよりそだち +4191,16385,うしろからてっぽうをうつ +4191,16386,うしろをみせる +4191,16387,うしろがみをひかれるおもい +4191,16388,うしろゆびをさされる +4207,16385,うすがみを〈はぐ/はがす〉よう +4247,16385,うすめをあける +4266,16385,うそからでたまこと +4266,16386,うそでぬりかためる +4266,16387,うそもひゃっかいいえばほんとうになる +4266,16388,うそもほうべん +4266,16389,うそをつけ +4306,16385,うだつがあがらない +4339,16385,うちかぶとをみすかす +4438,16385,うてばひびく +4466,16385,うっちゃりをくう +4468,16385,うつつをぬかす +4498,16385,うでがあがる +4498,16386,うでがたつ +4498,16387,うでがなる +4498,16388,うでがよなきをする +4498,16389,うでにおぼえがある +4498,16390,うでによりをかける +4498,16391,うでをこまねく +4498,16392,うでをふるう +4498,16393,うでをみがく +4576,16385,うぶごえをあげる +4587,16385,うまがあう +4587,16386,うまにはのってみよ、ひとにはそうてみよ +4587,16387,うまのせをわける +4587,16388,うまのみみにねんぶつ +4590,16385,うまいしるをすう +4611,16385,うみのもくずとなる +4611,16386,うみのものともやまのものともつかない +4633,16385,うみのおやよりそだてのおや +4646,16385,うむあいつうじる +4646,16386,うむをいわさず +4668,16385,うもれぎにはながさく +4673,16385,うゆうにきす +4678,16385,うらをかえす +4678,16386,うらをかえせば +4678,16387,うらをかく +4678,16388,うらをとる +4733,16385,うらみこつずいにてっする +4733,16386,うらみを{の・の}む +4737,16385,うらめにでる +4752,16385,{うり・うり}のつるに{なすび・なすび}は{な・な}らぬ +4766,16385,うりことばにかいことば +4823,16385,うれしいひめいをあげる +4858,16385,うわさがうわさをよぶ +4858,16386,うわさがたつ +4858,16387,うわさをすればかげ(がさす) +4883,16385,うわまえをはねる +4892,16385,うんをてんにまかせる +4893,16385,うんだともつぶれたとも +4893,16386,うんでもすんでもない +4893,16387,うんともすんとも +4928,16385,うんちくをかたむける +4956,16385,えのないところにえをすげる +4957,16385,えにかいたもち +4957,16386,えにかいたよう +4957,16387,えになる +5077,16385,えいゆういろをこのむ +5092,16385,エーからゼット(まで) +5342,16385,えつにいる +5370,16385,えつぼにいる +5376,16385,えてにほをあげる +5387,16385,えどのかたきをながさきでうつ +5434,16385,えびで{たい・たい}をつる +5514,16385,えらぶところがない +5518,16385,えりをただす +5589,16385,えんなきしゅじょうはどしがたし +5589,16386,えんはいなものあじなもの +5630,16385,えんぎでもない +5630,16386,えんぎをいわう +5630,16387,えんぎをかつぐ +5679,16385,えんじゃくいずくんぞこうこくのこころざしをしらんや +5703,16385,エンジンがかかる +5796,16385,えんぴつをなめる +5818,16385,えんまくをはる +5835,16385,えんりょえしゃくもなく +5840,16385,おをひく +5938,16385,おうたこにおしえられてあさせをわたる +5939,16385,おいつおわれつ +6014,16385,おうせつに{いとま・いとま}がない +6034,16385,おうとをもよおす +6100,16385,おおありなごやのきんの{しゃち・しゃち} +6148,16385,おおきくでる +6231,16385,おおでをひろげる +6231,16386,おおでをふる +6261,16385,おおなたをふるう +6286,16385,おおぶねにのったきもち +6289,16385,おおぶろしきをひろげる +6302,16385,おおみえをきる +6315,16385,おおめにみる +6353,16385,{おか・おか}にあがった{かっぱ・かっぱ} +6386,16385,おかぶをうばう +6389,16385,おかまをほる +6463,16385,おきゅうをすえる +6467,16385,おぎりにも +6471,16385,おくがふかい +6484,16385,おくじょうおくをかす +6492,16385,オクターブがあがる +6508,16385,おくばにもののはさまったいいかた +6509,16385,おくびにもださない +6510,16385,おくびょうかぜをふかす +6523,16385,おくらになる +6534,16385,おくれをとる +6566,16385,おことばにあまえる +6591,16385,おざがさめる +6600,16385,おさきまっくら +6600,16386,おさきぼうをかつぐ +6602,16385,おざしきがかかる +6605,16385,おさとがしれる +6627,16385,おしもおされぬ +6627,16386,おしもおされもせぬ +6760,16385,おしてしるべし +6789,16385,おそかりしゆらのすけ +6791,16385,おそきにしっする +6794,16385,おぞけをふるう +6812,16385,おそれいりやのきしもじん +6824,16385,おだをあげる +6833,16385,おたかくとまる +6853,16385,おだやかでない +6878,16385,おちゃをにごす +6878,16386,おちゃを{ひ・ひ}く +6960,16385,おつやのよう +6962,16385,おつりがくる +6963,16385,おてをはいしゃく +6991,16385,おとをたててくずれる +7006,16385,おとこになる +7006,16386,おとこをあげる +7006,16387,おとこごころとあきのそら +7021,16385,おとといきやがれ +7063,16385,おなじあなの{むじな・むじな} +7063,16386,おなじかまのめしをくう +7063,16387,おなじテーブルにつく +7076,16385,おにがでるかじゃがでるか +7076,16386,おにがわらう +7076,16387,おににかなぼう +7076,16388,おにのいぬまに(いのちの)せんたく +7076,16389,おにのくびをとったよう +7076,16390,おにのめにもなみだ +7076,16391,おにはそとふくはうち +7076,16392,おにもじゅうはち、ばんちゃもでばな +7076,16393,おにをあざむく +7122,16385,おのれにかつ +7122,16386,おのれを{むな・むな}しゅうする +7123,16385,おはうちからす +7124,16385,おはにあう +7144,16385,おはちがまわる +7161,16385,おびにみじかし{たすき・たすき}にながし +7172,16385,おひげの{ちり・ちり}をはらう +7197,16385,おひゃくどをふむ +7202,16385,おひれをつける +7243,16385,オブラートに〈つつむ/くるむ〉 +7283,16385,おぼれるものは{わら・わら}をもつかむ +7333,16385,おめにかかる +7333,16386,おめにかける +7356,16385,おもいなかばにすぎる +7356,16386,おもいにしずむ +7356,16387,おもいもつかない +7356,16388,おもいもよらない +7356,16389,おもいをいたす +7356,16390,おもいをはらす +7356,16391,おもいをめぐらす +7356,16392,おもいをよせる +7356,16393,おもいたったがきちにち +7358,16385,おもうにまかせない +7358,16386,おもってもいない +7366,16385,おもきをおく +7366,16386,おもきをなす +7385,16385,おもてをおかす +7422,16385,おやのいんががこにむくいる +7422,16386,おやのかおがみたい +7422,16387,おやのこころこしらず +7422,16388,おやのすねをかじる +7422,16389,おやのひかりはななひかり +7422,16390,おやはなくともこはそだつ +7429,16385,おやかたひのまる +7446,16385,おやすくない +7456,16385,おやぶねにのったつもり +7476,16385,およびがかかる +7476,16386,およびでない +7491,16385,おりにふれ(て) +7491,16386,おりもおり +7566,16385,おれがおれが +7602,16385,おわりよければすべてよし +7606,16385,おんにきる +7606,16386,おんを{あだ・あだ}でかえす +7606,16387,おんをうる +7606,16388,おんをかえす +7707,16385,おんなさんがいにいえなし +7707,16386,おんなになる +7707,16387,おんなごころとあきのそら +7721,16385,おんぶにだっこ +7749,16385,かもなくふかもなし +7755,16385,かをさりじつにつく +7756,16385,かのなくようなこえ +7777,16385,がをおる +7777,16386,がをはる +7782,16385,がをまげる +7845,16385,かいになる +7854,16385,かいよりはじめよ +7887,16385,かいいぬにてをかまれる +7921,16385,がいかをあげる +8057,16385,かいさいをさけぶ +8171,16385,かいじんにきす +8333,16385,かいとうらんまをたつ +8363,16385,かいなをかえす +8419,16385,がいぶんがわるい +8552,16385,かえすときの{えんま・えんま}がお +8565,16385,かえるのこはかえる +8565,16386,かえるのつらにみず +8567,16385,かえらぬたび +8567,16386,かえらぬひととなる +8567,16387,かえりなんいざ +8578,16385,かおがあう +8578,16386,かおがうれる +8578,16387,かおがきく +8578,16388,かおがたつ +8578,16389,かおがつぶれる +8578,16390,かおがひろい +8578,16391,かおがみえる +8578,16392,かおからひがでる +8578,16393,かおにかいてある +8578,16394,かおにでる +8578,16395,かおにどろをぬる +8578,16396,かおをあらってでなおしてこい +8578,16397,かおをあわせる +8578,16398,かおをかがやかせる +8578,16399,かおをかす +8578,16400,かおをくしゃくしゃにする +8578,16401,かおをだす +8578,16402,かおをつくる +8581,16385,かおいろをみる +8604,16385,かおりをきく +8944,16385,かくすよりあらわる +9069,16385,がくもんにおうどうなし +9107,16385,かげでいとをひく +9107,16386,かげになりひなたになって +9108,16385,かげがうすい +9108,16386,かげのかたちにそうよう +9108,16387,かげもかたちもない +9108,16388,かげをおとす +9108,16389,かげをかくす +9108,16390,かげをひそめる +9130,16385,かけがえのない +9161,16385,かけつけさんばい +9179,16385,かげひなたになって +9264,16385,かさにきる +9267,16385,かさにかかる +9272,16385,かざあなをあける +9288,16385,かざかみにもおけない +9303,16385,かざしもにたつ +9316,16385,かさのだいがとぶ +9329,16385,かさんをかたむける +9356,16385,{かじ・かじ}をきる +9356,16386,{かじ・かじ}をとる +9498,16385,かしらをおろす +9526,16385,かずしれず +9569,16385,かすみをくう +9592,16385,かぜかおる +9592,16386,かぜがふけばおけやがもうかる +9592,16387,かぜにたつ +9592,16388,かぜひかる +9592,16389,かぜをきる +9592,16390,かぜをくらって +9592,16391,かぜをふかせる +9593,16385,かぜをひく +9614,16385,かせぐにおいつくびんぼうなし +9661,16385,かたがつく +9662,16385,かたがつく +9664,16385,かたにはまる +9664,16386,かたにはめる +9665,16385,かたがこる +9665,16386,かたでいきをする +9665,16387,かたでかぜ(を)きる +9665,16388,かたにかかる +9665,16389,かたにちからをいれる +9665,16390,かたのちからをぬく +9665,16391,かたのにをおろす +9665,16392,かたをいからせる +9665,16393,かたをいれる +9665,16394,かたをおとす +9665,16395,かたをかす +9665,16396,かたを{すく・すく}める +9665,16397,かたをたたく +9665,16398,かたをならべる +9665,16399,かたをほぐす +9665,16400,かたをもつ +9665,16401,かたをよせあう +9670,16385,ガタがくる +9677,16385,かたあしをつっこむ +9749,16385,かたずを{の・の}む +9760,16385,かたちからはいる +9777,16385,かたなおれやつきる +9777,16386,かたなにかけても +9786,16385,かたはだ(を)ぬぐ +9796,16385,かたひじいからせる +9796,16386,かたひじはる +9807,16385,かたぼう(を)かつぐ +9817,16385,かたみがせまい +9842,16385,かたるにおちる +9842,16386,かたるにたる +9862,16385,かちをおく +9864,16385,かちにいく +9864,16386,かちにのる +9864,16387,かちをひろう +9872,16385,かちうまにのる +9913,16385,かちゅうの{くり・くり}をひろう +9926,16385,かちんとくる +9931,16385,かつをいれる +9933,16385,かって{かぶと・かぶと}のおをしめよ +9933,16386,かてばかんぐんまければぞくぐん +9994,16385,かっこにいれる +9996,16385,かっこうがつく +9996,16386,かっこうをつける +10026,16385,かっしてもとうせんのみずはのまず +10055,16385,かってなねつをふく +10055,16386,かってにしろ +10131,16385,かでんにくつをいれず +10142,16385,かどがたつ +10142,16386,かどがとれる +10197,16385,かなえのけいちょうをとう +10209,16385,かなぐつわをはめる +10247,16385,かにはこうらににせてあなをほる +10264,16385,かねが{うな・うな}る +10264,16386,かねにあかせて +10264,16387,かねにいとめをつけない +10264,16388,かねにする +10264,16389,かねになる +10264,16390,かねのきれめがえんのきれめ +10264,16391,かねのなるき +10264,16392,かねのわらじでさがす +10264,16393,かねはてんかのまわり〈もち/もの〉 +10264,16394,かねをおとす +10264,16395,かねをくう +10299,16385,かねもち{けんか・けんか}せず +10376,16385,かぶがあがる +10397,16385,かふくは{あざな・あざな}えるなわの{ごと・ごと}し +10410,16385,かぶとをぬぐ +10415,16385,かぶりをふる +10433,16385,かべにみみあり +10435,16385,がべいにきす +10452,16385,かほうはねてまて +10470,16385,かまをかける +10496,16385,かまどのしたのはいまで +10496,16386,かまどをおこす +10511,16385,かみならぬみ +10511,16386,かみにめされる +10511,16387,かみはさいぶにやどる +10511,16388,かみもほとけもない +10552,16385,かみしもをぬぐ +10567,16385,かみなりにうたれたよう +10567,16386,かみなりをおとす +10571,16385,かみのけをさかだてる +10595,16385,かんでふくめる +10616,16385,かめのこうよりとしのこう +10627,16385,かもが{ねぎ・ねぎ}をしょってくる +10652,16385,かやのそと(におかれる) +10664,16385,かゆいところにてがとどく +10681,16385,からにとじこもる +10681,16386,からをやぶる +10734,16385,からすのなかぬひはあっても +10746,16385,からだがあく +10746,16386,からだがいうことをきかない +10746,16387,からだがおよぐ +10746,16388,からだをうる +10746,16389,からだをはる +10746,16390,からだをゆるす +10912,16385,かりてきたねこ +10912,16386,かりるときのじぞうがお、かえすときの{えんま・えんま}がお +10969,16385,かれきにはな +10969,16386,かれきもやまの{にぎ・にぎ}わい +11018,16385,かわいいこにはたびをさせよ +11022,16385,かわいさあまってにくさひゃくばい +11035,16385,かわいたぞうきんをしぼる +11057,16385,かわだちはかわではてる +11102,16385,かんをかさねる +11104,16385,かんにさわる +11108,16385,かんをおおってことさだまる +11109,16385,かんをつうじる +11110,16385,かん、はつをいれず +11111,16385,かんきわまる +11111,16386,かんにたえた +11111,16387,かんにたえない +11115,16385,かんをつくす +11137,16385,ガンをつける +11160,16385,かんおけにかたあしをつっこむ +11163,16385,かんかをまじえる +11211,16385,かんきをこうむる +11303,16385,がんこうしはいにてっする +11316,16385,かんこどりがなく +11374,16385,かんしゃかんげきあめあられ +11377,16385,かんしゃくをおこす +11441,16385,がんしょくなし +11441,16386,がんしょくをうしなう +11504,16385,かんぜんするところがない +11543,16385,かんたんあいてらす +11543,16386,かんたんをくだく +11562,16385,かんちゅうぼうあり +11563,16385,がんちゅうにない +11586,16385,がんていにのこる +11634,16385,かんなんなんじをたまにす +11638,16385,かんにんぶくろのおがきれる +11677,16385,かんぱつをいれず +11683,16385,かんばんがなく +11683,16386,かんばんをおろす +11705,16385,かんぷなきまでに +11768,16385,かんむりをまげる +11808,16385,かんらくきわまりてあいじょうおおし +11845,16385,きからおちたさる +11845,16386,きではなを{くく・くく}る +11845,16387,きにたけをつぐ +11845,16388,きに{よ・よ}りてうおをもとむ +11845,16389,きをみてもりをみず +11848,16385,きがあう +11848,16386,きがある +11848,16387,きがいい +11848,16388,きがおおい +11848,16389,きがおおきくなる +11848,16390,きがおけない +11848,16391,きがおもい +11848,16392,きがかつ +11848,16393,きがかるい +11848,16394,きがききすぎてまがぬける +11848,16395,きがきく +11848,16396,きがきでない +11848,16397,きがくさる +11848,16398,きがくるう +11848,16399,きがさす +11848,16400,きがしずむ +11848,16401,きがしれない +11848,16402,きがすすまない +11848,16403,きがすむ +11848,16404,きがする +11848,16405,きが{せ・せ}く +11848,16406,きがたつ +11848,16407,きがちいさい +11848,16408,きがちがう +11848,16409,きがちる +11848,16410,きがつく +11848,16411,きがつまる +11848,16412,きがつよい +11848,16413,きがとおくなる +11848,16414,きがとがめる +11848,16415,きがない +11848,16416,きがながい +11848,16417,きがぬける +11848,16418,きがのらない +11848,16419,きがはずむ +11848,16420,きがはやい +11848,16421,きがはる +11848,16422,きがはれる +11848,16423,きがひける +11848,16424,きがふさぐ +11848,16425,きがふれる +11848,16426,きがまぎれる +11848,16427,きがまわる +11848,16428,きがみじかい +11848,16429,きがむく +11848,16430,きがめいる +11848,16431,きがもめる +11848,16432,きがやすまる +11848,16433,きがゆるむ +11848,16434,きがよわい +11848,16435,きがわかい +11848,16436,きでもつ +11848,16437,きにいる +11848,16438,きにかかる +11848,16439,きにかける +11848,16440,きにくわない +11848,16441,きにさわる +11848,16442,きにする +11848,16443,きにそう +11848,16444,きにそまない +11848,16445,きにとめる +11848,16446,きになる +11848,16447,きにやむ +11848,16448,きのおけない +11848,16449,きのきいた +11848,16450,きのない +11848,16451,きはこころ +11848,16452,きもそぞろ +11848,16453,きをいれる +11848,16454,きをうしなう +11848,16455,きをおく +11848,16456,きをおとす +11848,16457,きをかねる +11848,16458,きをきかせる +11848,16459,きをくばる +11848,16460,きをしずめる +11848,16461,きをたしかにもつ +11848,16462,きをちらす +11848,16463,きをつかう +11848,16464,きをつける +11848,16465,きをとられる +11848,16466,きをとりなおす +11848,16467,きをぬく +11848,16468,きを{の・の}まれる +11848,16469,きをはく +11848,16470,きをはらす +11848,16471,きをはる +11848,16472,きをひく +11848,16473,きをまぎらす +11848,16474,きをまわす +11848,16475,きをもたせる +11848,16476,きをもむ +11848,16477,きをゆるす +11848,16478,きをゆるめる +11848,16479,きをよくする +11848,16480,きをらくにする +11848,16481,きをわるくする +11849,16385,きを{てら・てら}う +11853,16385,きをいつにする +11856,16385,きがじゅくす +11856,16386,きにじょうじる +11856,16387,きをみるにびん +11869,16385,ぎ、しんにいる +11870,16385,ぎをみてせざるはゆうなきなり +11876,16385,ギアがはいる +11876,16386,ギアをあげる +11885,16385,きいにふれる +11888,16385,きいをえたい +11943,16385,きえんをあげる +11963,16385,きかおくべし +12039,16385,ききせまる +12097,16385,ききみみをたてる +12135,16385,きいてあきれる +12135,16386,きいてごくらくみてじごく +12135,16387,ききしにまさる +12135,16388,きくはいっときのはじ +12135,16389,きくみみをもたない +12135,16390,きくもなみだ、かたるもなみだ +12164,16385,きぐらいがたかい +12194,16385,きげんをとる +12291,16385,きじもなかずばうたれまい +12402,16385,きしんやのごとし +12419,16385,きずをなめあう +12436,16385,きずぐちにしおをすりこむ +12449,16385,きせをかける +12450,16385,きせいをあげる +12474,16385,きせきにいる +12495,16385,きせんをせいする +12534,16385,きたひには +12538,16385,きたいに{ひん・ひん}する +12593,16385,きちがいにはもの +12682,16385,きつねと{たぬき・たぬき}の〈ばかしあい/だましあい〉 +12682,16386,きつねにつままれる +12685,16385,きっぷがいい +12728,16385,きどうにのる +12764,16385,きぬをさくよう +12806,16385,きばをとぐ +12806,16386,きばをぬかれる +12806,16387,きばを{む・む}く +12848,16385,きびすをかえす +12848,16386,きびすをせっして +12885,16385,きぶんがいい +12885,16386,きぶんがわるい +12908,16385,きぼねがおれる +12914,16385,きまえがいい +12927,16385,きまりがわるい +12931,16385,きみがわるい +12945,16385,きみゃくをつうじる +12979,16385,きめんひとをおどろかす +12980,16385,きもがいれる +12980,16386,きもがすわる +12980,16387,きもがふとい +12980,16388,きもにめいじる +12980,16389,きもをつぶす +12980,16390,きもをひやす +12987,16385,きもちをいれる +13063,16385,ぎゃくてにとる +13106,16385,きゃっこうをあびる +13194,16385,きゅうをおって +13264,16385,きゅうきんをなおす +13280,16385,きゅうこうをあたためる +13303,16385,きゅうしにいっしょうをえる +13318,16385,ぎゅうじをとる +13373,16385,きゅうじんのこうをいっきにかく +13382,16385,きゅうすればつうじる +13399,16385,きゅうそねこをかむ +13431,16385,きゅうちょうふところにいる +13566,16385,きょをつく +13571,16385,ぎょいをえる +13574,16385,きょうのきだおれ +13577,16385,きょうをよむ +13668,16385,ぎょうかんをよむ +13683,16385,ぎょうぎがいい +13693,16385,きょうきんをひらく +13969,16385,きょうべんをとる +14014,16385,きょうらんをきとうに{めぐ・めぐ}らす +14040,16385,ぎょえいがこい +14206,16385,きょせいおつ +14280,16385,きよみずのぶたいからとびおりる +14332,16385,きりひとはおちててんかのあきをしる +14430,16385,きりょうをさげる +14440,16385,きりんもおいてはどばにおとる +14442,16385,きってもきれない +14442,16386,きればあかいちがでる +14442,16387,きればちのでるよう +14495,16385,ぎろんをじょうげする +14645,16385,きんしつあいわす +14672,16385,きんじょう(に)はなをそえる +14698,16385,きんせんにふれる +14735,16385,きんてきをいとめる +14830,16385,ぎんりんおどる +14841,16385,くにする +14841,16386,くにやむ +14841,16387,くはらくのたね +14843,16385,ぐにもつかない +14903,16385,くうかくわれるか +14903,16386,くうやくわず +14921,16385,くうきをよむ +14975,16385,ぐうのねもでない +15040,16385,くぎをさす +15091,16385,くさはえる +15093,16385,くさいめしをくう +15093,16386,くさいものにふたをする +15102,16385,くさきもなびく +15102,16386,くさきもねむる +15117,16385,くさのねをわけて(も)さがす +15121,16385,くさびをうちこむ +15140,16385,くさっても{たい・たい} +15146,16385,くしのはがかけるよう +15146,16386,くしのはを{ひ・ひ}くよう +15148,16385,くじをきる +15177,16385,くじゅうを〈なめる/のむ〉 +15225,16385,くすりがきく +15225,16386,くすりくそうばい +15225,16387,くすりにしたくもない +15233,16385,くせになる +15249,16385,くそくらえ +15249,16386,くそのやくにもたたない +15259,16385,くだをまく +15286,16385,くちがうまい +15286,16386,くちがうるさい +15286,16387,くちがおもい +15286,16388,くちがかかる +15286,16389,くちがかたい +15286,16390,くちがかるい +15286,16391,くちがくさっても +15286,16392,くちがこえる +15286,16393,くちがさけても +15286,16394,くちがすぎる +15286,16395,くちがすべる +15286,16396,くちがへらない +15286,16397,くちがほぐれる +15286,16398,くちがまがる +15286,16399,くちがまわる +15286,16400,くちからさきにうまれる +15286,16401,くちからしんぞうがとびでそう +15286,16402,くちからひがでる +15286,16403,くちがわるい +15286,16404,くちにかける +15286,16405,くちにする +15286,16406,くちになる +15286,16407,くちにのぼる +15286,16408,くちにのりする +15286,16409,くちにはいる +15286,16410,くちのしたから +15286,16411,くちはわざわいのかど +15286,16412,くちほどに(も)ない +15286,16413,くちもはっちょう、てもはっちょう +15286,16414,くちをあけてまつ +15286,16415,くちをあわせる +15286,16416,くちをかんする +15286,16417,くちをきく +15286,16418,くちをきる +15286,16419,くちをきわめて +15286,16420,くちをすっぱくして +15286,16421,くちをそろえて +15286,16422,くちをだす +15286,16423,くちをついてでる +15286,16424,くちをつぐむ +15286,16425,くちをつめる +15286,16426,くちをとがらせる +15286,16427,くちをとざす +15286,16428,くちをにごす +15286,16429,くちをぬぐって +15286,16430,くちをぬらす +15286,16431,くちをのりする +15286,16432,くちをはさむ +15286,16433,くちをひらく +15286,16434,くちをあけば +15286,16435,くちをふうじる +15286,16436,くちをふさぐ +15286,16437,くちをむすぶ +15286,16438,くちをわる +15295,16385,くちうらをあわせる +15311,16385,くちぐちにいう +15313,16385,くちぐるまにのせられる +15345,16385,くちのはにのぼる +15348,16385,くちばしがきいろい +15348,16386,くちばしをいれる +15350,16385,くちはっちょうてはっちょう +15354,16385,くちびをきる +15356,16385,くちびるをかむ +15356,16386,くちびるをつきだす +15356,16387,くちびるを〈ぬすむ/うばう〉 +15421,16385,ぐっとくる +15451,16385,くにやぶれてさんが{あ・あ}り +15476,16385,くはいを〈なめる/きっする〉 +15480,16385,くびがつながる +15480,16386,くびがとぶ +15480,16387,くびがまわらない +15480,16388,くびにする +15480,16389,くびになる +15480,16390,くびになわをつける +15480,16391,くびのかわいちまい +15480,16392,くびをあらってまっていろ +15480,16393,くびを{かし・かし}げる +15480,16394,くびをきる +15480,16395,くびをすげかえる +15480,16396,くびをたてにふる +15480,16397,くびをつっこむ +15480,16398,くびをながくする +15480,16399,くびをひねる +15480,16400,くびをふる +15480,16401,くびをよこにふる +15499,16385,くびつりのあしをひっぱる +15501,16385,くびねっこをおさえられる +15604,16385,くもつくばかり +15604,16386,くもを{かすみ・かすみ}と +15604,16387,くもをつかむよう +15605,16385,くものこをちらすよう +15637,16385,くらがたつ +15640,16385,くらいじんしんをきわめる +15667,16385,くらしがたつ +15675,16385,グラスをかたむける +15712,16385,くらべものにならない +15727,16385,くらやみからうしをひきだす +15819,16385,くるひもくるひも +15819,16386,くるものはこばまず +15841,16385,くるしいいきのしたから +15841,16386,くるしいときのかみだのみ +15956,16385,グローブを〈あわせる/まじえる〉 +16045,16385,くわをいれる +16074,16385,ぐんをぬく +16107,16385,くんし(は)あやうきにちかよらず +16107,16386,くんし(は)ひょうへんす +16115,16385,くんしゅさんもんにいるをゆるさず +16169,16385,ぐんばいをあげる +16169,16386,ぐんばいをかえす +16185,16385,ぐんもうぞうを〈{な・な}でる/ひょうする〉 +16186,16385,ぐんもんにくだる +16205,16385,けのはえたよう +16205,16386,けをふいて{きず・きず}をもとめる +16208,16385,けにもはれにも +16225,16385,けいたりがたくていたりがたし +16239,16385,げいがこまかい +16239,16386,げいがない +16239,16387,げいはみをたすける +16262,16385,けいがいをとどめない +16263,16385,けいがいにせっする +16305,16385,けいこうとなるもぎゅうごとなるなかれ +16359,16385,げいじゅつはながくじんせいはみじかし +16392,16385,けいしてとおざける +16401,16385,けいせつのこうをつむ +16416,16385,けいぞくはちからなり +16592,16385,げきをとばす +16655,16385,げきりんにふれる +16702,16385,けじめをつける +16723,16385,けすじほども +16741,16385,けたがちがう +16742,16385,げたをあずける +16742,16386,げたをはかせる +16742,16387,げたをはくまでわからない +16763,16385,けちがつく +16763,16386,けちをつける +16775,16385,ケツのあながちいさい +16775,16386,ケツをまくる +16775,16387,ケツをもつ +16775,16388,ケツをわる +16776,16385,けつをとる +16806,16385,けっきにはやる +16931,16385,げっぷがでるほど +16944,16385,けつまつをつける +16944,16386,けつまつをつげる +16960,16385,けつるいをしぼる +17007,16385,けびょうをつかう +17024,16385,けむにまく +17031,16385,けむりになる +17031,16386,けむりをたてる +17048,16385,けりをつける +17100,16385,げんをかまえる +17100,16386,げんをさゆうにする +17100,16387,げんを{ま・ま}たない +17137,16385,けんえんもただならぬなか +17149,16385,けんかをうる +17149,16386,けんかをかう +17196,16385,けんぎがかかる +17253,16385,げんげんひをはく +17259,16385,げんこをくらわす +17260,16385,げんごにぜっする +17278,16385,げんこつをみまう +17333,16385,げんじをろうする +17574,16385,けんばのろうをとる +17705,16385,こは{かすがい・かすがい} +17705,16386,こはさんがいのくび(っ){かせ・かせ} +17705,16387,こゆえのやみ +17705,16388,こをもってしるおやのおん +17721,16385,ごをうつ +17742,16385,こいにこいする +17742,16386,こいはしあんのほか +17742,16387,こいはもうもく +17795,16385,こうなりなとげる +17795,16386,こうをそうする +17796,16385,こうをそうする +17803,16385,こうかふこうか +17806,16385,こうをきく +17813,16385,こうをあらためる +17840,16385,ごうにいりてはごうにしたがえ +17841,16385,ごうをにやす +17869,16385,こういんやのごとし +17936,16385,こうかいさきにたたず +17957,16385,こうかくあわをとばす +18026,16385,ごうきぼくとつじんにちかし +18101,16385,こうけつをしぼる +18191,16385,こうざいあいなかばする +18209,16385,こうさんなきものはこうしんなし +18237,16385,こうじまおおし +18286,16385,こうしゅところをかえる +18366,16385,こうしんにみちをひらく +18377,16385,こうじんをはいする +18403,16385,こうせいおそるべし +18525,16385,こうだんにたつ +18604,16385,こうとししてそうくにらる +18625,16385,こうどうをふむ +18699,16385,こうばこをつくる +18763,16385,こうふんをもらす +18771,16385,こうべをたれる +18771,16386,こうべをめぐらす +18794,16385,こうぼうにもふでのあやまり +18794,16386,こうぼうふでをえらばず +18878,16385,こうらに{こけ・こけ}がはえる +18878,16386,こうらをへる +18925,16385,こうるいをしぼる +18956,16385,こえがうらがえる +18956,16386,こえがかかる +18956,16387,こえなきこえ +18956,16388,こえにならないこえ +18956,16389,こえのしたから +18956,16390,こえをおとす +18956,16391,こえをかける +18956,16392,こえをきく +18956,16393,こえをだいにする +18956,16394,こえをつまらせる +18956,16395,こえを{とが・とが}らす +18956,16396,こえを{の・の}む +18956,16397,こえをはずませる +19048,16385,ゴールをわる +19129,16385,ごきげんななめならず +19129,16386,ごきげんをうかがう +19138,16385,こきみがいい +19148,16385,こきょう〈へ/に〉にしきをかざる +19163,16385,こくにすぎる +19294,16385,こくびを{かし・かし}げる +19298,16385,こくびゃくをつける +19298,16386,こくびゃくをべんじない +19365,16385,こけつにいらずんばこじをえず +19379,16385,こけんにかかわる +19385,16385,ここのこえをあげる +19387,16385,ここであったがひゃくねんめ +19387,16386,ここにきて +19387,16387,ここをせんどと +19396,16385,ここうをしのぐ +19411,16385,こごしをかがめる +19434,16385,こころがあらわれる +19434,16386,こころがうごく +19434,16387,こころがおれる +19434,16388,こころがはずむ +19434,16389,こころここにあらず +19434,16390,こころにあながあく +19434,16391,こころにかかる +19434,16392,こころにかける +19434,16393,こころにしまない +19434,16394,こころにとめる +19434,16395,こころにもない +19434,16396,こころをあわせる +19434,16397,こころをいたす +19434,16398,こころをいためる +19434,16399,こころをいれかえる +19434,16400,こころをうごかす +19434,16401,こころをうつ +19434,16402,こころをうばわれる +19434,16403,こころをおににする +19434,16404,こころをくだく +19434,16405,こころをくばる +19434,16406,こころをこめる +19434,16407,こころをゆるす +19434,16408,こころをよせる +19490,16385,こしがおもい +19490,16386,こしがかるい +19490,16387,こしがすわる +19490,16388,こしがたかい +19490,16389,こしがたたない +19490,16390,こしがつよい +19490,16391,こしがひくい +19490,16392,こしがひける +19490,16393,こしをあげる +19490,16394,こしをいれる +19490,16395,こしをおちつける +19490,16396,こしをおる +19490,16397,こしをすえる +19490,16398,こしをぬかす +19490,16399,こしをわる +19500,16385,ごしにあまる +19500,16386,ごしにはいる +19585,16385,ごじゅうにしててんめいをしる +19589,16385,こじゅうとはおにせんびき +19650,16385,こしたことはない +19769,16385,こだてにとる +19771,16385,こだねをやどす +19774,16385,ごたぶんにもれず +19809,16385,こちんとくる +19897,16385,こつにくあい{は・は}む +19924,16385,こてがきく +19924,16386,こてをかざす +19955,16385,ことあるごとに +19955,16386,ことあれかし +19955,16387,ことここにいたる +19955,16388,ことこころざしとたがう +19955,16389,こととする +19955,16390,ことなきをえる +19955,16391,ことにあたる +19955,16392,ことにふれて +19955,16393,ことによると +19955,16394,こともあろうに +19955,16395,こともなし +19955,16396,ことをおこす +19955,16397,ことをかまえる +19955,16398,ことをこのむ +19955,16399,ことをすすめる +19955,16400,ことをなす +19955,16401,ことをはこぶ +19955,16402,ことをわける +20022,16385,ことばがすぎる +20022,16386,ことばがない +20022,16387,ことばにあまえる +20022,16388,ことばにあまる +20022,16389,ことばをうしなう +20022,16390,ことばをかえす +20022,16391,ことばをつくして +20022,16392,ことばをにごす +20028,16385,こどもができる +20028,16386,こどもはかぜのこ +20038,16385,こなをかける +20080,16385,このおやにしてこのこあり +20124,16385,こばかにする +20136,16385,こばなをうごめかす +20136,16386,こばなをふくらませる +20141,16385,こばらがたつ +20154,16385,こびをうる +20158,16385,こひざを〈うつ/たたく〉 +20187,16385,こぶさにわける +20221,16385,ごへいをかつぐ +20257,16385,こまをすすめる +20260,16385,ごまを{す・す}る +20287,16385,こまたのきれあがった +20306,16385,こまものやをひらく +20313,16385,こまわりがきく +20340,16385,こみみにはさむ +20463,16385,ごらんにいれる +20502,16385,こるいをまもる +20511,16385,これあるかな +20511,16386,これさいわい +20511,16387,これでもか +20511,16388,これよりさき +20511,16389,これをもって +20511,16390,これをようするに +20576,16385,ころばぬさきの{つえ・つえ} +20576,16386,ころんでもただではおきない +20593,16385,こわきにかかえる +20612,16385,こわもてにでる +20615,16385,こんをつめる +20890,16385,ざがしらける +20890,16386,ざがみだれる +20890,16387,ざをもつ +20938,16385,さいはなげられた +21023,16385,さいくはりゅうりゅうしあげをごろうじろ +21041,16385,さいげつひとをまたず +21080,16385,さいさんがとれる +21180,16385,さいだいもらさず +21249,16385,さいはいをふる +21273,16385,さいふのくちをしめる +21273,16386,さいふ(のそこ)をはたく +21273,16387,さいふ(のひも)がかたい +21273,16388,さいふのひもをゆるめる +21368,16385,さおをさす +21407,16385,さかずきをふくむ +21413,16385,さかてにとる +21453,16385,さきにたつ +21453,16386,さきをあらそう +21453,16387,さきをこす +21456,16385,{さぎ・さぎ}を{からす・からす}といいくるめる +21525,16385,さきんずればひとをせいす +21528,16385,さくをろうする +21558,16385,さくしさくにおぼれる +21624,16385,さぐりをいれる +21630,16385,さけにのまれる +21630,16386,さけはひゃくやくのちょう +21722,16385,さじをなげる +21832,16385,さしつさされつ +22007,16385,さつびらをきる +22090,16385,さばをよむ +22175,16385,さまになる +22220,16385,さもあらばあれ +22220,16386,さもありなん +22230,16385,さやをはらう +22317,16385,さるもきからおちる +22319,16385,さるものはおわず +22319,16386,さるものはひびにうとし +22389,16385,さわらぬかみにたたりなし +22395,16385,さんをみだす +22413,16385,さんうきたらんとほっしてかぜろうにみつ +22459,16385,ざんきにたえない +22570,16385,さんしゃをさける +22573,16385,さんじゃくさがってしのかげをふまず +22578,16385,さんじゅうにしてたつ +22578,16386,さんじゅうろっけいにげるにしかず +22584,16385,さんしょはこつぶでも〈ぴりり/ひりり〉とからい +22688,16385,さんちゅうれきじつなし +22703,16385,さんどのめしより +22725,16385,さんにんよればもんじゅのちえ +22788,16385,さんべんまわってたばこにしょ +22854,16385,しいっとうをげんずる +22854,16386,しのてんきをとる +22854,16387,しをたまう +22858,16385,しをつうじる +22881,16385,じでいく +22882,16385,じになる +22887,16385,じをひくくする +22917,16385,しあんにあまる +23105,16385,しおをする +23105,16386,しおをまく +23163,16385,しかをおうものはやまをみず +23168,16385,しがにもかけない +23293,16385,じかんをかせぐ +23323,16385,じぎに〈ひとしい/るいする〉 +23327,16385,しきいがたかい +23560,16385,じごくでほとけ +23560,16386,じごくのかま(のふた)があく +23560,16387,じごくのさたもかねしだい +23686,16385,じじつはしょうせつよりもきなり +23721,16385,しじゅうにしてまどわず +23799,16385,じしんかみなりかじ{おやじ・おやじ} +23810,16385,ししてのち{や・や}む +23810,16386,しせるこうめいいけるちゅうたつをはしらしむ +23856,16385,しせいてんにあり +23856,16386,しせいめいあり +23858,16385,しせいをただす +23896,16385,しせんをあつめる +23896,16386,しせんをあびる +23946,16385,じそんのためにびでんをかわず +23949,16385,したにもおかない +23950,16385,したがこえる +23950,16386,したがまわる +23950,16387,したをだす +23950,16388,したをならす +23950,16389,したをまく +23953,16385,じたともにゆるす +23974,16385,じだいがつく +24031,16385,したてにでる +24041,16385,したのねもかわかぬうちに +24080,16385,じだんだ(を)ふむ +24120,16385,しちゅうにかつをもとめる +24245,16385,じっしにあまる +24245,16386,じっしのさすところ +24364,16385,しっぽをだす +24364,16386,しっぽをつかまえる +24364,16387,しっぽをふる +24364,16388,しっぽをまく +24482,16385,しなをつくる +24549,16385,しにばなをさかせる +24551,16385,しにみずをとる +24559,16385,しにんにくちなし +24564,16385,しんではなみがさくものか +24564,16386,しんでもしにきれない +24575,16385,しのつく +24577,16385,しのぎをけずる +24597,16385,しばにむちうつ +24606,16385,しばいをうつ +24642,16385,じばらをきる +24694,16385,しびれがきれる +24716,16385,しぶかわの{む・む}けた +24721,16385,しふくをこやす +24746,16385,しふんのかにまよう +24752,16385,(じぶんで)じぶんのくびをしめる +24828,16385,しまつがわるい +24828,16386,しまつにおえない +24895,16385,しめいをせいする +24913,16385,しめしがつかない +24944,16385,しもをおく +24989,16385,しゃにかまえる +24994,16385,じゃのみちはへび +24994,16386,じゃはすんにしてひとを{の・の}む +25037,16385,しゃかにせっぽう +25080,16385,しゃくにさわる +25185,16385,しゃじくをながす +25276,16385,シャッターをきる +25284,16385,シャッポをぬぐ +25445,16385,しゅにまじわればあかくなる +25445,16386,しゅをいれる +25484,16385,しゆうをけっする +25488,16385,じゅうよくごうをせいす +25491,16385,じゅうかつだい +25530,16385,しゅうかてきせず +25677,16385,じゅうじをきる +25677,16386,じゅうじかをせおう +25827,16385,じゅうてんをおく +25882,16385,しゅうはをおくる +25886,16385,じゅうばこのすみを(⦅ようじ\ようじ⦆で)〈つつく/ほじくる〉 +25900,16385,しゅうびをひらく +25935,16385,じゅうまんおくどへたびだつ +25946,16385,じゅうもくのみるところ +26153,16385,しゅごがおおきい +26259,16385,しゅそりょうたんをじす +26283,16385,しゅちゅうにおさめる +26283,16386,しゅちゅうにきす +26432,16385,しゅはいをかたむける +26442,16385,しゅびはじょうじょう +26447,16385,しゅひつをいれる +26482,16385,じゅみょうがちぢまる +26628,16385,しゅんじゅうにとむ +26635,16385,しゅんしょういっこくあたいせんきん +26685,16385,じゅんぷうにほをあげる +26698,16385,しゅんみんあかつきをおぼえず +26744,16385,しょいなげをくわせる +26757,16385,しょうをいんとするものはまずうまをいよ +26764,16385,しょうにあたる +26791,16385,じょうがうつる +26791,16386,じょうにもろい +26791,16387,じょうをつうじる +26811,16385,しょういをすててだいどうにつく +26919,16385,しょうぎをさす +26924,16385,じょうきをいっする +27093,16385,しょうじきの{こうべ・こうべ}にかみやどる +27176,16385,しょうじんかんきょしてふぜんをなす +27189,16385,じょうずのてからみずがも(れ)る +27247,16385,しょうそくをたつ +27274,16385,じょうだんではない +27274,16386,じょうだんをとばす +27283,16385,しょうちゅうにおさめる +27370,16385,しょうねをすえる +27373,16385,しょうねんおいやすくがくなりがたし +27384,16385,しょうのむしをころしてだいのむしをたすける +27392,16385,しょうばいはみちによってかしこし +27425,16385,しょうぶはときのうん +27425,16386,しょうぶごとは{げた・げた}をはくまで +27500,16385,しょうめんきって +27500,16386,しょうめんをきる +27556,16385,しょうりゃくにしたがう +27674,16385,しょくがおちる +27674,16386,しょくがすすむ +27674,16387,しょくがほそい +27674,16388,しょくがほそる +27702,16385,しょくしがうごく +27704,16385,しょくじがすすむ +27707,16385,しょくしゅをのばす +27726,16385,しょくぜんにのぼせる +27851,16385,じょしとしょうじんはやしないがたし +27897,16385,しょしんわするべからず +27951,16385,しょたいをたたむ +28126,16385,しらをきる +28165,16385,しらぬがほとけ +28165,16386,しらぬぞんぜぬ +28165,16387,しらぬかおのはんべえ +28169,16385,しらはのやがたつ +28178,16385,しらべがつく +28189,16385,しりがあおい +28189,16386,しりがおもい +28189,16387,しりがかるい +28189,16388,しりがこそばゆい +28189,16389,しりがながい +28189,16390,しりがわれる +28189,16391,しりにひがつく +28189,16392,しりにほをかける +28189,16393,しりのけをぬく +28189,16394,しり(のした)にしく +28189,16395,しりをあげる +28189,16396,しりをすえる +28189,16397,しりをたたく +28189,16398,しりをぬぐう +28189,16399,しりをふる +28189,16400,しりをまくる +28189,16401,しりをもちこむ +28200,16385,しりうまにのる +28252,16385,しりょくをつくす +28260,16385,しったことではない +28260,16386,しってかしらでか +28260,16387,しってのとおり +28260,16388,しらしむべからず、よらしむべし +28260,16389,しるひとぞしる +28260,16390,しるやしらずや +28306,16385,しろいはをみせる +28306,16386,しろいめでみる +28381,16385,じわじわくる +28400,16385,しんなくばたたず +28400,16386,しんをとう +28402,16385,しんにせまる +28407,16385,しんはなきよりたにんはくいより +28503,16385,じんかんいたるところせいざんあり +28515,16385,しんき{ま・ま}きなおし +28524,16385,じんぎをきる +28524,16386,じんぎをとおす +28569,16385,しんけいを{とが・とが}らせる +28573,16385,しんけつをそそぐ +28597,16385,じんごにおちない +28614,16385,じんこうにかいしゃする +28614,16386,じんこうにのぼる +28616,16385,じんこうも{た・た}かず{へ・へ}もひらず +28627,16385,しんこんをかたむける +28643,16385,しんさんをなめる +28660,16385,じんじをつくしててんめいをまつ +28717,16385,しんしょうをはたく +28777,16385,じんせいいきにかんず +28813,16385,しんそうにそだつ +28818,16385,しんぞうがつよい +28818,16386,しんぞうがとびでそう +28818,16387,しんぞうにけがはえている +28850,16385,しんたんをさむからしめる +28914,16385,しんとう(を)めっきゃくすればひもまたすずし +28944,16385,しんにゅうをかける +29018,16385,しんぺんをきれいにする +29056,16385,しんめいをとして +29056,16386,しんめいをなげうって +29068,16385,じんもんにくだる +29130,16385,ずにあたる +29130,16386,ずにのる +29131,16385,ずがたかい +29142,16385,すいもあまいも{か・か}みわけた +29248,16385,すいせいのごとく +29280,16385,スイッチがはいる +29320,16385,すいほうにきす +29396,16385,スープのさめないきょり +29452,16385,すがたをけす +29482,16385,すきをこらす +29484,16385,すきこそもののじょうずなれ +29544,16385,すぎたるは{なお・なお}およばざるがごとし +29595,16385,スクラムをくむ +29664,16385,すごみをきかせる +29680,16385,すじがいい +29680,16386,すじがわるい +29714,16385,すずをころがすようなこえ +29714,16386,すずをつける +29714,16387,すずをはったようなめ +29740,16385,すずめひゃくまでおどりわすれず +29849,16385,すっぱいものがたべたい +29902,16385,すてたものではない +29902,16386,すててかえりみない +29902,16387,すてるかみあればひろうかみあり +29972,16385,すなをかむよう +30006,16385,すねにきずもつ +30123,16385,すべったのころんだのと +30136,16385,ずぼしをさされる +30155,16385,すまじきものはみやづかえ +30164,16385,すみにおけない +30201,16385,すめばみやこ +30211,16385,すもうにかってしょうぶにまける +30211,16386,すもうにならない +30388,16385,すんてつひとをさす +30406,16385,せにはらはかえられぬ +30406,16386,せをむける +30415,16385,せいをうける +30423,16385,せいがでる +30423,16386,せいをだす +30618,16385,せいこくを〈いる/える〉 +30640,16385,せいさつよだつのけんをにぎる +30736,16385,せいしんいっとうなにごとかならざらん +30814,16385,せいだくあわせ{の・の}む +30981,16385,せいめいをとす +31037,16385,せいるい{とも・とも}にくだる +31093,16385,せきのあたたまるいとまがない +31093,16386,せきをおなじゅうせず +31093,16387,せきをけがす +31094,16385,せきをきったよう +31097,16385,せきをいれる +31177,16385,せきひんあらうがごとし +31232,16385,セコンドをきざむ +31251,16385,せすじがさむくなる +31251,16386,せすじがのびる +31382,16385,せっしょくをたもつ +31510,16385,せなかがみえる +31510,16386,せなかをおう +31510,16387,せなかをおす +31510,16388,せなかをみてそだつ +31537,16385,ぜひにおよばぬ +31537,16386,ぜひもない +31647,16385,ゼロがひとつおおい +31647,16386,ゼロから(さい)〈しゅっぱつ/スタート〉する +31659,16385,せわがやける +31659,16386,せわにくだける +31659,16387,せわになる +31659,16388,せわはない +31659,16389,せわをやく +31669,16385,せんをこす +31674,16385,せんがほそい +31675,16385,せんにもれる +31675,16386,せんをことにする +31676,16385,せんにきる +31692,16385,ぜんをおって +31952,16385,ぜんしゃのてつをふむ +31952,16386,ぜんしゃのふくてつ、こうしゃのいましめ +32024,16385,ぜんしんぜんれいをうちこむ +32114,16385,せんたんをきる +32115,16385,せんたんをひらく +32116,16385,せんだんはふたばよりかんばし +32158,16385,ぜんてつをふむ +32179,16385,せんとうをきる +32183,16385,せんどうおおくしてふねやまにのぼる +32321,16385,せんまんにんといえどもわれいかん +32345,16385,ぜんもんのとら、こうもんのおおかみ +32369,16385,せんりのつつみも{あり・あり}のあなからくずれる +32369,16386,せんりのみちもいっぽよりはじまる +32431,16385,そうとする +32446,16385,そうはとんやがおろさない +32502,16385,そうがにかかる +32508,16385,そうかいへんじてそうでんとなる +32617,16385,そうごうをくずす +32794,16385,そうだんにのる +32864,16385,そうはくをなめる +33016,16385,ゾーンにはいる +33080,16385,ぞくじにいりやすい +33121,16385,そくせきを〈いんする/しるす〉 +33203,16385,そこがあさい +33203,16386,そこがしれない +33203,16387,そこがしれる +33203,16388,そこがぬける +33203,16389,そこがわれる +33203,16390,そこをいれる +33203,16391,そこをうつ +33203,16392,そこをつく +33203,16393,そこをつける +33203,16394,そこをはらう +33203,16395,そこをわる +33204,16385,そこへいくと +33204,16386,そこへもってきて +33209,16385,そこいじがわるい +33280,16385,そじょうにのせる +33386,16385,ぞっとしない +33393,16385,そっぽをむく +33395,16385,そでにすがる +33395,16386,そでにする +33395,16387,そでふりあうもたしょうのえん +33395,16388,そでをしぼる +33395,16389,そでをとおす +33395,16390,そでをひく +33438,16385,そとぼりをうめる +33463,16385,そのときはそのとき +33480,16385,そのてはくわなのやきはまぐり +33612,16385,そりがあわない +33612,16386,そりをうたせる +33636,16385,それかあらぬか +33636,16386,それみたことか +33671,16385,それはそうだ +33671,16386,それはないでしょう +33683,16385,そろいもそろって +33690,16385,そろばんをはじく +33737,16385,そんしてとくとれ +33756,16385,そんなこんな +33782,16385,たなし +33785,16385,たとする +33824,16385,たいをしずめる +33824,16386,たいをひらく +33837,16385,だいなりしょうなり +33837,16386,だいのむしをいかしてしょうのむしをころす +33837,16387,だいはしょうをかねる +33837,16388,だいをなす +33865,16385,だいいっせいをあげる +33868,16385,だいいっぽをふみだす +33919,16385,たいかくにれっする +33954,16385,たいぎしんをめっす +33971,16385,たいぎょをいっする +34027,16385,たいこばんをおす +34048,16385,だいこういでずんば +34073,16385,たいざんめいどうして{ねずみ・ねずみ}いっぴき +34091,16385,だいじにいたる +34091,16386,だいじのまえのしょうじ +34091,16387,だいじをとる +34183,16385,だいじんかぜをふかせる +34469,16385,たいよくはむよくににたり +34537,16385,タオルをなげる +34539,16385,たおれてのち{や・や}む +34542,16385,たかがしれている +34542,16386,たかのしれた +34542,16387,たかをくくる +34543,16385,たかはうえてもほをつまず +34544,16385,たががゆるむ +34544,16386,たがをしめる +34764,16385,たけをわったよう +34920,16385,たしてにでわる +34940,16385,たぜいにぶぜい +34954,16385,たたますますべんず +34955,16385,ただの{ねずみ・ねずみ}ではない +34955,16386,ただよりたかいものはない +34957,16385,だだをこねる +34973,16385,たたけばほこりがでる +34997,16385,たたみのうえでしぬ +35004,16385,たたらをふむ +35078,16385,たちばがない +35111,16385,たつとりあとをにごさず +35111,16386,たてばしゃくやくすわればぼたん +35202,16385,たづなをしめる +35229,16385,たてにとる +35229,16386,たてをつく +35231,16385,たてからみてもよこからみても +35231,16386,たてのものをよこにもしない +35234,16385,たでくうむしもすきずき +35241,16385,たていたにみず +35338,16385,たなからぼたもち +35338,16386,たなにあげる +35344,16385,たなごころをさすよう +35371,16385,たにんのせんきをずつうにやむ +35371,16386,たにんのふこうはみつのあじ +35374,16385,たねがわれる +35374,16386,たねをつける +35374,16387,たねをやどす +35407,16385,たばになってかかる +35424,16385,たびからたび +35424,16386,たびのはじはかきすて +35424,16387,たびはみちづれよはなさけ +35514,16385,たまに{きず・きず} +35514,16386,たまみがかざればひかりなし +35514,16387,たまをいだいてつみあり +35514,16388,たまをころがすよう +35515,16385,たまがつまる +35515,16386,たまがはしる +35515,16387,たまをなげる +35537,16385,たまごにめはな +35541,16385,たましいをいれかえる +35541,16386,たましいをてんがいへとばす +35543,16385,だまされたとおもって +35581,16385,(たみは)よらしむべし、しらしむべからず +35594,16385,ためにする +35597,16385,だめでもともと +35597,16386,だめをおす +35597,16387,だめをだす +35628,16385,たもとをしぼる +35628,16386,たもとをつらねる +35628,16387,たもとをわかつ +35717,16385,たれあって +35720,16385,だれあって +35720,16386,だれいうとなく +35720,16387,だれしらぬものがない +35720,16388,だれであれ +35720,16389,だれのめにも +35720,16390,だれ{はばか・はばか}らず +35758,16385,たわらをわる +35765,16385,たんをはっする +35798,16385,たんかをきる +35826,16385,たんきはそんき +35894,16385,だんしちゅうぼうにいらず +35905,16385,だんじておこなえばきしんもこれをさく +35924,16385,だんじょしちさいにしてせきをおなじゅうせず +36015,16385,たんちょにつく +36155,16385,ちにあしをつける +36155,16386,ちにおちる +36155,16387,ちをはらう +36156,16385,ちがかよう +36156,16386,ちがさわぐ +36156,16387,ちがのぼる +36156,16388,ちでちをあらう +36156,16389,ちとあせのけっしょう +36156,16390,ちとなりにくとなる +36156,16391,ちにうえる +36156,16392,ちのあせをながす +36156,16393,ちのあめがふる +36156,16394,ちのうみになる +36156,16395,ちのでる(よう) +36156,16396,ちのなみだをながす +36156,16397,ちのにじむ(よう) +36156,16398,ちはみずよりもこし +36156,16399,ちもなみだもない +36156,16400,ちわきにくおどる +36156,16401,ちをひく +36156,16402,ちをみる +36156,16403,ちをわける +36158,16385,ちにいてらんをわすれず +36199,16385,ちえがまわる +36199,16386,ちえをつける +36214,16385,チェックをいれる +36229,16385,ちかにもぐる +36269,16385,ちからになる +36269,16386,ちからをいれる +36269,16387,ちからをおとす +36269,16388,ちからをかす +36269,16389,ちからをかりる +36269,16390,ちからをこめる +36273,16385,ちきをえる +36469,16385,ちっとやそっと +36490,16385,ちにくをわけあう +36499,16385,ちのけがおおい +36545,16385,ちまつりにあげる +36552,16385,ちみちをあげる +36564,16385,ちゃにする +36708,16385,ちゃばらもいっとき +36768,16385,ちゅうにうく +36834,16385,ちゅうげんにしかをおう +36836,16385,ちゅうげんみみにさからう +36883,16385,ちゅうしんにくんにつかえず +36979,16385,ちゅうもんをつける +36980,16385,ちゅうやをわかたず +37011,16385,ちょにつく +37012,16385,ちよにやちよに +37026,16385,ちょうよはなよ +37124,16385,ちょうしがいい +37124,16386,ちょうしにのる +37124,16387,ちょうしのなみにのる +37124,16388,ちょうしをあわせる +37124,16389,ちょうしをとる +37167,16385,ちょうずをつかう +37199,16385,ちょうだをいっする +37220,16385,ちょうちんにつりがね +37303,16385,ちょうやのゆめをさます +37452,16385,ちょっとやそっと +37497,16385,ちりもつもればやまとなる +37497,16386,ちりをきる +37541,16385,ちんとする +37648,16385,ちんもくはきん +37648,16386,ちんもくをまもる +37648,16387,ちんもくをやぶる +37700,16385,ついずいをゆるさない +37867,16385,つえともはしらともたのむ +37867,16386,つえをひく +37910,16385,つきがみちる +37910,16386,つきとすっぽん +37910,16387,つきにむらくも、はなにかぜ +37910,16388,つきみちて +37917,16385,つぎからつぎへ +38015,16385,つきよにかまをぬかれる +38015,16386,つきよに{ちょうちん・ちょうちん} +38062,16385,つけがまわってくる +38062,16386,つけをまわす +38156,16385,つちいっしょうかねいっしょう +38156,16386,つちがつく +38156,16387,つちにかえる +38156,16388,つちにしたしむ +38156,16389,つちをふむ +38261,16385,つなをはる +38271,16385,つなみてんでんこ +38273,16385,つねにない +38280,16385,つのをためてうしをころす +38280,16386,つのをはやす +38290,16385,つばをつける +38303,16385,つぶがそろっている +38307,16385,つぶしがきく +38326,16385,つぼにはまる +38366,16385,つみがない +38366,16386,つみをきせる +38366,16387,つみをきる +38398,16385,つむじをまげる +38402,16385,つめにひをともす +38402,16386,つめの{あか・あか}ほど +38402,16387,つめの{あか・あか}をせんじてのむ +38402,16388,つめをとぐ +38402,16389,つめをのばす +38405,16385,つめあとをのこす +38418,16385,つめばらをきらせる +38478,16385,つらのかわがあつい +38478,16386,つらのかわがせんまいばりだ +38532,16385,つったさかなにえさを〈やら/あげ〉ない +38589,16385,てがあがる +38589,16386,てがうしろにまわる +38589,16387,てがかかる +38589,16388,てがこむ +38589,16389,てがすく +38589,16390,てがつく +38589,16391,てがつけられない +38589,16392,てがでない +38589,16393,てがでる +38589,16394,てがとどかない +38589,16395,てがとどく +38589,16396,てがない +38589,16397,てがながい +38589,16398,てがのびる +38589,16399,てがはいる +38589,16400,てがはなせない +38589,16401,てがはなれる +38589,16402,てがはやい +38589,16403,てがまわる +38589,16404,てにあせ(を)にぎる +38589,16405,てにあまる +38589,16406,てにいった +38589,16407,てにいれる +38589,16408,てにおえない +38589,16409,てにおちる +38589,16410,てにかける +38589,16411,てにしょくをつける +38589,16412,てにする +38589,16413,てにつかない +38589,16414,てにてをとって +38589,16415,てにとる +38589,16416,てにとるよう +38589,16417,てにのる +38589,16418,てのきれるよう +38589,16419,てのほどこしようがない +38589,16420,てのまいあしのふむところをしらず +38589,16421,てもあしもでない +38589,16422,てをあげる +38589,16423,てをあげろ +38589,16424,てをあわせる +38589,16425,てをいれる +38589,16426,てをうごかす +38589,16427,てをうつ +38589,16428,てをかえしなをかえ +38589,16429,てをかける +38589,16430,てをかす +38589,16431,てをかりる +38589,16432,てをきる +38589,16433,てをくだす +38589,16434,てをくむ +38589,16435,てをくわえる +38589,16436,てをこまねく +38589,16437,てをしめる +38589,16438,てをすかす +38589,16439,てをそめる +38589,16440,てをだす +38589,16441,てをたずさえる +38589,16442,てを{つか・つか}ねる +38589,16443,てをつくす +38589,16444,てをつける +38589,16445,てをとおす +38589,16446,てをとる +38589,16447,てをにぎる +38589,16448,てをぬく +38589,16449,てをのばす +38589,16450,てをひく +38589,16451,てをひろげる +38589,16452,てをほどこす +38589,16453,てをまわす +38589,16454,てをむすぶ +38589,16455,てをやく +38589,16456,てをやすめる +38589,16457,てをゆるめる +38589,16458,てをよごす +38589,16459,てをわかつ +38589,16460,てをわずらわす +38599,16385,てあかのついた +38776,16385,ていしゅのすきなあか{えぼし・えぼし} +38776,16386,ていしゅをしりにしく +38981,16385,テープをきる +38982,16385,テーブルにのせる +39021,16385,てかせあしかせ +39026,16385,てがたなをきる +39040,16385,てきにしおをおくる +39040,16386,てきにまわす +39040,16387,てきはほんのうじにあり +39040,16388,てきもさるもの +39155,16385,てぐすね(を)ひく +39156,16385,てぐせがわるい +39179,16385,てこでもうごかない +39186,16385,てごころをくわえる +39226,16385,てしおにかける +39293,16385,てだまにとる +39300,16385,てつはあついうちにうて +39301,16385,てつをふむ +39439,16385,てなべさげても +39459,16385,てのうらをかえすよう +39462,16385,てのひら(のうえ)でおどる +39462,16386,てのひらをかえすよう +39494,16385,ではなをくじく +39496,16385,でばなをくじく +39526,16385,てぶくろをなげる +39670,16385,でる{くい・くい}はうたれる +39670,16386,でるところへでる +39670,16387,でるまくではない +39711,16385,てんからふったかちからわいたか +39711,16386,てんたかくうまこゆ +39711,16387,てんにつばする +39711,16388,てんににじつなくくにににおうなし +39711,16389,てんにもちにも +39711,16390,てんにものぼるここち +39711,16391,てんのなせる +39711,16392,てんはにぶつをあたえず +39711,16393,てんはみずからたすくるものをたすく +39711,16394,てんをあおぐ +39711,16395,てんをまする +39736,16385,てんかをとる +39794,16385,てんぐになる +39823,16385,てんさいはわすれたころにやってくる +39855,16385,てんじゅをまっとうする +39865,16385,てんじょうてんげゆいがどくそん +39866,16385,てんじょうをうつ +39888,16385,てんすうをかせぐ +39898,16385,てんせきこけむさず +39951,16385,てんてきいしを{うが・うが}つ +40001,16385,てんばくうをゆく +40022,16385,てんびんにかける +40047,16385,てんぼうがない +40065,16385,てんもうかいかいそにしてもらさず +40104,16385,どがすぎる +40104,16386,どをうしなう +40104,16387,どをくわえる +40104,16388,どをこす +40128,16385,といえばうそになる +40145,16385,トイレがちかい +40150,16385,とうをえる +40159,16385,とうがたつ +40161,16385,とうにおちずかたるにおちる +40161,16386,とうはいっときのはじ +40177,16385,どうにいる +40181,16385,どうということはない +40224,16385,とうかしたしむべきこう +40234,16385,どうかとおもう +40261,16385,とうかんに(ふ)する +40447,16385,どうじつのだんで(は)ない +40585,16385,とうだいもとくらし +40673,16385,どうにもしようがない +40673,16386,どうにもならない +40683,16385,どうのこうの +40726,16385,どうびょうあいあわれむ +40728,16385,とうふにかすがい +40822,16385,とうりものいわざれども、したおのずからみちをなす +40877,16385,とおくのしんせきよりちかくのたにん +40916,16385,とおめがきく +40965,16385,ときはかねなり +40965,16386,ときをうつさず +40965,16387,ときをわかたず +40986,16385,ときしもあれ +41016,16385,どぎもをぬく +41040,16385,とくとする +41040,16386,とくはこならず +41050,16385,どくにもくすりにもならない +41050,16386,どくをあおぐ +41050,16387,どくをくらわばさらまで +41050,16388,どくをもってどくをせいする +41110,16385,どくしょひゃっぺん〈ぎ/い〉おのずからつうず +41238,16385,とぐろをまく +41259,16385,とこにつく +41259,16386,とこに〈ふす/ふせる〉 +41259,16387,とこをとる +41261,16385,どこにめをつけているのか +41261,16386,どこのうまのほねかわからない +41261,16387,どこふくかぜ +41261,16388,どこをおせば +41261,16389,どこをき(りと)っても +41261,16390,どこを{たた・たた}いても +41261,16391,どこを{たた・たた}けばそんなねがでるのか +41298,16385,ところかまわず +41298,16386,ところかわればしなかわる +41298,16387,ところきらわず +41298,16388,ところをえる +41309,16385,とさかにくる +41320,16385,としがあける +41320,16386,としがいく +41320,16387,としにはかてない +41320,16388,としはあらそえない +41320,16389,としをおって +41320,16390,としをこす +41320,16391,としをとる +41326,16385,どじをふむ +41367,16385,としはもいかぬ +41373,16385,としまわりがわるい +41380,16385,としゅなおじせず +41384,16385,としょにひかれるひつじのよう +41442,16385,どそくでふみこむ +41477,16385,どちらにころんでも +41502,16385,どっきにあてられる +41502,16386,どっきをぬかれる +41528,16385,とっこにとる +41551,16385,どっちへころんでも +41551,16386,どっちもどっち +41597,16385,どつぼにはまる +41621,16385,ととうをくむ +41640,16385,とどまるところをしらない +41641,16385,とどめをさす +41660,16385,となりのしばふは〈あおい/うつくしい〉 +41660,16386,となりのふこうはかものあじ +41676,16385,どのくち〈が/で〉いうのか +41676,16386,どのつらさげて +41778,16385,どひょうにつまる +41778,16386,どひょうをわる +41782,16385,とぶとりをおとすいきおい +41782,16386,とぶようにうれる +41782,16387,とんでひにいるなつのむし +41784,16385,どぶにすてる +41798,16385,とほうにくれる +41839,16385,とむねをつかれる +41888,16385,ともづなをとく +41893,16385,ともにする +41893,16386,ともにてんを{いただ・いただ}かず +41906,16385,とやにつく +41924,16385,とらのいをかる{きつね・きつね} +41924,16386,とらのおをふむ +41924,16387,とらはししてかわをのこす +41924,16388,とらをのにはなつ +41938,16385,ドライブがかかる +42013,16385,とりなきさとの{こうもり・こうもり} +42109,16385,とりつくしまもない +42125,16385,とりとめ(が)ない +42146,16385,とりはだがたつ +42197,16385,とるにたりない +42197,16386,とるものもとりあえず +42198,16385,とらぬ{たぬき・たぬき}のかわざんよう +42272,16385,どろのようにねむる +42272,16386,どろをかぶる +42272,16387,どろをぬる +42272,16388,どろをはく +42308,16385,どろぼうにおいせん +42308,16386,どろぼうにもさんぶのり +42308,16387,どろぼうをとらえてみればわがこなり +42308,16388,どろぼうをみてなわをなう +42319,16385,どろんをきめる +42325,16385,とわのねむりにつく +42412,16385,とんびが{たか・たか}をうむ +42412,16386,とんびにあぶらあげをさらわれる +42422,16385,とんぼをきる +42431,16385,なに(し)おう +42431,16386,なにはじない +42431,16387,な(の)ある +42431,16388,なのとおった +42431,16389,なはたいをあらわす +42431,16390,なもない +42431,16391,なをあげる +42431,16392,なをうる +42431,16393,なをおしむ +42431,16394,なをかりる +42431,16395,なをすててじつをとる +42431,16396,なをつらねる +42431,16397,なをなす +42431,16398,なをのこす +42431,16399,なをはずかしめる +42431,16400,なを{は・は}せる +42444,16385,ないそではふれぬ +42444,16386,ないでもない +42444,16387,ないものはない +42444,16388,なかったことにする +42444,16389,なくてななくせ +42624,16385,ながいねむりにつく +42625,16385,ながいめでみる +42625,16386,ながいものにはまかれろ +42625,16387,ながい{わらじ・わらじ}をはく +42710,16385,ながのいとまをつげる +42755,16385,ながれに{さお・さお}さす +42755,16386,ながれを{く・く}む +42761,16385,なきをいれる +42761,16386,なきをみる +42764,16385,なきにひとしい +42764,16386,なきをえない +42787,16385,なきだしそうな +42790,16385,なき(っ)つらにはち +42802,16385,なきべそをかく +42808,16385,なきものにする +42817,16385,ないてばしょくをきる +42817,16386,ないてもわらっても +42817,16387,なくことじとうにはかてぬ +42817,16388,なくこもだまる +42817,16389,なくになけない +42840,16385,なくんばあるべからず +42885,16385,なさけが{あだ・あだ}となる +42885,16386,なさけはひとのためならず +42912,16385,なせばなる +42940,16385,なたをふるう +42952,16385,なだれをうつ +42960,16385,なつとなる +43029,16385,ななえのひざをやえにおって +43047,16385,なにくわぬかお +43047,16386,なににもまして +43047,16387,なにはさておき +43047,16388,なには(とも)あれ +43047,16389,なにはなくとも +43047,16390,なにふじゆうなく +43047,16391,なにをおっしゃるうさぎさん +43050,16385,なにがかなしくて +43053,16385,なにからなにまで +43063,16385,なにするものぞ +43084,16385,なにわの{あし・あし}はいせの{はまおぎ・はまおぎ} +43085,16385,なにを{お・お}いても +43085,16386,なにをかいわんや +43085,16387,なにをかくそう +43097,16385,なのりをあげる +43116,16385,なべをふる +43141,16385,なまえをうる +43141,16386,なまえをかりる +43148,16385,なまきをさく +43152,16385,なまくびをきる +43155,16385,なまけもののせっくばたらき +43180,16385,なまつばをのみこむ +43196,16385,なまびょうほうはおおけがのもと +43212,16385,なまよいほんしょう{たが・たが}わず +43221,16385,なみしずか +43221,16386,なみたかし +43221,16387,なみにのる +43237,16385,なみだにくれる +43237,16386,なみだにしずむ +43237,16387,なみだにむせぶ +43237,16388,なみだをおさえる +43237,16389,なみだをおぼえる +43237,16390,なみだをきんじえない +43237,16391,なみだをさそう +43237,16392,なみだをそそぐ +43237,16393,なみだを{の・の}む +43237,16394,なみだをふるって +43237,16395,なみだをもよおす +43283,16385,ならいせいとなる +43286,16385,ならうよりなれよ +43305,16385,ならぬかんにんするがかんにん +43318,16385,なりをひそめる +43380,16385,なわをうつ +43426,16385,なんくせをつける +43446,16385,なんじじしんをしれ +43466,16385,なんぞしらん +43466,16386,なんぞはからん +43513,16385,なんのことはない +43550,16385,にがおもい +43550,16386,にがかつ +43592,16385,にえゆをのまされる +43610,16385,にかいからめぐすり +43610,16386,にかいへあげてはしごをはずす +43620,16385,にがしたさかなはおおきい +43629,16385,にがむしになる +43629,16386,にがむしをかみつぶしたようなかお +43652,16385,にくがあがる +43652,16386,にくをきらせてほねをたつ +43704,16385,にくまれっこよに{はばか・はばか}る +43707,16385,にくんでもあまりある +43718,16385,にげをうつ +43739,16385,にげるがかち +43766,16385,にさんにとどまらない +43770,16385,にしもひがしもわからない +43773,16385,にじのはしをわたる +43780,16385,にしきをかざる +43831,16385,にせをちぎる +43836,16385,にせものをつかまされる +43844,16385,にそくのわらじをはく +43869,16385,にちにちこれこうじつ +43949,16385,にとをおう +43950,16385,にどあることはさんどある +43976,16385,にのあしをふむ +43978,16385,にのくがつげない +44244,16385,にてもやいてもくえない +44244,16386,にるなりやくなり +44258,16385,にわとりがさきかたまごがさきか +44258,16386,にわとりをさくにぎゅうとうをもちいる +44261,16385,にんをみてほうをとけ +44275,16385,にんげんいたるところせいざんあり +44275,16386,にんげんのかわをかぶった +44275,16387,にんげんばんじさいおうがうま +44340,16385,ぬかに{くぎ・くぎ} +44350,16385,ぬかみそがくさる +44369,16385,ぬきさしならない +44381,16385,ぬきつぬかれつ +44389,16385,ぬけがいい +44404,16385,ぬけめ(が)ない +44409,16385,ぬすっとたけだけしい +44409,16386,ぬすっとにおいせん +44409,16387,ぬすっとにもさんぶのり +44409,16388,ぬすっとのひるね +44409,16389,ぬすっとをとらえてみればわがこなり +44458,16385,ぬるまゆにつかる +44471,16385,ぬれて〈で/に〉{あわ・あわ} +44477,16385,ぬれぬさきこそつゆをも{いと・いと}え +44480,16385,ねをあげる +44481,16385,ねがはる +44481,16386,ねをけす +44482,16385,ねがはえる +44482,16386,ねにもつ +44482,16387,ねもはもない +44482,16388,ねをおろす +44482,16389,ねをすえる +44518,16385,ねおきがわるい +44524,16385,ねがってもない +44553,16385,ねくびを{か・か}く +44561,16385,ねこにかつおせち +44561,16386,ねこにこばん +44561,16387,ねこにまたたび +44561,16388,ねこのくびにすずをつける +44561,16389,ねこのこいっぴきいない +44561,16390,ねこのてもかりたい +44561,16391,ねこも{しゃくし・しゃくし}も +44561,16392,ねこを{かぶ・かぶ}る +44601,16385,ねざめがわるい +44603,16385,ねじをまく +44632,16385,ねずみにひかれる +44643,16385,ねたばをあわせる +44656,16385,ねつがさめる +44656,16386,ねつにうかされる +44656,16387,ねつをあげる +44703,16385,ねってつをのむおもい +44777,16385,ねみみにみず +44816,16385,ねたこをおこす +44816,16386,ねていて +44816,16387,ねてもさめても +44826,16385,ねんをいれる +44826,16386,ねんをおす +44844,16385,ねんきがはいる +44917,16385,ねんりんをきざむ +44929,16385,のうある{たか・たか}はつめをかくす +44929,16386,のうがない +44929,16387,のうではない +44983,16385,のうじおわれりとする +45137,16385,のしをつけて〈しんていする/さしあげる〉 +45213,16385,のどがなる +45213,16386,のどからてがでる +45213,16387,のどにささったとげ +45213,16388,のどまででかかる +45218,16385,のどくびをおさえる +45226,16385,のどもとすぎればあつさをわすれる +45226,16386,のどもとまででかかる +45363,16385,のりとはさみ +45374,16385,のりかかったふね +45412,16385,のれんにうでおし +45412,16386,のれんをおろす +45412,16387,のれんをわける +45420,16385,のろしをあげる +45450,16385,はがたたない +45454,16385,はがうくよう +45454,16386,はがたたない +45454,16387,はに{きぬ・きぬ}きせぬ +45454,16388,はにははを +45454,16389,はのぬけたよう +45454,16390,はのねがあわない +45454,16391,はをくいしばる +45454,16392,はをみせる +45530,16385,ハードルがたかい +45575,16385,はいになる +45693,16385,はいぐんのしょう、へいをかたらず +45922,16385,はいふをつく +46009,16385,はえばたて、たてばあゆめのおやごころ +46050,16385,ばかと{はさみ・はさみ}はつかいよう +46050,16386,ばかにする +46050,16387,ばかにつけるくすりはない +46050,16388,ばかにならない +46050,16389,ばかをいえ +46050,16390,ばかをみる +46117,16385,はかりにかける +46151,16385,はきだめにつる(がおりたよう) +46160,16385,ばきゃくをあらわす +46179,16385,はいてすてるほど +46184,16385,ばくにつく +46226,16385,はくじつのもとに +46228,16385,はくしゃをかける +46275,16385,ばくだんをかかえる +46283,16385,はくちゅうのかんにある +46311,16385,はくはつさんぜんじょう +46320,16385,はくひょうをふむおもい +46394,16385,ばけのかわがはがれる +46458,16385,はさみをいれる +46471,16385,はしがころんでもおかしい +46471,16386,はしがころんでもわらう +46471,16387,はしがすすむ +46471,16388,はしにもぼうにもかからない +46473,16385,はじをかく +46473,16386,はじを{すす・すす}ぐ +46482,16385,はしかのようなもの +46494,16385,はしごをはずされる +46521,16385,はじめよければおわりよし +46547,16385,ばじょうゆたかに +46566,16385,はすにかまえる +46571,16385,バスにのりおくれる +46619,16385,はずみをくらう +46638,16385,ばせいをあびせる +46660,16385,はたのみるめも +46662,16385,はたをあげる +46662,16386,はたをふる +46662,16387,はたをまく +46665,16385,はだがあわない +46665,16386,はだでかんじる +46665,16387,はだをいれる +46665,16388,はだをゆるす +46780,16385,はちのじをよせる +46781,16385,はちのすをつ(つ)いたよう +46814,16385,ばつがわるい +46858,16385,はっきゅうをおう +46864,16385,はっきりいって +47015,16385,ぱっとしない +47032,16385,はっぱをかける +47114,16385,はとがまめでっぽうをくったよう +47144,16385,バトンをわたす +47145,16385,はながさく +47145,16386,はなにあらし +47145,16387,はなはさくらぎひとはぶし +47145,16388,はなもはじらう +47145,16389,はなもみもある +47145,16390,はなより{だんご・だんご} +47145,16391,はなをさかせる +47145,16392,はなをそえる +47145,16393,はなをもたせる +47147,16385,はなもひっかけない +47148,16385,はながあぐらをかく +47148,16386,はながきく +47148,16387,はながたかい +47148,16388,はながまがる +47148,16389,はなであしらう +47148,16390,はなでわらう +47148,16391,はなにかかる +47148,16392,はなにかける +47148,16393,はなにつく +47148,16394,はなをあかす +47148,16395,はなをおる +47148,16396,はなをたかくする +47148,16397,はなをつきあわす +47148,16398,はなをつく +47148,16399,はなをつままれてもわからない +47148,16400,はなをつまむ +47148,16401,はなをならす +47156,16385,はないきがあらい +47156,16386,はないきをうかがう +47185,16385,はなげをのばす +47185,16386,はなげをよまれる +47195,16385,はなしがおちる +47195,16386,はなしがかみあわない +47195,16387,はなしがつく +47195,16388,はなしがとおい +47195,16389,はなしがはずむ +47195,16390,はなしがはやい +47195,16391,はなしがみえない +47195,16392,はなしかわって +47195,16393,はなしにならない +47195,16394,はなしにはながさく +47195,16395,はなしにみがはいる +47195,16396,はなしのほをつぐ +47195,16397,はなしをあわせる +47195,16398,はなしをつける +47195,16399,はなしをとおす +47221,16385,はなぢもでない +47229,16385,はなっぱしらがつよい +47229,16386,はなっぱしらをへしおる +47241,16385,はなのさきであしらう +47242,16385,はなのしたがながい +47242,16386,はなのしたがひあがる +47242,16387,はなのしたをのばす +47283,16385,はなもちならない +47314,16385,はねがはえたよう +47314,16386,はねをのばす +47353,16385,はははつよし +47354,16385,はばをきかせる +47356,16385,ババをつかむ +47356,16386,ババをひく +47469,16385,はめをはずす +47485,16385,はもんをとうじる +47485,16386,はもんをひろげる +47485,16387,はもんをよぶ +47500,16385,はやおきはさんもんのとく +47507,16385,はやがねをうつ +47556,16385,はやめしもげいのうち +47569,16385,はらがいえる +47569,16386,はらがおおきくなる +47569,16387,はらがくだる +47569,16388,はらがくろい +47569,16389,はらがすわる +47569,16390,はらがたつ +47569,16391,はらができる +47569,16392,はらがはる +47569,16393,はらがふくれる +47569,16394,はらがふとい +47569,16395,はらがへる +47569,16396,はらがよじれる +47569,16397,はらにいちもつ +47569,16398,はらにおさめる +47569,16399,はらにおちる +47569,16400,はらにすえかねる +47569,16401,はらのかわがよじれる +47569,16402,はらはちぶんめにいしゃいらず +47569,16403,はらもみのうち +47569,16404,はらをあわせる +47569,16405,はらをいためる +47569,16406,はらをいやす +47569,16407,はらをかかえる +47569,16408,はらをかためる +47569,16409,はらをきめる +47569,16410,はらをきる +47569,16411,はらをくくる +47569,16412,はらをくだす +47569,16413,はらをこしらえる +47569,16414,はらをこやす +47569,16415,はらをこわす +47569,16416,はらをさぐる +47569,16417,はらをすえる +47569,16418,はらをたてる +47569,16419,はらをめす +47569,16420,はらをよじる +47569,16421,はらをよむ +47569,16422,はらをわる +47669,16385,はらわたがにえくりかえる +47669,16386,はらわたがよじれる +47759,16385,はるたつ +47759,16386,はるをうる +47816,16385,ばれいをかさねる +47839,16385,はれものにさわるよう +47873,16385,はんでおしたよう +47878,16385,はんをたれる +47893,16385,ばんいかんなきをきす +47893,16386,ばんやむをえず +47960,16385,はんきをひるがえす +48056,16385,ばんしにいっしょうをえる +48058,16385,ばんじきゅうす +48185,16385,ばんちゃもでばな +48371,16385,ばんりょくそうちゅうこういってん +48387,16385,ひいずるくに +48387,16386,ひいちにちと +48387,16387,ひがあさい +48387,16388,ひがあたらない +48387,16389,ひがくれてしまう +48387,16390,ひくれてみちとおし +48387,16391,ひにつきに +48387,16392,ひのあたるばしょ +48387,16393,ひをあらためて +48387,16394,ひをうつす +48387,16395,ひをおって +48387,16396,ひをおなじくしてろんずべきでない +48388,16385,ひをみない +48389,16385,ひがつく +48389,16386,ひがでる +48389,16387,ひがはいる +48389,16388,ひにあぶらをそそぐ +48389,16389,ひにかける +48389,16390,ひのきえたよう +48389,16391,ひのついたよう +48389,16392,ひのでるよう +48389,16393,ひのないところにけむりはたたぬ +48389,16394,ひのなかみずのなか +48389,16395,ひをいれる +48389,16396,ひをおとす +48389,16397,ひをかける +48389,16398,ひをしっする +48389,16399,ひをつける +48389,16400,ひをとおす +48389,16401,ひをふく +48389,16402,ひをみるより(も)あきらか +48390,16385,ひがはいる +48390,16386,ひをけす +48390,16387,ひをたやす +48393,16385,ひのうちどころがない +48393,16386,ひをならす +48405,16385,びにいりさいをうがつ +48470,16385,ピークをうつ +48494,16385,ピースがそろう +48643,16385,ひかりをあてる +48801,16385,ひくにひけない +48821,16385,びくともしない +48912,16385,ひざがぬける +48912,16386,ひざがわらう +48912,16387,ひざともだんごう +48912,16388,ひざをうつ +48912,16389,ひざをおる +48912,16390,ひざをかかえる +48912,16391,ひざをくずす +48912,16392,ひざをくっする +48912,16393,ひざをくむ +48912,16394,ひざをすすめる +48912,16395,ひざをただす +48912,16396,ひざをつきあわせる +48912,16397,ひざをつめる +48912,16398,ひざをのりだす +48912,16399,ひざをまじえる +48935,16385,ひさしをかしておもやをとられる +49088,16385,ひすればはな +49131,16385,ひそみにならう +49142,16385,ひたいがくもる +49142,16386,ひたいでみる +49142,16387,ひたいにあせする +49142,16388,ひたいをあつめる +49162,16385,ひだねをのこす +49238,16385,ひっけんをあらたにする +49245,16385,ひっこみがつかない +49255,16385,ひっしにつくしがたい +49259,16385,ひつじのかわをかぶった{おおかみ・おおかみ} +49272,16385,ひつじんをはる +49277,16385,ひつぜつにつくしがたい +49293,16385,ピッチがはやい +49293,16386,ピッチをあげる +49340,16385,ひつようにせまられる +49340,16386,ひつようははつめいのはは +49358,16385,ひとあって +49358,16386,ひとがいい +49358,16387,ひとがかわる +49358,16388,ひとがわるい +49358,16389,ひととなる +49358,16390,ひとにはそうてみよ、うまにはのってみよ +49358,16391,ひとのうわさもしちじゅうごにち +49358,16392,ひとのくちにとはたてられない +49358,16393,ひとのふこうはみつのあじ +49358,16394,ひとのふりみてわがふりなおせ +49358,16395,ひとのふんどしですもうをとる +49358,16396,ひとはいちだい、なはまつだい +49358,16397,ひとはパンのみにていくるものにあらず +49358,16398,ひともなげ +49358,16399,ひとよんで +49358,16400,ひとをえらぶ +49358,16401,ひとをくう +49358,16402,ひとをたてる +49358,16403,ひとをのろわばあなふたつ +49358,16404,ひとをばかにする +49358,16405,ひとをひとともおもわない +49358,16406,ひとをみたらどろぼうとおもえ +49358,16407,ひとをみてほうをとけ +49364,16385,ひとあせ〈かく/ながす〉 +49369,16385,ひとあわふかせる +49372,16385,ひといきいれる +49398,16385,ひとかたつく +49406,16385,ひとかわ{む・む}く +49406,16386,ひとかわ{む・む}ける +49430,16385,ひとこえせんりょう +49462,16385,ひとすじなわではいかない +49472,16385,ひとたまりもない +49478,16385,ひとつあなの{むじな・むじな} +49478,16386,ひとつうえ +49478,16387,ひとつかまのめしをくう +49478,16388,ひとつまちがえば +49484,16385,ひとっこひとり +49524,16385,ひとはたあげる +49525,16385,ひとはだぬぐ +49528,16385,ひとはなさかせる +49537,16385,ひとふでしめしまいらせそろ +49540,16385,ひとふろあびる +49542,16385,ひとほねおる +49554,16385,ひとみをこらす +49561,16385,ひとめにたつ +49561,16386,ひとめにつく +49561,16387,ひとめをしのぶ +49561,16388,ひとめをはばかる +49561,16389,ひとめをひく +49568,16385,ひとやくかう +49570,16385,ひとやまあてる +49570,16386,ひとやまこす +49575,16385,ひとりぐちはくえぬがふたりぐちはくえる +49651,16385,ひのてがあがる +49658,16385,ひのめをみる +49676,16385,ひばなをちらす +49686,16385,ひびこれこうじつ +49688,16385,ひびがはいる +49725,16385,ひぶたをきる +49769,16385,ひまがいる +49769,16386,ひまにあかせて +49769,16387,ひまをおしむ +49769,16388,ひまをくう +49769,16389,ひまをさく +49769,16390,ひまをだす +49769,16391,ひまをつぶす +49769,16392,ひまをとる +49769,16393,ひまをぬすむ +49769,16394,ひまをみる +49834,16385,ひゃくもしょうち +49841,16385,ひゃくしゃくかんとういっぽをすすめる +49856,16385,ひゃくにちのせっぽう、{へ・へ}ひとつ +49858,16385,ひゃくねんかせいをまつ +49858,16386,ひゃくねんのこいもいちじにさめる +49862,16385,ひゃくぶんはいっけんにしかず +49892,16385,ひゃっぽゆずって +49896,16385,ひやみずを〈あびせる/かける〉 +49898,16385,ひやめしをくう +49943,16385,ひょうがあく +50020,16385,ひょうしをとる +50075,16385,ひょうそくがあわない +50081,16385,ひょうたんあいいれず +50082,16385,ひょうたんからこま(がでる) +50118,16385,びょうぶとしょうにんはまっすぐではたたぬ +50139,16385,ひょうめんきって +50145,16385,ひょうりをなす +50236,16385,ピリオドをうつ +50260,16385,ひるをあざむく +50360,16385,ひんにせまる +50367,16385,ピンからキリまで +50367,16386,ピンをはねる +50386,16385,ひんくにせまる +50407,16385,ひんしゅくをかう +50414,16385,ひんすればどんする +50440,16385,ぴんとくる +50462,16385,びんぼうひまなし +50462,16386,びんぼうゆるぎもしない +50489,16385,ふにおちる +50489,16386,ふのぬけたよう +50497,16385,ぶがあつい +50497,16386,ぶがある +50497,16387,ぶがわるい +50498,16385,ぶにあわない +50592,16385,ふいをうつ +50592,16386,ふいをくらう +50645,16385,ぶいぶいいわせる +50665,16385,フィルムにおさめる +50690,16385,ふううんきゅうをつげる +50709,16385,ふうげつをともとする +50779,16385,ふうふげんかはいぬもくわない +50810,16385,ふえふけどおどらず +50810,16386,ふえをふく +50936,16385,ふかくをとる +50987,16385,ふきのきゃくとなる +51060,16385,ふぎょうてんちにはじない +51081,16385,ふくをきて(あるいて)いる +51084,16385,ふくはうち、おにはそと +51087,16385,ふけばとぶよう +51091,16385,ふぐはくいたしいのちはおしし +51173,16385,ふくすいぼんにかえらず +51243,16385,ふくむところがある +51362,16385,ぶしににごんはない +51362,16386,ぶしはあいみたがい +51362,16387,ぶしはくわねどたか{ようじ・ようじ} +51503,16385,ふせつをあわせるよう +51532,16385,ふたをあける +51532,16386,ふたをする +51536,16385,ぶたにしんじゅ +51536,16386,ぶたもおだてりゃきにのぼる +51541,16385,ぶたいにたつ +51566,16385,ふたつにひとつ +51583,16385,ふためとみられない +51669,16385,ぶつぎをかもす +51805,16385,ぶつもんにはいる +51815,16385,ふでがあれる +51815,16386,ふでがすべる +51815,16387,ふでがたつ +51815,16388,ふでをおく +51815,16389,ふでをおこす +51815,16390,ふでをおる +51815,16391,ふでをおろす +51815,16392,ふでをくわえる +51815,16393,ふでをそめる +51815,16394,ふでをたつ +51815,16395,ふでをとる +51815,16396,ふでをはしらせる +51815,16397,ふでをふるう +51815,16398,ふでをまげる +51877,16385,ふとくのいたすところ +51883,16385,ふところがあたたかい +51883,16386,ふところがいたむ +51883,16387,ふところが〈さびしい/さむい〉 +51883,16388,ふところがふかい +51883,16389,ふところにいれる +51883,16390,ふところにする +51883,16391,ふところにはいる +51883,16392,ふところをいためる +51883,16393,ふところをこやす +51951,16385,ふねを{こ・こ}ぐ +52083,16385,ふもんにふす +52089,16385,ふゆきたりなばはるとおからじ +52287,16385,ふりあげたこぶしのおろしどころ +52417,16385,ふってわいた +52422,16385,ふるいにかける +52457,16385,ふるきを{たず・たず}ねてあたらしきをしる +52601,16385,ふれなばおちん +52898,16385,ふんどしをしめる +52934,16385,ぶんぼをはらう +52978,16385,{へ・へ}でもない +52978,16386,{へ・へ}ともおもわない +52978,16387,{へ・へ}のつっぱりにもならない +53013,16385,へいを〈あげる/おこす〉 +53013,16386,へいをおさめる +53013,16387,へいをかまえる +53013,16388,へいをもよおす +53144,16385,へいたんをひらく +53148,16385,へいちにはらんをおこす +53198,16385,へいりのごとくすてる +53317,16385,へそがちゃをわかす +53317,16386,へそをまげる +53321,16385,へそのおきっていらい +53325,16385,へたなてっぽうもかずうちゃあたる +53325,16386,へたのかんがえやすむににたり +53325,16387,へたをうつ +53325,16388,へた(を)すると +53526,16385,へびににらまれた{かえる・かえる} +53587,16385,へるもんじゃなし +53632,16385,べんにきょうする +53633,16385,ペンはけんよりもつよし +53633,16386,ペンをいれる +53633,16387,ペンをおく +53633,16388,ペンをおる +53633,16389,ペンをとる +53633,16390,ペンをはしらせる +53709,16385,べんじょがちかい +53760,16385,ベンチをあたためる +53812,16385,ぺんぺんぐさがはえる +53843,16385,ほをいつにする +53847,16385,ほにでる +53893,16385,ポイントがたかい +53893,16386,ポイントをおとす +53895,16385,ほうにてらす +53909,16385,ぼうにふる +53909,16386,ぼうを{の・の}んだよう +53910,16385,ぼうをもってぼうにむくいる +53961,16385,ほうかをまじえる +54190,16385,ぼうずにくけりゃ{けさ・けさ}までにくい +54256,16385,ぼうちゅうかんあり +54259,16385,ほうちょうをにぎる +54391,16385,ほうもうをくぐる +54394,16385,ほうもんをひらく +54464,16385,ほおがおちる +54464,16386,ほおがゆるむ +54464,16387,ほおをそめる +54464,16388,ほおをつねる +54464,16389,ほおをふくらませる +54469,16385,ほおがえしがつかない +54673,16385,ぼけつをほる +54685,16385,ほこをおさめる +54687,16385,ほごにする +54716,16385,ほこりをかぶる +54735,16385,ほしがふる +54735,16386,ほしがわれる +54735,16387,ほしになる +54735,16388,ほしのかずほど +54735,16389,ほしをいただく +54735,16390,ほしをおとす +54849,16385,ほぞをかためる +54849,16386,ほぞを{か・か}む +54850,16385,ほそくながく +54882,16385,ぼだいをとむらう +55006,16385,ほっぺたがおちそう +55006,16386,ほっぺたをつねる +55037,16385,ほどがある +55037,16386,ほどがいい +55037,16387,ほどへて +55044,16385,ほとけつくってたましいいれず +55044,16386,ほとけのかおもさんど(まで) +55071,16385,ほねがおれる +55071,16386,ほねとかわになる +55071,16387,ほねのずいまで +55071,16388,ほねまでしゃぶる +55071,16389,ほねを{うず・うず}める +55071,16390,ほねをおる +55071,16391,ほねをひろう +55073,16385,ほねおりぞんのくたびれもうけ +55079,16385,ほねっぷしがふとい +55087,16385,ほねみにこたえる +55087,16386,ほねみにしみる +55087,16387,ほねみをおしまない +55087,16388,ほねみをけずる +55269,16385,ほれたはれた +55316,16385,ぼんとしょうがつがいっしょにきたよう +55356,16385,ほんきにする +55356,16386,ほんきになる +55389,16385,ほんごしをいれる +55598,16385,ほんらいむいちもつ +55613,16385,まにうける +55614,16385,まがぬける +55614,16386,まがもてない +55614,16387,まがわるい +55614,16388,まにあう +55615,16385,まがさす +55663,16385,まいきょにいとまがない +55737,16385,マウンドをふむ +55738,16385,まえにおちる +55738,16386,まえをむく +55827,16385,まがりなりにも +55891,16385,まぎれもない +55895,16385,まくがあがる +55895,16386,まくがあく +55895,16387,まくがおりる +55895,16388,まくがきっておとされる +55895,16389,まくになる +55898,16385,まかぬたねははえぬ +55924,16385,まくらをかわす +55924,16386,まくらをたかくしてねる +55924,16387,まくらをならべる +55924,16388,まくらをぬらす +55953,16385,まけるがかち +55959,16385,まごにもいしょう +55997,16385,まさるともおとらない +56067,16385,マスクをかぶる +56117,16385,またにかける +56223,16385,まつがとれる +56225,16385,まて{しば・しば}し +56225,16386,まてどくらせど +56225,16387,まてばかいろのひよりあり +56242,16385,まっくろになって +56276,16385,まっせきをけがす +56296,16385,マットにしずむ +56351,16385,まとをいる +56351,16386,まとをえる +56390,16385,まないたにのせる +56397,16385,まなじりをけっする +56426,16385,まねきをうける +56436,16385,まのあたりにする +56457,16385,まぶたがあわない +56457,16386,まぶたがおもくなる +56457,16387,まぶたにうかぶ +56521,16385,まもりにはいる +56525,16385,まゆがくもる +56525,16386,まゆにつばをつける +56525,16387,まゆにひがつく +56525,16388,まゆひとつうごかさない +56525,16389,まゆを{つ・つ}りあげる +56525,16390,まゆをひそめる +56525,16391,まゆをひらく +56525,16392,まゆをよせる +56540,16385,まよえるひつじ +56570,16385,まるいたまごもきりよ(う)でしかく +56643,16385,まれにみる +56655,16385,まわしをきる +56657,16385,まわたでくびをしめるよう +56663,16385,まんをじす +56663,16386,まんをひく +56704,16385,まんざらでもない +56716,16385,まんじりともしない +56776,16385,まんぷくのしんらいをおく +56797,16385,まんめんしゅをそそぐ +56810,16385,みがたつ +56810,16386,みがちぢむ +56810,16387,みがはいる +56810,16388,みがもたない +56810,16389,みがもてない +56810,16390,みからでた{さび・さび} +56810,16391,みにあまる +56810,16392,みにおぼえがない +56810,16393,みにしみる +56810,16394,みにつける +56810,16395,みにつける +56810,16396,みにつまされる +56810,16397,みのおきどころがない +56810,16398,みのふりかた +56810,16399,みもふたもない +56810,16400,みもほそる +56810,16401,みもよもない +56810,16402,みをあやまる +56810,16403,みをいれる +56810,16404,みをうる +56810,16405,みをおく +56810,16406,みをおこす +56810,16407,みをかくす +56810,16408,みをかためる +56810,16409,みをきられる +56810,16410,みをきる +56810,16411,みをこがす +56810,16412,みをこにして +56810,16413,みを{ささ・ささ}げる +56810,16414,みをしずめる +56810,16415,みをじする +56810,16416,みをすててこそうかぶせもあれ +56810,16417,みをたてる +56810,16418,みをつつしむ +56810,16419,みをつつむ +56810,16420,みをていする +56810,16421,みをとうじる +56810,16422,みをなげる +56810,16423,みをのりだす +56810,16424,みをひく +56810,16425,みをひそめる +56810,16426,みをまかせる +56810,16427,みをもちくずす +56810,16428,みを{もっ・もっ}て +56810,16429,みをやつす +56810,16430,みをよせる +56811,16385,みがいる +56811,16386,みをむすぶ +56843,16385,ミイラとりがミイラになる +56857,16385,みえもがいぶんもない +56857,16386,みえをきる +56857,16387,みえをはる +56891,16385,みかけによらず +56914,16385,みぎからひだり +56914,16386,みぎといえばひだり +56914,16387,みぎにでるものがいない +56914,16388,みぎもひだりもわからない +56961,16385,みこしをあげる +56961,16386,みこしをかつぐ +56961,16387,みこしをすえる +56981,16385,みさおをたてる +56989,16385,みざるきかざるいわざる +57015,16385,ミシンをふむ +57020,16385,みずがあう +57020,16386,みずがあく +57020,16387,みずがつく +57020,16388,みずがでる +57020,16389,みずがはいる +57020,16390,みずきよければうお{す・す}まず +57020,16391,みずとあぶら +57020,16392,みずにおちたいぬはうて +57020,16393,みずにながす +57020,16394,みずになる +57020,16395,みずにのる +57020,16396,みずのひくきにつくよう +57020,16397,みずはほうえんのうつわにしたがう +57020,16398,みずもしたたる +57020,16399,みずも{た・た}まらず +57020,16400,みずももらさぬ +57020,16401,みずをあける +57020,16402,みずをうったよう +57020,16403,みずをえたうおのよう +57020,16404,みずをかける +57020,16405,みずをさす +57020,16406,みずをつかむ +57020,16407,みずをつける +57020,16408,みずをむける +57054,16385,みずから(を)じする +57166,16385,みせをたたむ +57183,16385,みぜにをきる +57195,16385,みぜんにふせぐ +57196,16385,みそも{くそ・くそ}もいっしょに +57196,16386,みそをつける +57250,16385,みちがひらける +57250,16386,みちなきみち +57250,16387,みちをけっする +57250,16388,みちをつける +57250,16389,みちをひらく +57250,16390,みちをひろう +57250,16391,みちをわける +57259,16385,みちくさをくう +57284,16385,みちゆくひとのそでにすがる +57291,16385,みつればかく +57296,16385,みっかにあげず +57296,16386,みっかみぬまのさくら +57316,16385,みつごのたましいひゃくまで +57376,16385,みつゆびをついて +57475,16385,みのかわをはぐ +57476,16385,みのけがよだつ +57493,16385,みはてぬゆめ +57510,16385,みふたつになる +57532,16385,みみがいたい +57532,16386,みみがこえる +57532,16387,みみがとおい +57532,16388,みみがはやい +57532,16389,みみにする +57532,16390,みみにたこ(ができる) +57532,16391,みみにたつ +57532,16392,みみにたっする +57532,16393,みみにつく +57532,16394,みみにのこる +57532,16395,みみにはいる +57532,16396,みみにはさむ +57532,16397,みみにもかけず +57532,16398,みみをあらう +57532,16399,みみをうたがう +57532,16400,みみをうつ +57532,16401,みみをおおう +57532,16402,みみをおおってすずをぬすむ +57532,16403,みみをかす +57532,16404,みみをかたむける +57532,16405,みみをけがす +57532,16406,みみをすます +57532,16407,みみをそばだてる +57532,16408,みみをそろえる +57532,16409,みみをダンボにする +57532,16410,みみをつんざく +57532,16411,みみをふさぐ +57532,16412,みみをほる +57532,16413,みみを{ろう・ろう}する +57549,16385,みみずののたくったよう +57567,16385,みむきもしない +57584,16385,みゃくがある +57584,16386,みゃくをとる +57673,16385,みょうりがわるい +57673,16386,みょうりにつきる +57697,16385,みてのとおり +57697,16386,みてみぬふりをする +57697,16387,みぬこときよし +57697,16388,みるかげもない +57697,16389,みるにしのびない +57697,16390,みるにみかねて +57697,16391,みるめがない +57697,16392,みるめも +57769,16385,むにする +57814,16385,むかしとった{きねづか・きねづか} +57928,16385,むこうさんげんりょうどなり +57928,16386,むこうにまわす +57928,16387,むこうをはる +57962,16385,むしがいい +57962,16386,むしがしらせる +57962,16387,むしがすかない +57962,16388,むしがつく +57962,16389,むしのいどころがわるい +57962,16390,むしもころさない +57962,16391,むしをおこす +57986,16385,むしずがはしる +58078,16385,むすめひとりにむこはちにん +58124,16385,むだめしをくう +58129,16385,ムチと{ニンジン・にんじん} +58218,16385,むねがあつくなる +58218,16386,むねがいっぱいになる +58218,16387,むねがおどる +58218,16388,むねがさわぐ +58218,16389,むねがつぶれる +58218,16390,むねがつまる +58218,16391,むねがはずむ +58218,16392,むねがふくらむ +58218,16393,むねがふさがる +58218,16394,むねがやける +58218,16395,むねがわるくなる +58218,16396,むねにいちもつ +58218,16397,むねにおちる +58218,16398,むねにきく +58218,16399,むねにささる +58218,16400,むねにせまる +58218,16401,むねにたたむ +58218,16402,むねにてをあてて +58218,16403,むねにひびく +58218,16404,むねのすくよう +58218,16405,むねのつかえがおりる +58218,16406,むねをいためる +58218,16407,むねをうつ +58218,16408,むねをかす +58218,16409,むねをかりる +58218,16410,むねをこがす +58218,16411,むねをさする +58218,16412,むねをだす +58218,16413,むねをたたく +58218,16414,むねをつかれる +58218,16415,むねをなでおろす +58218,16416,むねをはる +58218,16417,むねをふくらませる +58307,16385,むりがとおればどうりがひっこむ +58348,16385,めがあく +58348,16386,めがおよぐ +58348,16387,めがかたい +58348,16388,めがきく +58348,16389,めがくもる +58348,16390,めがくらむ +58348,16391,めがこえる +58348,16392,めがさえる +58348,16393,めがさめる +58348,16394,めがすわる +58348,16395,めがたかい +58348,16396,めがちかい +58348,16397,めがでる +58348,16398,めがてんになる +58348,16399,めがとおい +58348,16400,めがとどく +58348,16401,めがとびでる +58348,16402,めがとまる +58348,16403,めがない +58348,16404,めがなくなる +58348,16405,めがはいる +58348,16406,めがはなせない +58348,16407,めがはやい +58348,16408,めがひかる +58348,16409,めがまわる +58348,16410,めからうろこがおちる +58348,16411,めからはなへぬける +58348,16412,めからひがでる +58348,16413,めじゃない +58348,16414,めとはなのさき +58348,16415,めにあまる +58348,16416,めにいっていじもない +58348,16417,めにいれてもいたくない +58348,16418,めにうかぶ +58348,16419,めにうつる +58348,16420,めにおさめる +58348,16421,めにかどをたてる +58348,16422,めにしみる +58348,16423,めにする +58348,16424,めにたつ +58348,16425,めにつく +58348,16426,めにとどめる +58348,16427,めにのこる +58348,16428,めにはいる +58348,16429,めにはめを、はにははを +58348,16430,めにふれる +58348,16431,めにみえる +58348,16432,めにもとまらぬ +58348,16433,めにものいわす +58348,16434,めにもの(を)みせる +58348,16435,めのくろいうち +58348,16436,めはくちほどにものをいう +58348,16437,めはこころのまど +58348,16438,めひきそでひき +58348,16439,めもあてられない +58348,16440,めもあや〈な/に〉 +58348,16441,めもくれない +58348,16442,めをあそばせる +58348,16443,めをあてる +58348,16444,めをあわせる +58348,16445,めをうたがう +58348,16446,めをうばわれる +58348,16447,めをおおう +58348,16448,めをおとす +58348,16449,めをかける +58348,16450,めをかすめる +58348,16451,めをきる +58348,16452,めをくばる +58348,16453,めをくれる +58348,16454,めをこらす +58348,16455,めをさらす +58348,16456,めをさら(のよう)にする +58348,16457,めをさんかくにする +58348,16458,めをしろくろさせる +58348,16459,めをすえる +58348,16460,めをそばだてる +58348,16461,めをそむける +58348,16462,めを{そ・そ}らす +58348,16463,めをたてる +58348,16464,めをつける +58348,16465,めをつぶる +58348,16466,めをてんじる +58348,16467,めをとおす +58348,16468,めをとめる +58348,16469,めをぬすむ +58348,16470,めをはなす +58348,16471,めをはなつ +58348,16472,めをひからせる +58348,16473,めをひく +58348,16474,めをひらく +58348,16475,めをほそめる +58348,16476,めをまるくする +58348,16477,めをまわす +58348,16478,めをみはる +58348,16479,めを{む・む}く +58348,16480,めをむける +58348,16481,めをやる +58348,16482,めをよろこばせる +58350,16385,めがでる +58350,16386,めをつむ +58350,16387,めをふく +58363,16385,めいたんせきにせまる +58363,16386,めいをほうじる +58366,16385,めいをきる +58370,16385,めいあんをべんじない +58370,16386,めいあんをわける +58491,16385,めいどのみやげにする +58576,16385,メートルをあげる +58593,16385,めがしらがあつくなる +58593,16386,めがしらをおさえる +58596,16385,めかどをたてる +58602,16385,めがねにかなう +58606,16385,メガホンをとる +58617,16385,めくじらをたてる +58619,16385,めくそはなくそをわらう +58626,16385,めくらせんにんめあきせんにん +58626,16386,めくらへびに{お・お}じず +58643,16385,めさきがきく +58643,16386,めさきをかえる +58654,16385,めしをくう +58694,16385,メスをいれる +58726,16385,めだまがとびでる +58748,16385,めっきがはげる +58785,16385,めどがつく +58803,16385,めのいろをかえる +58810,16385,めのたまがとびでる +58810,16386,めのたまのくろいうち +58815,16385,めのまえが〈くらく/まっくらに〉なる +58822,16385,めはながつく +58874,16385,めりはりがきく +58896,16385,めんがわれる +58896,16386,めんとむかって +58896,16387,めんをきる +58896,16388,めんをとる +58914,16385,めんしきがある +58914,16386,めんしきをもつ +58944,16385,メンチ(を)きる +58954,16385,めんどうみがいい +58954,16386,めんどうをかける +58954,16387,めんどうをみる +58961,16385,めんぴをはぐ +58966,16385,めんぼくしだいもない +58966,16386,めんぼくをほどこす +58977,16385,もにふくする +58977,16386,もをはっする +58984,16385,もうを{ひら・ひら}く +59011,16385,もうげんたしゃ +59096,16385,もうひょうたしゃ +59134,16385,モーションをかける +59320,16385,もちはもちや +59383,16385,もちやはもちや +59390,16385,もちつもたれつ +59416,16385,もったいをつける +59420,16385,もって{いかん・いかん}となす +59420,16386,もってめいすべし +59459,16385,もとの{さや・さや}におさまる +59459,16386,もともこもない +59459,16387,もとをただせば +59459,16388,もとをとる +59513,16385,ものともせず +59513,16386,ものにする +59513,16387,ものになる +59513,16388,ものはいいよう +59513,16389,ものはそうだん +59513,16390,ものはためし +59513,16391,ものをいう +59513,16392,ものをいわせる +59519,16385,ものいえばくちびるさむしあきのかぜ +59552,16385,ものごころ(が)つく +59584,16385,もののかずではない +59642,16385,もみじをちらす +59659,16385,もも{くり・くり}さんねんかきはちねん +59762,16385,もんをたたく +59780,16385,もんこをはる +59806,16385,もんぜんいちをなす +59806,16386,もんぜん{じゃくら・じゃくら}をはる +59806,16387,もんぜんのこぞうならわぬきょうをよむ +59840,16385,やでもてっぽうでももってこい +59840,16386,やもたてもたまらず +59842,16385,やにいけんなし +59842,16386,やにくだる +59865,16385,やいばにかかる +59865,16386,やいばにちぬる +59900,16385,やきがまわる +59900,16386,やきをいれる +59957,16385,やくにたつ +59957,16386,やくをつくる +60059,16385,やぐらをふる +60075,16385,やけいしにみず +60091,16385,やけのの{きぎす・きぎす}よるのつる +60097,16385,やけぼっくいにひがつく +60149,16385,やすかろうわるかろう +60154,16385,{やす・やす}きにつく +60163,16385,やすみやすみいえ +60167,16385,やすものかいのぜにうしない +60192,16385,やせてもかれても +60292,16385,やなぎにかぜ +60292,16386,やなぎにゆきおれなし +60292,16387,やなぎのしたにいつも{どじょう・どじょう}はいない +60323,16385,やぶからぼう +60323,16386,やぶをつついてへびをだす +60350,16385,やまありたにあり +60350,16386,やまがみえる +60350,16387,やまたかきがゆえにたっとからず +60350,16388,やまたかければたにふかし +60350,16389,やまねむる +60350,16390,やまわらう +60350,16391,やまをあてる +60350,16392,やまをかける +60350,16393,やまをこす +60350,16394,やまをたてる +60350,16395,やまをはる +60350,16396,やまをふむ +60355,16385,やまいこうこうにいる +60355,16386,やまいはきから +60355,16387,やまいをえる +60355,16388,やまいをやしなう +60464,16385,やみからやみへほうむる +60484,16385,やみよにてっぽう +60485,16385,やむにやまれぬ +60503,16385,ややあって +60519,16385,やりがふっても +60532,16385,やりだまにあげる +60587,16385,ゆをつかう +60611,16385,ゆうをこす +60762,16385,ゆうしゅうのびを〈かざる/なす〉 +60830,16385,ゆうだちはうまのせをわける +60904,16385,ゆうめいさかいをことにする +60924,16385,ゆうようせまらぬ +60938,16385,ゆうれいのしょうたいみたりかれおばな +60946,16385,ゆえあって +60946,16386,ゆえあるかな +60946,16387,ゆえしらず +60946,16388,ゆえなき +60946,16389,ゆえなく(して) +60946,16390,ゆえなしとしない +60978,16385,ゆきとすみ +60978,16386,ゆきをいただく +61077,16385,ゆきつもどりつ +61077,16386,ゆくとしてかならざるはない +61087,16385,ゆげがたっている +61140,16385,ゆだんもすきも(なら)ない +61181,16385,ユニフォームをぬぐ +61193,16385,ゆびをくっする +61193,16386,ゆびをくわえる +61193,16387,ゆびをさす +61193,16388,ゆびをそめる +61193,16389,ゆびをつめる +61197,16385,ゆびおりかぞえてまつ +61218,16385,ゆみおれやつきる +61218,16386,ゆみをひく +61220,16385,ゆみずのようにつかう +61226,16385,ゆみやとるみ +61228,16385,ゆめにゆめみる +61228,16386,ゆめの(また)ゆめ +61228,16387,ゆめはさかゆめ +61228,16388,ゆめをむすぶ +61236,16385,ゆめじをたどる +61243,16385,ゆめみるよう +61269,16385,ゆりかごからはかばまで +61306,16385,よがよなら +61306,16386,よにいう +61306,16387,よにでる +61306,16388,よにとう +61306,16389,よをさる +61306,16390,よをしのぶ +61306,16391,よをすくう +61306,16392,よをそむく +61306,16393,よをわたる +61308,16385,よのめもねずに +61308,16386,よもひもあけない +61308,16387,よるもひもなく +61308,16388,よをあかす +61308,16389,よをこめて +61308,16390,よをてっする +61308,16391,よをひについで +61326,16385,よいごしのかねはもたない +61348,16385,ようにきょうする +61348,16386,ようにたつ +61348,16387,ようをたす +61348,16388,ようを{な・な}す +61348,16389,ようをべんずる +61349,16385,ようをつまんで +61351,16385,ようのとうざいをとわず +61355,16385,よういわんわ +61659,16385,ようりょうがいい +61659,16386,ようりょうをえない +61709,16385,よぎなくされる +61715,16385,よくのかわがつっぱる +61715,16386,よくもとくもない +61715,16387,よくをかく +61795,16385,よこからみてもたてからみても +61795,16386,よこにおく +61795,16387,よこになる +61795,16388,よこのものをたてにもしない +61795,16389,よこをむく +61815,16385,よこぐるまをおす +61837,16385,よこづなをはる +61840,16385,よこでをうつ +61870,16385,よこやりをいれる +61889,16385,よしにする +61891,16385,よしのずいからてんじょうのぞく +61892,16385,よしとする +61937,16385,よせいをかる +61955,16385,よぜんをたもつ +61962,16385,よそおいをこらす +61975,16385,よたをとばす +61985,16385,よだれをながす +61988,16385,よだんをゆるさない +61995,16385,よつにくむ +62061,16385,よねん(が)ない +62192,16385,よんでじのごとく +62193,16385,よめとおめ{かさ・かさ}のうち +62216,16385,よりをもどす +62241,16385,よりどりみどり +62255,16385,よらばたいじゅのかげ +62290,16385,よろしくやる +62298,16385,よわいをかさねる +62299,16385,よわいいぬほどよく{ほ・ほ}える +62300,16385,よわきをだす +62301,16385,よわきをたすけつよきを{くじ・くじ}く +62306,16385,よわねをはく +62313,16385,よわりめにたたりめ +62430,16385,らいねんのことをいうとおにがわらう +62471,16385,らくあればくあり +62474,16385,らくいんをおされる +62535,16385,らくようのしかをたかからしめる +62579,16385,らちがあかない +62579,16386,らちもない +62579,16387,らちをあける +62609,16385,ラッパをふく +62797,16385,りあらず +62799,16385,りがひでも +62799,16386,りにおちる +62845,16385,りかにかんむりをたださず +62919,16385,りくつをこねる +63027,16385,りちぎもののこだくさん +63064,16385,りっすいのよちもない +63253,16385,りゅういんがさがる +63273,16385,りゅうこあいうつ +63325,16385,りゅうびをさかだてる +63356,16385,りょうとする +63359,16385,りょうをいれる +63359,16386,りょうをとる +63496,16385,りょうてにはな +63551,16385,りょうめがあく +63559,16385,りょうやくはくちににがし +63560,16385,りょうゆうならびたたず +63577,16385,りょうりょうあい{ま・ま}って +63620,16385,りょそうをとく +63685,16385,りんげんあせの{ごと・ごと}し +63760,16385,るいがおよぶ +63761,16385,るいをまする +63762,16385,るいはともをよぶ +63804,16385,るいらんのあやうきにある +63839,16385,るすになる +63862,16385,ルビコン(がわ)をわたる +63888,16385,れいをあつくする +63888,16386,れいをしっする +63888,16387,れいをとる +63890,16385,れいにない +63890,16386,れいによって +63890,16387,れいによってれいのごとし +63892,16385,れいがひとつおおい +63982,16385,れいすいを〈あびせる/かける〉 +64039,16385,れいひつをふるう +64095,16385,レールをしく +64222,16385,レッテルをはる +64391,16385,ろをきる +64405,16385,ろうおおくしてこうすくなし +64409,16385,ろうをえてしょくをのぞむ +64410,16385,ろうを{か・か}むよう +64461,16385,ろうこうにくちはてる +64510,16385,ろうしてこうなし +64625,16385,ローマはいちにちにしてならず +64652,16385,ろくを{は・は}む +64667,16385,ろくじゅうにしてみみしたがう +64762,16385,ろとうにまよう +64797,16385,ろめいをつなぐ +64802,16385,ろれつがまわらない +64803,16385,ろんよりしょうこ +64803,16386,ろんを{ま・ま}たない +64813,16385,ろんごよみのろんごしらず +64826,16385,ろんじんをはる +64850,16385,わをこうじる +64851,16385,わをかける +64903,16385,わがたにみずをひく +64903,16386,わがみちをいく +64907,16385,わがいをえたり +64960,16385,わがみかわいさ +64960,16386,わがみを{つね・つね}ってひとのいたさをしれ +64991,16385,わきがあまい +65021,16385,わきめもふらず +65027,16385,わくをはめる +65087,16385,わさびがきく +65089,16385,わざわいをてんじてふくとなす +65126,16385,わしてどうぜず +65146,16385,わたのようにつかれる +65149,16385,わだいを{ま・ま}く +65159,16385,わたしとしたことが +65164,16385,わたぼうしをかぶる +65168,16385,わたりにふね +65170,16385,わたるせけんにおにはない +65197,16385,わとうをてんじる +65220,16385,わびをいれる +65253,16385,わらにもすがり(つき)たい +65253,16386,わらのうえから +65253,16387,わらをもつかみたい +65254,16385,わらいがとまらない +65255,16385,わらうかどにはふくきたる +65256,16385,わらえないはなし +65263,16385,わらじをぬぐ +65281,16385,わりがいい +65281,16386,わりにあう +65281,16387,わりをくう +65321,16385,わるいむしがつく +65321,16386,わるいようにはしない +65344,16385,われおとらじと +65344,16386,われかんせず +65344,16387,われともなく +65344,16388,われにかえる +65344,16389,われにもあらず +65344,16390,われにもなく +65344,16391,われもわれも +65344,16392,われをわすれる +65347,16385,われがねのようなこえ +65355,16385,われなべに{と・と}じぶた +65361,16385,われるようなはくしゅ diff --git a/data/smk8/phrase_readings.csv b/data/entries/smk8/phrase_readings.csv similarity index 100% rename from data/smk8/phrase_readings.csv rename to data/entries/smk8/phrase_readings.csv diff --git a/data/variant_kanji.csv b/data/entries/variant_kanji.csv similarity index 62% rename from data/variant_kanji.csv rename to data/entries/variant_kanji.csv index 458c86c..0272164 100644 --- a/data/variant_kanji.csv +++ b/data/entries/variant_kanji.csv @@ -1,45 +1,61 @@ -俠,侠 -俱,倶 -儘,侭 -凜,凛 -剝,剥 𠮟,叱 吞,呑 +靭,靱 +臈,﨟 啞,唖 -噓,嘘 嚙,噛 +屛,屏 +幷,并 +彎,弯 +搔,掻 +攪,撹 +枡,桝 +濾,沪 +繡,繍 +蔣,蒋 +蠟,蝋 +醬,醤 +穎,頴 +鷗,鴎 +鹼,鹸 +麴,麹 +俠,侠 +俱,倶 +剝,剥 +噓,嘘 囊,嚢 塡,填 -姸,妍 -屛,屏 屢,屡 -拋,抛 -搔,掻 摑,掴 -攪,撹 -潑,溌 瀆,涜 +潑,溌 焰,焔 -禱,祷 -竜,龍 -筓,笄 簞,箪 -籠,篭 -繡,繍 繫,繋 -腁,胼 萊,莱 -藪,薮 蟬,蝉 -蠟,蝋 軀,躯 -醬,醤 醱,醗 頰,頬 顚,顛 驒,騨 -鶯,鴬 -鷗,鴎 -鷽,鴬 -鹼,鹸 -麴,麹 +姸,妍 +攢,攅 +𣜜,杤 +檔,档 +槶,椢 +櫳,槞 +纊,絋 +纘,纉 +隯,陦 +筓,笄 +逬,迸 +腁,胼 +騈,駢 +拋,抛 +篡,簒 +檜,桧 +禰,祢 +禱,祷 +蘆,芦 +凜,凛 \ No newline at end of file diff --git a/data/mdict/css/daijirin2.css b/data/mdict/css/daijirin2.css new file mode 100644 index 0000000..2407677 --- /dev/null +++ b/data/mdict/css/daijirin2.css @@ -0,0 +1,436 @@ + +@font-face { + font-family: jpgothic; + src: local("Noto Sans CJK JP"), local("IPAexGothic"), local("Source Han Sans JP"); +} + +@font-face { + font-family: jpmincho; + src: local("Noto Serif CJK JP"), local("IPAexMincho"), local("IPAmjMincho"), local("Source Han Serif JP"), local("HanaMinA"), local("HanaMinB"); +} + +body { + /*margin: 0em 1em;*/ + line-height: 1.5em; + font-family: jpmincho, serif; + /*font-size: 1.2em;*/ + color: black; +} + +body.ABC { + margin: 0.5em 0.5em 2em 0.5em; +} + +a { + text-decoration: none; +} + +img.gaiji { + height: 1em; +} + +img.cut { + max-height: 200px; + max-width: 600px; +} + +p { + margin: 0.5em 0 +} + +span[data-name="i"] { + font-style: italic; +} + +span[data-name="h1"] { + font-family: jpgothic, sans-serif; + font-size: 1em; + font-weight: bold; +} + +span[data-name="image"] { + display: block; +} + +span[data-name="ref"] a { + text-decoration: none; +} + +span[data-name="sl"] { + text-decoration: accent; +} + +span[data-name="sm"] { + font-size: 0.7em; +} + +span[data-name="small"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="sub"] { + font-size: 0.7em; + vertical-align: -0.35em; +} + +span[data-name="sup"] { + font-size: 0.7em; + vertical-align: super; +} + +span[data-name="ty2"] span[data-name="sub"] { + vertical-align: 0em; +} + +span[data-name="ty2"] span[data-name="sup"] { + vertical-align: 0.5em; +} + +span[data-name="文語形"] { + display: block; +} + +span[data-name="用例"] { + display: block; +} + +span[data-name="補説G"] { + display: block; +} + +span[data-name="語義Gnum"] + span[data-name="補説G"] { + display: inline; +} + +span[data-name="アクセントG"] + span[data-name="補説G"] { + display: inline; +} + +span[data-name="補説G"] + span[data-name="語釈"] { + display: block; +} + +span[data-name="アクセントG"] { + font-size: 0.7em; + vertical-align: super; + margin-left: 0.25em; + margin-right: 0.25em; +} + +span[data-name="カット"] { + display: block; +} + +span[data-name="カットG"] { + display: block; + margin-top: 0.5em; + margin-bottom: 0.5em; + margin-left: 1em; +} + +span[data-name="キャプション"] { + display: block; +} + +span[data-name="ルビG"] { + font-family: jpgothic, sans-serif; + font-size: 0.7em; + font-weight: normal; + vertical-align: 0.35em; + -webkit-user-select: none; +} + +.warichu span[data-name="ルビG"] { + font-family: jpmincho, serif; + font-size: 0.5em; + font-weight: normal; + vertical-align: 0em; + -webkit-user-select: none; +} + +span[data-name="中語義"] { + display: block; +} + +span[data-name="付記"] { + /* font-size: 0.7em; */ + /* vertical-align: 0.35em; */ +} + +span[data-name="副義"] { + display: block; + margin-left: 1em; +} + +span[data-name="単位名"] { + font-size: 0.5em; +} + +span[data-name="原籍"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="句仮名"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="句表記"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="句項目"] { + margin-top: 0.5em; + margin-left: 1em; + display: block; +} + +span[data-name="和字"] { + font-family: jpgothic, sans-serif; +} + +span[data-name="品詞行"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="品詞用法"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="大語義"] { + display: block; +} + +span[data-name="大語義num"] { + margin: 0.025em; + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + color: white; + background-color: black; +} + +span[data-name="子項目"] { + display: block; + margin-top: 0.5em; + margin-left: 1em; +} + +span[data-name="慣用G"] { + display: block; + margin-top: 0.5em; +} + +span[data-name="欧字"] { + font-family: jpgothic, sans-serif; +} + +span[data-name="歴史仮名"] { + font-size: 0.7em; + vertical-align: 0.35em; + -webkit-user-select: none; +} + +span[data-name="派生G"] { + display: block; + margin-top: 0.5em; +} + +span[data-name="準大語義"] { + display: block; +} + +span[data-name="準大語義num"] { + margin: 0.025em; + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + border: solid 1px black; +} + +span[data-name="漢字音logo"] { + margin: 0.025em; + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + border: solid 0.5px black; + border-radius: 1em; +} + +span[data-name="漢字音G"] { + font-size: 0.7em; + font-weight: normal; + vertical-align: 0.35em; +} + +span[data-name="生没年"] { + margin-left: 0.25em; + margin-right: 0.25em; + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="生没年"]:first-child { + margin-left: 0; +} + +span[data-name="用法"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="異字同訓"] { + display: block; + margin-top: 0.5em; +} + +span[data-name="異字同訓仮名"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="異字同訓漢字"] { + font-family: jpmincho, serif; + font-weight: normal; +} + +span[data-name="異字同訓表記"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="異字同訓解説"] { + display: block; +} + +span[data-name="異字同訓語義G"] { + display: block; +} + +span[data-name="細義"] { + display: block; +} + +rt { + -webkit-user-select: none; +} + +span[data-name="見出仮名"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="見出相当部"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="見出部"] { + display: block; +} + +span[data-name="解説部"] { + display: block; + margin-left: 1em; +} + +span[data-name="語義G"] { + display: block; +} + +span[data-name="語義区切"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="返り点"] { + font-size: 0.5em; + font-weight: normal; + vertical-align: 1em; +} + +span[data-name="返り点"].熟語記号 { + vertical-align: 0em; +} + +span[data-name="項目"] { + display: block; +} + +span[data-name="logo"] { + margin: 0.025em 0.25em; + padding: 0.1em; + font-size: 0.8em; + border: solid 1px black; + border-radius: 0.2em; +} + +.gothic { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +.warichu { + font-size: 1em; +} + +.refnum { + font-size: 0.7em; + vertical-align: 0.35em; +} + +#index { + display: none; +} + +span[data-name="歴史仮名"]:before, +span[data-name="ルビG"]:before, +span[data-name="品詞行"]:before, +span[data-name="原籍"]:before, +span[data-name="品詞用法"]:before, +span[data-name="付記"]:before { + content: "("; +} + +span[data-name="歴史仮名"]:after, +span[data-name="ルビG"]:after, +span[data-name="品詞行"]:after, +span[data-name="原籍"]:after, +span[data-name="品詞用法"]:after, +span[data-name="付記"]:after { + content: ")"; +} + +div[data-child-links] { + padding-left: 1em; +} + +div[data-child-links] ul { + margin: 0; + padding-left: 2em; +} + +div[data-child-links] span { + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + color: white; + border-width: 0.05em; + border-style: none; + border-color: black; + word-break: keep-all; + -webkit-border-radius: 0.2em; +} + +div[data-child-links="子項目"] span { + background-color: rgb(153, 42, 103); +} + +div[data-child-links="句項目"] span { + background-color: rgb(176, 127, 57); +} diff --git a/data/mdict/css/jitenon-kokugo.css b/data/mdict/css/jitenon-kokugo.css new file mode 100644 index 0000000..da46bf8 --- /dev/null +++ b/data/mdict/css/jitenon-kokugo.css @@ -0,0 +1,63 @@ + +@font-face { + font-family: jpgothic; + src: local("Noto Sans CJK JP"), local("IPAexGothic"), local("Source Han Sans JP"); +} + +@font-face { + font-family: jpmincho; + src: local("Noto Serif CJK JP"), local("IPAexMincho"), local("IPAmjMincho"), local("Source Han Serif JP"), local("HanaMinA"), local("HanaMinB"); +} + +body { + font-family: jpmincho, serif; + line-height: 1.5em; +} + +table, th, td { + border: 1px solid; + border-collapse: collapse; + padding: 0.5em; +} + +th { + font-family: jpgothic, sans-serif; + color: black; + background-color: lightgray; + font-weight: normal; + white-space: nowrap; +} + +a { + text-decoration: none; +} + +td ul { + margin: -0.1em 0em -0.1em -1em; +} + +.見出し { +} + +.読み方 { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +.意味, +.kanjirighttb { + margin-left: 1.0em; + margin-bottom: 0.5em; +} + +.num_icon { + font-family: jpgothic, sans-serif; + padding-left: 0.25em; + margin-right: 0.5em; + font-size: 0.8em; + word-break: keep-all; + color: white; + background-color: gray; + border-style: none; + -webkit-border-radius: 0.1em; +} diff --git a/data/mdict/css/jitenon-kotowaza.css b/data/mdict/css/jitenon-kotowaza.css new file mode 100644 index 0000000..2929f89 --- /dev/null +++ b/data/mdict/css/jitenon-kotowaza.css @@ -0,0 +1,47 @@ + +@font-face { + font-family: jpgothic; + src: local("Noto Sans CJK JP"), local("IPAexGothic"), local("Source Han Sans JP"); +} + +@font-face { + font-family: jpmincho; + src: local("Noto Serif CJK JP"), local("IPAexMincho"), local("IPAmjMincho"), local("Source Han Serif JP"), local("HanaMinA"), local("HanaMinB"); +} + +body { + font-family: jpmincho, serif; + line-height: 1.5em; +} + +table, th, td { + border: 1px solid; + border-collapse: collapse; + padding: 0.5em; +} + +th { + font-family: jpgothic, sans-serif; + color: black; + background-color: lightgray; + font-weight: normal; + white-space: nowrap; +} + +a { + text-decoration: none; +} + +.見出し { +} + +.読み方 { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +.意味, +.kanjirighttb { + margin-left: 1.0em; + margin-bottom: 0.5em; +} diff --git a/data/mdict/css/jitenon-yoji.css b/data/mdict/css/jitenon-yoji.css new file mode 100644 index 0000000..2929f89 --- /dev/null +++ b/data/mdict/css/jitenon-yoji.css @@ -0,0 +1,47 @@ + +@font-face { + font-family: jpgothic; + src: local("Noto Sans CJK JP"), local("IPAexGothic"), local("Source Han Sans JP"); +} + +@font-face { + font-family: jpmincho; + src: local("Noto Serif CJK JP"), local("IPAexMincho"), local("IPAmjMincho"), local("Source Han Serif JP"), local("HanaMinA"), local("HanaMinB"); +} + +body { + font-family: jpmincho, serif; + line-height: 1.5em; +} + +table, th, td { + border: 1px solid; + border-collapse: collapse; + padding: 0.5em; +} + +th { + font-family: jpgothic, sans-serif; + color: black; + background-color: lightgray; + font-weight: normal; + white-space: nowrap; +} + +a { + text-decoration: none; +} + +.見出し { +} + +.読み方 { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +.意味, +.kanjirighttb { + margin-left: 1.0em; + margin-bottom: 0.5em; +} diff --git a/data/mdict/css/sankoku8.css b/data/mdict/css/sankoku8.css new file mode 100644 index 0000000..47b7ec0 --- /dev/null +++ b/data/mdict/css/sankoku8.css @@ -0,0 +1,611 @@ + +@font-face { + font-family: jpgothic; + src: local("Noto Sans CJK JP"), local("IPAexGothic"), local("Source Han Sans JP"); +} + +@font-face { + font-family: jpmincho; + src: local("Noto Serif CJK JP"), local("IPAexMincho"), local("IPAmjMincho"), local("Source Han Serif JP"), local("HanaMinA"), local("HanaMinB"); +} + +@font-face { + font-family: jpkyokasho; + src: local("A-OTF Kyoukasho ICA Pro R"), local("DFKyoKaSho-W4"); +} + +body { + /*margin: 0em 1em;*/ + line-height: 1.5em; + font-family: jpmincho, serif; + /*font-size: 1.2em;*/ +} + +span[data-name="entry-index"] > a { + display: none; +} + +span[data-name="項目"] { + display: block; + /*max-width: 39em;*/ +} + +span[data-name="見出部"] { + display: block; +} + +span[data-name="見出仮名"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="見出仮名"].アンチック { + font-family: jpmincho, serif; +} + +span[data-name="表記G"] { + margin-left: 0.25em; +} + +span[data-name="専門G"] { + margin-right: 0.25em; +} + +span[data-name="常用漢字"] { + font-family: jpmincho, serif; +} + +span[data-name="教育漢字"] { + font-family: jpkyokasho, jpmincho, serif; + color: green; +} + +span[data-name="解説部"], +span[data-name="子解説部"], +span[data-name="句解説部"] { + display: block; + margin-left: 1em; +} + +span[data-name="大語義"] { + display: block; +} + +span[data-name="大語義"] + span[data-name="大語義"] { + margin-top: 0.5em; +} + +span[data-name="大語義番号"] { + margin-right: 0.25em; + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + font-weight: bold; + color: white; + background-color: gray; + border-radius: 0.2em; +} + +a span[data-name="大語義番号"] { + background-color: blue; + text-decoration-color: blue; +} + +span[data-name="語義番号"] { + margin-right: 0.25em; +} + +span[data-name="参照語義番号"] { + margin-left: 0.1em; +} + +span[data-name="参照語義番号"]>span[data-name="語義番号"] { + margin-right: 0.1em; +} + +span[data-name="参照語義番号"]:first-child { + margin-left: 0em; +} + +span[data-name="語義"] { + display: block; +} + +span[data-name="副義"] { + display: block; + margin-left: 1.0em; +} + +span[data-name="注記語義"] { + margin-left: 0.5em; +} + +span[data-name="語釈"] { +} + +span[data-name="用例G"] { + display: block; + /*margin-left: 1.25em;*/ +} + +span[data-name="百科"] span[data-name="用例G"] { + display: inline; +} + +span[data-name="注記"] span[data-name="用例G"] { + display: inline; +} + +span[data-name="用例"] { +} + +span[data-name="見出相当部"] { + margin-left: 0.125em; + margin-right: 0.125em; +} + +span[data-name="ルビG"] { + font-size: 0.7em; + font-weight: normal; + vertical-align: 0.5em; + -webkit-user-select: none; +} + +span[data-name="名詞形G"], +span[data-name="動詞形G"], +span[data-name="自動詞形G"], +span[data-name="他動詞形G"], +span[data-name="可能形G"], +span[data-name="人G"], +span[data-name="名詞人形G"] { + display: block; +} + +span[data-name="語義"] span[data-name="名詞形G"], +span[data-name="語義"] span[data-name="動詞形G"], +span[data-name="語義"] span[data-name="自動詞形G"], +span[data-name="語義"] span[data-name="他動詞形G"], +span[data-name="語義"] span[data-name="可能形G"], +span[data-name="語義"] span[data-name="人G"] { + display: inline; +} + +span[data-name="副義"] span[data-name="名詞形G"], +span[data-name="副義"] span[data-name="動詞形G"], +span[data-name="副義"] span[data-name="自動詞形G"], +span[data-name="副義"] span[data-name="他動詞形G"], +span[data-name="副義"] span[data-name="可能形G"], +span[data-name="副義"] span[data-name="人G"] { + display: inline; +} + +span[data-name="注記"] span[data-name="名詞形G"], +span[data-name="注記"] span[data-name="動詞形G"], +span[data-name="注記"] span[data-name="自動詞形G"], +span[data-name="注記"] span[data-name="他動詞形G"], +span[data-name="注記"] span[data-name="可能形G"], +span[data-name="注記"] span[data-name="人G"] { + display: inline; +} + +span[data-name="共通"] span[data-name="名詞形G"], +span[data-name="共通"] span[data-name="動詞形G"], +span[data-name="共通"] span[data-name="自動詞形G"], +span[data-name="共通"] span[data-name="他動詞形G"], +span[data-name="共通"] span[data-name="可能形G"], +span[data-name="共通"] span[data-name="人G"] { + display: inline; +} + +span[data-name="名詞形G"] span[data-name="用例G"], +span[data-name="動詞形G"] span[data-name="用例G"], +span[data-name="自動詞形G"] span[data-name="用例G"], +span[data-name="他動詞形G"] span[data-name="用例G"], +span[data-name="可能形G"] span[data-name="用例G"], +span[data-name="人G"] span[data-name="用例G"] { + display: inline; +} + +span[data-name="参照G"] { + display: inline; +} + +span[data-name="参照矢印"] { + margin-right: 0.25em; +} + +span[data-name="参照"] { +} + +span[data-name="子項目"] { + display: block; + margin-top: 0.5em; +} + +span[data-name="子見出部"] { + display: block; + /* text-indent: -1em; */ +} + +span[data-name="子見出仮名"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="親見出省略"] { + font-family: jpgothic, sans-serif; +} + +span[data-name="句項目"] { + display: block; + margin-top: 0.5em; +} + +span[data-name="句見出部"] { + display: block; + /* text-indent: -1em; */ +} + +span[data-name="句表記"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="対義語G"] { +} + +span[data-name="派生語G"] { + display: block; +} + +span[data-name="謙譲形G"] { + display: block; +} + +span[data-name="共通"] { + display: block; +} + +span[data-name="共通ロゴ"] { + color: red; + margin-right: 0.25em; +} + +span[data-name="rank1"] a, +span[data-name="rank2"] a, +span[data-name="rank3"] a, +span[data-name="表外字マーク"] a, +span[data-name="表外音訓マーク"] a, +span[data-name="省略形"] a, +span[data-name="熟字訓"] a, +span[data-name="原籍"] a, +span[data-name="品詞"] a, +span[data-name="専門"] a, +span[data-name="使用域"] a, +span[data-name="rect"] a { + color: black; + border-top-style: none; +} + +span[data-name="共通ロゴ"] a { + color: red; + border-top-style: none; +} + +span[data-name="rect"].red a { + color: red; +} + +a { + text-decoration: none; + padding-top: 0.04em; + /* border-top: solid 1px blue; */ +} + +a.appendix { + color: black; + text-decoration: none; + border-top-style: none; +} + +a.black { + color: black; +} + +span[data-name="カット"] { + display: block; + width: 75%; + margin-top: 1em; + margin-left: auto; + margin-right: auto; +} + +span[data-name="カット"] img { + max-height: 200px; + max-width: 600px; +} + +span[data-name="イタリック"] { + font-style: italic; +} + +span[data-name="ボールド"] { + font-weight: bold; +} + +span[data-name="色版"] { + color: red; +} + +/* 独自定義 */ + +span[data-name="rect"] { + margin-left: 0.25em; + margin-right: 0.25em; + padding: 0.1em; + font-size: 0.8em; + border-width: 0.04em; + border-style: solid; + border-color: black; + word-break: keep-all; + border-radius: 0.1em; +} + +span[data-name="rect"].fill { + color: white; + border-style: none; + background-color: gray; +} + +span[data-name="rect"].red { + color: red; + border-color: red; +} + +span[data-name="red"] { + color: red; +} + +span[data-name="glyph"] { + font-family: jpmincho, serif; +} + +span[data-name="gaiji"] { + width: 1em; +} + +span[data-name="frac"] { + width: 2em; +} + +img.logo { + display: gaiji; + margin-right: 0.25em; + height: 1em; + text-combine-horizontal: all; +} + +.logo-red { + height: 1em; + color: red; +} + +span[data-name="平板"] .logo-red { + margin-left: 0.1em; +} + +img.区切り線 { + display: gaiji; + height: 1em; + padding: 0 0.3em 0 0.05em; + color: gray; + text-combine-horizontal: all; +} + +/* *, **, and ☆☆ symbols */ +span[data-src^="svg-logo"].rank { + font-size: 0.65em; + vertical-align: super; +} + +img.gaiji { + display: gaiji; + height: 1em; + text-combine-horizontal: all; +} + +img.svg { + zoom: 250%; +} + +span[data-name="表外字マーク"] { + font-size: 0.5em; + vertical-align: 1em; + -webkit-user-select: none; +} + +span[data-name="表外音訓マーク"] { + font-size: 0.5em; + vertical-align: 1em; + -webkit-user-select: none; +} + +span[data-name="表外字ロゴ"], +span[data-name="表外音訓ロゴ"] { + margin: 0em 0.5em; + font-size: 0.5em; +} + +span[data-name="アクセント"] { +} + +span[data-name="アクセント表記"] { + font-family: jpgothic, sans-serif; + font-weight: bold; + font-size: 0.85em; +} + +span[data-name="横"] { + text-combine-horizontal: all 1; +} + +span[data-name="縦中横"] { + text-combine-horizontal: all; +} + +span[data-name="分子"], +span[data-name="分母"] { + text-combine-horizontal: all; +} + +span[data-name="英"], +span[data-name="回転"] { + writing-mode: horizontal-tb; +} + +span[data-name="i"] { + font-family: "Times New Roman"; + font-style: italic; +} + +span[data-name="横"] span[data-name="sub"] { + font-size: 0.7em; + vertical-align: 0.35em; +} + +span[data-name="kanbun"] { + font-size: 0.5em; + vertical-align: -1em; +} + +span[data-name="歴史仮名"] { + font-size: 0.7em; + vertical-align: 0.5em; + -webkit-user-select: none; +} + +span[data-name="品詞G"] { + /* margin-left: 0.25em; */ +} + +span[data-name="ロゴ"] { + margin-right: 0.25em; +} + +span[data-name="割書"] { + /*display: warichu;*/ + /*font-size: 0.5em;*/ +} + +span[data-name="尊敬形G"], +span[data-name="謙譲形G"], +span[data-name="丁寧形G"] { + display: block; +} + +span[data-name="百科"], +span[data-name="由来"], +span[data-name="区別"], +span[data-name="アクセント注記"] { + display: block; +} + +span[data-name="表記情報"] { + display: block; +} + +span[data-name="別見出"] { + font-family: jpmincho, serif; + font-weight: bold; +} + +span[data-name="読み"] { + font-size: 0.7em; +} + +span[data-name="歴史仮名"]:before, +span[data-name="ルビ"]:before { + content: "("; +} + +span[data-name="歴史仮名"]:after, +span[data-name="ルビ"]:after { + content: ")"; +} + +div[data-child-links] { + padding-left: 1em; +} + +div[data-child-links] ul { + margin: 0; + padding-left: 2em; +} + +div[data-child-links] span { + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + color: white; + border-width: 0.05em; + border-style: none; + border-color: black; + border-radius: 0.2em; + word-break: keep-all; +} + +div[data-child-links="子項目"] span { + background-color: rgb(153, 42, 103); +} + +div[data-child-links="句項目"] span { + background-color: rgb(176, 127, 57); +} + + +/* Replacements for vertical SVG icons */ +span[data-src="svg-logo/区別.svg"], +span[data-src="svg-logo/由来.svg"] { + font-family: jpgothic, sans-serif; + margin-left: 0.25em; + margin-right: 0.25em; + padding: 0em 0.1em 0.1em 0.1em; + font-size: 0.8em; + word-break: keep-all; + border-radius: 0.2em; + border-style: none; + color: white; + background-color: red; +} + +span[data-src="svg-logo/アク.svg"], +span[data-src="svg-logo/丁寧.svg"], +span[data-src="svg-logo/可能.svg"], +span[data-src="svg-logo/尊敬.svg"], +span[data-src="svg-logo/表記.svg"], +span[data-src="svg-logo/謙譲.svg"], +span[data-src="svg-logo/接尾.svg"], +span[data-src="svg-logo/接頭.svg"] { + margin-left: 0.25em; + margin-right: 0.25em; + padding: 0em 0.1em 0.1em 0.1em; + font-size: 0.8em; + word-break: keep-all; + border-width: 0.1em; + border-style: solid; + border-radius: 0.2em; +} + +span[data-src="svg-logo/アク.svg"], +span[data-src="svg-logo/丁寧.svg"], +span[data-src="svg-logo/可能.svg"], +span[data-src="svg-logo/尊敬.svg"], +span[data-src="svg-logo/表記.svg"], +span[data-src="svg-logo/謙譲.svg"] { + font-family: jpgothic, sans-serif; +} + +span[data-src="svg-logo/接尾.svg"], +span[data-src="svg-logo/接頭.svg"] { + font-family: jpmincho, serif; +} diff --git a/data/mdict/css/smk8.css b/data/mdict/css/smk8.css new file mode 100644 index 0000000..55ef814 --- /dev/null +++ b/data/mdict/css/smk8.css @@ -0,0 +1,450 @@ + +@font-face { + font-family: jpgothic; + src: local("Noto Sans CJK JP"), local("IPAexGothic"), local("Source Han Sans JP"); +} + +@font-face { + font-family: jpmincho; + src: local("Noto Serif CJK JP"), local("IPAexMincho"), local("IPAmjMincho"), local("Source Han Serif JP"), local("HanaMinA"), local("HanaMinB"); +} + +body { + /*margin: 0em 1em;*/ + line-height: 1.5em; + font-family: jpmincho, serif; + /*font-size: 1.2em;*/ + color: black; +} + +span[data-name="項目"] { + display: block; +} + +span[data-name="見出部"] { + display: block; +} + +span[data-name="見出部"].pri { + /*margin-left: -0.4em;*/ +} + +span[data-name="見出仮名"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +rt[data-name="表音表記"] { + font-size: 0.5em; + -webkit-user-select: none; +} + +rt[data-name="表外音訓マーク"] { + font-size: 1em; + -webkit-user-select: none; +} + +rt[data-name="表外字マーク"] { + font-size: 1em; + -webkit-user-select: none; +} + +span[data-name="解説部"] { + display: block; + margin-left: 1em; +} + +span[data-name="大語義"] { + display: block; +} + +span[data-name="語義"] { + display: block; +} + +span[data-name="副義"] { + display: block; +} + +span[data-name="用例G"] { + display: block; +} + +span[data-name="注記"] span[data-name="用例G"] { + display: inline; +} + +span[data-name="用例"] { + display: block; +} + +span[data-name="注記"] span[data-name="用例"] { + display: inline; +} + +span[data-name="見出語省略"] { + margin-left: 0.125em; + margin-right: 0.125em; +} + +span[data-name="教育漢字"] { + color: green; +} + +span[data-name="ルビ"] { + font-size: 0.7em; + vertical-align: 0.5em; + -webkit-user-select: none; +} + +span[data-name="ルビ区切"] { + font-size: 0.7em; + vertical-align: 0.65em; +} + +span[data-name="名詞形G"] { + display: block; +} + +span[data-name="可能形G"] { + display: block; +} + +span[data-name="参照G"] { + display: block; +} + +span[data-name="参照"] { + color: blue; +} + +span[data-name="子項目"], +span[data-name="句項目"] { + display: block; + margin-bottom: 0.5em; +} + +span[data-name="子項目F"], +span[data-name="句項目F"] { + display: block; + margin-bottom: 0.5em; + margin-top: 0.5em; +} + +span[data-name="子見出部"] { + display: block; +} + +span[data-name="子解説部"] { + display: block; + margin-left: 1em; +} + +span[data-name="句見出部"] { + display: block; +} + +span[data-name="句解説部"] { + display: block; + margin-left: 1em; +} + +span[data-name="運用解説"] { + display: block; +} + +span[data-name="表記解説"] { + display: block; +} + +span[data-name="文法解説"] { + display: block; +} + +span[data-name="かぞえ方解説"] { + display: block; +} + +span[data-name="派生"] { + display: block; + margin-left: 1.25em; +} + +span[data-name="派生SubGF"] { + display: block; + text-indent: -1.25em; +} + +span[data-name="派生SubG"] { + display: block; +} + +span[data-name="派生SubGF"] span[data-name="用例G"] { + text-indent: 0; +} + +span[data-name="派生見出"] { + font-weight: bold; +} + +span[data-name="派生見出"].normal { + font-weight: normal +} + +span[data-name="造語成分項目"] { + display: block; + margin-top: 1em; +} + +span[data-name="造語成分見出"] { + font-size:1.4em; +} + +span[data-name="EM"] { + font-weight: bold; +} + +span[data-name="アクセント"], +span[data-name="アクセント組M"] { + font-size: 0.7em; + vertical-align: super; +} + +span[data-name="反意語M"], +span[data-name="同意語M"] { + vertical-align: 0.15em; +} + +span[data-name="B"] { + font-weight: bold; +} + +span[data-name="IT"] { + font-family: "Times New Roman"; + font-style: italic; +} + +span[data-name="EXCLAMATION"] { + font-family: "Times New Roman"; + font-style: italic; + font-size: 1.2em; +} + +span[data-name="歴史仮名"] { + font-family: jpmincho, serif; + font-size: 0.7em; + font-weight: normal; + vertical-align: 0.35em; + -webkit-user-select: none; +} + +span[data-name="出現形"] { + font-weight: bold; +} + +span[data-name="品詞用法"] { + font-size: 0.7em; +} + +span[data-name="品詞用法"] span[data-name="品詞G"] { + font-size: 1.2em; +} + +span[data-name="基本構文型"] { + font-size: 0.8em; +} + +span[data-name="基本構文em"] { + font-weight: bold; +} + +span[data-name="ウ濁音参照"] { + font-family: jpgothic, sans-serif; + font-weight: bold; +} + +span[data-name="rect"] { + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + border-width: 0.05em; + border-style: solid; + border-color: black; + word-break: keep-all; + -webkit-border-radius: 0.1em; +} + +span[data-name="rect"].fill { + color: white; + border-style: none; + background-color: gray; +} + +span[data-name="rect"].red { + color: red; + border-color: red; +} + +span[data-name="rect"].redfill { + color: white; + border-style: none; + background-color: red; +} + +span[data-name="red"] { + color: red; +} + +span[data-name="大語義番号"], +span[data-name="語義番号"], +span[data-name="副義番号"] { + margin-right: 0.25em; + font-family: jpgothic, sans-serif; +} + +span[data-name="ref"] span[data-name="大語義番号"], +span[data-name="ref"] span[data-name="語義番号"], +span[data-name="ref"] span[data-name="副義番号"] { + font-size: 0.8em; + margin-right: 0; +} + +span[data-name="言換M"] { + font-size: 0.5em; +} + +span[data-name="字音語参照項目"] { + display: block; +} + +span[data-name="本文項目M"] { + font-size: 0.7em; +} + +span[data-name="運用解説M"], +span[data-name="表記解説M"], +span[data-name="文法解説M"], +span[data-name="かぞえ方解説M"], +span[data-name="派生M"] { + margin-right: 0.25em; + font-family: jpgothic, sans-serif; +} + +span[data-name="派生ロゴ"] { + margin-left: 0.1em; + margin-right: 0.1em; +} + +span[data-name="文字"] { + margin: 0 0.2em; +} + +span[data-name="二分"] { + font-size: 0.5em; +} + +span[data-name="四分"] { + font-size: 0.25em; +} + +span[data-name="ref"] { + margin-left: 0.1em; + margin-right: 0.1em; +} + +span[data-name="ref-small"] { + font-size: 0.7em; +} + +span[data-name="sup"] { + font-size: 0.6em; +} + +span[data-name="外字"] img { + height: 1em; +} + +img.audio { + height: 1em; + margin: 0 0.25em; +} + +img.外字 { + height: 1em; +} + +img.外字欧 { + height: 1em; +} + +span[data-name="レ点M"] { + font-size: 0.6em; + vertical-align: -0.7em; +} + +a { + text-decoration: none; +} + +span[data-name="audio"] a { + padding-bottom: 0; + border-bottom: none; +} + +span[data-name="アクセント"] a, +span[data-name="古語M"] a, +span[data-name="雅語M"] a, +span[data-name="派生M"] a, +span[data-name="原籍M"] a, +span[data-name="品詞M"] a { + color: black; + border-bottom-style: none; +} + + +span[data-name="歴史仮名"]:before, +span[data-name="ルビ"]:before { + content: "("; +} + +span[data-name="歴史仮名"]:after, +span[data-name="ルビ"]:after { + content: ")"; +} + +div[data-child-links] { + padding-left: 1em; +} + +div[data-child-links] ul { + margin: 0; + padding-left: 2em; +} + +div[data-child-links] span { + padding: 0.1em; + font-family: jpgothic, sans-serif; + font-size: 0.8em; + color: white; + border-width: 0.05em; + border-style: none; + border-color: black; + word-break: keep-all; + -webkit-border-radius: 0.2em; +} + +div[data-child-links="子項目"] span { + background-color: rgb(153, 42, 103); +} + +div[data-child-links="句項目"] span { + background-color: rgb(176, 127, 57); +} + +span.pri > span.外字 { + font-size: 0.65em; + vertical-align: super; +} + + + diff --git a/data/mdict/description/daijirin2.mdx.description.html b/data/mdict/description/daijirin2.mdx.description.html new file mode 100644 index 0000000..c1eb401 --- /dev/null +++ b/data/mdict/description/daijirin2.mdx.description.html @@ -0,0 +1,7 @@ +大辞林 第四版 +

    +https://www.monokakido.jp/ja/dictionaries/daijirin2/index.html +

    +{{revision}} +

    +{{attribution}} diff --git a/data/mdict/description/jitenon-kokugo.mdx.description.html b/data/mdict/description/jitenon-kokugo.mdx.description.html new file mode 100644 index 0000000..a1c7489 --- /dev/null +++ b/data/mdict/description/jitenon-kokugo.mdx.description.html @@ -0,0 +1,7 @@ +国語辞典オンライン +

    +https://kokugo.jitenon.jp/ +

    +{{revision}} +

    +{{attribution}} diff --git a/data/mdict/description/jitenon-kotowaza.mdx.description.html b/data/mdict/description/jitenon-kotowaza.mdx.description.html new file mode 100644 index 0000000..b6d3c99 --- /dev/null +++ b/data/mdict/description/jitenon-kotowaza.mdx.description.html @@ -0,0 +1,7 @@ +故事・ことわざ・慣用句オンライン +

    +https://kotowaza.jitenon.jp/ +

    +{{revision}} +

    +{{attribution}} diff --git a/data/mdict/description/jitenon-yoji.mdx.description.html b/data/mdict/description/jitenon-yoji.mdx.description.html new file mode 100644 index 0000000..d7e3729 --- /dev/null +++ b/data/mdict/description/jitenon-yoji.mdx.description.html @@ -0,0 +1,7 @@ +四字熟語辞典オンライン +

    +https://yoji.jitenon.jp/ +

    +{{revision}} +

    +{{attribution}} diff --git a/data/mdict/description/sankoku8.mdx.description.html b/data/mdict/description/sankoku8.mdx.description.html new file mode 100644 index 0000000..12abb77 --- /dev/null +++ b/data/mdict/description/sankoku8.mdx.description.html @@ -0,0 +1,7 @@ +三省堂国語辞典 第八版 +

    +https://www.monokakido.jp/ja/dictionaries/sankoku8/index.html +

    +{{revision}} +

    +{{attribution}} diff --git a/data/mdict/description/smk8.mdx.description.html b/data/mdict/description/smk8.mdx.description.html new file mode 100644 index 0000000..7486250 --- /dev/null +++ b/data/mdict/description/smk8.mdx.description.html @@ -0,0 +1,7 @@ +新明解国語辞典 第八版 +

    +https://www.monokakido.jp/ja/dictionaries/smk8/index.html +

    +{{revision}} +

    +{{attribution}} diff --git a/data/mdict/icon/jitenon-kokugo.png b/data/mdict/icon/jitenon-kokugo.png new file mode 100644 index 0000000..1ef1eb3 Binary files /dev/null and b/data/mdict/icon/jitenon-kokugo.png differ diff --git a/data/mdict/icon/jitenon-kotowaza.png b/data/mdict/icon/jitenon-kotowaza.png new file mode 100644 index 0000000..15ccb92 Binary files /dev/null and b/data/mdict/icon/jitenon-kotowaza.png differ diff --git a/data/mdict/icon/jitenon-yoji.png b/data/mdict/icon/jitenon-yoji.png new file mode 100644 index 0000000..0603db0 Binary files /dev/null and b/data/mdict/icon/jitenon-yoji.png differ diff --git a/data/mdict/name_conversion/daijirin2.json b/data/mdict/name_conversion/daijirin2.json new file mode 100644 index 0000000..d783d28 --- /dev/null +++ b/data/mdict/name_conversion/daijirin2.json @@ -0,0 +1,12 @@ +{ + "a": {}, + "br": {}, + "img": {}, + "div": {}, + "span": {}, + "ruby": {}, + "rt": {}, + "p": {}, + "漢字音G": {"name": "ul"}, + "漢字音": {"name": "li"} +} diff --git a/data/mdict/name_conversion/sankoku8.json b/data/mdict/name_conversion/sankoku8.json new file mode 100644 index 0000000..c790845 --- /dev/null +++ b/data/mdict/name_conversion/sankoku8.json @@ -0,0 +1,22 @@ +{ + "a": {}, + "br": {}, + "img": {}, + "div": {}, + "span": {}, + "small": {}, + "sup": {}, + "sub": {}, + "表外字": { + "name": "ruby" + }, + "表外字マーク": { + "name": "rt" + }, + "表外音訓": { + "name": "ruby" + }, + "表外音訓マーク": { + "name": "rt" + } +} diff --git a/data/mdict/name_conversion/smk8.json b/data/mdict/name_conversion/smk8.json new file mode 100644 index 0000000..b9a4387 --- /dev/null +++ b/data/mdict/name_conversion/smk8.json @@ -0,0 +1,25 @@ +{ + "a": {}, + "br": {}, + "img": {}, + "div": {}, + "span": {}, + "表外字": { + "name": "ruby" + }, + "表外字マーク": { + "name": "rt" + }, + "表外音訓": { + "name": "ruby" + }, + "表外音訓マーク": { + "name": "rt" + }, + "表音式": { + "name": "ruby" + }, + "表音表記": { + "name": "rt" + } +} diff --git a/data/mdict/title/daijirin2.mdx.title.html b/data/mdict/title/daijirin2.mdx.title.html new file mode 100644 index 0000000..43fdfd7 --- /dev/null +++ b/data/mdict/title/daijirin2.mdx.title.html @@ -0,0 +1 @@ +大辞林 第四版 diff --git a/data/mdict/title/jitenon-kokugo.mdx.title.html b/data/mdict/title/jitenon-kokugo.mdx.title.html new file mode 100644 index 0000000..3fee892 --- /dev/null +++ b/data/mdict/title/jitenon-kokugo.mdx.title.html @@ -0,0 +1 @@ +国語辞典オンライン diff --git a/data/mdict/title/jitenon-kotowaza.mdx.title.html b/data/mdict/title/jitenon-kotowaza.mdx.title.html new file mode 100644 index 0000000..438ffaf --- /dev/null +++ b/data/mdict/title/jitenon-kotowaza.mdx.title.html @@ -0,0 +1 @@ +故事・ことわざ・慣用句オンライン diff --git a/data/mdict/title/jitenon-yoji.mdx.title.html b/data/mdict/title/jitenon-yoji.mdx.title.html new file mode 100644 index 0000000..456dc99 --- /dev/null +++ b/data/mdict/title/jitenon-yoji.mdx.title.html @@ -0,0 +1 @@ +四字熟語辞典オンライン diff --git a/data/mdict/title/sankoku8.mdx.title.html b/data/mdict/title/sankoku8.mdx.title.html new file mode 100644 index 0000000..fbb17d9 --- /dev/null +++ b/data/mdict/title/sankoku8.mdx.title.html @@ -0,0 +1 @@ +三省堂国語辞典 第八版 diff --git a/data/mdict/title/smk8.mdx.title.html b/data/mdict/title/smk8.mdx.title.html new file mode 100644 index 0000000..9f41cd1 --- /dev/null +++ b/data/mdict/title/smk8.mdx.title.html @@ -0,0 +1 @@ +新明解国語辞典 第八版 diff --git a/data/yomichan/dictionary-term-bank-v3-schema.json b/data/yomichan/dictionary-term-bank-v3-schema.json new file mode 100644 index 0000000..335144c --- /dev/null +++ b/data/yomichan/dictionary-term-bank-v3-schema.json @@ -0,0 +1,474 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "structuredContent": { + "oneOf": [ + { + "type": "string", + "description": "Represents a text node." + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/structuredContent", + "description": "An array of child content." + } + }, + { + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Empty tags.", + "required": [ + "tag" + ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "const": "br" + }, + "data": { + "$ref": "#/definitions/structuredContentData" + } + } + }, + { + "type": "object", + "description": "Generic container tags.", + "required": [ + "tag" + ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "enum": ["ruby", "rt", "rp", "table", "thead", "tbody", "tfoot", "tr"] + }, + "content": { + "$ref": "#/definitions/structuredContent" + }, + "data": { + "$ref": "#/definitions/structuredContentData" + }, + "lang": { + "type": "string", + "description": "Defines the language of an element in the format defined by RFC 5646." + } + } + }, + { + "type": "object", + "description": "Table tags.", + "required": [ + "tag" + ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "enum": ["td", "th"] + }, + "content": { + "$ref": "#/definitions/structuredContent" + }, + "data": { + "$ref": "#/definitions/structuredContentData" + }, + "colSpan": { + "type": "integer", + "minimum": 1 + }, + "rowSpan": { + "type": "integer", + "minimum": 1 + }, + "style": { + "$ref": "#/definitions/structuredContentStyle" + }, + "lang": { + "type": "string", + "description": "Defines the language of an element in the format defined by RFC 5646." + } + } + }, + { + "type": "object", + "description": "Container tags supporting configurable styles.", + "required": [ + "tag" + ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "enum": ["span", "div", "ol", "ul", "li"] + }, + "content": { + "$ref": "#/definitions/structuredContent" + }, + "data": { + "$ref": "#/definitions/structuredContentData" + }, + "style": { + "$ref": "#/definitions/structuredContentStyle" + }, + "lang": { + "type": "string", + "description": "Defines the language of an element in the format defined by RFC 5646." + } + } + }, + { + "type": "object", + "description": "Image tag.", + "required": [ + "tag", + "path" + ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "const": "img" + }, + "data": { + "$ref": "#/definitions/structuredContentData" + }, + "path": { + "type": "string", + "description": "Path to the image file in the archive." + }, + "width": { + "type": "number", + "description": "Preferred width of the image.", + "minimum": 0 + }, + "height": { + "type": "number", + "description": "Preferred width of the image.", + "minimum": 0 + }, + "title": { + "type": "string", + "description": "Hover text for the image." + }, + "pixelated": { + "type": "boolean", + "description": "Whether or not the image should appear pixelated at sizes larger than the image's native resolution.", + "default": false + }, + "imageRendering": { + "type": "string", + "description": "Controls how the image is rendered. The value of this field supersedes the pixelated field.", + "enum": ["auto", "pixelated", "crisp-edges"], + "default": "auto" + }, + "appearance": { + "type": "string", + "description": "Controls the appearance of the image. The \"monochrome\" value will mask the opaque parts of the image using the current text color.", + "enum": ["auto", "monochrome"], + "default": "auto" + }, + "background": { + "type": "boolean", + "description": "Whether or not a background color is displayed behind the image.", + "default": true + }, + "collapsed": { + "type": "boolean", + "description": "Whether or not the image is collapsed by default.", + "default": false + }, + "collapsible": { + "type": "boolean", + "description": "Whether or not the image can be collapsed.", + "default": false + }, + "verticalAlign": { + "type": "string", + "description": "The vertical alignment of the image.", + "enum": ["baseline", "sub", "super", "text-top", "text-bottom", "middle", "top", "bottom"] + }, + "sizeUnits": { + "type": "string", + "description": "The units for the width and height.", + "enum": ["px", "em"] + } + } + }, + { + "type": "object", + "description": "Link tag.", + "required": [ + "tag", + "href" + ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "const": "a" + }, + "content": { + "$ref": "#/definitions/structuredContent" + }, + "href": { + "type": "string", + "description": "The URL for the link. URLs starting with a ? are treated as internal links to other dictionary content.", + "pattern": "^(?:https?:|\\?)[\\w\\W]*" + }, + "lang": { + "type": "string", + "description": "Defines the language of an element in the format defined by RFC 5646." + } + } + } + ] + } + ] + }, + "structuredContentData": { + "type": "object", + "description": "Generic data attributes that should be added to the element.", + "additionalProperties": { + "type": "string" + } + }, + "structuredContentStyle": { + "type": "object", + "additionalProperties": false, + "properties": { + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"], + "default": "normal" + }, + "fontWeight": { + "type": "string", + "enum": ["normal", "bold"], + "default": "normal" + }, + "fontSize": { + "type": "string", + "default": "medium" + }, + "textDecorationLine": { + "oneOf": [ + { + "type": "string", + "enum": ["none", "underline", "overline", "line-through"], + "default": "none" + }, + { + "type": "array", + "items": { + "type": "string", + "enum": ["underline", "overline", "line-through"], + "default": "none" + } + } + ] + }, + "verticalAlign": { + "type": "string", + "enum": ["baseline", "sub", "super", "text-top", "text-bottom", "middle", "top", "bottom"], + "default": "baseline" + }, + "textAlign": { + "type": "string", + "enum": ["start", "end", "left", "right", "center", "justify", "justify-all", "match-parent"], + "default": "start" + }, + "marginTop": { + "type": "number", + "default": 0 + }, + "marginLeft": { + "type": "number", + "default": 0 + }, + "marginRight": { + "type": "number", + "default": 0 + }, + "marginBottom": { + "type": "number", + "default": 0 + }, + "listStyleType": { + "type": "string", + "default": "disc" + } + } + } + }, + "type": "array", + "description": "Data file containing term information.", + "items": { + "type": "array", + "description": "Information about a single term.", + "minItems": 8, + "additionalItems": false, + "items": [ + { + "type": "string", + "description": "The text for the term." + }, + { + "type": "string", + "description": "Reading of the term, or an empty string if the reading is the same as the term." + }, + { + "type": ["string", "null"], + "description": "String of space-separated tags for the definition. An empty string is treated as no tags." + }, + { + "type": "string", + "description": "String of space-separated rule identifiers for the definition which is used to validate delinflection. Valid rule identifiers are: v1: ichidan verb; v5: godan verb; vs: suru verb; vk: kuru verb; adj-i: i-adjective. An empty string corresponds to words which aren't inflected, such as nouns." + }, + { + "type": "number", + "description": "Score used to determine popularity. Negative values are more rare and positive values are more frequent. This score is also used to sort search results." + }, + { + "type": "array", + "description": "Array of definitions for the term.", + "items": { + "oneOf": [ + { + "type": "string", + "description": "Single definition for the term." + }, + { + "type": "object", + "description": "Single detailed definition for the term.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of the data for this definition.", + "enum": ["text", "image", "structured-content"] + } + }, + "oneOf": [ + { + "required": [ + "type", + "text" + ], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "const": "text" + }, + "text": { + "type": "string", + "description": "Single definition for the term." + } + } + }, + { + "required": [ + "type", + "content" + ], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "const": "structured-content" + }, + "content": { + "$ref": "#/definitions/structuredContent", + "description": "Single definition for the term using a structured content object." + } + } + }, + { + "required": [ + "type", + "path" + ], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "const": "image" + }, + "path": { + "type": "string", + "description": "Path to the image file in the archive." + }, + "width": { + "type": "integer", + "description": "Preferred width of the image.", + "minimum": 1 + }, + "height": { + "type": "integer", + "description": "Preferred width of the image.", + "minimum": 1 + }, + "title": { + "type": "string", + "description": "Hover text for the image." + }, + "description": { + "type": "string", + "description": "Description of the image." + }, + "pixelated": { + "type": "boolean", + "description": "Whether or not the image should appear pixelated at sizes larger than the image's native resolution.", + "default": false + }, + "imageRendering": { + "type": "string", + "description": "Controls how the image is rendered. The value of this field supersedes the pixelated field.", + "enum": ["auto", "pixelated", "crisp-edges"], + "default": "auto" + }, + "appearance": { + "type": "string", + "description": "Controls the appearance of the image. The \"monochrome\" value will mask the opaque parts of the image using the current text color.", + "enum": ["auto", "monochrome"], + "default": "auto" + }, + "background": { + "type": "boolean", + "description": "Whether or not a background color is displayed behind the image.", + "default": true + }, + "collapsed": { + "type": "boolean", + "description": "Whether or not the image is collapsed by default.", + "default": false + }, + "collapsible": { + "type": "boolean", + "description": "Whether or not the image can be collapsed.", + "default": true + } + } + } + ] + } + ] + } + }, + { + "type": "integer", + "description": "Sequence number for the term. Terms with the same sequence number can be shown together when the \"resultOutputMode\" option is set to \"merge\"." + }, + { + "type": "string", + "description": "String of space-separated tags for the term. An empty string is treated as no tags." + } + ] + } +} \ No newline at end of file diff --git a/data/yomichan_metadata.json b/data/yomichan/index.json similarity index 87% rename from data/yomichan_metadata.json rename to data/yomichan/index.json index 3fa8cb4..45e2dc6 100644 --- a/data/yomichan_metadata.json +++ b/data/yomichan/index.json @@ -56,5 +56,16 @@ ["子", "name", 0, "子項目", 0], ["句", "expression", 0, "句項目", 0] ] + }, + "sankoku8": { + "index": { + "title": "三省堂国語辞典 第八版", + "sequenced": true, + "format": 3 + }, + "tags": [ + ["子", "name", 0, "子項目", 0], + ["句", "expression", 0, "句項目", 0] + ] } } diff --git a/data/yomichan_inflection_categories.json b/data/yomichan/inflection_categories.json similarity index 78% rename from data/yomichan_inflection_categories.json rename to data/yomichan/inflection_categories.json index 396ddb6..a36c589 100644 --- a/data/yomichan_inflection_categories.json +++ b/data/yomichan/inflection_categories.json @@ -25,5 +25,13 @@ "keiyoushi": ["形"], "kahen": ["カ変"], "sudachi": ["助動", "接尾", "枕詞", "連体", "連語"] + }, + "sankoku8": { + "sahen": ["サ", "サ型"], + "godan": ["上二", "下二", "下二型", "四","四型", "五", "五型", "特殊型", "マス","マス型"], + "ichidan": ["上一", "下一", "下一型"], + "keiyoushi": ["形", "形型"], + "kahen": ["カ"], + "sudachi": [] } } diff --git a/data/daijirin2/yomichan_name_conversion.json b/data/yomichan/name_conversion/daijirin2.json similarity index 100% rename from data/daijirin2/yomichan_name_conversion.json rename to data/yomichan/name_conversion/daijirin2.json diff --git a/data/yomichan/name_conversion/sankoku8.json b/data/yomichan/name_conversion/sankoku8.json new file mode 100644 index 0000000..f9323ac --- /dev/null +++ b/data/yomichan/name_conversion/sankoku8.json @@ -0,0 +1,495 @@ +{ + "a": {}, + "br": {}, + "img": {}, + "div": {}, + "span": {}, + "ruby": {}, + "rt": {}, + "small": { + "name": "span", + "style": "vertical-align: super; font-size: 0.65em; font-weight: normal; margin-right: 0.25em;", + "procedures": [ + { + "procedure_name": "wrap", + "parameters": { + "l_wrap": "(", + "r_wrap": ")" + } + } + ] + }, + "sup": { + "name": "span", + "style": "vertical-align: super; font-size: 0.65em;" + }, + "sub": { + "name": "span", + "style": "vertical-align: sub; font-size: 0.65em;" + }, + "表外字": { + "name": "ruby" + }, + "表外字マーク": { + "name": "rt" + }, + "表外音訓": { + "name": "ruby" + }, + "表外音訓マーク": { + "name": "rt" + }, + "語構成": { + "name": "span", + "style": "margin-right: 0.5em;" + }, + "分書": { + "name": "span", + "style": "margin-right: 0.5em;" + }, + "見出仮名": { + "name": "span", + "style": "font-weight: bold;" + }, + "解説部": { + "name": "div" + }, + "子解説部": { + "name": "div" + }, + "句解説部": { + "name": "div" + }, + "大語義": { + "name": "div" + }, + "語義": { + "name": "div" + }, + "副義": { + "name": "div", + "style": "margin-left: 1.0em;" + }, + "注記語義": { + "name": "span", + "style": "margin-left: 0.5em;" + }, + "用例G": { + "name": "div", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "百科", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "注記", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "名詞形G", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "動詞形G", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "自動詞形G", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "他動詞形G", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "可能形G", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "人G", + "key": "name", + "value": "span" + } + } + ] + }, + "見出相当部": { + "name": "span", + "style": "margin-left: 0.125em; margin-right: 0.125em;" + }, + "ルビG": { + "name": "span", + "style": "vertical-align: super; font-size: 0.65em; font-weight: normal;" + }, + "名詞人形G": { + "name": "div" + }, + "名詞形G": { + "name": "div", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "語義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "副義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "注記", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "共通", + "key": "name", + "value": "span" + } + } + ] + }, + "動詞形G": { + "name": "div", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "語義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "副義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "注記", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "共通", + "key": "name", + "value": "span" + } + } + ] + }, + "自動詞形G": { + "name": "div", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "語義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "副義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "注記", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "共通", + "key": "name", + "value": "span" + } + } + ] + }, + "他動詞形G": { + "name": "div", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "語義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "副義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "注記", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "共通", + "key": "name", + "value": "span" + } + } + ] + }, + "可能形G": { + "name": "div", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "語義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "副義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "注記", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "共通", + "key": "name", + "value": "span" + } + } + ] + }, + "人G": { + "name": "div", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "語義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "副義", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "注記", + "key": "name", + "value": "span" + } + }, + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "共通", + "key": "name", + "value": "span" + } + } + ] + }, + "参照矢印": { + "name": "span", + "style": "margin-right: 0.25em;" + }, + "子見出仮名": { + "name": "span", + "style": "font-weight: bold;" + }, + "句表記": { + "name": "span", + "style": "font-weight: bold;" + }, + "派生語G": { + "name": "div" + }, + "謙譲形G": { + "name": "div" + }, + "共通": { + "name": "div" + }, + "イタリック": { + "name": "span", + "style": "font-style: italic;" + }, + "ボールド": { + "name": "span", + "style": "font-weight: bold;" + }, + "アクセント表記": { + "name": "span", + "style": "font-weight: bold; font-size: 0.85em;" + }, + "i": { + "name": "span", + "style": "font-style: italic;" + }, + "sub": { + "name": "span", + "style": "font-size: 0.7em; vertical-align: sub;", + "procedures": [ + { + "procedure_name": "has_parent", + "parameters": { + "parent_name": "横", + "key": "style", + "value": "font-size: 0.7em; vertical-align: super;" + } + } + ] + }, + "kanbun": { + "name": "span", + "style": "font-size: 0.5em; vertical-align: sub;" + }, + "ロゴ": { + "name": "span", + "style": "margin-right: 0.25em" + }, + "尊敬形G": { + "name": "div" + }, + "謙譲形G": { + "name": "div" + }, + "丁寧形G": { + "name": "div" + }, + "百科": { + "name": "div" + }, + "由来": { + "name": "div" + }, + "区別": { + "name": "div" + }, + "アクセント注記": { + "name": "div" + }, + "表記情報": { + "name": "div" + }, + "別見出": { + "name": "span", + "style": "font-weight: bold;" + }, + "読み": { + "name": "span", + "style": "font-size: 0.7em;" + }, + "歴史仮名": { + "name": "span", + "style": "vertical-align: super; font-size: 0.65em; font-weight: normal;", + "procedures": [ + { + "procedure_name": "wrap", + "parameters": { + "l_wrap": "(", + "r_wrap": ")" + } + } + ] + }, + "ルビ": { + "name": "span", + "procedures": [ + { + "procedure_name": "wrap", + "parameters": { + "l_wrap": "(", + "r_wrap": ")" + } + } + ] + } +} diff --git a/data/smk8/yomichan_name_conversion.json b/data/yomichan/name_conversion/smk8.json similarity index 84% rename from data/smk8/yomichan_name_conversion.json rename to data/yomichan/name_conversion/smk8.json index 82c491f..91a6593 100644 --- a/data/smk8/yomichan_name_conversion.json +++ b/data/yomichan/name_conversion/smk8.json @@ -121,25 +121,31 @@ "style": "font-weight: bold;" }, "表外字": { - "name": "ruby", + "name": "ruby" + }, + "表外字マーク": { + "name": "rt", "procedures": [ { - "procedure_name": "add_ruby_text", + "procedure_name": "insert_span", "parameters": { - "mark": "︿", - "style": "font-size: 2em;" + "attr_name": "style", + "attr_val": "font-size: 2em;" } } ] }, "表外音訓": { - "name": "ruby", + "name": "ruby" + }, + "表外音訓マーク": { + "name": "rt", "procedures": [ { - "procedure_name": "add_ruby_text", + "procedure_name": "insert_span", "parameters": { - "mark": "︽", - "style": "font-size: 2em;" + "attr_name": "style", + "attr_val": "font-size: 2em;" } } ] @@ -148,23 +154,7 @@ "name": "ruby" }, "表音表記": { - "name": "rt", - "procedures": [ - { - "procedure_name": "replace", - "parameters": { - "old": "(", - "new": "" - } - }, - { - "procedure_name": "replace", - "parameters": { - "old": ")", - "new": "" - } - } - ] + "name": "rt" }, "派生見出": { "name": "span", diff --git a/jitenbot.py b/jitenbot.py index 0a25b96..f0a2719 100644 --- a/jitenbot.py +++ b/jitenbot.py @@ -17,9 +17,20 @@ along with this program. If not, see . """ import os +import sys import argparse +import subprocess from bot.targets import Targets -from bot.crawlers.factory import new_crawler +from bot.factory import new_crawler + + +def filename(f): + if not os.path.isfile(f): + raise argparse.ArgumentTypeError(f"`{f}` is not a valid filename") + elif not os.access(f, os.R_OK): + raise argparse.ArgumentTypeError(f"Cannot access file `{f}`") + else: + return f def directory(d): @@ -35,34 +46,78 @@ def parse_args(target_names): parser = argparse.ArgumentParser( prog="jitenbot", description="Convert Japanese dictionary files to new formats.", + epilog="See README.md for details regarding media directory structures", ) parser.add_argument( "target", choices=target_names, - help="name of dictionary to convert" + help="name of dictionary to convert", ) parser.add_argument( "-p", "--page-dir", help="path to directory containing XML page files", - type=directory + type=directory, ) parser.add_argument( - "-i", "--image-dir", - help="path to directory containing image folders (gaiji, graphics, etc.)", - type=directory + "-m", "--media-dir", + help="path to directory containing media folders (gaiji, graphics, audio, etc.)", + type=directory, + ) + parser.add_argument( + "-i", "--mdict-icon", + help="path to icon file to be used with MDict", + type=filename, + ) + parser.add_argument( + "--no-mdict-export", + help="skip export of dictionary data to MDict format", + action='store_true', + ) + parser.add_argument( + "--no-yomichan-export", + help="skip export of dictionary data to Yomichan format", + action='store_true', + ) + parser.add_argument( + "--validate-yomichan-terms", + help="validate JSON structure of exported Yomichan dictionary terms", + action='store_true', ) args = parser.parse_args() return args +def test_mdict(): + try: + subprocess.run( + ["mdict", "--version"], + check=True, + stdout=subprocess.DEVNULL, + ) + except FileNotFoundError: + print("Could not find `mdict` pack tool.") + print("Ensure that mdict-utils is installed and") + print("included in the environment PATH.\n") + print("Mdict export functionality may also be") + print("disabled with the --no-mdict-export flag.") + sys.exit() + + def main(): target_names = [x.value for x in Targets] args = parse_args(target_names) + if not args.no_mdict_export: + test_mdict() selected_target = Targets(args.target) crawler = new_crawler(selected_target) crawler.collect_pages(args.page_dir) crawler.read_pages() - crawler.make_yomichan_dictionary(args.image_dir) + if not args.no_yomichan_export: + crawler.make_yomichan_dictionary( + args.media_dir, args.validate_yomichan_terms) + if not args.no_mdict_export: + crawler.make_mdict_dictionary( + args.media_dir, args.mdict_icon) if __name__ == "__main__": diff --git a/requirements.txt b/requirements.txt index 1c111af..b19e14b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,25 @@ +attrs==23.1.0 beautifulsoup4==4.12.2 bs4==0.0.1 certifi==2022.12.7 charset-normalizer==3.1.0 css-parser==1.0.8 +fastjsonschema==2.17.1 html5lib==1.1 idna==3.4 +jsonschema-specifications==2023.6.1 lxml==4.9.2 +mdict-utils==1.3.12 Pillow==9.5.0 platformdirs==3.5.0 +referencing==0.29.1 requests==2.29.0 +rpds-py==0.8.10 six==1.16.0 soupsieve==2.4.1 SudachiDict-full==20230110 SudachiPy==0.6.7 +tqdm==4.65.0 urllib3==1.26.15 webencodings==0.5.1 +xxhash==3.2.0 diff --git a/run_all.sh b/run_all.sh new file mode 100755 index 0000000..9dcdfda --- /dev/null +++ b/run_all.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +export PYTHONPYCACHEPREFIX=/tmp/pycache + +python -m unittest discover -s tests + +python jitenbot.py jitenon-kokugo +python jitenbot.py jitenon-yoji +python jitenbot.py jitenon-kotowaza + +python jitenbot.py smk8 \ + --media-dir monokakido/SMK8/media \ + --page-dir monokakido/SMK8/pages \ + --mdict-icon monokakido/SMK8/SMK8-76@3x.png + +python jitenbot.py daijirin2 \ + --media-dir monokakido/DAIJIRIN2/media \ + --page-dir monokakido/DAIJIRIN2/pages \ + --mdict-icon monokakido/DAIJIRIN2/DAIJIRIN2-76@3x.png + +python jitenbot.py sankoku8 \ + --media-dir monokakido/SANKOKU8/media \ + --page-dir monokakido/SANKOKU8/pages \ + --mdict-icon monokakido/SANKOKU8/SANKOKU8-76@3x.png diff --git a/tests/test_daijirin_phrases.py b/tests/test_daijirin_phrases.py new file mode 100644 index 0000000..3ab02dd --- /dev/null +++ b/tests/test_daijirin_phrases.py @@ -0,0 +1,21 @@ +import unittest +from bot.entries.daijirin2.phrase_entry import parse_phrase + + +class TestDaijirin2PhraseParse(unittest.TestCase): + def test1(self): + text = "同じ穴の=狢(=狐・狸)" + exps = parse_phrase(text) + self.assertEqual(len(exps), 3) + self.assertIn("同じ穴の狢", exps) + self.assertIn("同じ穴の狐", exps) + self.assertIn("同じ穴の狸", exps) + + def test2(self): + text = "聞くは=一時(=一旦)の恥、聞かぬは=末代(=一生)の恥" + exps = parse_phrase(text) + self.assertEqual(len(exps), 4) + self.assertIn("聞くは一時の恥、聞かぬは末代の恥", exps) + self.assertIn("聞くは一時の恥、聞かぬは一生の恥", exps) + self.assertIn("聞くは一旦の恥、聞かぬは末代の恥", exps) + self.assertIn("聞くは一旦の恥、聞かぬは一生の恥", exps) diff --git a/tests/test_expressions.py b/tests/test_expressions.py index b2ebc26..9091dda 100644 --- a/tests/test_expressions.py +++ b/tests/test_expressions.py @@ -1,5 +1,5 @@ import unittest -import bot.entries.expressions as Expressions +import bot.entries.base.expressions as Expressions class TestExpressions(unittest.TestCase): @@ -34,8 +34,8 @@ class TestExpressions(unittest.TestCase): self.assertIn("凶々しい", exps) self.assertIn("凶凶しい", exps) - def test_add_variant_kanji(self): - exps = ["剝く", "掴む", "摑む"] + def test_add_variant_kanji1(self): + exps = ["剥く", "摑む"] Expressions.add_variant_kanji(exps) self.assertEqual(len(exps), 4) self.assertIn("剥く", exps) @@ -44,6 +44,15 @@ class TestExpressions(unittest.TestCase): self.assertIn("摑む", exps) def test_add_variant_kanji2(self): + exps = ["剝く", "掴む", "摑む"] + Expressions.add_variant_kanji(exps) + self.assertEqual(len(exps), 4) + self.assertIn("剥く", exps) + self.assertIn("剝く", exps) + self.assertIn("掴む", exps) + self.assertIn("摑む", exps) + + def test_add_variant_kanji3(self): exps = ["剝摑"] Expressions.add_variant_kanji(exps) self.assertEqual(len(exps), 4) @@ -52,6 +61,15 @@ class TestExpressions(unittest.TestCase): self.assertIn("剥掴", exps) self.assertIn("剥摑", exps) + def test_add_variant_kanji4(self): + exps = ["剥掴"] + Expressions.add_variant_kanji(exps) + self.assertEqual(len(exps), 4) + self.assertIn("剝摑", exps) + self.assertIn("剝掴", exps) + self.assertIn("剥掴", exps) + self.assertIn("剥摑", exps) + def test_expand_abbreviation(self): text = "有(り)合(わ)せ" abbrs = Expressions.expand_abbreviation(text) @@ -69,28 +87,3 @@ class TestExpressions(unittest.TestCase): self.assertIn("有合わせ", abbrs) self.assertIn("有り合せ", abbrs) self.assertIn("有合せ", abbrs) - - def test_smk_expand_alternatives(self): - text = "△金(時間・暇)に飽かして" - exps = Expressions.expand_smk_alternatives(text) - self.assertEqual(len(exps), 3) - self.assertIn("金に飽かして", exps) - self.assertIn("時間に飽かして", exps) - self.assertIn("暇に飽かして", exps) - - def test_daijirin_expand_alternatives(self): - text = "同じ穴の=狢(=狐・狸)" - exps = Expressions.expand_daijirin_alternatives(text) - self.assertEqual(len(exps), 3) - self.assertIn("同じ穴の狢", exps) - self.assertIn("同じ穴の狐", exps) - self.assertIn("同じ穴の狸", exps) - - def test_daijirin_expand_alternatives2(self): - text = "聞くは=一時(=一旦)の恥、聞かぬは=末代(=一生)の恥" - exps = Expressions.expand_daijirin_alternatives(text) - self.assertEqual(len(exps), 4) - self.assertIn("聞くは一時の恥、聞かぬは末代の恥", exps) - self.assertIn("聞くは一時の恥、聞かぬは一生の恥", exps) - self.assertIn("聞くは一旦の恥、聞かぬは末代の恥", exps) - self.assertIn("聞くは一旦の恥、聞かぬは一生の恥", exps) diff --git a/tests/test_sankoku_phrases.py b/tests/test_sankoku_phrases.py new file mode 100644 index 0000000..c3894e9 --- /dev/null +++ b/tests/test_sankoku_phrases.py @@ -0,0 +1,46 @@ +import unittest +from bot.entries.sankoku8.parse import parse_hyouki_pattern + + +class TestSankoku8PhraseParse(unittest.TestCase): + def test1(self): + pattern = '耳にたこ(ができる)' + exps = parse_hyouki_pattern(pattern) + self.assertEqual(len(exps), 2) + self.assertIn("耳にたこ", exps) + self.assertIn("耳にたこができる", exps) + + def test2(self): + pattern = '一斑を〈見て/もって〉全豹を〈卜す/推す〉' + exps = parse_hyouki_pattern(pattern) + self.assertEqual(len(exps), 4) + self.assertIn("一斑を見て全豹を卜す", exps) + self.assertIn("一斑を見て全豹を推す", exps) + self.assertIn("一斑をもって全豹を卜す", exps) + self.assertIn("一斑をもって全豹を推す", exps) + + def test3(self): + pattern = '{かじ・舵}を切る' + exps = parse_hyouki_pattern(pattern) + self.assertEqual(len(exps), 2) + self.assertIn("かじを切る", exps) + self.assertIn("舵を切る", exps) + + def test4(self): + pattern = '重箱の隅を(⦅ようじ\楊枝⦆で)〈つつく/ほじくる〉' + exps = parse_hyouki_pattern(pattern) + self.assertEqual(len(exps), 6) + self.assertIn("重箱の隅をつつく", exps) + self.assertIn("重箱の隅をようじでつつく", exps) + self.assertIn("重箱の隅を楊枝でつつく", exps) + self.assertIn("重箱の隅をほじくる", exps) + self.assertIn("重箱の隅をようじでほじくる", exps) + self.assertIn("重箱の隅を楊枝でほじくる", exps) + + def test5(self): + pattern = '群盲象を〈{な・撫}でる/評する〉' + exps = parse_hyouki_pattern(pattern) + self.assertEqual(len(exps), 3) + self.assertIn("群盲象をなでる", exps) + self.assertIn("群盲象を撫でる", exps) + self.assertIn("群盲象を評する", exps) diff --git a/tests/test_smk_phrases.py b/tests/test_smk_phrases.py new file mode 100644 index 0000000..e5ce231 --- /dev/null +++ b/tests/test_smk_phrases.py @@ -0,0 +1,19 @@ +import unittest +from bot.entries.smk8.phrase_entry import parse_phrase + + +class TestSmk8PhraseParse(unittest.TestCase): + def test1(self): + text = "目と鼻の△先(間)" + exps = parse_phrase(text) + self.assertEqual(len(exps), 2) + self.assertIn("目と鼻の先", exps) + self.assertIn("目と鼻の間", exps) + + def test2(self): + text = "△金(時間・暇)に飽かして" + exps = parse_phrase(text) + self.assertEqual(len(exps), 3) + self.assertIn("金に飽かして", exps) + self.assertIn("時間に飽かして", exps) + self.assertIn("暇に飽かして", exps)