penut85420 commited on
Commit
9a61c89
ยท
1 Parent(s): f6a4c89

refactor app

Browse files
app.py CHANGED
@@ -1,158 +1,216 @@
 
1
  import random
2
- from copy import deepcopy
3
 
 
4
  import gradio as gr
5
- from romkan import to_hiragana, to_katakana, to_roma
6
 
7
- a_base = ["a", "k", "s", "t", "n", "h", "m", "y", "r", "w", "nn"]
8
- b_base = ["a", "i", "u", "e", "o"]
9
- ext = ["g", "z", "d", "b", "p", "ya", "yo", "yu"]
10
 
11
- invalid = ["yi", "ye", "wi", "wu", "we"]
12
- spec_map = {
13
- "cya": "cha",
14
- "cyu": "chu",
15
- "cyo": "cho",
16
- "ji": "di",
17
- "zu": "du",
18
- "ja": "dya",
19
- "jo": "dyo",
20
- "ju": "dyu",
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
 
 
23
 
24
- def next_question(hira, hira_ext, kata, kata_ext, quiz_list: list):
25
- if not quiz_list:
26
- quiz_list = init_question_list(hira, hira_ext, kata, kata_ext)
27
 
28
- if not quiz_list:
29
- gr.Info("่ซ‹้ธๆ“‡่‡ณๅฐ‘ไธ€้ …ๆธฌ้ฉ—๏ผ")
30
- return None, quiz_list
 
 
 
31
 
32
- return quiz_list.pop(), quiz_list
33
 
 
 
 
 
 
 
34
 
35
- def init_question_list(hira, hira_ext, kata, kata_ext):
36
- hira_ext_a, hira_ext_b = split_ext(hira_ext)
37
- kata_ext_a, kata_ext_b = split_ext(kata_ext)
 
38
 
39
- curr_hira_a = deepcopy(hira) + deepcopy(hira_ext_a)
40
- curr_kata_a = deepcopy(kata) + deepcopy(kata_ext_a)
41
 
42
- curr_hira_b = deepcopy(b_base) + deepcopy(hira_ext_b)
43
- curr_kata_b = deepcopy(b_base) + deepcopy(kata_ext_b)
44
 
45
- hira_list = [to_hira(a, b) for a in curr_hira_a for b in curr_hira_b if is_valid(a, b)]
46
- kata_list = [to_kata(a, b) for a in curr_kata_a for b in curr_kata_b if is_valid(a, b)]
47
 
48
- quiz_list = hira_list + kata_list
49
- quiz_list = list(set(quiz_list))
50
- random.shuffle(quiz_list)
51
-
52
- return quiz_list
53
-
54
-
55
- def split_ext(ext: list):
56
- ext_a = [a for a in ext if a[0] != "y"]
57
- ext_b = [b for b in ext if b[0] == "y"]
58
- return ext_a, ext_b
59
-
60
-
61
- def is_valid(aa: str, bb: str):
62
- if f"{aa}{bb}" in invalid:
63
- return False
64
-
65
- if bb[0] == "y":
66
- if aa[0] == "y":
67
- return False
68
- if aa == "w":
69
- return False
70
- if aa == "":
71
- return False
72
-
73
- if aa == "a" and bb[0] == "y":
74
- return False
75
-
76
- return True
77
-
78
-
79
- def to_hira(a, b):
80
- return to_hiragana(combine(a, b))
81
-
82
-
83
- def to_kata(a, b):
84
- return to_katakana(combine(a, b))
85
-
86
-
87
- def combine(a, b):
88
- if a == "nn":
89
- return "n"
90
- if a == "a":
91
- a = ""
92
- return f"{a}{b}"
93
-
94
-
95
- def check(kana: str, roma: str, correct, total, record, quiz_list):
96
- roma = roma.lower()
97
-
98
- kana_roma = to_roma(kana)
99
- roma_hira = to_hiragana(roma)
100
- roma_kata = to_katakana(roma)
101
- spec_roma = spec_map.get(roma, roma)
102
- spec_hira = to_hiragana(spec_roma)
103
- spec_kata = to_katakana(spec_roma)
104
-
105
- c0 = kana_roma == roma
106
- c1 = roma_hira == kana
107
- c2 = roma_kata == kana
108
- c3 = spec_hira == kana
109
- c4 = spec_kata == kana
110
-
111
- ans_correct = any((c0, c1, c2, c3, c4))
112
- correct += ans_correct
113
- total += 1
114
-
115
- info = "ๆญฃ็ขบ" if ans_correct else f"้Œฏ่ชค - {kana} ({kana_roma})"
116
- msg = f"{correct}/{total} - " + info
117
-
118
- if not ans_correct:
119
- record = f"{record}{info}\n"
120
-
121
- tab_idx = gr.Tabs(selected=1)
122
- if not quiz_list:
123
- record = f"{record}ๆญค่ผชๅพ—ๅˆ† - {correct}/{total}\n\n"
124
- correct, total = 0, 0
125
- tab_idx = gr.Tabs(selected=2)
126
-
127
- return correct, total, msg, None, record, tab_idx
128
-
129
-
130
- def reset_score():
131
- return 0, 0, "0/0", None, None, gr.Tabs()
132
-
133
-
134
- def select_all():
135
- return a_base, ext, a_base, ext
136
-
137
-
138
- def deselect_all():
139
- return [], [], [], []
140
-
141
-
142
- def select_all_hira():
143
- return a_base, ext, [], []
144
-
145
-
146
- def select_all_kata():
147
- return [], [], a_base, ext
148
-
149
-
150
- def select_init():
151
- return a_base[:5], [], [], []
152
-
153
-
154
- def back_to_setting():
155
- return gr.Tabs(selected=0)
156
 
157
 
158
  def read_text(path):
@@ -160,89 +218,5 @@ def read_text(path):
160
  return fp.read()
161
 
162
 
163
- font = gr.themes.GoogleFont("Noto Sans Mono")
164
- theme = gr.themes.Soft(font=font, text_size=gr.themes.sizes.text_lg)
165
-
166
- with gr.Blocks(theme=theme, title="ๅ‡ๅๅฐๆธฌ้ฉ—") as app:
167
- correct = gr.State(0)
168
- total = gr.State(0)
169
- quiz_list = gr.State(init_question_list(*select_init()))
170
-
171
- with gr.Row():
172
- with gr.Tabs() as tabs:
173
- with gr.Tab("่จญๅฎš", id=0):
174
- with gr.Tab("ๅนณๅ‡ๅ"):
175
- setting_hira = gr.CheckboxGroup(a_base, value=a_base[:5], label="ๅŸบๆœฌ")
176
- setting_hira_ext = gr.CheckboxGroup(ext, label="ๆฟ้Ÿณใ€ๅŠๆฟ้Ÿณใ€ๆ‹—้Ÿณ")
177
-
178
- with gr.Tab("็‰‡ๅ‡ๅ"):
179
- setting_kata = gr.CheckboxGroup(a_base, label="ๅŸบๆœฌ")
180
- setting_kata_ext = gr.CheckboxGroup(ext, label="ๆฟ้Ÿณใ€ๅŠๆฟ้Ÿณใ€ๆ‹—้Ÿณ")
181
-
182
- with gr.Row():
183
- select_all_hira_btn = gr.Button("ๅนณๅ‡ๅๅ…จ้ธ")
184
- select_all_kata_btn = gr.Button("็‰‡ๅ‡ๅๅ…จ้ธ")
185
- with gr.Row():
186
- select_all_btn = gr.Button("ๅ…จ้ธ")
187
- deselect_all_btn = gr.Button("ๅ…จไธ้ธ")
188
- with gr.Row():
189
- apply_btn = gr.Button("้–‹ๅง‹ๆธฌ้ฉ—")
190
-
191
- with gr.Tab("ๆธฌ้ฉ—", id=1):
192
- with gr.Row():
193
- desc = "ๅฎŒๆˆ่จญๅฎšๅพŒๆŒ‰ไธ‹ใ€Œ้–‹ๅง‹ๆธฌ้ฉ—ใ€"
194
- question = gr.Textbox(placeholder=desc, label="้กŒ็›ฎ", interactive=False)
195
- score = gr.Textbox("0/0", label="ๅˆ†ๆ•ธ")
196
- answer = gr.Textbox(label="ไฝœ็ญ”")
197
-
198
- with gr.Tab("็ด€้Œ„", id=2):
199
- record = gr.TextArea(show_label=False)
200
- with gr.Row():
201
- again_btn = gr.Button("ๅ†ๆฌกๆธฌ้ฉ—")
202
- back_to_setting_btn = gr.Button("ๅ›žๅˆฐ่จญๅฎš")
203
-
204
- with gr.Tab("ๅฐ็…ง่กจ"):
205
- with gr.Tab("ๅนณๅ‡ๅ"):
206
- with gr.Tab("ๅŸบๆœฌ"):
207
- gr.Markdown(read_text("info/hiragana-gojuon.md"))
208
- with gr.Tab("ๆฟ้Ÿณใ€ๅŠๆฟ้Ÿณ"):
209
- gr.Markdown(read_text("info/hiragana-dakuten.md"))
210
- with gr.Tab("ๆ‹—้Ÿณ"):
211
- gr.Markdown(read_text("info/hiragana-yoon.md"))
212
- with gr.Tab("็‰‡ๅ‡ๅ"):
213
- with gr.Tab("ๅŸบๆœฌ"):
214
- gr.Markdown(read_text("info/katakana-gojuon.md"))
215
- with gr.Tab("ๆฟ้Ÿณใ€ๅŠๆฟ้Ÿณ"):
216
- gr.Markdown(read_text("info/katakana-dakuten.md"))
217
- with gr.Tab("ๆ‹—้Ÿณ"):
218
- gr.Markdown(read_text("info/katakana-yoon.md"))
219
-
220
- def init_question(set_hira, set_hira_ext, set_kata, set_kata_ext):
221
- quiz_list = init_question_list(set_hira, set_hira_ext, set_kata, set_kata_ext)
222
- select = gr.Tabs(selected=1) if quiz_list else gr.Tabs(selected=0)
223
- return quiz_list, select
224
-
225
- chk_inn = [question, answer, correct, total, record, quiz_list]
226
- chk_out = [correct, total, score, answer, record, tabs]
227
- chk_arg = dict(fn=check, inputs=chk_inn, outputs=chk_out, show_progress="hidden")
228
-
229
- nq_inn = [setting_hira, setting_hira_ext, setting_kata, setting_kata_ext, quiz_list]
230
- nq_out = [question, quiz_list]
231
- nq_arg = dict(fn=next_question, inputs=nq_inn, outputs=nq_out, show_progress="hidden")
232
-
233
- ini_inn = [setting_hira, setting_hira_ext, setting_kata, setting_kata_ext]
234
- ini_out = [quiz_list, tabs]
235
- ini_arg = dict(fn=init_question, inputs=ini_inn, outputs=ini_out, show_progress="hidden")
236
-
237
- reset_arg = dict(fn=reset_score, outputs=chk_out, show_progress="hidden")
238
-
239
- answer.submit(**chk_arg).then(**nq_arg)
240
- apply_btn.click(**ini_arg).then(**reset_arg).then(**nq_arg)
241
- again_btn.click(**ini_arg).then(**reset_arg).then(**nq_arg)
242
- select_all_hira_btn.click(select_all_hira, outputs=ini_inn, show_progress="hidden")
243
- select_all_kata_btn.click(select_all_kata, outputs=ini_inn, show_progress="hidden")
244
- select_all_btn.click(select_all, outputs=ini_inn, show_progress="hidden")
245
- deselect_all_btn.click(deselect_all, outputs=ini_inn, show_progress="hidden")
246
- back_to_setting_btn.click(back_to_setting, None, tabs, show_progress="hidden")
247
-
248
- app.launch(favicon_path="icon.png")
 
1
+ import json
2
  import random
3
+ from dataclasses import dataclass
4
 
5
+ import fire
6
  import gradio as gr
 
7
 
 
 
 
8
 
9
+ class KanaQuizApp:
10
+ def __init__(self, data_path="data/kana-spell.json", font="Noto Sans JP"):
11
+ self.data = KanaSpell.load(data_path)
12
+ self.font = font
13
+ self.init_app()
14
+ self.launch()
15
+
16
+ def init_app(self):
17
+ font = gr.themes.GoogleFont(self.font)
18
+ text_size = gr.themes.sizes.text_lg
19
+ theme = gr.themes.Ocean(font=font, text_size=text_size)
20
+ with gr.Blocks(theme=theme) as self.app:
21
+ self.init_state()
22
+ self.init_layout()
23
+ self.register_events()
24
+
25
+ def init_state(self):
26
+ self.st_queue = gr.State(None)
27
+
28
+ def init_layout(self):
29
+ with gr.Tabs(selected=0) as self.tabs:
30
+ with gr.Tab(label="่จญๅฎš โš™๏ธ", id=0):
31
+ self.init_setting_tab()
32
+
33
+ with gr.Tab(label="ๆธฌ้ฉ— ๐Ÿ“", id=1):
34
+ self.init_quiz_tab()
35
+
36
+ with gr.Tab(label="็ด€้Œ„ ๐Ÿ“œ", id=2):
37
+ self.init_record_tab()
38
+
39
+ with gr.Tab(label="ๅฐ็…ง่กจ ๐Ÿ’ซ", id=3):
40
+ self.init_table()
41
+
42
+ self.txt_debug = gr.TextArea(label="Debug", visible=False)
43
+
44
+ def init_setting_tab(self):
45
+ with gr.Group():
46
+ self.chk_kana = gr.CheckboxGroup(["ๅนณๅ‡ๅ", "็‰‡ๅ‡ๅ"], value=["ๅนณๅ‡ๅ"], label="ๅ‡ๅ")
47
+ self.chk_seion = gr.CheckboxGroup(self.data.category.seion, value=["a"], label="ๆธ…้Ÿณ")
48
+ with gr.Row():
49
+ self.chk_dakuon = gr.CheckboxGroup(self.data.category.dakuon, label="ๆฟ้Ÿณ")
50
+ self.chk_handakuon = gr.CheckboxGroup(self.data.category.handakuon, label="ๅŠๆฟ้Ÿณ")
51
+ self.chk_youon = gr.CheckboxGroup(self.data.category.youon, label="ๆ‹—้Ÿณ")
52
+
53
+ with gr.Row():
54
+ self.btn_select_all = gr.Button("ๅ…จ้ธ")
55
+ self.btn_select_none = gr.Button("ๅ…จไธ้ธ")
56
+ self.btn_start = gr.Button("้–‹ๅง‹ๆธฌ้ฉ— ๐Ÿš€")
57
+
58
+ def init_quiz_tab(self):
59
+ with gr.Group():
60
+ with gr.Row():
61
+ self.txt_test = gr.Textbox(label="้กŒ็›ฎ ๐Ÿ‘€", interactive=False)
62
+ self.txt_info = gr.Textbox(label="็‹€ๆ…‹ ๐Ÿ“Š", interactive=False)
63
+ with gr.Row():
64
+ with gr.Column():
65
+ self.txt_input = gr.Textbox(label="ไฝœ็ญ” โœ๏ธ", submit_btn=True)
66
+ with gr.Column():
67
+ with gr.Row():
68
+ self.n_correct = gr.Number(label="็ญ”ๅฐ้กŒๆ•ธ โœ…", value=0, interactive=False)
69
+ self.n_total = gr.Number(label="็ธฝ็ญ”้กŒๆ•ธ ๐Ÿงฎ", value=0, interactive=False)
70
+
71
+ def init_record_tab(self):
72
+ self.txt_record = gr.TextArea(show_label=False, interactive=False)
73
+
74
+ with gr.Row():
75
+ self.btn_back_to_settings = gr.Button("ๅ›žๅˆฐ่จญๅฎš โš™๏ธ")
76
+ self.btn_again = gr.Button("ๅ†ๆฌกๆธฌ้ฉ— ๐Ÿ”„")
77
+
78
+ def init_table(self):
79
+ with gr.Tab("ๅนณๅ‡ๅ"):
80
+ with gr.Tab("ๅŸบๆœฌ"):
81
+ gr.Markdown(read_text("data/hiragana-gojuon.md"))
82
+ with gr.Tab("ๆฟ้Ÿณใ€ๅŠๆฟ้Ÿณ"):
83
+ gr.Markdown(read_text("data/hiragana-dakuten.md"))
84
+ with gr.Tab("ๆ‹—้Ÿณ"):
85
+ gr.Markdown(read_text("data/hiragana-yoon.md"))
86
+ with gr.Tab("็‰‡ๅ‡ๅ"):
87
+ with gr.Tab("ๅŸบๆœฌ"):
88
+ gr.Markdown(read_text("data/katakana-gojuon.md"))
89
+ with gr.Tab("ๆฟ้Ÿณใ€ๅŠๆฟ้Ÿณ"):
90
+ gr.Markdown(read_text("data/katakana-dakuten.md"))
91
+ with gr.Tab("ๆ‹—้Ÿณ"):
92
+ gr.Markdown(read_text("data/katakana-yoon.md"))
93
+
94
+ def register_events(self):
95
+ start_test_inputs = [self.chk_kana, self.chk_seion, self.chk_dakuon]
96
+ start_test_inputs += [self.chk_handakuon, self.chk_youon]
97
+ start_test_outputs = [self.txt_test, self.st_queue, self.n_correct, self.n_total]
98
+ start_test_outputs += [self.txt_record, self.tabs]
99
+ start_test_args = gr_args(self.start_test, start_test_inputs, start_test_outputs)
100
+
101
+ check_answer_inputs = [self.txt_test, self.txt_input]
102
+ check_answer_inputs += [self.n_correct, self.n_total, self.txt_record]
103
+ check_answer_outputs = [self.txt_input, self.n_correct, self.n_total]
104
+ check_answer_outputs += [self.txt_info, self.txt_record]
105
+ check_answer_args = gr_args(self.check_answer, check_answer_inputs, check_answer_outputs)
106
+
107
+ next_char_inputs = [self.st_queue, self.n_correct, self.n_total, self.txt_record]
108
+ next_char_outputs = [self.txt_test, self.st_queue, self.txt_record, self.tabs]
109
+ next_char_args = gr_args(self.next_char, next_char_inputs, next_char_outputs)
110
+
111
+ select_outputs = [self.chk_kana, self.chk_seion, self.chk_dakuon]
112
+ select_outputs += [self.chk_handakuon, self.chk_youon]
113
+ select_all_args = gr_args(self.select_all, outputs=select_outputs)
114
+ select_none_args = gr_args(self.select_none, outputs=select_outputs)
115
+
116
+ back_to_settings_args = gr_args(self.back_to_settings, outputs=[self.tabs])
117
+
118
+ self.btn_start.click(**start_test_args)
119
+ self.txt_input.submit(**check_answer_args).then(**next_char_args)
120
+ self.btn_select_all.click(**select_all_args)
121
+ self.btn_select_none.click(**select_none_args)
122
+ self.btn_again.click(**start_test_args)
123
+ self.btn_back_to_settings.click(**back_to_settings_args)
124
+
125
+ def start_test(self, kana, seion, dakuon, handakuon, yoon):
126
+ category = [*seion, *dakuon, *handakuon, *yoon]
127
+
128
+ use_hiragana = "ๅนณๅ‡ๅ" in kana
129
+ use_katakana = "็‰‡ๅ‡ๅ" in kana
130
+
131
+ char_list = list()
132
+ char_list += [ch for k in category for ch in self.data.hiragana[k]] if use_hiragana else []
133
+ char_list += [ch for k in category for ch in self.data.katakana[k]] if use_katakana else []
134
+
135
+ if not char_list:
136
+ raise gr.Error("่ซ‹่‡ณๅฐ‘้ธๆ“‡ไธ€ๅ€‹้กžๅˆฅ")
137
+
138
+ random.shuffle(char_list)
139
+ char = char_list.pop(0)
140
+
141
+ return char, char_list, 0, 0, None, gr.Tabs(selected=1)
142
+
143
+ def check_answer(self, txt_test, txt_input, n_correct, n_total, txt_record):
144
+ txt_input = str.lower(txt_input).strip()
145
+
146
+ if txt_input in self.data.spell[txt_test]:
147
+ n_correct += 1
148
+ message = "ๆญฃ็ขบ"
149
+ else:
150
+ answer = " / ".join(self.data.spell[txt_test])
151
+ message = f"้Œฏ่ชค๏ผŒ็ญ”ๆกˆ็‚บ {answer}"
152
+ txt_record += f"้กŒ็›ฎ๏ผš{txt_test}ใ€ๆญฃ่งฃ๏ผš{answer}ใ€่ผธๅ…ฅ๏ผš{txt_input}\n"
153
+
154
+ n_total += 1
155
+
156
+ return None, n_correct, n_total, message, txt_record
157
+
158
+ def next_char(self, st_queue, n_correct, n_total, txt_record):
159
+ if not st_queue:
160
+ gr.Info("ๆธฌ้ฉ—็ตๆŸ๏ผ")
161
+ accuracy = n_correct / n_total
162
+ txt_record += f"ๆญฃ็ขบ็އ {accuracy:.2%} ({n_correct}/{n_total})"
163
+ return None, None, txt_record, gr.Tabs(selected=2)
164
+
165
+ char = list.pop(st_queue, 0)
166
+ return char, st_queue, txt_record, gr.Tabs(selected=1)
167
+
168
+ def select_all(self):
169
+ return (
170
+ ["ๅนณๅ‡ๅ", "็‰‡ๅ‡ๅ"],
171
+ self.data.category.seion,
172
+ self.data.category.dakuon,
173
+ self.data.category.handakuon,
174
+ self.data.category.youon,
175
+ )
176
+
177
+ def select_none(self):
178
+ return [], [], [], [], []
179
+
180
+ def back_to_settings(self):
181
+ return gr.Tabs(selected=0)
182
 
183
+ def launch(self):
184
+ self.app.launch()
185
 
 
 
 
186
 
187
+ @dataclass
188
+ class KanaCategory:
189
+ seion: list[str]
190
+ dakuon: list[str]
191
+ handakuon: list[str]
192
+ youon: list[str]
193
 
 
194
 
195
+ @dataclass
196
+ class KanaSpell:
197
+ category: KanaCategory
198
+ hiragana: dict[str, list[str]]
199
+ katakana: dict[str, list[str]]
200
+ spell: dict[str, list[str]]
201
 
202
+ @classmethod
203
+ def load(cls, path: str) -> "KanaSpell":
204
+ with open(path, "rt", encoding="UTF-8") as fp:
205
+ data = json.load(fp)
206
 
207
+ data["category"] = KanaCategory(**data["category"])
 
208
 
209
+ return cls(**data)
 
210
 
 
 
211
 
212
+ def gr_args(fn, inputs=None, outputs=None, show_progress="hidden", **kwargs):
213
+ return dict(fn=fn, inputs=inputs, outputs=outputs, show_progress=show_progress, **kwargs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
 
216
  def read_text(path):
 
218
  return fp.read()
219
 
220
 
221
+ if __name__ == "__main__":
222
+ fire.Fire(KanaQuizApp)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{info โ†’ data}/hiragana-dakuten.md RENAMED
File without changes
{info โ†’ data}/hiragana-gojuon.md RENAMED
File without changes
{info โ†’ data}/hiragana-yoon.md RENAMED
File without changes
data/kana-spell.json ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "category": {
3
+ "seion": ["a", "ka", "sa", "ta", "na", "ha", "ma", "ya", "ra", "wa", "n"],
4
+ "dakuon": ["ga", "za", "da", "ba"],
5
+ "handakuon": ["pa"],
6
+ "youon": [
7
+ "kya",
8
+ "sha",
9
+ "cha",
10
+ "hya",
11
+ "gya",
12
+ "ja",
13
+ "dya",
14
+ "nya",
15
+ "bya",
16
+ "pya"
17
+ ]
18
+ },
19
+ "hiragana": {
20
+ "a": ["ใ‚", "ใ„", "ใ†", "ใˆ", "ใŠ"],
21
+ "ka": ["ใ‹", "ใ", "ใ", "ใ‘", "ใ“"],
22
+ "sa": ["ใ•", "ใ—", "ใ™", "ใ›", "ใ"],
23
+ "ta": ["ใŸ", "ใก", "ใค", "ใฆ", "ใจ"],
24
+ "na": ["ใช", "ใซ", "ใฌ", "ใญ", "ใฎ"],
25
+
26
+ "ha": ["ใฏ", "ใฒ", "ใต", "ใธ", "ใป"],
27
+ "ma": ["ใพ", "ใฟ", "ใ‚€", "ใ‚", "ใ‚‚"],
28
+ "ya": ["ใ‚„", "ใ‚†", "ใ‚ˆ"],
29
+ "ra": ["ใ‚‰", "ใ‚Š", "ใ‚‹", "ใ‚Œ", "ใ‚"],
30
+ "wa": ["ใ‚", "ใ‚’"],
31
+ "n": ["ใ‚“"],
32
+
33
+ "ga": ["ใŒ", "ใŽ", "ใ", "ใ’", "ใ”"],
34
+ "za": ["ใ–", "ใ˜", "ใš", "ใœ", "ใž"],
35
+ "da": ["ใ ", "ใข", "ใฅ", "ใง", "ใฉ"],
36
+ "ba": ["ใฐ", "ใณ", "ใถ", "ใน", "ใผ"],
37
+ "pa": ["ใฑ", "ใด", "ใท", "ใบ", "ใฝ"],
38
+
39
+ "kya": ["ใใ‚ƒ", "ใใ‚…", "ใใ‚‡"],
40
+ "sha": ["ใ—ใ‚ƒ", "ใ—ใ‚…", "ใ—ใ‚‡"],
41
+ "cha": ["ใกใ‚ƒ", "ใกใ‚…", "ใกใ‚‡"],
42
+ "hya": ["ใฒใ‚ƒ", "ใฒใ‚…", "ใฒใ‚‡"],
43
+ "gya": ["ใŽใ‚ƒ", "ใŽใ‚…", "ใŽใ‚‡"],
44
+ "ja": ["ใ˜ใ‚ƒ", "ใ˜ใ‚…", "ใ˜ใ‚‡"],
45
+ "dya": ["ใขใ‚ƒ", "ใขใ‚…", "ใขใ‚‡"],
46
+ "nya": ["ใซใ‚ƒ", "ใซใ‚…", "ใซใ‚‡"],
47
+ "bya": ["ใณใ‚ƒ", "ใณใ‚…", "ใณใ‚‡"],
48
+ "pya": ["ใดใ‚ƒ", "ใดใ‚…", "ใดใ‚‡"]
49
+ },
50
+ "katakana": {
51
+ "a": ["ใ‚ข", "ใ‚ค", "ใ‚ฆ", "ใ‚จ", "ใ‚ช"],
52
+ "ka": ["ใ‚ซ", "ใ‚ญ", "ใ‚ฏ", "ใ‚ฑ", "ใ‚ณ"],
53
+ "sa": ["ใ‚ต", "ใ‚ท", "ใ‚น", "ใ‚ป", "ใ‚ฝ"],
54
+ "ta": ["ใ‚ฟ", "ใƒ", "ใƒ„", "ใƒ†", "ใƒˆ"],
55
+ "na": ["ใƒŠ", "ใƒ‹", "ใƒŒ", "ใƒ", "ใƒŽ"],
56
+
57
+ "ha": ["ใƒ", "ใƒ’", "ใƒ•", "ใƒ˜", "ใƒ›"],
58
+ "ma": ["ใƒž", "ใƒŸ", "ใƒ ", "ใƒก", "ใƒข"],
59
+ "ya": ["ใƒค", "ใƒฆ", "ใƒจ"],
60
+ "ra": ["ใƒฉ", "ใƒช", "ใƒซ", "ใƒฌ", "ใƒญ"],
61
+ "wa": ["ใƒฏ", "ใƒฒ"],
62
+ "n": ["ใƒณ"],
63
+
64
+ "ga": ["ใ‚ฌ", "ใ‚ฎ", "ใ‚ฐ", "ใ‚ฒ", "ใ‚ด"],
65
+ "za": ["ใ‚ถ", "ใ‚ธ", "ใ‚บ", "ใ‚ผ", "ใ‚พ"],
66
+ "da": ["ใƒ€", "ใƒ‚", "ใƒ…", "ใƒ‡", "ใƒ‰"],
67
+ "ba": ["ใƒ", "ใƒ“", "ใƒ–", "ใƒ™", "ใƒœ"],
68
+ "pa": ["ใƒ‘", "ใƒ”", "ใƒ—", "ใƒš", "ใƒ"],
69
+
70
+ "kya": ["ใ‚ญใƒฃ", "ใ‚ญใƒฅ", "ใ‚ญใƒง"],
71
+ "gya": ["ใ‚ฎใƒฃ", "ใ‚ฎใƒฅ", "ใ‚ฎใƒง"],
72
+ "sha": ["ใ‚ทใƒฃ", "ใ‚ทใƒฅ", "ใ‚ทใƒง"],
73
+ "ja": ["ใ‚ธใƒฃ", "ใ‚ธใƒฅ", "ใ‚ธใƒง"],
74
+ "cha": ["ใƒใƒฃ", "ใƒใƒฅ", "ใƒใƒง"],
75
+ "dya": ["ใƒ‚ใƒฃ", "ใƒ‚ใƒฅ", "ใƒ‚ใƒง"],
76
+ "nya": ["ใƒ‹ใƒฃ", "ใƒ‹ใƒฅ", "ใƒ‹ใƒง"],
77
+ "hya": ["ใƒ’ใƒฃ", "ใƒ’ใƒฅ", "ใƒ’ใƒง"],
78
+ "bya": ["ใƒ“ใƒฃ", "ใƒ“ใƒฅ", "ใƒ“ใƒง"],
79
+ "pya": ["ใƒ”ใƒฃ", "ใƒ”ใƒฅ", "ใƒ”ใƒง"]
80
+ },
81
+ "spell": {
82
+ "ใ‚": ["a"],
83
+ "ใ„": ["i"],
84
+ "ใ†": ["u"],
85
+ "ใˆ": ["e"],
86
+ "ใŠ": ["o"],
87
+
88
+ "ใ‹": ["ka"],
89
+ "ใ": ["ki"],
90
+ "ใ": ["ku"],
91
+ "ใ‘": ["ke"],
92
+ "ใ“": ["ko"],
93
+
94
+ "ใŒ": ["ga"],
95
+ "ใŽ": ["gi"],
96
+ "ใ": ["gu"],
97
+ "ใ’": ["ge"],
98
+ "ใ”": ["go"],
99
+
100
+ "ใ•": ["sa"],
101
+ "ใ—": ["shi"],
102
+ "ใ™": ["su"],
103
+ "ใ›": ["se"],
104
+ "ใ": ["so"],
105
+
106
+ "ใ–": ["za"],
107
+ "ใ˜": ["ji"],
108
+ "ใš": ["zu"],
109
+ "ใœ": ["ze"],
110
+ "ใž": ["zo"],
111
+
112
+ "ใŸ": ["ta"],
113
+ "ใก": ["chi"],
114
+ "ใค": ["tsu"],
115
+ "ใฆ": ["te"],
116
+ "ใจ": ["to"],
117
+
118
+ "ใ ": ["da"],
119
+ "ใข": ["di", "ji"],
120
+ "ใฅ": ["du", "zu"],
121
+ "ใง": ["de"],
122
+ "ใฉ": ["do"],
123
+
124
+ "ใช": ["na"],
125
+ "ใซ": ["ni"],
126
+ "ใฌ": ["nu"],
127
+ "ใญ": ["ne"],
128
+ "ใฎ": ["no"],
129
+
130
+ "ใฏ": ["ha"],
131
+ "ใฒ": ["hi"],
132
+ "ใต": ["fu"],
133
+ "ใธ": ["he"],
134
+ "ใป": ["ho"],
135
+
136
+ "ใฐ": ["ba"],
137
+ "ใณ": ["bi"],
138
+ "ใถ": ["bu"],
139
+ "ใน": ["be"],
140
+ "ใผ": ["bo"],
141
+
142
+ "ใฑ": ["pa"],
143
+ "ใด": ["pi"],
144
+ "ใท": ["pu"],
145
+ "ใบ": ["pe"],
146
+ "ใฝ": ["po"],
147
+
148
+ "ใพ": ["ma"],
149
+ "ใฟ": ["mi"],
150
+ "ใ‚€": ["mu"],
151
+ "ใ‚": ["me"],
152
+ "ใ‚‚": ["mo"],
153
+
154
+ "ใ‚„": ["ya"],
155
+ "ใ‚†": ["yu"],
156
+ "ใ‚ˆ": ["yo"],
157
+
158
+ "ใ‚‰": ["ra"],
159
+ "ใ‚Š": ["ri"],
160
+ "ใ‚‹": ["ru"],
161
+ "ใ‚Œ": ["re"],
162
+ "ใ‚": ["ro"],
163
+
164
+ "ใ‚": ["wa"],
165
+ "ใ‚’": ["o", "wo"],
166
+ "ใ‚“": ["n"],
167
+
168
+ "ใใ‚ƒ": ["kya"],
169
+ "ใใ‚…": ["kyu"],
170
+ "ใใ‚‡": ["kyo"],
171
+
172
+ "ใŽใ‚ƒ": ["gya"],
173
+ "ใŽใ‚…": ["gyu"],
174
+ "ใŽใ‚‡": ["gyo"],
175
+
176
+ "ใ—ใ‚ƒ": ["sha", "sya"],
177
+ "ใ—ใ‚…": ["shu", "syu"],
178
+ "ใ—ใ‚‡": ["sho", "syo"],
179
+
180
+ "ใ˜ใ‚ƒ": ["ja", "jya"],
181
+ "ใ˜ใ‚…": ["ju", "jyu"],
182
+ "ใ˜ใ‚‡": ["jo", "jyo"],
183
+
184
+ "ใกใ‚ƒ": ["cha", "cya"],
185
+ "ใกใ‚…": ["chu", "cyu"],
186
+ "ใกใ‚‡": ["cho", "cyo"],
187
+
188
+ "ใขใ‚ƒ": ["dya", "ja"],
189
+ "ใขใ‚…": ["dyu", "ju"],
190
+ "ใขใ‚‡": ["dyo", "jo"],
191
+
192
+ "ใซใ‚ƒ": ["nya"],
193
+ "ใซใ‚…": ["nyu"],
194
+ "ใซใ‚‡": ["nyo"],
195
+
196
+ "ใฒใ‚ƒ": ["hya"],
197
+ "ใฒใ‚…": ["hyu"],
198
+ "ใฒใ‚‡": ["hyo"],
199
+
200
+ "ใณใ‚ƒ": ["bya"],
201
+ "ใณใ‚…": ["byu"],
202
+ "ใณใ‚‡": ["byo"],
203
+
204
+ "ใดใ‚ƒ": ["pya"],
205
+ "ใดใ‚…": ["pyu"],
206
+ "ใดใ‚‡": ["pyo"],
207
+
208
+ "ใ‚ข": ["a"],
209
+ "ใ‚ค": ["i"],
210
+ "ใ‚ฆ": ["u"],
211
+ "ใ‚จ": ["e"],
212
+ "ใ‚ช": ["o"],
213
+
214
+ "ใ‚ซ": ["ka"],
215
+ "ใ‚ญ": ["ki"],
216
+ "ใ‚ฏ": ["ku"],
217
+ "ใ‚ฑ": ["ke"],
218
+ "ใ‚ณ": ["ko"],
219
+
220
+ "ใ‚ฌ": ["ga"],
221
+ "ใ‚ฎ": ["gi"],
222
+ "ใ‚ฐ": ["gu"],
223
+ "ใ‚ฒ": ["ge"],
224
+ "ใ‚ด": ["go"],
225
+
226
+ "ใ‚ต": ["sa"],
227
+ "ใ‚ท": ["shi"],
228
+ "ใ‚น": ["su"],
229
+ "ใ‚ป": ["se"],
230
+ "ใ‚ฝ": ["so"],
231
+
232
+ "ใ‚ถ": ["za"],
233
+ "ใ‚ธ": ["ji"],
234
+ "ใ‚บ": ["zu"],
235
+ "ใ‚ผ": ["ze"],
236
+ "ใ‚พ": ["zo"],
237
+
238
+ "ใ‚ฟ": ["ta"],
239
+ "ใƒ": ["chi"],
240
+ "ใƒ„": ["tsu"],
241
+ "ใƒ†": ["te"],
242
+ "ใƒˆ": ["to"],
243
+
244
+ "ใƒ€": ["da"],
245
+ "ใƒ‚": ["di", "ji"],
246
+ "ใƒ…": ["du", "zu"],
247
+ "ใƒ‡": ["de"],
248
+ "ใƒ‰": ["do"],
249
+
250
+ "ใƒŠ": ["na"],
251
+ "ใƒ‹": ["ni"],
252
+ "ใƒŒ": ["nu"],
253
+ "ใƒ": ["ne"],
254
+ "ใƒŽ": ["no"],
255
+
256
+ "ใƒ": ["ha"],
257
+ "ใƒ’": ["hi"],
258
+ "ใƒ•": ["fu"],
259
+ "ใƒ˜": ["he"],
260
+ "ใƒ›": ["ho"],
261
+
262
+ "ใƒ": ["ba"],
263
+ "ใƒ“": ["bi"],
264
+ "ใƒ–": ["bu"],
265
+ "ใƒ™": ["be"],
266
+ "ใƒœ": ["bo"],
267
+
268
+ "ใƒ‘": ["pa"],
269
+ "ใƒ”": ["pi"],
270
+ "ใƒ—": ["pu"],
271
+ "ใƒš": ["pe"],
272
+ "ใƒ": ["po"],
273
+
274
+ "ใƒž": ["ma"],
275
+ "ใƒŸ": ["mi"],
276
+ "ใƒ ": ["mu"],
277
+ "ใƒก": ["me"],
278
+ "ใƒข": ["mo"],
279
+
280
+ "ใƒค": ["ya"],
281
+ "ใƒฆ": ["yu"],
282
+ "ใƒจ": ["yo"],
283
+
284
+ "ใƒฉ": ["ra"],
285
+ "ใƒช": ["ri"],
286
+ "ใƒซ": ["ru"],
287
+ "ใƒฌ": ["re"],
288
+ "ใƒญ": ["ro"],
289
+
290
+ "ใƒฏ": ["wa"],
291
+ "ใƒฒ": ["o", "wo"],
292
+ "ใƒณ": ["n"],
293
+
294
+ "ใ‚ญใƒฃ": ["kya"],
295
+ "ใ‚ญใƒฅ": ["kyu"],
296
+ "ใ‚ญใƒง": ["kyo"],
297
+
298
+ "ใ‚ฎใƒฃ": ["gya"],
299
+ "ใ‚ฎใƒฅ": ["gyu"],
300
+ "ใ‚ฎใƒง": ["gyo"],
301
+
302
+ "ใ‚ทใƒฃ": ["sha"],
303
+ "ใ‚ทใƒฅ": ["shu"],
304
+ "ใ‚ทใƒง": ["sho"],
305
+
306
+ "ใ‚ธใƒฃ": ["ja", "jya"],
307
+ "ใ‚ธใƒฅ": ["ju", "jyu"],
308
+ "ใ‚ธใƒง": ["jo", "jyo"],
309
+
310
+ "ใƒใƒฃ": ["cha", "cya"],
311
+ "ใƒใƒฅ": ["chu", "cyu"],
312
+ "ใƒใƒง": ["cho", "cyo"],
313
+
314
+ "ใƒ‚ใƒฃ": ["dya", "ja"],
315
+ "ใƒ‚ใƒฅ": ["dyu", "ju"],
316
+ "ใƒ‚ใƒง": ["dyo", "jo"],
317
+
318
+ "ใƒ‹ใƒฃ": ["nya"],
319
+ "ใƒ‹ใƒฅ": ["nyu"],
320
+ "ใƒ‹ใƒง": ["nyo"],
321
+
322
+ "ใƒ’ใƒฃ": ["hya"],
323
+ "ใƒ’ใƒฅ": ["hyu"],
324
+ "ใƒ’ใƒง": ["hyo"],
325
+
326
+ "ใƒ“ใƒฃ": ["bya"],
327
+ "ใƒ“ใƒฅ": ["byu"],
328
+ "ใƒ“ใƒง": ["byo"],
329
+
330
+ "ใƒ”ใƒฃ": ["pya"],
331
+ "ใƒ”ใƒฅ": ["pyu"],
332
+ "ใƒ”ใƒง": ["pyo"]
333
+ }
334
+ }
{info โ†’ data}/katakana-dakuten.md RENAMED
File without changes
{info โ†’ data}/katakana-gojuon.md RENAMED
File without changes
{info โ†’ data}/katakana-yoon.md RENAMED
File without changes