jlpt 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.ja.md +246 -0
- data/README.md +224 -0
- data/assets/logo.png +0 -0
- data/bin/jlpt +7 -0
- data/lib/jlpt/active_model/validator.rb +43 -0
- data/lib/jlpt/analyzers/comparer.rb +44 -0
- data/lib/jlpt/analyzers/conjugation.rb +60 -0
- data/lib/jlpt/analyzers/corpus_stats.rb +38 -0
- data/lib/jlpt/analyzers/dokkai.rb +53 -0
- data/lib/jlpt/analyzers/exam_readiness.rb +52 -0
- data/lib/jlpt/analyzers/grammar.rb +50 -0
- data/lib/jlpt/analyzers/jukugo.rb +46 -0
- data/lib/jlpt/analyzers/loanwords.rb +54 -0
- data/lib/jlpt/analyzers/onomatopeia.rb +29 -0
- data/lib/jlpt/analyzers/pitch_accent.rb +51 -0
- data/lib/jlpt/analyzers/pos_profiler.rb +47 -0
- data/lib/jlpt/analyzers/simplifier.rb +48 -0
- data/lib/jlpt/analyzers/style_profiler.rb +62 -0
- data/lib/jlpt/analyzers/syntax.rb +43 -0
- data/lib/jlpt/cli.rb +121 -0
- data/lib/jlpt/core/analysis_result.rb +45 -0
- data/lib/jlpt/core/analyzer.rb +58 -0
- data/lib/jlpt/core/cache_manager.rb +90 -0
- data/lib/jlpt/core/config.rb +45 -0
- data/lib/jlpt/core/dictionary.rb +113 -0
- data/lib/jlpt/core/engine.rb +142 -0
- data/lib/jlpt/core/kanji.rb +54 -0
- data/lib/jlpt/core/kanji_details.rb +60 -0
- data/lib/jlpt/core/preprocessor.rb +48 -0
- data/lib/jlpt/core/tokenizer.rb +64 -0
- data/lib/jlpt/core/vocab_classifier.rb +48 -0
- data/lib/jlpt/data/kanji.json +2213 -0
- data/lib/jlpt/data/vocab.json +50298 -0
- data/lib/jlpt/formatters/batch_processor.rb +41 -0
- data/lib/jlpt/formatters/exporter.rb +55 -0
- data/lib/jlpt/formatters/furigana.rb +69 -0
- data/lib/jlpt/formatters/reporter.rb +72 -0
- data/lib/jlpt/version.rb +5 -0
- data/lib/jlpt.rb +50 -0
- metadata +184 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 351c7b81b049e4ef39a34cacf7d20e1d7ce04f1d302b7ed0033f62d48dd6eef3
|
|
4
|
+
data.tar.gz: 903b1e3d8e7b5054a20ae4cb532325cd823dfe0e9de98800ec9705acd80b8651
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6b5c45194f1469bc1c9e2345b8ea82766a36aa0d21e276f696baa560c7f02a1e4b57f252b9998d1d8e8cf15a2ee1ace6cda520c242ba5e39462fd413b5cb8567
|
|
7
|
+
data.tar.gz: b41d13beb80bcc12238ed986a5c516f5c3d7ea38a5fd2be6e994c39bdf10ffa62c1c0ab023bf0307bdc74ffdc44ff30b7054aba84fce6a7197db7eb585f8ea96
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jakub Polak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.ja.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.png" alt="JLPT Ruby Gem Logo" width="500">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">JLPT</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/kupolak/jlpt/actions/workflows/ci.yml"><img src="https://github.com/kupolak/jlpt/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
9
|
+
<a href="https://badge.fury.io/rb/jlpt"><img src="https://badge.fury.io/rb/jlpt.svg" alt="Gem Version"></a>
|
|
10
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<strong>日本語テキスト解析およびJLPT難易度判定(N5〜N1)のためのRuby Gem</strong>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="README.md">English</a> | <strong>日本語</strong>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
JLPT Gemは、日本語テキストの形態素解析、語彙および漢字のJLPT難易度レベル分類(N5〜N1)、ルビ(ふりがな)生成、品詞分布プロファイリング、言い換え・簡略化提案、試験合格可能性評価、Anki用カード出力などを提供する包括的なライブラリです。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 目次
|
|
26
|
+
|
|
27
|
+
- [インストール](#インストール)
|
|
28
|
+
- [クイックスタート](#クイックスタート)
|
|
29
|
+
- [1. 基本的なテキスト解析](#1-基本的なテキスト解析)
|
|
30
|
+
- [2. グローバル設定とオプション](#2-グローバル設定とオプション)
|
|
31
|
+
- [3. ルビ(ふりがな)・ローマ字生成](#3-ルビふりがなローマ字生成)
|
|
32
|
+
- [4. テキストプロファイラーと分析機能](#4-テキストプロファイラーと分析機能)
|
|
33
|
+
- [5. Ankiカード出力・一括処理](#5-ankiカード出力一括処理)
|
|
34
|
+
- [6. HTML / Markdown ビジュアルレポート](#6-html--markdown-ビジュアルレポート)
|
|
35
|
+
- [7. Rails ActiveModel バリデーション](#7-rails-activemodel-バリデーション)
|
|
36
|
+
- [8. CLI コマンドラインツール](#8-cli-コマンドラインツール)
|
|
37
|
+
- [開発](#開発)
|
|
38
|
+
- [ライセンス](#ライセンス)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## インストール
|
|
43
|
+
|
|
44
|
+
`Gemfile` に以下を追加してください:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
gem 'jlpt', '~> 1.0.0'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
その後、以下のコマンドを実行します:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bundle install
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
または直接インストールします:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
gem install jlpt
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## クイックスタート
|
|
65
|
+
|
|
66
|
+
### 1. 基本的なテキスト解析
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
require 'jlpt'
|
|
70
|
+
|
|
71
|
+
result = JLPT.analyze("日本語の勉強はとても面白いです。")
|
|
72
|
+
|
|
73
|
+
result.recommended_level #=> :n5
|
|
74
|
+
result.score #=> 25.0
|
|
75
|
+
result.word_count #=> 7
|
|
76
|
+
result.sentence_count #=> 1
|
|
77
|
+
result.kanji_density #=> 0.44
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. グローバル設定とオプション
|
|
81
|
+
|
|
82
|
+
`JLPT.configure` で動作オプションをカスタマイズできます:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
JLPT.configure do |config|
|
|
86
|
+
# ふりがなのデフォルト出力フォーマット (:html, :anki, :markdown)
|
|
87
|
+
config.default_furigana_format = :anki # デフォルト: :html
|
|
88
|
+
|
|
89
|
+
# LRUキャッシュ容量 (メモリ上に保持する解析結果の件数)
|
|
90
|
+
config.cache_capacity = 2000 # デフォルト: 1000
|
|
91
|
+
|
|
92
|
+
# MeCab / natto が未インストール時のPure-Rubyフォールバック
|
|
93
|
+
config.mecab_fallback = true # デフォルト: true
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
設定をデフォルト値にリセットするには:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
JLPT.reset_config!
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### 設定オプション一覧
|
|
104
|
+
|
|
105
|
+
| オプション / パラメータ | 指定可能な値 | デフォルト | 説明 |
|
|
106
|
+
| :--- | :--- | :--- | :--- |
|
|
107
|
+
| `default_furigana_format` | `:html`, `:anki`, `:markdown` | `:html` | `JLPT::Furigana.render` のデフォルトフォーマット |
|
|
108
|
+
| `cache_capacity` | `Integer` (例: `500`, `2000`) | `1000` | LRUキャッシュの最大件数 |
|
|
109
|
+
| `mecab_fallback` | `true`, `false` | `true` | MeCab非搭載環境でのPure-Rubyトークナイザー利用 |
|
|
110
|
+
| `target_level` | `:n5`, `:n4`, `:n3`, `:n2`, `:n1` | `:n4` | 目標JLPTレベル(試験準備度およびエクスポート用) |
|
|
111
|
+
| `format` (CLI & Reporter) | `:text`, `:json`, `:html`, `:markdown` | `:text` | 出力フォーマット |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### 3. ルビ(ふりがな)・ローマ字生成
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
JLPT::Furigana.render("学校に行く", format: :html)
|
|
119
|
+
#=> "<ruby><rb>学校</rb><rt>がっこう</rt></ruby>に<ruby><rb>行</rb><rt>い</rt></ruby>く"
|
|
120
|
+
|
|
121
|
+
JLPT::Furigana.render("学校に行く", format: :anki)
|
|
122
|
+
#=> "学校[がっこう]に行[い]く"
|
|
123
|
+
|
|
124
|
+
JLPT::Furigana.to_romaji("がっこう")
|
|
125
|
+
#=> "gakkou"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### 4. テキストプロファイラーと分析機能
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
# 品詞分布プロファイル
|
|
134
|
+
JLPT::PosProfiler.profile("学校に行きます。")
|
|
135
|
+
|
|
136
|
+
# 文法パターン抽出 (N5〜N1)
|
|
137
|
+
JLPT::Grammar.extract("食べてはいけない")
|
|
138
|
+
#=> [{ name: "〜てはいけない", level: :n5, pattern: /.../ }]
|
|
139
|
+
|
|
140
|
+
# 構文・節の複雑さ分析
|
|
141
|
+
JLPT::Syntax.analyze("雨が降ったから、学校に行かなかった。")
|
|
142
|
+
#=> { conjunction_count: 1, conjunctions: ["から"], avg_clauses_per_sentence: 2.0, complexity_index: 50.0 }
|
|
143
|
+
|
|
144
|
+
# JLPT目標レベルに対する準備度評価
|
|
145
|
+
JLPT::ExamReadiness.evaluate("割合", target_level: :n3)
|
|
146
|
+
#=> { target_level: :n3, current_level: :n4, readiness_score: 80.86, status: :moderate }
|
|
147
|
+
|
|
148
|
+
# 敬語・文体プロファイリング
|
|
149
|
+
JLPT::StyleProfiler.profile("ご覧になる")
|
|
150
|
+
#=> { formality_score: 90.0, register: :polite, sonkeigo_count: 1, ... }
|
|
151
|
+
|
|
152
|
+
# 外来語(カタカナ語)抽出
|
|
153
|
+
JLPT::Loanwords.extract("デパートでパンを買う")
|
|
154
|
+
#=> ["デパート", "パン"]
|
|
155
|
+
|
|
156
|
+
# 漢字詳細情報照会 (画数・部首・音訓読み・レベル)
|
|
157
|
+
JLPT::KanjiDetails.lookup("学")
|
|
158
|
+
#=> { character: "学", strokes: 8, radical: "子", onyomi: ["ガク"], kunyomi: ["まな.ぶ"], level: :n5 }
|
|
159
|
+
|
|
160
|
+
# 難解語の言い換え・簡略化提案
|
|
161
|
+
JLPT::Simplifier.simplify("著しい")
|
|
162
|
+
#=> [{ word: "著しい", level: :n1, reading: "いちじるしい", suggestions: ["大きい", "目立つ"] }]
|
|
163
|
+
|
|
164
|
+
# テキスト・教材間比較(ジャカード係数・共通語彙)
|
|
165
|
+
JLPT::Comparer.compare("私と学校に行く", "私と図書館に行く")
|
|
166
|
+
#=> { overlap_ratio: 0.71, shared_vocab: ["私", "と", "に", "行", "く"], shared_kanji: ["私", "行"] }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### 5. Ankiカード出力・一括処理
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
# 学習目標レベルを超える未知語の抽出
|
|
175
|
+
JLPT::Exporter.find_unknown("急速な発展", target_level: :n4)
|
|
176
|
+
|
|
177
|
+
# Anki用TSV形式で出力
|
|
178
|
+
JLPT::Exporter.to_anki_tsv("急速な発展", target_level: :n4)
|
|
179
|
+
|
|
180
|
+
# 複数章・ドキュメントの一括解析
|
|
181
|
+
JLPT::BatchProcessor.process({ "Chapter 1" => "私と学校", "Chapter 2" => "急速な発展" })
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### 6. HTML / Markdown ビジュアルレポート
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
result = JLPT.analyze("日本語の勉強")
|
|
190
|
+
|
|
191
|
+
# 自己完結型HTMLレポート文字列の生成
|
|
192
|
+
html_report = JLPT::Reporter.render_html(result)
|
|
193
|
+
|
|
194
|
+
# Markdownサマリーテーブルの生成
|
|
195
|
+
md_report = JLPT::Reporter.render_markdown(result)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### 7. Rails ActiveModel バリデーション
|
|
201
|
+
|
|
202
|
+
Railsモデルでユーザー入力の難易度制限を検証します:
|
|
203
|
+
|
|
204
|
+
```ruby
|
|
205
|
+
class Lesson < ApplicationRecord
|
|
206
|
+
validates :content, jlpt_level: { max_level: :n3 }
|
|
207
|
+
end
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### 8. CLI コマンドラインツール
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# テキスト解析
|
|
216
|
+
jlpt analyze "日本語の勉強"
|
|
217
|
+
|
|
218
|
+
# ふりがな生成 (Anki形式)
|
|
219
|
+
jlpt furigana "漢字の読み方" --format markdown
|
|
220
|
+
|
|
221
|
+
# 未知語をAnki TSV形式でファイル出力
|
|
222
|
+
jlpt export "急速な発展" --target n4 > flashcards.tsv
|
|
223
|
+
|
|
224
|
+
# 漢字詳細・活用・簡略化・統計コマンド
|
|
225
|
+
jlpt kanji "学"
|
|
226
|
+
jlpt conjugate "食べさせられた"
|
|
227
|
+
jlpt simplify "著しい"
|
|
228
|
+
jlpt stats "私と学校"
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 開発
|
|
234
|
+
|
|
235
|
+
テストおよびリンターの実行:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
bundle exec rspec
|
|
239
|
+
bundle exec rubocop
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## ライセンス
|
|
245
|
+
|
|
246
|
+
オープンソースソフトウェアとして [MIT License](LICENSE) の下で利用可能です。
|
data/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.png" alt="JLPT Ruby Gem Logo" width="500">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">JLPT</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/kupolak/jlpt/actions/workflows/ci.yml"><img src="https://github.com/kupolak/jlpt/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
9
|
+
<a href="https://badge.fury.io/rb/jlpt"><img src="https://badge.fury.io/rb/jlpt.svg" alt="Gem Version"></a>
|
|
10
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<strong>A Ruby gem for Japanese text analysis and JLPT difficulty classification (N5–N1).</strong>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<strong>English</strong> | <a href="README.ja.md">日本語</a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
It parses Japanese text, classifies vocabulary and kanji by JLPT level, generates furigana, profiles part-of-speech distributions, and provides utilities for text simplification, exam readiness evaluation, and Anki card export.
|
|
22
|
+
|
|
23
|
+
## Table of Contents
|
|
24
|
+
|
|
25
|
+
- [Installation](#installation)
|
|
26
|
+
- [Quick Start](#quick-start)
|
|
27
|
+
- [1. Basic Analysis](#1-basic-analysis)
|
|
28
|
+
- [2. Global Configuration & Options](#2-global-configuration--options)
|
|
29
|
+
- [3. Furigana & Romaji Generation](#3-furigana--romaji-generation)
|
|
30
|
+
- [4. Text Profiling & Analyzers](#4-text-profiling--analyzers)
|
|
31
|
+
- [5. Anki Exporting & Batch Processing](#5-anki-exporting--batch-processing)
|
|
32
|
+
- [6. HTML & Markdown Visual Reports](#6-html--markdown-visual-reports)
|
|
33
|
+
- [7. Rails ActiveModel Integration](#7-rails-activemodel-integration)
|
|
34
|
+
- [8. CLI Runner](#8-cli-runner)
|
|
35
|
+
- [Development](#development)
|
|
36
|
+
- [License](#license)
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
Add this line to your application's `Gemfile`:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
gem 'jlpt'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
And then run:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bundle install
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install it directly:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
gem install jlpt
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
### 1. Basic Analysis
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
require 'jlpt'
|
|
64
|
+
|
|
65
|
+
result = JLPT.analyze("日本語の勉強はとても面白いです。")
|
|
66
|
+
|
|
67
|
+
result.recommended_level #=> :n5
|
|
68
|
+
result.score #=> 25.0
|
|
69
|
+
result.word_count #=> 7
|
|
70
|
+
result.sentence_count #=> 1
|
|
71
|
+
result.kanji_density #=> 0.44
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2. Global Configuration & Options
|
|
75
|
+
|
|
76
|
+
Customize global library settings using `JLPT.configure`:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
JLPT.configure do |config|
|
|
80
|
+
# Default output format for Furigana (:html, :anki, or :markdown)
|
|
81
|
+
config.default_furigana_format = :anki # Default: :html
|
|
82
|
+
|
|
83
|
+
# Capacity for the LRU cache (number of analyzed texts stored in memory)
|
|
84
|
+
config.cache_capacity = 2000 # Default: 1000
|
|
85
|
+
|
|
86
|
+
# Automatic pure-Ruby tokenizer fallback when MeCab/natto gem is absent
|
|
87
|
+
config.mecab_fallback = true # Default: true
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
To reset configuration back to defaults at any point:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
JLPT.reset_config!
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Summary of Available Options
|
|
98
|
+
|
|
99
|
+
| Option / Parameter | Allowed Values / Symbols | Default | Description |
|
|
100
|
+
| :--- | :--- | :--- | :--- |
|
|
101
|
+
| `default_furigana_format` | `:html`, `:anki`, `:markdown` | `:html` | Default format used by `JLPT::Furigana.render` |
|
|
102
|
+
| `cache_capacity` | `Integer` (e.g. `500`, `2000`) | `1000` | Max entries stored in memory LRU cache |
|
|
103
|
+
| `mecab_fallback` | `true`, `false` | `true` | Enable pure-Ruby tokenizer fallback if MeCab is missing |
|
|
104
|
+
| `target_level` | `:n5`, `:n4`, `:n3`, `:n2`, `:n1` | `:n4` | Target JLPT level for exam readiness & exporter |
|
|
105
|
+
| `format` (CLI & Reporter) | `:text`, `:json`, `:html`, `:markdown` | `:text` | Output format for CLI runner and Reporter |
|
|
106
|
+
|
|
107
|
+
### 3. Furigana & Romaji Generation
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
JLPT::Furigana.render("学校に行く", format: :html)
|
|
111
|
+
#=> "<ruby><rb>学校</rb><rt>がっこう</rt></ruby>に<ruby><rb>行</rb><rt>い</rt></ruby>く"
|
|
112
|
+
|
|
113
|
+
JLPT::Furigana.render("学校に行く", format: :anki)
|
|
114
|
+
#=> "学校[がっこう]に行[い]く"
|
|
115
|
+
|
|
116
|
+
JLPT::Furigana.to_romaji("がっこう")
|
|
117
|
+
#=> "gakkou"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 4. Text Profiling & Analyzers
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
# Part-of-Speech Distribution
|
|
124
|
+
JLPT::PosProfiler.profile("学校に行きます。")
|
|
125
|
+
|
|
126
|
+
# Grammar Pattern Extraction
|
|
127
|
+
JLPT::Grammar.extract("食べてはいけない")
|
|
128
|
+
#=> [{ name: "〜てはいけない", level: :n5, pattern: /.../ }]
|
|
129
|
+
|
|
130
|
+
# Syntactic Clause Complexity
|
|
131
|
+
JLPT::Syntax.analyze("雨が降ったから、学校に行かなかった。")
|
|
132
|
+
#=> { conjunction_count: 1, conjunctions: ["から"], avg_clauses_per_sentence: 2.0, complexity_index: 50.0 }
|
|
133
|
+
|
|
134
|
+
# Exam Readiness Evaluation
|
|
135
|
+
JLPT::ExamReadiness.evaluate("割合", target_level: :n3)
|
|
136
|
+
#=> { target_level: :n3, current_level: :n4, readiness_score: 80.86, status: :moderate }
|
|
137
|
+
|
|
138
|
+
# Style & Keigo Profiling
|
|
139
|
+
JLPT::StyleProfiler.profile("ご覧になる")
|
|
140
|
+
#=> { formality_score: 90.0, register: :polite, sonkeigo_count: 1, ... }
|
|
141
|
+
|
|
142
|
+
# Katakana Loanwords Extraction
|
|
143
|
+
JLPT::Loanwords.extract("デパートでパンを買う")
|
|
144
|
+
#=> ["デパート", "パン"]
|
|
145
|
+
|
|
146
|
+
# Kanji Details Lookup
|
|
147
|
+
JLPT::KanjiDetails.lookup("学")
|
|
148
|
+
#=> { character: "学", strokes: 8, radical: "子", onyomi: ["ガク"], kunyomi: ["まな.ぶ"], level: :n5 }
|
|
149
|
+
|
|
150
|
+
# Beginner Text Simplification
|
|
151
|
+
JLPT::Simplifier.simplify("著しい")
|
|
152
|
+
#=> [{ word: "著しい", level: :n1, reading: "いちじるしい", suggestions: ["大きい", "目立つ"] }]
|
|
153
|
+
|
|
154
|
+
# Lesson & Text Comparison
|
|
155
|
+
JLPT::Comparer.compare("私と学校に行く", "私と図書館に行く")
|
|
156
|
+
#=> { overlap_ratio: 0.71, shared_vocab: ["私", "と", "に", "行", "く"], shared_kanji: ["私", "行"] }
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 5. Anki Exporting & Batch Processing
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# Find words above learner target level
|
|
163
|
+
JLPT::Exporter.find_unknown("急速な発展", target_level: :n4)
|
|
164
|
+
|
|
165
|
+
# Export TSV for Anki flashcards
|
|
166
|
+
JLPT::Exporter.to_anki_tsv("急速な発展", target_level: :n4)
|
|
167
|
+
|
|
168
|
+
# Multi-chapter / Document Batch Processing
|
|
169
|
+
JLPT::BatchProcessor.process({ "Chapter 1" => "私と学校", "Chapter 2" => "急速な発展" })
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 6. HTML & Markdown Visual Reports
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
result = JLPT.analyze("日本語の勉強")
|
|
176
|
+
|
|
177
|
+
# Render self-contained HTML report string
|
|
178
|
+
html_report = JLPT::Reporter.render_html(result)
|
|
179
|
+
|
|
180
|
+
# Render Markdown summary table
|
|
181
|
+
md_report = JLPT::Reporter.render_markdown(result)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 7. Rails ActiveModel Integration
|
|
185
|
+
|
|
186
|
+
Use the built-in ActiveModel validator to ensure user input does not exceed a given JLPT level:
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
class Lesson < ApplicationRecord
|
|
190
|
+
validates :content, jlpt_level: { max_level: :n3 }
|
|
191
|
+
end
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 8. CLI Runner
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Analyze Japanese text
|
|
198
|
+
jlpt analyze "日本語の勉強"
|
|
199
|
+
|
|
200
|
+
# Generate Furigana annotations in Anki format
|
|
201
|
+
jlpt furigana "漢字の読み方" --format markdown
|
|
202
|
+
|
|
203
|
+
# Export unknown words exceeding N4 level to TSV
|
|
204
|
+
jlpt export "急速な発展" --target n4 > flashcards.tsv
|
|
205
|
+
|
|
206
|
+
# Inspect Kanji details or verb conjugations
|
|
207
|
+
jlpt kanji "学"
|
|
208
|
+
jlpt conjugate "食べさせられた"
|
|
209
|
+
jlpt simplify "著しい"
|
|
210
|
+
jlpt stats "私と学校"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Development
|
|
214
|
+
|
|
215
|
+
Run tests and linter:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
bundle exec rspec
|
|
219
|
+
bundle exec rubocop
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/assets/logo.png
ADDED
|
Binary file
|
data/bin/jlpt
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JLPT
|
|
4
|
+
module ActiveModel
|
|
5
|
+
# ActiveModel Validator enforcing JLPT level difficulty constraints on Rails models.
|
|
6
|
+
#
|
|
7
|
+
# @example Usage in Rails Model
|
|
8
|
+
# class Lesson < ApplicationRecord
|
|
9
|
+
# validates :content, jlpt_level: { max_level: :n3 }
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
class Validator
|
|
13
|
+
LEVEL_VALS = { n5: 1, n4: 2, n3: 3, n2: 4, n1: 5 }.freeze
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Validate model attribute against max_level constraint
|
|
17
|
+
#
|
|
18
|
+
# @param record [Object] Rails model instance
|
|
19
|
+
# @param attribute [Symbol] attribute name
|
|
20
|
+
# @param value [String] text content
|
|
21
|
+
# @param max_level [Symbol] maximum allowed level (:n5, :n4, :n3, :n2)
|
|
22
|
+
def validate(record, attribute, value, max_level: :n3)
|
|
23
|
+
return if value.nil? || value.to_s.strip.empty?
|
|
24
|
+
|
|
25
|
+
res = JLPT.analyze(value)
|
|
26
|
+
add_error_if_exceeds(record, attribute, res, max_level)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def add_error_if_exceeds(record, attribute, res, max_level)
|
|
32
|
+
current_val = LEVEL_VALS[res.recommended_level] || 1
|
|
33
|
+
max_val = LEVEL_VALS[max_level.to_s.downcase.to_sym] || 3
|
|
34
|
+
return unless current_val > max_val
|
|
35
|
+
|
|
36
|
+
msg = "difficulty level (#{res.recommended_level.to_s.upcase}) exceeds maximum allowed " \
|
|
37
|
+
"level (#{max_level.to_s.upcase})"
|
|
38
|
+
record.errors.add(attribute, msg) if record.respond_to?(:errors)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JLPT
|
|
4
|
+
# Text Similarity and Lesson Vocabulary Comparer.
|
|
5
|
+
#
|
|
6
|
+
# Compares two Japanese texts for vocabulary overlap (Jaccard similarity),
|
|
7
|
+
# shared kanji characters, and relative JLPT difficulty differences.
|
|
8
|
+
#
|
|
9
|
+
module Comparer
|
|
10
|
+
class << self
|
|
11
|
+
# Compare two Japanese texts
|
|
12
|
+
#
|
|
13
|
+
# @param text_a [String] first text
|
|
14
|
+
# @param text_b [String] second text
|
|
15
|
+
# @return [Hash] comparison metrics hash
|
|
16
|
+
def compare(text_a, text_b)
|
|
17
|
+
lem_a = Tokenizer.lemmata(text_a).to_set
|
|
18
|
+
lem_b = Tokenizer.lemmata(text_b).to_set
|
|
19
|
+
return empty_comparison if lem_a.empty? || lem_b.empty?
|
|
20
|
+
|
|
21
|
+
compute_metrics(lem_a, lem_b, text_a, text_b)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def compute_metrics(lem_a, lem_b, text_a, text_b)
|
|
27
|
+
shared_v = (lem_a & lem_b).to_a
|
|
28
|
+
jaccard = (shared_v.length.to_f / (lem_a | lem_b).length).round(2)
|
|
29
|
+
|
|
30
|
+
{ overlap_ratio: jaccard, shared_vocab: shared_v, shared_kanji: extract_shared_kanji(text_a, text_b),
|
|
31
|
+
text_a_level: JLPT.analyze(text_a).recommended_level,
|
|
32
|
+
text_b_level: JLPT.analyze(text_b).recommended_level }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def extract_shared_kanji(text_a, text_b)
|
|
36
|
+
(Kanji.extract(text_a).to_set & Kanji.extract(text_b).to_set).to_a
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def empty_comparison
|
|
40
|
+
{ overlap_ratio: 0.0, shared_vocab: [], shared_kanji: [], text_a_level: :n5, text_b_level: :n5 }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JLPT
|
|
4
|
+
# Verb and Adjective Inflection & Conjugation Analyzer.
|
|
5
|
+
#
|
|
6
|
+
# Identifies grammatical conjugations including te-form, causative, passive,
|
|
7
|
+
# potential, volitional, conditional, and past tense.
|
|
8
|
+
#
|
|
9
|
+
module Conjugation
|
|
10
|
+
class << self
|
|
11
|
+
# Analyze inflection type and form tags of a verb or adjective
|
|
12
|
+
#
|
|
13
|
+
# @param word [String] conjugated Japanese verb or adjective
|
|
14
|
+
# @return [Hash] conjugation details hash
|
|
15
|
+
def analyze(word)
|
|
16
|
+
return empty_result if word.nil? || word.to_s.strip.empty?
|
|
17
|
+
|
|
18
|
+
str = word.to_s.strip
|
|
19
|
+
forms = detect_voice_forms(str) + detect_tense_and_mood_forms(str)
|
|
20
|
+
{ word: str, type: detect_type(str), forms: forms.uniq }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def detect_voice_forms(str)
|
|
26
|
+
if str.end_with?('せられた', 'せられる', 'させられた', 'させられる')
|
|
27
|
+
[:causative_passive]
|
|
28
|
+
elsif str.end_with?('させる', 'させた', 'させ')
|
|
29
|
+
[:causative]
|
|
30
|
+
elsif str.end_with?('られる', 'られた', 'れる', 'れた')
|
|
31
|
+
[:passive]
|
|
32
|
+
else
|
|
33
|
+
[]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def detect_tense_and_mood_forms(str)
|
|
38
|
+
forms = []
|
|
39
|
+
forms << :te_form if str.end_with?('て', 'で')
|
|
40
|
+
forms << :past if str.end_with?('た', 'だ', 'ました', 'でした')
|
|
41
|
+
forms << :volitional if str.end_with?('よう', 'おう', 'ましょう')
|
|
42
|
+
forms << :conditional if str.end_with?('たら', 'ば', 'なら')
|
|
43
|
+
forms
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def detect_type(str)
|
|
47
|
+
return :suru_verb if str.end_with?('する', 'した', 'して', 'します')
|
|
48
|
+
return :kuru_verb if str.end_with?('くる', 'きた', 'きて', 'きます')
|
|
49
|
+
return :i_adjective if str.end_with?('い', 'かった', 'くて')
|
|
50
|
+
return :na_adjective if str.end_with?('だ', 'です', 'でした')
|
|
51
|
+
|
|
52
|
+
:verb
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def empty_result
|
|
56
|
+
{ word: '', type: :unknown, forms: [] }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|