diff --git a/README.md b/README.md index fb465ab61f..0e59907ef1 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,8 @@ Screenshot showing translation of 四畳半 into 7.29 square meters - Recognition of a wide range of grammatical forms - (e.g. irregular verbs like いらっしゃいます, + (e.g. irregular inflections of する-verbs like 罰せられる, + irregular verbs like いらっしゃいます, continuous forms like 食べてた, ん as a negative form like 分からん、知らん, words with ー like じーちゃん、頑張ろー、そーゆー, diff --git a/_locales/en/messages.json b/_locales/en/messages.json index dfb4f0f715..d79ac3d14d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -371,6 +371,9 @@ "deinflect_imperative_negative": { "message": "imperative negative" }, + "deinflect_irregular": { + "message": "irregular" + }, "deinflect_ki": { "message": "-ki" }, diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index 55ac6885f2..410076301f 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -371,6 +371,9 @@ "deinflect_imperative_negative": { "message": "否定命令形" }, + "deinflect_irregular": { + "message": "不規則" + }, "deinflect_ki": { "message": "連体形" }, diff --git a/_locales/zh_hans/messages.json b/_locales/zh_hans/messages.json index 89c02237b0..8ddb5e0f20 100644 --- a/_locales/zh_hans/messages.json +++ b/_locales/zh_hans/messages.json @@ -371,6 +371,9 @@ "deinflect_imperative_negative": { "message": "否定命令形" }, + "deinflect_irregular": { + "message": "" + }, "deinflect_ki": { "message": "连体形" }, diff --git a/src/background/deinflect.test.ts b/src/background/deinflect.test.ts index 5394b0ce76..f8b2357950 100644 --- a/src/background/deinflect.test.ts +++ b/src/background/deinflect.test.ts @@ -128,6 +128,64 @@ describe('deinflect', () => { }); }); + it('deinflects all forms of する', () => { + const cases = [ + ['した', [Reason.Past]], + ['しよう', [Reason.Volitional]], + ['しない', [Reason.Negative]], + ['せぬ', [Reason.Negative]], + ['せん', [Reason.Negative]], + ['せず', [Reason.Zu]], + ['される', [Reason.Passive]], + ['させる', [Reason.Causative]], + ['しろ', [Reason.Imperative]], + ['せよ', [Reason.Imperative]], + ['すれば', [Reason.Ba]], + ['できる', [Reason.Potential]], + ]; + + for (const [inflected, reasons] of cases) { + const result = deinflect(inflected as string); + const match = result.find( + (candidate) => + candidate.word == 'する' && candidate.type & WordType.SuruVerb + ); + expect(match).toBeDefined(); + expect(match!.reasonChains).toEqual([reasons]); + } + }); + + it('deinflects additional forms of special class suru-verbs', () => { + const cases = [ + ['発する', '発せさせる', [Reason.Irregular, Reason.Causative]], + ['発する', '発せられる', [Reason.Irregular, Reason.PotentialOrPassive]], + ['発する', '発しさせる', [Reason.Irregular, Reason.Causative]], + ['発する', '発しられる', [Reason.Irregular, Reason.PotentialOrPassive]], + // 五段化 + ['発する', '発さない', [Reason.Irregular, Reason.Negative]], + ['発する', '発さず', [Reason.Irregular, Reason.Zu]], + ['発する', '発そう', [Reason.Irregular, Reason.Volitional]], + ['愛する', '愛せる', [Reason.Irregular, Reason.Potential]], + ['愛する', '愛せば', [Reason.Irregular, Reason.Ba]], + ['愛する', '愛せ', [Reason.Irregular, Reason.Imperative]], + // ずる + ['信ずる', '信ぜぬ', [Reason.Irregular, Reason.Negative]], + ['信ずる', '信ぜず', [Reason.Irregular, Reason.Zu]], + ['信ずる', '信ぜさせる', [Reason.Irregular, Reason.Causative]], + ['信ずる', '信ぜられる', [Reason.Irregular, Reason.PotentialOrPassive]], + ['信ずる', '信ずれば', [Reason.Irregular, Reason.Ba]], + ['信ずる', '信ぜよ', [Reason.Irregular, Reason.Imperative]], + ]; + + for (const [plain, inflected, reasons] of cases) { + const result = deinflect(inflected as string); + const match = result.find((candidate) => candidate.word == plain); + expect(match).toBeDefined(); + expect(match!.type).toEqual(WordType.SpecialSuruVerb); + expect(match!.reasonChains).toEqual([reasons]); + } + }); + it('deinflects irregular forms of 行く', () => { const cases = [ ['行った', '行く', Reason.Past, 2], @@ -300,8 +358,8 @@ describe('deinflect', () => { ['歩いてる', '歩く', 2, undefined], ['泳いでいる', '泳ぐ', 2, undefined], ['泳いでる', '泳ぐ', 2, undefined], - ['話している', '話す', 18, undefined], - ['話してる', '話す', 18, undefined], + ['話している', '話す', 2, undefined], + ['話してる', '話す', 2, undefined], ['死んでいる', '死ぬ', 2, undefined], ['死んでる', '死ぬ', 2, undefined], ['飼っている', '飼う', 2, undefined], diff --git a/src/background/deinflect.ts b/src/background/deinflect.ts index b25889a164..aecd2c7335 100644 --- a/src/background/deinflect.ts +++ b/src/background/deinflect.ts @@ -37,6 +37,7 @@ export const enum Reason { SuruNoun, ZaruWoEnai, NegativeTe, + Irregular, } export const deinflectL10NKeys: { [key: number]: string } = { @@ -76,6 +77,7 @@ export const deinflectL10NKeys: { [key: number]: string } = { [Reason.SuruNoun]: 'deinflect_suru_noun', [Reason.ZaruWoEnai]: 'deinflect_zaru_wo_enai', [Reason.NegativeTe]: 'deinflect_negative_te', + [Reason.Irregular]: 'deinflect_irregular', }; const enum Type { @@ -85,14 +87,21 @@ const enum Type { IAdj = 1 << 2, KuruVerb = 1 << 3, SuruVerb = 1 << 4, - NounVS = 1 << 5, - All = IchidanVerb | GodanVerb | IAdj | KuruVerb | SuruVerb | NounVS, + SpecialSuruVerb = 1 << 5, + NounVS = 1 << 6, + All = IchidanVerb | + GodanVerb | + IAdj | + KuruVerb | + SuruVerb | + SpecialSuruVerb | + NounVS, // Intermediate types - Initial = 1 << 6, // original word before any deinflection (from-type only) - TaTeStem = 1 << 7, - DaDeStem = 1 << 8, - MasuStem = 1 << 9, - IrrealisStem = 1 << 10, + Initial = 1 << 7, // original word before any deinflection (from-type only) + TaTeStem = 1 << 8, + DaDeStem = 1 << 9, + MasuStem = 1 << 10, + IrrealisStem = 1 << 11, } export { Type as WordType }; @@ -169,6 +178,12 @@ const deinflectRuleData: Array< ['ざるえぬ', '', Type.IAdj, Type.IrrealisStem, [Reason.ZaruWoEnai]], ['ざる得ぬ', '', Type.IAdj, Type.IrrealisStem, [Reason.ZaruWoEnai]], ['しないで', 'する', Type.Initial, Type.SuruVerb, [Reason.NegativeTe]], + ['しさせる', 'する', Type.IchidanVerb, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Causative]], + ['しられる', 'する', Type.IchidanVerb, Type.SpecialSuruVerb, [Reason.Irregular, Reason.PotentialOrPassive]], + ['せさせる', 'する', Type.IchidanVerb, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Causative]], + ['せられる', 'する', Type.IchidanVerb, Type.SpecialSuruVerb, [Reason.Irregular, Reason.PotentialOrPassive]], + ['ぜさせる', 'ずる', Type.IchidanVerb, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Causative]], + ['ぜられる', 'ずる', Type.IchidanVerb, Type.SpecialSuruVerb, [Reason.Irregular, Reason.PotentialOrPassive]], ['たゆたう', 'たゆたう', Type.TaTeStem, Type.GodanVerb, []], ['たゆとう', 'たゆとう', Type.TaTeStem, Type.GodanVerb, []], ['のたまう', 'のたまう', Type.TaTeStem, Type.GodanVerb, []], @@ -193,6 +208,7 @@ const deinflectRuleData: Array< ['御座い', '御座る', Type.MasuStem, Type.GodanVerb, [Reason.MasuStem]], ['させる', 'る', Type.IchidanVerb, Type.IchidanVerb | Type.KuruVerb, [Reason.Causative]], ['させる', 'する', Type.IchidanVerb, Type.SuruVerb, [Reason.Causative]], + ['さない', 'する', Type.IAdj, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Negative]], ['される', '', Type.IchidanVerb, Type.IrrealisStem, [Reason.CausativePassive]], ['される', 'する', Type.IchidanVerb, Type.SuruVerb, [Reason.Passive]], ['しない', 'する', Type.IAdj, Type.SuruVerb, [Reason.Negative]], @@ -202,6 +218,7 @@ const deinflectRuleData: Array< ['すぎる', '', Type.IchidanVerb, Type.MasuStem, [Reason.Sugiru]], ['過ぎる', 'い', Type.IchidanVerb, Type.IAdj, [Reason.Sugiru]], ['過ぎる', '', Type.IchidanVerb, Type.MasuStem, [Reason.Sugiru]], + ['ずれば', 'ずる', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Ba]], ['たまう', 'たまう', Type.TaTeStem, Type.GodanVerb, []], ['たもう', 'たもう', Type.TaTeStem, Type.GodanVerb, []], ['揺蕩う', '揺蕩う', Type.TaTeStem, Type.GodanVerb, []], @@ -240,6 +257,7 @@ const deinflectRuleData: Array< ['こう', 'く', Type.Initial, Type.GodanVerb, [Reason.Volitional]], ['ごう', 'ぐ', Type.Initial, Type.GodanVerb, [Reason.Volitional]], ['しろ', 'する', Type.Initial, Type.SuruVerb, [Reason.Imperative]], + ['さず', 'する', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Zu]], ['すぎ', 'い', Type.Initial, Type.IAdj, [Reason.Sugiru]], ['すぎ', '', Type.Initial, Type.MasuStem, [Reason.Sugiru]], ['過ぎ', 'い', Type.Initial, Type.IAdj, [Reason.Sugiru]], @@ -249,12 +267,18 @@ const deinflectRuleData: Array< ['せぬ', 'する', Type.Initial, Type.SuruVerb, [Reason.Negative]], ['せん', 'する', Type.Initial, Type.SuruVerb, [Reason.Negative]], ['せば', 'す', Type.Initial, Type.GodanVerb, [Reason.Ba]], + ['せば', 'する', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Ba]], + ['せる', 'する', Type.IchidanVerb, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Potential]], ['せよ', 'する', Type.Initial, Type.SuruVerb, [Reason.Imperative]], ['せる', 'す', Type.IchidanVerb, Type.GodanVerb, [Reason.Potential]], ['せる', '', Type.IchidanVerb, Type.IrrealisStem, [Reason.Causative]], + ['ぜず', 'ずる', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Zu]], + ['ぜぬ', 'ずる', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Negative]], + ['ぜよ', 'ずる', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Imperative]], ['そう', '', Type.Initial, Type.MasuStem, [Reason.Sou]], ['そう', 'い', Type.Initial, Type.IAdj, [Reason.Sou]], ['そう', 'す', Type.Initial, Type.GodanVerb, [Reason.Volitional]], + ['そう', 'する', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Volitional]], ['たい', '', Type.IAdj, Type.MasuStem, [Reason.Tai]], ['たら', '', Type.Initial, Type.TaTeStem, [Reason.Tara]], ['だら', '', Type.Initial, Type.DaDeStem, [Reason.Tara]], @@ -333,6 +357,7 @@ const deinflectRuleData: Array< ['し', 'する', Type.TaTeStem, Type.SuruVerb, []], ['ず', '', Type.Initial, Type.IrrealisStem, [Reason.Zu]], ['せ', 'す', Type.Initial, Type.GodanVerb, [Reason.Imperative]], + ['せ', 'する', Type.Initial, Type.SpecialSuruVerb, [Reason.Irregular, Reason.Imperative]], ['た', 'つ', Type.IrrealisStem, Type.GodanVerb, []], ['た', '', Type.Initial, Type.TaTeStem, [Reason.Past]], ['だ', '', Type.Initial, Type.DaDeStem, [Reason.Past]], diff --git a/src/background/word-search.ts b/src/background/word-search.ts index 55af50eccd..8fdbc99613 100644 --- a/src/background/word-search.ts +++ b/src/background/word-search.ts @@ -286,7 +286,14 @@ function entryMatchesType(entry: DictionaryWordResult, type: number): boolean { if ( type & WordType.SuruVerb && - hasMatchingSense((pos) => pos.startsWith('vs-')) + hasMatchingSense((pos) => pos === 'vs-i' || pos === 'vs-s') + ) { + return true; + } + + if ( + type & WordType.SpecialSuruVerb && + hasMatchingSense((pos) => pos === 'vs-s' || pos === 'vz') ) { return true; }