Spaces:
Running
Running
Commit
ยท
9a61c89
1
Parent(s):
f6a4c89
refactor app
Browse files- app.py +200 -226
- {info โ data}/hiragana-dakuten.md +0 -0
- {info โ data}/hiragana-gojuon.md +0 -0
- {info โ data}/hiragana-yoon.md +0 -0
- data/kana-spell.json +334 -0
- {info โ data}/katakana-dakuten.md +0 -0
- {info โ data}/katakana-gojuon.md +0 -0
- {info โ data}/katakana-yoon.md +0 -0
app.py
CHANGED
@@ -1,158 +1,216 @@
|
|
|
|
1 |
import random
|
2 |
-
from
|
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 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
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 |
-
|
29 |
-
|
30 |
-
|
|
|
|
|
|
|
31 |
|
32 |
-
return quiz_list.pop(), quiz_list
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
38 |
|
39 |
-
|
40 |
-
curr_kata_a = deepcopy(kata) + deepcopy(kata_ext_a)
|
41 |
|
42 |
-
|
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 |
-
|
49 |
-
|
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 |
-
|
164 |
-
|
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
|