plugins: add Japanese Support plugin

This plugin provides support for Japanese deinflection during lookup as
well as making long-hold word selection actually select whole words
properly. With this plugin, word lookups in Japanese text in KOReader
become much easier, and no longer requires users to use special
dictionaries that have synonym-based deinflection rules defined (which
were always fairly annoying to use).

The basic idea and deinflection data for this plugin come from
Yomichan (which is also a GPL-3.0+ project), but everything was
implemented specifically for KOReader.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
pull/8364/head
Aleksa Sarai 3 years ago committed by Frans de Jonge
parent 7c5243667b
commit 3d4e54c7e6

@ -0,0 +1,26 @@
## Japanese Support Plugin for KOReader ##
This plugin is heavily based on Yomichan's design, and makes use of Yomichan's
deinflection data, but was written specifically for KOReader. There are two
major features implemented by this plugin:
1. Verb deinflection (aka deconjugation) support, based on Yomichan's very
elegant rule-matching suffix replacement system using Yomichan's data.
2. Text segmentation support without needing MeCab or any other binary helper,
by re-using the users' installed dictionaries to exhaustively try every
length of text and select the longest match which is present in the
dictionary. This is similar to how Yomichan does MeCab-less segmentation.
On paper this plugin should also be work with Chinese text if the user has
Chinese dictionaries installed, though that is not its primary intended
use-case.
The backbone of this plugin is the included `yomichan-deinflect.json`. This
file is copied verbatim from [Yomichan's `ext/data/deinflect.json`][yomichan-deinflect.json]
and can be updated when necessary by simply getting a newer copy.
Note that Yomichan and KOReader use the same license (GPL-3.0-or-later) so any
theoretical licensing problems are a non-issue.
[yomichan-deinflect.json]: https://github.com/FooSoft/yomichan/blob/21.9.30.0/ext/data/deinflect.json

@ -0,0 +1,11 @@
local _ = require("gettext")
return {
name = "japanese",
fullname = _("Japanese support"),
description = _([[
Japanese language support for KOReader, modeled after Yomichan.
This plugin extends KOReader's built-in dictionary and selection system to support Yomichan-style deinflection and text scanning, allowing for one-tap searches of inflected verbs and multi-character words and phrases. As such, this plugin removes the need for synonym-based deinflection rules for StarDict-converted Japanese dictionaries.
You must have at least one Japanese dictionary installed in order for this plugin to work smoothly with Japanese text.]]),
}

@ -0,0 +1,470 @@
--- Yomichan deinflector implementation in pure Lua.
-- This is very heavily modelled after Yomichan's deinflection code, with some
-- minor changes to make it slightly more performant in the more restricted
-- environment KOReader tends to run in.
--
-- @module koplugin.japanese.deinflector
-- @alias Deinflector
-- Copyright (C) 2021 Aleksa Sarai <cyphar@cyphar.com>
-- Licensed under the GPLv3 or later.
--
-- This deinflection logic is heavily modelled after Yomichan
-- <https://github.com/FooSoft/yomichan>, up to and including the deinflection
-- table.
local InfoMessage = require("ui/widget/infomessage")
local JSON = require("rapidjson")
local UIManager = require("ui/uimanager")
local Utf8Proc = require("ffi/utf8proc")
local bit = require("bit")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
local N_ = _.ngettext
local T = require("ffi/util").template
local Deinflector = {}
local RULE_TYPES = {
["v1"] = 0x01, -- Verb ichidan (so-called ru-verb)
["v5"] = 0x02, -- Verb godan (so-called u-verb)
["vs"] = 0x04, -- Verb suru
["vk"] = 0x08, -- Verb kuru
["vz"] = 0x0A, -- Verb zuru
["adj-i"] = 0x10, -- Adjectival verb (i-adjective)
["iru"] = 0x20, -- Intermediate -iru endings for progressive or perfect tense
}
local function toRuleTypes(...)
final = 0
for _, ruleType in ipairs({...}) do
if RULE_TYPES[ruleType] then
final = bit.bor(final, RULE_TYPES[ruleType])
end
end
return final
end
local function getSourceDir()
local callerSource = debug.getinfo(2, "S").source
if callerSource:find("^@") then
return callerSource:gsub("^@(.*)/[^/]*", "%1")
end
end
local function parsePluginJson(filename)
local jsonPath = getSourceDir().."/"..filename
local file, err = io.open(jsonPath, "r")
if file then
local contents = file:read("*all")
local ok, parsed = pcall(JSON.decode, contents)
if ok then
return parsed
else
logger.err("japanese.koplugin: failed to parse plugin json", filename)
end
else
logger.err("japanese.koplugin: failed to open plugin json", filename, err)
end
return {}
end
--- A single deinflection result.
-- @field term Deinflected form of the term (string).
-- @field rules Rules bitmask the term has applied (int).
-- @field reasons Array of reasons applied to reach the term ({string,...}).
-- @table DeinflectResult
local function makeDeinflectionResult(term, rules, reasons)
return { term = term, rules = rules, reasons = reasons }
end
--- Deinflect some text as-is (without trying any possible conversions between
-- types of kana or any other such modifications). You probably want to use
-- Deinflector:deinflect() because it is more thorough.
--
-- @see deinflect
-- @tparam string text Japanese text to deinflect verbatim.
-- @treturn {DeinflectResult,...} An array of possible deinflections (including the text given).
function Deinflector:deinflectVerbatim(text)
self:init() -- in case this is being called directly
local results = {makeDeinflectionResult(text, 0, {})}
local seen = {}
seen[text] = true
for _, current in ipairs(results) do
for reason, rules in pairs(self.rules) do
for _, rule in ipairs(rules) do
local rulesMatch = current.rules == 0 or bit.band(current.rules, rule.rulesIn) ~= 0
local endsWithKana = current.term:sub(-#rule.kanaIn) == rule.kanaIn
local longEnough = #current.term - #rule.kanaIn + #rule.kanaOut > 0
if rulesMatch and endsWithKana and longEnough then
-- Check if we've already found this deinflection. If so,
-- that means there was a shorter reason path to it and
-- this deinflection is almost certainly theoretical.
new_term = current.term:sub(1, -#rule.kanaIn-1) .. rule.kanaOut
if not seen[new_term] then
table.insert(results, makeDeinflectionResult(
new_term,
rule.rulesOut,
{reason, unpack(current.reasons)}
))
seen[new_term] = true
end
end
end
end
end
return results
end
-- These are all in 五十音 order, but we list variants in their 五十音 order
-- before the base kana.
-- @todo Maybe add historic (ゐ, ゑ) or lingustic (う゚, か゚, さ゚, ら゚) kana too?
local FULLWIDTH_HIRAGANA = {
-- 小書き
"", "", "", "", "",
"", "",
"",
"", "", "",
"",
-- 濁点・半濁点 つき
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"わ゙", "", "を゙",
-- 五十音
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "",
"", "", "", "", "",
"", "",
-- 撥音と長音符
"", "",
}
local FULLWIDTH_KATAKANA = {
-- 小書き
"", "", "", "", "",
"", "",
"",
"", "", "",
"",
-- 濁点・半濁点 つき
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "",
-- 五十音
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "",
"", "", "", "", "",
"", "",
-- 撥音と長音符
"", "",
}
local HALFWIDTH_KATAKANA = {
-- 小書き
"", "", "", "", "",
"", "", -- no ヵ・ヶ (small か・け)
"",
"", "", "",
"", -- no ゎ (small わ)
-- 濁点・半濁点 つき
"ガ", "ギ", "グ", "ゲ", "ゴ",
"ザ", "ジ", "ズ", "ゼ", "ゾ",
"ダ", "ヂ", "ヅ", "デ", "ド",
"バ", "ビ", "ブ", "ベ", "ボ",
"パ", "ピ", "プ", "ペ", "ポ",
"ヷ", "ヴ", "ヺ",
-- 五十音
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "ソ",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "", "", "",
"", "", "",
"", "", "", "", "",
"", "",
-- 撥音と長音符
"", "",
}
-- Ensure all of the tables are normalised.
for i, c in ipairs(HALFWIDTH_KATAKANA) do HALFWIDTH_KATAKANA[i] = Utf8Proc.normalize_NFC(c) end
for i, c in ipairs(FULLWIDTH_KATAKANA) do FULLWIDTH_KATAKANA[i] = Utf8Proc.normalize_NFC(c) end
for i, c in ipairs(FULLWIDTH_HIRAGANA) do FULLWIDTH_HIRAGANA[i] = Utf8Proc.normalize_NFC(c) end
-- Ensure all tables are the same size.
assert(#HALFWIDTH_KATAKANA == #FULLWIDTH_KATAKANA)
assert(#FULLWIDTH_KATAKANA == #FULLWIDTH_HIRAGANA)
-- Create fast conversion tables.
local HALFWIDTH_TO_FULLWIDTH, KATAKANA_TO_HIRAGANA, HIRAGANA_TO_KATAKANA = {}, {}, {}
for i in ipairs(FULLWIDTH_KATAKANA) do
KATAKANA_TO_HIRAGANA[FULLWIDTH_KATAKANA[i]] = FULLWIDTH_HIRAGANA[i]
HIRAGANA_TO_KATAKANA[FULLWIDTH_HIRAGANA[i]] = FULLWIDTH_KATAKANA[i]
-- Some entries are "" but that doesn't matter since we won't hit them during conversion.
HALFWIDTH_TO_FULLWIDTH[HALFWIDTH_KATAKANA[i]] = FULLWIDTH_KATAKANA[i]
end
local function kana_mapper(map)
return function(text)
local new_text = {}
local last_char
for c in text:gmatch(util.UTF8_CHAR_PATTERN) do
if last_char and (c == "" or c == "") then
-- Replace the last character with the correct mapping for the
-- combined character and mark. This is needed specifically for
-- half-width kana.
if map[last_char .. c] then
new_text[#new_text] = map[last_char .. c]
end
else
table.insert(new_text, map[c] or c)
end
last_char = c
end
return {table.concat(new_text, "")}
end
end
local EMPHATIC_SYMBOLS = {
[""] = true, [""] = true,
[""] = true, [""] = true,
}
local function collapse_emphatic(text)
local complete_collapse, partial_collapse = {}, {}
local last_char
for c in text:gmatch(util.UTF8_CHAR_PATTERN) do
if not EMPHATIC_SYMBOLS[c] then
table.insert(partial_collapse, c)
table.insert(complete_collapse, c)
elseif last_char ~= c then -- first instance of this emphatic marker
table.insert(partial_collapse, c)
end
last_char = c
end
return {
table.concat(partial_collapse, ""),
table.concat(complete_collapse, ""),
}
end
--- The set of defined map functions available to the deinflector.
local ALL_TEXT_CONVERSIONS = {
{
name = "halfwidth_to_fullwidth",
pretty_name = _("Halfwidth to fullwidth kana"),
-- @translators If possible, keep the example Japanese text.
help_text = _("Convert half-width katakana to full-width katakana (for instance, カタカナ will be converted to カタカナ)."),
func = kana_mapper(HALFWIDTH_TO_FULLWIDTH),
},
{
name = "hiragana_to_katakana",
pretty_name = _("Hiragana to katakana"),
-- @translators If possible, keep the example Japanese text.
help_text = _("Convert hiragana to katakana (for instance, ひらがな will be converted to ヒラガナ)."),
func = kana_mapper(HIRAGANA_TO_KATAKANA),
},
{
name = "katakana_to_hiragana",
pretty_name = _("Katakana to hiragana"),
-- @translators If possible, keep the example Japanese text.
help_text = _("Convert katakana to hiragana (for instance, カタカナ will be converted to かたかな)."),
func = kana_mapper(KATAKANA_TO_HIRAGANA),
},
{
name = "collapse_emphatic",
pretty_name = _("Collapse emphatic sequences"),
-- @translators If possible, keep the example Japanese text.
help_text = _("Collapse any character sequences which are sometimes used as emphasis in speech (for instance, すっっごーーい will be converted to both すっごーい and すごい)."),
func = collapse_emphatic,
},
}
--- Default enabled/disabled settings for ALL_TEXT_CONVERSIONS.
local DEFAULT_TEXT_CONVERSIONS = {
["halfwidth_to_fullwidth"] = true,
["hiragana_to_katakana"] = false,
["katakana_to_hiragana"] = true,
["collapse_emphatic"] = false,
}
--- Return the set of deinflections (and the reason path taken) for the
-- provided text. In addition to the verbatim text provided, several cleanups
-- will be attempted on the text (conversion from half-width kana, conversion
-- between katakana and hiragana, and collapsing of any emphatic sequences) and
-- any valid deinflections found will also be returned.
--
-- @tparam string text Japanese text to deinflect.
-- @treturn {DeinflectResult,...} An array of possible deinflections (including the text given).
function Deinflector:deinflect(text)
-- Normalise the text to ensure that we handle full-width text that
-- inexplicably uses combining 濁点・半濁点 (◌゙・◌゚) marks.
text = Utf8Proc.normalize_NFC(util.fixUtf8(text, "<EFBFBD>"))
local seen = {}
local all_results = {}
-- Iterate over the powerset of text_conversions by looping over every
-- possible bitmask for text_conversions then applying the functions which
-- have their corresponding bit set in the mask.
local enabled_text_conversions = {}
for name, enabled in pairs(self.enabled_text_conversions) do
if enabled then table.insert(enabled_text_conversions, name) end
end
local max_mapfn_bitmask = bit.lshift(1, #enabled_text_conversions) - 1 -- (2^n - 1)
for mapfn_bitmask = 0, max_mapfn_bitmask do
local func_names = {}
for i, func_name in ipairs(enabled_text_conversions) do
local mapfn_bit = bit.lshift(1, i-1) -- the bit for this function
if bit.band(mapfn_bit, mapfn_bitmask) ~= 0 then
func_names[func_name] = true
end
end
-- Apply the converters in the order specified in ALL_TEXT_CONVERSIONS.
local mapped_texts = {text}
for _, converter in ipairs(ALL_TEXT_CONVERSIONS) do
if func_names[converter.name] then
local old_texts = mapped_texts
mapped_texts = {}
for _, old_text in ipairs(old_texts) do
util.arrayAppend(mapped_texts, converter.func(old_text))
end
end
end
for _, mapped_text in ipairs(mapped_texts) do
if not seen[mapped_text] then
if text ~= mapped_text then
logger.dbg("japanese.koplugin deinflector: trying converted variant", text, "->", mapped_text)
end
local results = self:deinflectVerbatim(mapped_text)
if results then
util.arrayAppend(all_results, results)
end
seen[mapped_text] = true
end
end
end
return all_results
end
function Deinflector:genTextConversionMenuItems()
local item_table = {}
for _, conversion in pairs(ALL_TEXT_CONVERSIONS) do
local name = conversion.name
table.insert(item_table, {
text = conversion.pretty_name,
help_text = conversion.help_text,
checked_func = function()
return self.enabled_text_conversions[name] or false
end,
callback = function(touchmenu_instance)
self.enabled_text_conversions[name] = not self.enabled_text_conversions[name]
G_reader_settings:saveSetting("language_japanese_text_conversions", self.enabled_text_conversions)
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
})
end
return item_table
end
function Deinflector:genMenuItems()
return {
{
text_func = function()
local nenabled = 0
for _, enabled in pairs(self.enabled_text_conversions) do
if enabled then nenabled = nenabled + 1 end
end
if nenabled == 0 then
return _("Text conversions: none enabled")
else
return T(N_("Text conversions: %1 enabled", "Text conversions: %1 enabled", nenabled), nenabled)
end
end,
help_text = _([[
Configure which text conversions to apply when trying to deinflect Japanese text. These primarily include conversions between different kinds of kana, in order to make sure that a word written using different kana to your installed dictionaries can still be looked up.
Not every conversion will be applied at once. Instead, all possible combinations of enabled conversions will be attempted in order to maximise the chance of at least one conversion matching the form used in the dictionary.]]),
sub_item_table = self:genTextConversionMenuItems(),
},
{
-- @translators A deinflector is a program which converts a word into its dictionary form, similar to deconjugation in European languages. See <https://en.wikipedia.org/wiki/Japanese_verb_conjugation> for more detail.
text = _("Deinflector information"),
keep_menu_open = true,
callback = function()
local nrules, nvariants = 0, 0
for _, rules in pairs(self.rules) do
nvariants = nvariants + #rules
nrules = nrules + 1
end
local nrules_str = T(N_("%1 rule", "%1 rules", nrules), nrules)
local nvariants_str = T(N_("%1 variant", "%1 variants", nvariants), nvariants)
UIManager:show(InfoMessage:new{
-- @translators %1 is the "%1 rule(s)" string, %2 is the "%1 variant(s)" string.
text = T(_("Deinflector has %1 and %2 loaded."), nrules_str, nvariants_str),
})
end,
},
}
end
--- Initialise a Deflector instance with the set of rules defined in
-- yomichan-deflect.json.
function Deinflector:init()
self.enabled_text_conversions = self.enabled_text_conversions or
G_reader_settings:readSetting("language_japanese_text_conversions") or
DEFAULT_TEXT_CONVERSIONS
if self.rules ~= nil then return end -- already loaded
-- @todo Maybe make this location configurable?
inflections = parsePluginJson("yomichan-deinflect.json")
-- Normalise the reasons and convert the rules to the rule_types bitflags.
self.rules = {}
local nrules, nvariants = 0, 0
for reason, rules in pairs(inflections) do
variants = {}
for i, variant in ipairs(rules) do
variants[i] = {
kanaIn = variant.kanaIn,
kanaOut = variant.kanaOut,
rulesIn = toRuleTypes(unpack(variant.rulesIn)),
rulesOut = toRuleTypes(unpack(variant.rulesOut)),
}
end
self.rules[reason] = variants
nrules = nrules + 1
nvariants = nvariants + #variants
end
logger.dbg("japanese.koplugin deinflector: loaded inflection table with", nrules, "rules and", nvariants, "variants")
end
--- Create a new Deflector instance.
function Deinflector:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
o:init()
return o
end
return Deinflector

@ -0,0 +1,250 @@
--- Japanese language support for KOReader, modelled after Yomichan.
-- This plugin extends KOReader's built-in dictionary and selection system to
-- support Yomichan-style deinflection and text scanning, allowing for one-tap
-- searches of inflected verbs and multi-character words and phrases. As such,
-- this plugin removes the need for synonym-based deinflection rules for
-- StarDict-converted Japanese dictionaries.
--
-- @module koplugin.japanese
-- @alias Japanese
-- Copyright (C) 2021 Aleksa Sarai <cyphar@cyphar.com>
-- Licensed under the GPLv3 or later.
--
-- The deinflection logic is heavily modelled after Yomichan
-- <https://github.com/FooSoft/yomichan>, up to and including the deinflection
-- table. The way we try to find candidate words is also fairly similar (the
-- naive approach), though because dictionary lookups are quite expensive (we
-- have to call sdcv each time) we batch as many candidates as possible
-- together in order to reduce the impact we have on text selection.
local Deinflector = require("deinflector")
local InfoMessage = require("ui/widget/infomessage")
local LanguageSupport = require("languagesupport")
local ReaderDictionary = require("apps/reader/modules/readerdictionary")
local UIManager = require("ui/uimanager")
local JSON = require("json")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local util = require("util")
local _ = require("gettext")
local N_ = _.ngettext
local T = require("ffi/util").template
local SingleInstanceDeinflector = Deinflector:new()
local Japanese = WidgetContainer:new({
name = "japanese",
pretty_name = "Japanese",
})
-- Yomichan uses 10 characters as the default look-ahead, but crengine's
-- getNextVisibleChar counts furigana if any are present, so use a higher
-- threshold to be able to look-ahead an equivalent number of characters.
local DEFAULT_TEXT_SCAN_LENGTH = 20
function Japanese:init()
self.deinflector = SingleInstanceDeinflector
self.dictionary = (self.ui and self.ui.dictionary) or ReaderDictionary:new()
self.max_scan_length = G_reader_settings:readSetting("language_japanese_text_scan_length") or DEFAULT_TEXT_SCAN_LENGTH
LanguageSupport:registerPlugin(self)
end
function Japanese:supportsLanguage(language_code)
return language_code == "ja" or language_code == "jpn"
end
--- Called from @{languagesupport.extraDictionaryFormCandidates} for Japanese
-- text. Tries to find and return any possible deinflections for the given text.
-- @param args arguments from language support
-- @treturn {string,...} extra dictionary form candiadates found (or nil)
-- @see languagesupport.extraDictionaryFormCandidates
-- @see languagesupport.registerPlugin
function Japanese:onWordLookup(args)
local text = args.text
-- If there are no CJK characters in the text, there's nothing to do.
if not util.hasCJKChar(text) then
return
end
-- @todo Try to repeatedly reduce the text and deinflect the shortened text
-- to provide more candidates. This is particularly needed because
-- JMDict has a habit of creating entries for compounds or phrases
-- that do not exist in monolingual dictionaries (even in 大辞林 or
-- 広辞苑) and our onWordSelection expansion accepts any dictionary's
-- largest entry. Unfortunately doing this nicely requires a bit of
-- extra work to be efficient (since we need to remove the last
-- character in the string).
results = self.deinflector:deinflect(text)
logger.dbg("japanese.koplugin: deinflection of", text, "results:", results)
-- @todo Pass up the reasons list (formatted Yomichan style) to the
-- dictionary pop-up so you can get some more information about the
-- inflection. But this would require adding some kind of tag
-- metadata that we have to pass through from the lookup to the
-- dictionary pop-up.
candidates = {}
for i, result in ipairs(results) do
candidates[i] = result.term
end
return candidates
end
-- @todo Switch this to utf8proc_category or something similar.
local JAPANESE_PUNCTUATION = "「」『』【】〘〙〖〗・・、、,。。.!?  \n"
local function isPossibleJapaneseWord(str)
for c in str:gmatch(util.UTF8_CHAR_PATTERN) do
if not util.isCJKChar(c) or JAPANESE_PUNCTUATION:find(c) ~= nil then
return false
end
end
return true
end
--- Called from @{languagesupport.improveWordSelection} for Japanese text.
-- Tries to expand the word selection defined by args.
-- @param args arguments from language support
-- @treturn {pos0,pos1} the new selection range (or nil)
-- @see languagesupport.improveWordSelection
-- @see languagesupport.registerPlugin
function Japanese:onWordSelection(args)
local pos0, pos1 = args.pos0, args.pos1
local callbacks = args.callbacks
local current_text = args.text
-- If the initial selection contains only non-CJK characters, then there's
-- no point trying to expand it because no Japanese words mix CJK and
-- non-CJK characters (there are non-CJK words in Japanese -- CM, NG, TKG
-- and their full-width equivalents for instance -- but they are selected
-- by crengine correctly already and are full words by themselves).
if current_text ~= "" and not util.hasCJKChar(current_text) then
return
end
-- We reset the end of the range to pos0+1 because crengine will select
-- half-width katakana (カタカナ) in strange ways that often overshoots the
-- end of words.
pos1 = callbacks.get_next_char_pos(pos0)
-- We try to advance the end position until we hit a word.
--
-- Unfortunately it's possible for the complete word to be longer than the
-- first match (obvious examples include 読み込む or similar compound verbs
-- where it would be less than ideal to match 読み as the full word, but
-- there are more subtle kana-only cases as well) so we need to keep
-- looking forward, but unfortunately there isn't a great endpoint defined
-- either (aside from punctuation). So we just copy Yomichan and set a hard
-- limit (20 characters) and stop early if we ever hit punctuation. We then
-- select the longest word present in one of the user's installed
-- dictionaries (after deinflection).
local all_candidates = {}
local all_words = {}
local current_end = pos1
local num_expansions = 0
repeat
-- Move to the next character.
current_end = callbacks.get_next_char_pos(current_end)
current_text = callbacks.get_text_in_range(pos0, current_end)
num_expansions = num_expansions + 1
-- If the text could not be a complete Japanese word (i.e. it contains
-- a punctuation or some other special character), quit early. We test
-- the whole string rather than the last character because finding the
-- last character requires a linear walk through the string anyway, and
-- get_next_char_pos() skips over newlines.
if not isPossibleJapaneseWord(current_text) then
logger.dbg("japanese.koplugin: stopping expansion at", current_text, "because in contains non-word characters")
break
end
-- Get the selection and try to deinflect it.
local candidates = self.deinflector:deinflect(current_text)
local terms = {}
for _, candidate in ipairs(candidates) do
table.insert(terms, candidate.term)
end
-- Add the candidates to the set of words to attempt.
for _, term in ipairs(terms) do
table.insert(all_candidates, {
pos0 = pos0,
pos1 = current_end,
text = term,
})
table.insert(all_words, term)
end
until current_end == nil or num_expansions >= self.max_scan_length
logger.dbg("japanese.koplugin: attempted", num_expansions, "expansions up to", current_text)
-- Calling sdcv is fairly expensive, so reduce the cost by trying every
-- candidate in one shot and then picking the longest one which gave us a
-- result.
-- @todo Given there is a limit to how many command-line arguments you can
-- pass, we should split up the candidate list if it's too long.
local best_word
local cancelled, all_results = self.dictionary:rawSdcv(all_words)
if not cancelled and all_results ~= nil then
for i, term_results in ipairs(all_results) do
if #term_results ~= 0 then
best_word = all_candidates[i]
end
end
end
if best_word ~= nil then
return {best_word.pos0, best_word.pos1}
end
end
function Japanese:genMenuItem()
local sub_item_table = {
-- self.max_scan_length configuration
{
text_func = function()
return T(N_("Text scan length: %1 character", "Text scan length: %1 characters", self.max_scan_length), self.max_scan_length)
end,
help_text = _("Number of characters to look ahead when trying to expand tap-and-hold word selection in documents."),
keep_menu_open = true,
callback = function(touchmenu_instance)
local SpinWidget = require("ui/widget/spinwidget")
local Screen = require("device").screen
local items = SpinWidget:new{
title_text = _("Text scan length"),
info_text = T(_([[
The maximum number of characters to look ahead when trying to expand tap-and-hold word selection in documents.
Larger values allow longer phrases to be selected automatically, but with the trade-off that selections may become slower.
Default value: %1]]), DEFAULT_TEXT_SCAN_LENGTH),
width = math.floor(Screen:getWidth() * 0.75),
value = self.max_scan_length,
value_min = 0,
value_max = 1000,
value_step = 1,
value_hold_step = 10,
ok_text = _("Set scan length"),
default_value = DEFAULT_TEXT_SCAN_LENGTH,
callback = function(spin)
self.max_scan_length = spin.value
G_reader_settings:saveSetting("language_japanese_text_scan_length", self.max_scan_length)
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
}
UIManager:show(items)
end,
},
}
-- self.deinflector configuration
util.arrayAppend(sub_item_table, self.deinflector:genMenuItems())
return {
text = _("Japanese"),
sub_item_table = sub_item_table,
}
end
return Japanese

@ -0,0 +1,643 @@
{
"-ba": [
{"kanaIn": "ければ", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "えば", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "けば", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "げば", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "せば", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "てば", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ねば", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "べば", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "めば", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "れば", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1", "v5", "vk", "vs", "vz"]}
],
"-chau": [
{"kanaIn": "ちゃう", "kanaOut": "る", "rulesIn": ["v5"], "rulesOut": ["v1"]},
{"kanaIn": "いじゃう", "kanaOut": "ぐ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "いちゃう", "kanaOut": "く", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "しちゃう", "kanaOut": "す", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちゃう", "kanaOut": "う", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちゃう", "kanaOut": "く", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちゃう", "kanaOut": "つ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちゃう", "kanaOut": "る", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んじゃう", "kanaOut": "ぬ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んじゃう", "kanaOut": "ぶ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んじゃう", "kanaOut": "む", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "じちゃう", "kanaOut": "ずる", "rulesIn": ["v5"], "rulesOut": ["vz"]},
{"kanaIn": "しちゃう", "kanaOut": "する", "rulesIn": ["v5"], "rulesOut": ["vs"]},
{"kanaIn": "為ちゃう", "kanaOut": "為る", "rulesIn": ["v5"], "rulesOut": ["vs"]},
{"kanaIn": "きちゃう", "kanaOut": "くる", "rulesIn": ["v5"], "rulesOut": ["vk"]},
{"kanaIn": "来ちゃう", "kanaOut": "来る", "rulesIn": ["v5"], "rulesOut": ["vk"]},
{"kanaIn": "來ちゃう", "kanaOut": "來る", "rulesIn": ["v5"], "rulesOut": ["vk"]}
],
"-chimau": [
{"kanaIn": "ちまう", "kanaOut": "る", "rulesIn": ["v5"], "rulesOut": ["v1"]},
{"kanaIn": "いじまう", "kanaOut": "ぐ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "いちまう", "kanaOut": "く", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "しちまう", "kanaOut": "す", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちまう", "kanaOut": "う", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちまう", "kanaOut": "く", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちまう", "kanaOut": "つ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っちまう", "kanaOut": "る", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んじまう", "kanaOut": "ぬ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んじまう", "kanaOut": "ぶ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んじまう", "kanaOut": "む", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "じちまう", "kanaOut": "ずる", "rulesIn": ["v5"], "rulesOut": ["vz"]},
{"kanaIn": "しちまう", "kanaOut": "する", "rulesIn": ["v5"], "rulesOut": ["vs"]},
{"kanaIn": "為ちまう", "kanaOut": "為る", "rulesIn": ["v5"], "rulesOut": ["vs"]},
{"kanaIn": "きちまう", "kanaOut": "くる", "rulesIn": ["v5"], "rulesOut": ["vk"]},
{"kanaIn": "来ちまう", "kanaOut": "来る", "rulesIn": ["v5"], "rulesOut": ["vk"]},
{"kanaIn": "來ちまう", "kanaOut": "來る", "rulesIn": ["v5"], "rulesOut": ["vk"]}
],
"-shimau": [
{"kanaIn": "てしまう", "kanaOut": "て", "rulesIn": ["v5"], "rulesOut": ["iru"]},
{"kanaIn": "でしまう", "kanaOut": "で", "rulesIn": ["v5"], "rulesOut": ["iru"]}
],
"-nasai": [
{"kanaIn": "なさい", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いなさい", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "きなさい", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎなさい", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "しなさい", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ちなさい", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "になさい", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "びなさい", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "みなさい", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "りなさい", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じなさい", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しなさい", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為なさい", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きなさい", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来なさい", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來なさい", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"-sou": [
{"kanaIn": "そう", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "そう", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いそう", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "きそう", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎそう", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "しそう", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ちそう", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "にそう", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "びそう", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "みそう", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "りそう", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じそう", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しそう", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為そう", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きそう", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来そう", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來そう", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"-sugiru": [
{"kanaIn": "すぎる", "kanaOut": "い", "rulesIn": ["v1"], "rulesOut": ["adj-i"]},
{"kanaIn": "すぎる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v1"]},
{"kanaIn": "いすぎる", "kanaOut": "う", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "きすぎる", "kanaOut": "く", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ぎすぎる", "kanaOut": "ぐ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "しすぎる", "kanaOut": "す", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ちすぎる", "kanaOut": "つ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "にすぎる", "kanaOut": "ぬ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "びすぎる", "kanaOut": "ぶ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "みすぎる", "kanaOut": "む", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "りすぎる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "じすぎる", "kanaOut": "ずる", "rulesIn": ["v1"], "rulesOut": ["vz"]},
{"kanaIn": "しすぎる", "kanaOut": "する", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "為すぎる", "kanaOut": "為る", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "きすぎる", "kanaOut": "くる", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "来すぎる", "kanaOut": "来る", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "來すぎる", "kanaOut": "來る", "rulesIn": ["v1"], "rulesOut": ["vk"]}
],
"-tai": [
{"kanaIn": "たい", "kanaOut": "る", "rulesIn": ["adj-i"], "rulesOut": ["v1"]},
{"kanaIn": "いたい", "kanaOut": "う", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "きたい", "kanaOut": "く", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "ぎたい", "kanaOut": "ぐ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "したい", "kanaOut": "す", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "ちたい", "kanaOut": "つ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "にたい", "kanaOut": "ぬ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "びたい", "kanaOut": "ぶ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "みたい", "kanaOut": "む", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "りたい", "kanaOut": "る", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "じたい", "kanaOut": "ずる", "rulesIn": ["adj-i"], "rulesOut": ["vz"]},
{"kanaIn": "したい", "kanaOut": "する", "rulesIn": ["adj-i"], "rulesOut": ["vs"]},
{"kanaIn": "為たい", "kanaOut": "為る", "rulesIn": ["adj-i"], "rulesOut": ["vs"]},
{"kanaIn": "きたい", "kanaOut": "くる", "rulesIn": ["adj-i"], "rulesOut": ["vk"]},
{"kanaIn": "来たい", "kanaOut": "来る", "rulesIn": ["adj-i"], "rulesOut": ["vk"]},
{"kanaIn": "來たい", "kanaOut": "來る", "rulesIn": ["adj-i"], "rulesOut": ["vk"]}
],
"-tara": [
{"kanaIn": "かったら", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "たら", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いたら", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "いだら", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "したら", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ったら", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ったら", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ったら", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだら", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだら", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだら", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じたら", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "したら", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為たら", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きたら", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来たら", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來たら", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "いったら", "kanaOut": "いく", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "おうたら", "kanaOut": "おう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "こうたら", "kanaOut": "こう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "そうたら", "kanaOut": "そう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "とうたら", "kanaOut": "とう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "行ったら", "kanaOut": "行く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "逝ったら", "kanaOut": "逝く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "往ったら", "kanaOut": "往く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "請うたら", "kanaOut": "請う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "乞うたら", "kanaOut": "乞う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "恋うたら", "kanaOut": "恋う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "問うたら", "kanaOut": "問う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "負うたら", "kanaOut": "負う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "沿うたら", "kanaOut": "沿う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "添うたら", "kanaOut": "添う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "副うたら", "kanaOut": "副う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "厭うたら", "kanaOut": "厭う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "のたもうたら", "kanaOut": "のたまう", "rulesIn": [], "rulesOut": ["v5"]}
],
"-tari": [
{"kanaIn": "かったり", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "たり", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いたり", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "いだり", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "したり", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ったり", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ったり", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ったり", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだり", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだり", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだり", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じたり", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "したり", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為たり", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きたり", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来たり", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來たり", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "いったり", "kanaOut": "いく", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "おうたり", "kanaOut": "おう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "こうたり", "kanaOut": "こう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "そうたり", "kanaOut": "そう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "とうたり", "kanaOut": "とう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "行ったり", "kanaOut": "行く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "逝ったり", "kanaOut": "逝く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "往ったり", "kanaOut": "往く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "請うたり", "kanaOut": "請う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "乞うたり", "kanaOut": "乞う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "恋うたり", "kanaOut": "恋う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "問うたり", "kanaOut": "問う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "負うたり", "kanaOut": "負う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "沿うたり", "kanaOut": "沿う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "添うたり", "kanaOut": "添う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "副うたり", "kanaOut": "副う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "厭うたり", "kanaOut": "厭う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "のたもうたり", "kanaOut": "のたまう", "rulesIn": [], "rulesOut": ["v5"]}
],
"-te": [
{"kanaIn": "くて", "kanaOut": "い", "rulesIn": ["iru"], "rulesOut": ["adj-i"]},
{"kanaIn": "て", "kanaOut": "る", "rulesIn": ["iru"], "rulesOut": ["v1"]},
{"kanaIn": "いて", "kanaOut": "く", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "いで", "kanaOut": "ぐ", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "して", "kanaOut": "す", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "って", "kanaOut": "う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "って", "kanaOut": "つ", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "って", "kanaOut": "る", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "んで", "kanaOut": "ぬ", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "んで", "kanaOut": "ぶ", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "んで", "kanaOut": "む", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "じて", "kanaOut": "ずる", "rulesIn": ["iru"], "rulesOut": ["vz"]},
{"kanaIn": "して", "kanaOut": "する", "rulesIn": ["iru"], "rulesOut": ["vs"]},
{"kanaIn": "為て", "kanaOut": "為る", "rulesIn": ["iru"], "rulesOut": ["vs"]},
{"kanaIn": "きて", "kanaOut": "くる", "rulesIn": ["iru"], "rulesOut": ["vk"]},
{"kanaIn": "来て", "kanaOut": "来る", "rulesIn": ["iru"], "rulesOut": ["vk"]},
{"kanaIn": "來て", "kanaOut": "來る", "rulesIn": ["iru"], "rulesOut": ["vk"]},
{"kanaIn": "いって", "kanaOut": "いく", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "おうて", "kanaOut": "おう", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "こうて", "kanaOut": "こう", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "そうて", "kanaOut": "そう", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "とうて", "kanaOut": "とう", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "行って", "kanaOut": "行く", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "逝って", "kanaOut": "逝く", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "往って", "kanaOut": "往く", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "請うて", "kanaOut": "請う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "乞うて", "kanaOut": "乞う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "恋うて", "kanaOut": "恋う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "問うて", "kanaOut": "問う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "負うて", "kanaOut": "負う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "沿うて", "kanaOut": "沿う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "添うて", "kanaOut": "添う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "副うて", "kanaOut": "副う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "厭うて", "kanaOut": "厭う", "rulesIn": ["iru"], "rulesOut": ["v5"]},
{"kanaIn": "のたもうて", "kanaOut": "のたまう", "rulesIn": ["iru"], "rulesOut": ["v5"]}
],
"-zu": [
{"kanaIn": "ず", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "かず", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "がず", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "さず", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "たず", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "なず", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ばず", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "まず", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "らず", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "わず", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぜず", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "せず", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ず", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "こず", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来ず", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來ず", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"-nu": [
{"kanaIn": "ぬ", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "かぬ", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "がぬ", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "さぬ", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "たぬ", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "なぬ", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ばぬ", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "まぬ", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "らぬ", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "わぬ", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぜぬ", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "せぬ", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ぬ", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "こぬ", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来ぬ", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來ぬ", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"adv": [
{"kanaIn": "く", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]}
],
"causative": [
{"kanaIn": "させる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v1"]},
{"kanaIn": "かせる", "kanaOut": "く", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "がせる", "kanaOut": "ぐ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "させる", "kanaOut": "す", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "たせる", "kanaOut": "つ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "なせる", "kanaOut": "ぬ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ばせる", "kanaOut": "ぶ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ませる", "kanaOut": "む", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "らせる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "わせる", "kanaOut": "う", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "じさせる", "kanaOut": "ずる", "rulesIn": ["v1"], "rulesOut": ["vz"]},
{"kanaIn": "ぜさせる", "kanaOut": "ずる", "rulesIn": ["v1"], "rulesOut": ["vz"]},
{"kanaIn": "させる", "kanaOut": "する", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "為せる", "kanaOut": "為る", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "せさせる", "kanaOut": "する", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "為させる", "kanaOut": "為る", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "こさせる", "kanaOut": "くる", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "来させる", "kanaOut": "来る", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "來させる", "kanaOut": "來る", "rulesIn": ["v1"], "rulesOut": ["vk"]}
],
"imperative": [
{"kanaIn": "ろ", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "よ", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "え", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "け", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "げ", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "せ", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "て", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ね", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "べ", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "め", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "れ", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じろ", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "ぜよ", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しろ", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "せよ", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ろ", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為よ", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "こい", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来い", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來い", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"imperative negative": [
{"kanaIn": "な", "kanaOut": "", "rulesIn": [], "rulesOut": ["v1", "v5", "vk", "vs", "vz"]}
],
"masu stem": [
{"kanaIn": "い", "kanaOut": "いる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "え", "kanaOut": "える", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "き", "kanaOut": "きる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "ぎ", "kanaOut": "ぎる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "け", "kanaOut": "ける", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "げ", "kanaOut": "げる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "じ", "kanaOut": "じる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "せ", "kanaOut": "せる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "ぜ", "kanaOut": "ぜる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "ち", "kanaOut": "ちる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "て", "kanaOut": "てる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "で", "kanaOut": "でる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "に", "kanaOut": "にる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "ね", "kanaOut": "ねる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "ひ", "kanaOut": "ひる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "び", "kanaOut": "びる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "へ", "kanaOut": "へる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "べ", "kanaOut": "べる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "み", "kanaOut": "みる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "め", "kanaOut": "める", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "り", "kanaOut": "りる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "れ", "kanaOut": "れる", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "い", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "き", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎ", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "し", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ち", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "に", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "び", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "み", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "り", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "き", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"negative": [
{"kanaIn": "くない", "kanaOut": "い", "rulesIn": ["adj-i"], "rulesOut": ["adj-i"]},
{"kanaIn": "ない", "kanaOut": "る", "rulesIn": ["adj-i"], "rulesOut": ["v1"]},
{"kanaIn": "かない", "kanaOut": "く", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "がない", "kanaOut": "ぐ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "さない", "kanaOut": "す", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "たない", "kanaOut": "つ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "なない", "kanaOut": "ぬ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "ばない", "kanaOut": "ぶ", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "まない", "kanaOut": "む", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "らない", "kanaOut": "る", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "わない", "kanaOut": "う", "rulesIn": ["adj-i"], "rulesOut": ["v5"]},
{"kanaIn": "じない", "kanaOut": "ずる", "rulesIn": ["adj-i"], "rulesOut": ["vz"]},
{"kanaIn": "しない", "kanaOut": "する", "rulesIn": ["adj-i"], "rulesOut": ["vs"]},
{"kanaIn": "為ない", "kanaOut": "為る", "rulesIn": ["adj-i"], "rulesOut": ["vs"]},
{"kanaIn": "こない", "kanaOut": "くる", "rulesIn": ["adj-i"], "rulesOut": ["vk"]},
{"kanaIn": "来ない", "kanaOut": "来る", "rulesIn": ["adj-i"], "rulesOut": ["vk"]},
{"kanaIn": "來ない", "kanaOut": "來る", "rulesIn": ["adj-i"], "rulesOut": ["vk"]}
],
"noun": [
{"kanaIn": "さ", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]}
],
"passive": [
{"kanaIn": "かれる", "kanaOut": "く", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "がれる", "kanaOut": "ぐ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "される", "kanaOut": "す", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "たれる", "kanaOut": "つ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "なれる", "kanaOut": "ぬ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ばれる", "kanaOut": "ぶ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "まれる", "kanaOut": "む", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "われる", "kanaOut": "う", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "られる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "じされる", "kanaOut": "ずる", "rulesIn": ["v1"], "rulesOut": ["vz"]},
{"kanaIn": "ぜされる", "kanaOut": "ずる", "rulesIn": ["v1"], "rulesOut": ["vz"]},
{"kanaIn": "される", "kanaOut": "する", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "為れる", "kanaOut": "為る", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "こられる", "kanaOut": "くる", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "来られる", "kanaOut": "来る", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "來られる", "kanaOut": "來る", "rulesIn": ["v1"], "rulesOut": ["vk"]}
],
"past": [
{"kanaIn": "かった", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "た", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いた", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "いだ", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "した", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "った", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "った", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "った", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだ", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだ", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "んだ", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じた", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "した", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為た", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きた", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来た", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來た", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "いった", "kanaOut": "いく", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "おうた", "kanaOut": "おう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "こうた", "kanaOut": "こう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "そうた", "kanaOut": "そう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "とうた", "kanaOut": "とう", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "行った", "kanaOut": "行く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "逝った", "kanaOut": "逝く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "往った", "kanaOut": "往く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "請うた", "kanaOut": "請う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "乞うた", "kanaOut": "乞う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "恋うた", "kanaOut": "恋う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "問うた", "kanaOut": "問う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "負うた", "kanaOut": "負う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "沿うた", "kanaOut": "沿う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "添うた", "kanaOut": "添う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "副うた", "kanaOut": "副う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "厭うた", "kanaOut": "厭う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "のたもうた", "kanaOut": "のたまう", "rulesIn": [], "rulesOut": ["v5"]}
],
"polite": [
{"kanaIn": "ます", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "います", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "きます", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎます", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "します", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ちます", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "にます", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "びます", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "みます", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ります", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じます", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "します", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ます", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きます", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来ます", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來ます", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"polite negative": [
{"kanaIn": "くありません", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ません", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いません", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "きません", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎません", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "しません", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ちません", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "にません", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "びません", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "みません", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "りません", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じません", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しません", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ません", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きません", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来ません", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來ません", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"polite past": [
{"kanaIn": "ました", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いました", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "きました", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎました", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "しました", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ちました", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "にました", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "びました", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "みました", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "りました", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じました", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しました", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ました", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きました", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来ました", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來ました", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"polite past negative": [
{"kanaIn": "くありませんでした", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ませんでした", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いませんでした", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "きませんでした", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎませんでした", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "しませんでした", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ちませんでした", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "にませんでした", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "びませんでした", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "みませんでした", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "りませんでした", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じませんでした", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しませんでした", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ませんでした", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きませんでした", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来ませんでした", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來ませんでした", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"polite volitional": [
{"kanaIn": "ましょう", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "いましょう", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "きましょう", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぎましょう", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "しましょう", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ちましょう", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "にましょう", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "びましょう", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "みましょう", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "りましょう", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じましょう", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しましょう", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為ましょう", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "きましょう", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来ましょう", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來ましょう", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"potential": [
{"kanaIn": "れる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v1", "v5"]},
{"kanaIn": "える", "kanaOut": "う", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ける", "kanaOut": "く", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "げる", "kanaOut": "ぐ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "せる", "kanaOut": "す", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "てる", "kanaOut": "つ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ねる", "kanaOut": "ぬ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "べる", "kanaOut": "ぶ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "める", "kanaOut": "む", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "これる", "kanaOut": "くる", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "来れる", "kanaOut": "来る", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "來れる", "kanaOut": "來る", "rulesIn": ["v1"], "rulesOut": ["vk"]}
],
"potential or passive": [
{"kanaIn": "られる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v1"]},
{"kanaIn": "ざれる", "kanaOut": "ずる", "rulesIn": ["v1"], "rulesOut": ["vz"]},
{"kanaIn": "ぜられる", "kanaOut": "ずる", "rulesIn": ["v1"], "rulesOut": ["vz"]},
{"kanaIn": "せられる", "kanaOut": "する", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "為られる", "kanaOut": "為る", "rulesIn": ["v1"], "rulesOut": ["vs"]},
{"kanaIn": "こられる", "kanaOut": "くる", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "来られる", "kanaOut": "来る", "rulesIn": ["v1"], "rulesOut": ["vk"]},
{"kanaIn": "來られる", "kanaOut": "來る", "rulesIn": ["v1"], "rulesOut": ["vk"]}
],
"volitional": [
{"kanaIn": "よう", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v1"]},
{"kanaIn": "おう", "kanaOut": "う", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "こう", "kanaOut": "く", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ごう", "kanaOut": "ぐ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "そう", "kanaOut": "す", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "とう", "kanaOut": "つ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "のう", "kanaOut": "ぬ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ぼう", "kanaOut": "ぶ", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "もう", "kanaOut": "む", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "ろう", "kanaOut": "る", "rulesIn": [], "rulesOut": ["v5"]},
{"kanaIn": "じよう", "kanaOut": "ずる", "rulesIn": [], "rulesOut": ["vz"]},
{"kanaIn": "しよう", "kanaOut": "する", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "為よう", "kanaOut": "為る", "rulesIn": [], "rulesOut": ["vs"]},
{"kanaIn": "こよう", "kanaOut": "くる", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "来よう", "kanaOut": "来る", "rulesIn": [], "rulesOut": ["vk"]},
{"kanaIn": "來よう", "kanaOut": "來る", "rulesIn": [], "rulesOut": ["vk"]}
],
"causative passive": [
{"kanaIn": "かされる", "kanaOut": "く", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "がされる", "kanaOut": "ぐ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "たされる", "kanaOut": "つ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "なされる", "kanaOut": "ぬ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "ばされる", "kanaOut": "ぶ", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "まされる", "kanaOut": "む", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "らされる", "kanaOut": "る", "rulesIn": ["v1"], "rulesOut": ["v5"]},
{"kanaIn": "わされる", "kanaOut": "う", "rulesIn": ["v1"], "rulesOut": ["v5"]}
],
"-toku": [
{"kanaIn": "とく", "kanaOut": "る", "rulesIn": ["v5"], "rulesOut": ["v1"]},
{"kanaIn": "いとく", "kanaOut": "く", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "いどく", "kanaOut": "ぐ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "しとく", "kanaOut": "す", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っとく", "kanaOut": "う", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っとく", "kanaOut": "つ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "っとく", "kanaOut": "る", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んどく", "kanaOut": "ぬ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んどく", "kanaOut": "ぶ", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "んどく", "kanaOut": "む", "rulesIn": ["v5"], "rulesOut": ["v5"]},
{"kanaIn": "じとく", "kanaOut": "ずる", "rulesIn": ["v5"], "rulesOut": ["vz"]},
{"kanaIn": "しとく", "kanaOut": "する", "rulesIn": ["v5"], "rulesOut": ["vs"]},
{"kanaIn": "為とく", "kanaOut": "為る", "rulesIn": ["v5"], "rulesOut": ["vs"]},
{"kanaIn": "きとく", "kanaOut": "くる", "rulesIn": ["v5"], "rulesOut": ["vk"]},
{"kanaIn": "来とく", "kanaOut": "来る", "rulesIn": ["v5"], "rulesOut": ["vk"]},
{"kanaIn": "來とく", "kanaOut": "來る", "rulesIn": ["v5"], "rulesOut": ["vk"]}
],
"progressive or perfect": [
{"kanaIn": "ている", "kanaOut": "て", "rulesIn": ["v1"], "rulesOut": ["iru"]},
{"kanaIn": "ておる", "kanaOut": "て", "rulesIn": ["v5"], "rulesOut": ["iru"]},
{"kanaIn": "てる", "kanaOut": "て", "rulesIn": ["v1"], "rulesOut": ["iru"]},
{"kanaIn": "でいる", "kanaOut": "で", "rulesIn": ["v1"], "rulesOut": ["iru"]},
{"kanaIn": "でおる", "kanaOut": "で", "rulesIn": ["v5"], "rulesOut": ["iru"]},
{"kanaIn": "でる", "kanaOut": "で", "rulesIn": ["v1"], "rulesOut": ["iru"]},
{"kanaIn": "とる", "kanaOut": "て", "rulesIn": ["v5"], "rulesOut": ["iru"]},
{"kanaIn": "ないでいる", "kanaOut": "ない", "rulesIn": ["v1"], "rulesOut": ["adj-i"]}
],
"-ki": [
{"kanaIn": "き", "kanaOut": "い", "rulesIn": [], "rulesOut": ["adj-i"]}
],
"-ge": [
{"kanaIn": "しげ", "kanaOut": "しい", "rulesIn": [], "rulesOut": ["adj-i"]}
],
"-e": [
{"kanaIn": "ねえ", "kanaOut": "ない", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "めえ", "kanaOut": "むい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "みい", "kanaOut": "むい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ちぇえ", "kanaOut": "つい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ちい", "kanaOut": "つい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "せえ", "kanaOut": "すい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ええ", "kanaOut": "いい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ええ", "kanaOut": "わい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ええ", "kanaOut": "よい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "いぇえ", "kanaOut": "よい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "うぇえ", "kanaOut": "わい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "けえ", "kanaOut": "かい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "げえ", "kanaOut": "がい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "げえ", "kanaOut": "ごい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "せえ", "kanaOut": "さい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "めえ", "kanaOut": "まい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ぜえ", "kanaOut": "ずい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "っぜえ", "kanaOut": "ずい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "れえ", "kanaOut": "らい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "れえ", "kanaOut": "らい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "ちぇえ", "kanaOut": "ちゃい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "でえ", "kanaOut": "どい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "れえ", "kanaOut": "れい", "rulesIn": [], "rulesOut": ["adj-i"]},
{"kanaIn": "べえ", "kanaOut": "ばい", "rulesIn": [], "rulesOut": ["adj-i"]}
]
}
Loading…
Cancel
Save