From 178f170143b7be1a9ac793988555750c7ab953a0 Mon Sep 17 00:00:00 2001 From: Tatsu723 Date: Mon, 24 Aug 2026 21:38:31 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E3=83=95=E3=83=A9=E3=83=B3=E3=82=B9?= =?UTF-8?q?=E8=AA=9E=E5=8F=8A=E3=81=B3=E3=82=B9=E3=83=9A=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E8=AA=9E=E3=81=AE=E6=B4=BB=E7=94=A8=E7=B7=B4=E7=BF=92=E6=95=99?= =?UTF-8?q?=E6=9D=90=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/learn/content.ts | 16 +- app/learn/french/01/page.mdx | 30 - app/learn/french/01/page.tsx | 19 + app/learn/french/01/verbs.ts | 798 +++++++++++++++++++++++ app/learn/french/02/page.mdx | 74 +-- app/learn/french/03/page.mdx | 122 ++-- app/learn/french/04/page.mdx | 68 +- app/learn/french/05/page.mdx | 23 +- app/learn/french/06/page.mdx | 28 +- app/learn/french/07/page.mdx | 32 +- app/learn/french/08/page.mdx | 23 +- app/learn/french/09/page.mdx | 21 +- app/learn/french/10/page.mdx | 24 +- app/learn/french/11/page.mdx | 29 +- app/learn/french/12/page.mdx | 32 +- app/learn/french/13/page.mdx | 36 +- app/learn/french/14/page.mdx | 30 +- app/learn/french/15/page.mdx | 26 +- app/learn/french/16/page.mdx | 24 +- app/learn/french/17/page.mdx | 24 +- app/learn/french/18/page.mdx | 28 +- app/learn/french/19/page.mdx | 27 +- app/learn/french/20/page.mdx | 32 +- app/learn/french/21/page.mdx | 32 +- app/learn/french/22/page.mdx | 17 + app/learn/spanish/01/page.mdx | 12 - app/learn/spanish/01/page.tsx | 19 + app/learn/spanish/01/verbs.ts | 410 ++++++++++++ components/verbs/AccentInput.tsx | 145 ++++ components/verbs/ConjugationPractice.tsx | 385 +++++++++++ lib/conjugation/es/auxiliaries.ts | 18 + lib/conjugation/es/config.ts | 26 + lib/conjugation/es/engine.ts | 80 +++ lib/conjugation/es/types.ts | 85 +++ lib/conjugation/fr/auxiliaries.ts | 25 + lib/conjugation/fr/config.ts | 27 + lib/conjugation/fr/engine.ts | 101 +++ lib/conjugation/fr/types.ts | 82 +++ lib/conjugation/shared.ts | 20 + lib/speech.ts | 67 ++ 40 files changed, 2712 insertions(+), 405 deletions(-) delete mode 100644 app/learn/french/01/page.mdx create mode 100644 app/learn/french/01/page.tsx create mode 100644 app/learn/french/01/verbs.ts create mode 100644 app/learn/french/22/page.mdx delete mode 100644 app/learn/spanish/01/page.mdx create mode 100644 app/learn/spanish/01/page.tsx create mode 100644 app/learn/spanish/01/verbs.ts create mode 100644 components/verbs/AccentInput.tsx create mode 100644 components/verbs/ConjugationPractice.tsx create mode 100644 lib/conjugation/es/auxiliaries.ts create mode 100644 lib/conjugation/es/config.ts create mode 100644 lib/conjugation/es/engine.ts create mode 100644 lib/conjugation/es/types.ts create mode 100644 lib/conjugation/fr/auxiliaries.ts create mode 100644 lib/conjugation/fr/config.ts create mode 100644 lib/conjugation/fr/engine.ts create mode 100644 lib/conjugation/fr/types.ts create mode 100644 lib/conjugation/shared.ts create mode 100644 lib/speech.ts diff --git a/app/learn/content.ts b/app/learn/content.ts index 1747b8d..1aac18a 100644 --- a/app/learn/content.ts +++ b/app/learn/content.ts @@ -1,3 +1,4 @@ +import { existsSync } from "node:fs"; import { readdir } from "node:fs/promises"; import path from "node:path"; @@ -44,6 +45,13 @@ async function listSlugs(languageSlug: string): Promise { ); } +// 任意のセクションについて、page.mdx / page.tsx のうち実際に存在する方の拡張子を返す関数 +// (動詞活用ドリルのような練習教材は、静的なMDXではなくインタラクティブなpage.tsxとして実装されるため) +function resolveSectionExtension(languageSlug: string, sectionSlug: string): "mdx" | "tsx" { + const dirPath = path.join(process.cwd(), "app", "learn", languageSlug, sectionSlug); + return existsSync(path.join(dirPath, "page.tsx")) ? "tsx" : "mdx"; +} + // 任意の言語について、セクションの番号配列を返す関数 export async function getSections(languageSlug: string): Promise { const sectionSlugs = await listSlugs(languageSlug); @@ -52,8 +60,12 @@ export async function getSections(languageSlug: string): Promise { // ここでのimport()は動的インポートであり、指定されたパスの情報を読み込む - // page.mdxファイルの中からtitleプロパティを、分割代入によって取得している - const { title } = await import(`./${languageSlug}/${sectionSlug}/page.mdx`); + // page.mdx / page.tsx ファイルの中からtitleプロパティを、分割代入によって取得している + const extension = resolveSectionExtension(languageSlug, sectionSlug); + const { title } = + extension === "tsx" + ? await import(`./${languageSlug}/${sectionSlug}/page.tsx`) + : await import(`./${languageSlug}/${sectionSlug}/page.mdx`); return { sectionSlug, title }; }), ); diff --git a/app/learn/french/01/page.mdx b/app/learn/french/01/page.mdx deleted file mode 100644 index 8c8b82e..0000000 --- a/app/learn/french/01/page.mdx +++ /dev/null @@ -1,30 +0,0 @@ -export const title = "冠詞の一覧と縮約形"; - -# 冠詞の一覧と縮約形 - -_第1章 品詞と文型より_ - -## 冠詞の種類 - -| | | | 男性形 | 女性形 | -| ------ | -------- | ------ | ---------- | ------------- | -| 可算 | 不定冠詞 | 単数形 | un | une | -| 可算 | 不定冠詞 | 複数形 | des | des | -| 可算 | 定冠詞 | 単数形 | le (l') | la (l') | -| 可算 | 定冠詞 | 複数形 | les | les | -| 不可算 | 部分冠詞 | - | du (de l') | de la (de l') | -| 不可算 | 定冠詞 | - | le (l') | la (l') | - -- 括弧内は直後の名詞が母音始まりの時に用いる。 -- 「du」は「de le」の短縮版であり、部分冠詞は「de+定冠詞」で作られると考えてよい。 - -## 冠詞の縮約形 - -- à+le → au、à+les → aux -- de+le → du、de+les → des - -## 否定文における冠詞「de」 - -否定文において、①不定冠詞や部分冠詞が、②直接目的語に付いている場合、冠詞が「de」に変化する。 - -- J'ai un chien. → Je n'ai pas de chien. diff --git a/app/learn/french/01/page.tsx b/app/learn/french/01/page.tsx new file mode 100644 index 0000000..b4624d8 --- /dev/null +++ b/app/learn/french/01/page.tsx @@ -0,0 +1,19 @@ +"use client"; + +import { ConjugationPractice } from "@/components/verbs/ConjugationPractice"; +import { frenchConjugationConfig } from "@/lib/conjugation/fr/config"; +import { verbs } from "./verbs"; + +export const title = "動詞の活用"; + +export default function FrenchVerbConjugationPage() { + return ( +
+

動詞の活用

+

動詞・時制・法を選んで、活用形を入力しながら覚えましょう。

+
+ +
+
+ ); +} diff --git a/app/learn/french/01/verbs.ts b/app/learn/french/01/verbs.ts new file mode 100644 index 0000000..3f460e6 --- /dev/null +++ b/app/learn/french/01/verbs.ts @@ -0,0 +1,798 @@ +import type { Auxiliary, IrregularVerb, SixForms, VerbEntry } from "@/lib/conjugation/fr/types"; + +// ==== 派生ヘルパー ==== +// フランス語の不規則動詞には、語幹や語尾のパターンを共有する「動詞ファミリー」が多い。 +// 手入力のミスを避けるため、そうしたファミリーはベース動詞や共通パターンから機械的に導出する。 + +// 接頭辞を付けるだけで活用が作れる動詞(revenir=re+venir, apprendre=ap+prendre など) +function prefixed( + base: IrregularVerb, + prefix: string, + id: string, + infinitive: string, + meaning: string, + overrides: Partial<{ auxiliary: Auxiliary; futurStem: string }> = {}, +): IrregularVerb { + const p6 = (arr: SixForms): SixForms => arr.map((f) => prefix + f) as SixForms; + return { + id, + infinitive, + meaning, + kind: "irregular", + auxiliary: overrides.auxiliary ?? base.auxiliary, + pastParticiple: prefix + base.pastParticiple, + présent: p6(base.présent), + subjonctifPrésent: p6(base.subjonctifPrésent), + passéSimple: p6(base.passéSimple), + futurStem: overrides.futurStem ?? prefix + base.futurStem, + ...(base.imparfaitStem ? { imparfaitStem: prefix + base.imparfaitStem } : {}), + }; +} + +// -crire型(écrire, décrire, inscrire, prescrire, souscrire, transcrire) +// stemは「〜cri」で終わる語幹(例: "écri", "décri", "inscri")を渡す +function crireVerb(stem: string, id: string, infinitive: string, meaning: string): IrregularVerb { + const longStem = stem + "v"; + return { + id, + infinitive, + meaning, + kind: "irregular", + auxiliary: "avoir", + pastParticiple: stem + "t", + présent: [stem + "s", stem + "s", stem + "t", longStem + "ons", longStem + "ez", longStem + "ent"], + subjonctifPrésent: [longStem + "e", longStem + "es", longStem + "e", longStem + "ions", longStem + "iez", longStem + "ent"], + passéSimple: [longStem + "is", longStem + "is", longStem + "it", longStem + "îmes", longStem + "îtes", longStem + "irent"], + futurStem: stem + "r", + }; +} + +// -uire型(conduire, produire, traduire, construire, détruire, réduire) +// stemは「〜du/stru」などで終わる語幹(例: "condu", "constru")を渡す +function uireVerb(stem: string, id: string, infinitive: string, meaning: string): IrregularVerb { + return { + id, + infinitive, + meaning, + kind: "irregular", + auxiliary: "avoir", + pastParticiple: stem + "it", + présent: [stem + "is", stem + "is", stem + "it", stem + "isons", stem + "isez", stem + "isent"], + subjonctifPrésent: [stem + "ise", stem + "ises", stem + "ise", stem + "isions", stem + "isiez", stem + "isent"], + passéSimple: [stem + "isis", stem + "isis", stem + "isit", stem + "isîmes", stem + "isîtes", stem + "isirent"], + futurStem: stem + "ir", + }; +} + +// ==== 不規則動詞: 派生のベースとなる基本形 ==== +const etre: IrregularVerb = { + id: "etre", + infinitive: "être", + meaning: "〜である、いる", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "été", + présent: ["suis", "es", "est", "sommes", "êtes", "sont"], + subjonctifPrésent: ["sois", "sois", "soit", "soyons", "soyez", "soient"], + passéSimple: ["fus", "fus", "fut", "fûmes", "fûtes", "furent"], + futurStem: "ser", + imparfaitStem: "ét", +}; + +const avoir: IrregularVerb = { + id: "avoir", + infinitive: "avoir", + meaning: "持っている", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "eu", + présent: ["ai", "as", "a", "avons", "avez", "ont"], + subjonctifPrésent: ["aie", "aies", "ait", "ayons", "ayez", "aient"], + passéSimple: ["eus", "eus", "eut", "eûmes", "eûtes", "eurent"], + futurStem: "aur", +}; + +const aller: IrregularVerb = { + id: "aller", + infinitive: "aller", + meaning: "行く", + kind: "irregular", + auxiliary: "être", + pastParticiple: "allé", + présent: ["vais", "vas", "va", "allons", "allez", "vont"], + subjonctifPrésent: ["aille", "ailles", "aille", "allions", "alliez", "aillent"], + passéSimple: ["allai", "allas", "alla", "allâmes", "allâtes", "allèrent"], + futurStem: "ir", +}; + +const faire: IrregularVerb = { + id: "faire", + infinitive: "faire", + meaning: "する、作る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "fait", + présent: ["fais", "fais", "fait", "faisons", "faites", "font"], + subjonctifPrésent: ["fasse", "fasses", "fasse", "fassions", "fassiez", "fassent"], + passéSimple: ["fis", "fis", "fit", "fîmes", "fîtes", "firent"], + futurStem: "fer", +}; + +const pouvoir: IrregularVerb = { + id: "pouvoir", + infinitive: "pouvoir", + meaning: "〜できる", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "pu", + présent: ["peux", "peux", "peut", "pouvons", "pouvez", "peuvent"], + subjonctifPrésent: ["puisse", "puisses", "puisse", "puissions", "puissiez", "puissent"], + passéSimple: ["pus", "pus", "put", "pûmes", "pûtes", "purent"], + futurStem: "pourr", +}; + +const vouloir: IrregularVerb = { + id: "vouloir", + infinitive: "vouloir", + meaning: "〜したい、欲しい", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "voulu", + présent: ["veux", "veux", "veut", "voulons", "voulez", "veulent"], + subjonctifPrésent: ["veuille", "veuilles", "veuille", "voulions", "vouliez", "veuillent"], + passéSimple: ["voulus", "voulus", "voulut", "voulûmes", "voulûtes", "voulurent"], + futurStem: "voudr", +}; + +const devoir: IrregularVerb = { + id: "devoir", + infinitive: "devoir", + meaning: "〜しなければならない", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "dû", + présent: ["dois", "dois", "doit", "devons", "devez", "doivent"], + subjonctifPrésent: ["doive", "doives", "doive", "devions", "deviez", "doivent"], + passéSimple: ["dus", "dus", "dut", "dûmes", "dûtes", "durent"], + futurStem: "devr", +}; + +const savoir: IrregularVerb = { + id: "savoir", + infinitive: "savoir", + meaning: "知っている", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "su", + présent: ["sais", "sais", "sait", "savons", "savez", "savent"], + subjonctifPrésent: ["sache", "saches", "sache", "sachions", "sachiez", "sachent"], + passéSimple: ["sus", "sus", "sut", "sûmes", "sûtes", "surent"], + futurStem: "saur", +}; + +const voir: IrregularVerb = { + id: "voir", + infinitive: "voir", + meaning: "見る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "vu", + présent: ["vois", "vois", "voit", "voyons", "voyez", "voient"], + subjonctifPrésent: ["voie", "voies", "voie", "voyions", "voyiez", "voient"], + passéSimple: ["vis", "vis", "vit", "vîmes", "vîtes", "virent"], + futurStem: "verr", +}; + +const prendre: IrregularVerb = { + id: "prendre", + infinitive: "prendre", + meaning: "取る、乗る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "pris", + présent: ["prends", "prends", "prend", "prenons", "prenez", "prennent"], + subjonctifPrésent: ["prenne", "prennes", "prenne", "prenions", "preniez", "prennent"], + passéSimple: ["pris", "pris", "prit", "prîmes", "prîtes", "prirent"], + futurStem: "prendr", +}; + +const venir: IrregularVerb = { + id: "venir", + infinitive: "venir", + meaning: "来る", + kind: "irregular", + auxiliary: "être", + pastParticiple: "venu", + présent: ["viens", "viens", "vient", "venons", "venez", "viennent"], + subjonctifPrésent: ["vienne", "viennes", "vienne", "venions", "veniez", "viennent"], + passéSimple: ["vins", "vins", "vint", "vînmes", "vîntes", "vinrent"], + futurStem: "viendr", +}; + +const tenir: IrregularVerb = { + id: "tenir", + infinitive: "tenir", + meaning: "持つ、保つ", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "tenu", + présent: ["tiens", "tiens", "tient", "tenons", "tenez", "tiennent"], + subjonctifPrésent: ["tienne", "tiennes", "tienne", "tenions", "teniez", "tiennent"], + passéSimple: ["tins", "tins", "tint", "tînmes", "tîntes", "tinrent"], + futurStem: "tiendr", +}; + +const dire: IrregularVerb = { + id: "dire", + infinitive: "dire", + meaning: "言う", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "dit", + présent: ["dis", "dis", "dit", "disons", "dites", "disent"], + subjonctifPrésent: ["dise", "dises", "dise", "disions", "disiez", "disent"], + passéSimple: ["dis", "dis", "dit", "dîmes", "dîtes", "dirent"], + futurStem: "dir", +}; + +const mettre: IrregularVerb = { + id: "mettre", + infinitive: "mettre", + meaning: "置く、着る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "mis", + présent: ["mets", "mets", "met", "mettons", "mettez", "mettent"], + subjonctifPrésent: ["mette", "mettes", "mette", "mettions", "mettiez", "mettent"], + passéSimple: ["mis", "mis", "mit", "mîmes", "mîtes", "mirent"], + futurStem: "mettr", +}; + +const partir: IrregularVerb = { + id: "partir", + infinitive: "partir", + meaning: "出発する", + kind: "irregular", + auxiliary: "être", + pastParticiple: "parti", + présent: ["pars", "pars", "part", "partons", "partez", "partent"], + subjonctifPrésent: ["parte", "partes", "parte", "partions", "partiez", "partent"], + passéSimple: ["partis", "partis", "partit", "partîmes", "partîtes", "partirent"], + futurStem: "partir", +}; + +const sortir: IrregularVerb = { + id: "sortir", + infinitive: "sortir", + meaning: "出る、外出する", + kind: "irregular", + auxiliary: "être", + pastParticiple: "sorti", + présent: ["sors", "sors", "sort", "sortons", "sortez", "sortent"], + subjonctifPrésent: ["sorte", "sortes", "sorte", "sortions", "sortiez", "sortent"], + passéSimple: ["sortis", "sortis", "sortit", "sortîmes", "sortîtes", "sortirent"], + futurStem: "sortir", +}; + +const connaitre: IrregularVerb = { + id: "connaitre", + infinitive: "connaître", + meaning: "知っている(人・場所)", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "connu", + présent: ["connais", "connais", "connaît", "connaissons", "connaissez", "connaissent"], + subjonctifPrésent: ["connaisse", "connaisses", "connaisse", "connaissions", "connaissiez", "connaissent"], + passéSimple: ["connus", "connus", "connut", "connûmes", "connûtes", "connurent"], + futurStem: "connaîtr", +}; + +const naitre: IrregularVerb = { + id: "naitre", + infinitive: "naître", + meaning: "生まれる", + kind: "irregular", + auxiliary: "être", + pastParticiple: "né", + présent: ["nais", "nais", "naît", "naissons", "naissez", "naissent"], + subjonctifPrésent: ["naisse", "naisses", "naisse", "naissions", "naissiez", "naissent"], + passéSimple: ["naquis", "naquis", "naquit", "naquîmes", "naquîtes", "naquirent"], + futurStem: "naîtr", +}; + +const joindre: IrregularVerb = { + id: "joindre", + infinitive: "joindre", + meaning: "つなぐ、連絡する", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "joint", + présent: ["joins", "joins", "joint", "joignons", "joignez", "joignent"], + subjonctifPrésent: ["joigne", "joignes", "joigne", "joignions", "joigniez", "joignent"], + passéSimple: ["joignis", "joignis", "joignit", "joignîmes", "joignîtes", "joignirent"], + futurStem: "joindr", +}; + +// これらは他の動詞ファミリーの派生元にはならない、単独の不規則動詞 +const otherBaseIrregularVerbs: IrregularVerb[] = [ + { + id: "valoir", + infinitive: "valoir", + meaning: "価値がある", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "valu", + présent: ["vaux", "vaux", "vaut", "valons", "valez", "valent"], + subjonctifPrésent: ["vaille", "vailles", "vaille", "valions", "valiez", "vaillent"], + passéSimple: ["valus", "valus", "valut", "valûmes", "valûtes", "valurent"], + futurStem: "vaudr", + }, + { + id: "recevoir", + infinitive: "recevoir", + meaning: "受け取る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "reçu", + présent: ["reçois", "reçois", "reçoit", "recevons", "recevez", "reçoivent"], + subjonctifPrésent: ["reçoive", "reçoives", "reçoive", "recevions", "receviez", "reçoivent"], + passéSimple: ["reçus", "reçus", "reçut", "reçûmes", "reçûtes", "reçurent"], + futurStem: "recevr", + }, + { + id: "courir", + infinitive: "courir", + meaning: "走る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "couru", + présent: ["cours", "cours", "court", "courons", "courez", "courent"], + subjonctifPrésent: ["coure", "coures", "coure", "courions", "couriez", "courent"], + passéSimple: ["courus", "courus", "courut", "courûmes", "courûtes", "coururent"], + futurStem: "courr", + }, + { + id: "mourir", + infinitive: "mourir", + meaning: "死ぬ", + kind: "irregular", + auxiliary: "être", + pastParticiple: "mort", + présent: ["meurs", "meurs", "meurt", "mourons", "mourez", "meurent"], + subjonctifPrésent: ["meure", "meures", "meure", "mourions", "mouriez", "meurent"], + passéSimple: ["mourus", "mourus", "mourut", "mourûmes", "mourûtes", "moururent"], + futurStem: "mourr", + }, + { + id: "fuir", + infinitive: "fuir", + meaning: "逃げる", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "fui", + présent: ["fuis", "fuis", "fuit", "fuyons", "fuyez", "fuient"], + subjonctifPrésent: ["fuie", "fuies", "fuie", "fuyions", "fuyiez", "fuient"], + passéSimple: ["fuis", "fuis", "fuit", "fuîmes", "fuîtes", "fuirent"], + futurStem: "fuir", + }, + { + id: "dormir", + infinitive: "dormir", + meaning: "眠る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "dormi", + présent: ["dors", "dors", "dort", "dormons", "dormez", "dorment"], + subjonctifPrésent: ["dorme", "dormes", "dorme", "dormions", "dormiez", "dorment"], + passéSimple: ["dormis", "dormis", "dormit", "dormîmes", "dormîtes", "dormirent"], + futurStem: "dormir", + }, + { + id: "servir", + infinitive: "servir", + meaning: "仕える、役立つ", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "servi", + présent: ["sers", "sers", "sert", "servons", "servez", "servent"], + subjonctifPrésent: ["serve", "serves", "serve", "servions", "serviez", "servent"], + passéSimple: ["servis", "servis", "servit", "servîmes", "servîtes", "servirent"], + futurStem: "servir", + }, + { + id: "mentir", + infinitive: "mentir", + meaning: "嘘をつく", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "menti", + présent: ["mens", "mens", "ment", "mentons", "mentez", "mentent"], + subjonctifPrésent: ["mente", "mentes", "mente", "mentions", "mentiez", "mentent"], + passéSimple: ["mentis", "mentis", "mentit", "mentîmes", "mentîtes", "mentirent"], + futurStem: "mentir", + }, + { + id: "acquerir", + infinitive: "acquérir", + meaning: "獲得する", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "acquis", + présent: ["acquiers", "acquiers", "acquiert", "acquérons", "acquérez", "acquièrent"], + subjonctifPrésent: ["acquière", "acquières", "acquière", "acquérions", "acquériez", "acquièrent"], + passéSimple: ["acquis", "acquis", "acquit", "acquîmes", "acquîtes", "acquirent"], + futurStem: "acquerr", + }, + { + id: "ouvrir", + infinitive: "ouvrir", + meaning: "開ける", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "ouvert", + présent: ["ouvre", "ouvres", "ouvre", "ouvrons", "ouvrez", "ouvrent"], + subjonctifPrésent: ["ouvre", "ouvres", "ouvre", "ouvrions", "ouvriez", "ouvrent"], + passéSimple: ["ouvris", "ouvris", "ouvrit", "ouvrîmes", "ouvrîtes", "ouvrirent"], + futurStem: "ouvrir", + }, + { + id: "offrir", + infinitive: "offrir", + meaning: "贈る、提供する", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "offert", + présent: ["offre", "offres", "offre", "offrons", "offrez", "offrent"], + subjonctifPrésent: ["offre", "offres", "offre", "offrions", "offriez", "offrent"], + passéSimple: ["offris", "offris", "offrit", "offrîmes", "offrîtes", "offrirent"], + futurStem: "offrir", + }, + { + id: "souffrir", + infinitive: "souffrir", + meaning: "苦しむ", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "souffert", + présent: ["souffre", "souffres", "souffre", "souffrons", "souffrez", "souffrent"], + subjonctifPrésent: ["souffre", "souffres", "souffre", "souffrions", "souffriez", "souffrent"], + passéSimple: ["souffris", "souffris", "souffrit", "souffrîmes", "souffrîtes", "souffrirent"], + futurStem: "souffrir", + }, + { + id: "croire", + infinitive: "croire", + meaning: "信じる、思う", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "cru", + présent: ["crois", "crois", "croit", "croyons", "croyez", "croient"], + subjonctifPrésent: ["croie", "croies", "croie", "croyions", "croyiez", "croient"], + passéSimple: ["crus", "crus", "crut", "crûmes", "crûtes", "crurent"], + futurStem: "croir", + }, + { + id: "boire", + infinitive: "boire", + meaning: "飲む", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "bu", + présent: ["bois", "bois", "boit", "buvons", "buvez", "boivent"], + subjonctifPrésent: ["boive", "boives", "boive", "buvions", "buviez", "boivent"], + passéSimple: ["bus", "bus", "but", "bûmes", "bûtes", "burent"], + futurStem: "boir", + }, + { + id: "lire", + infinitive: "lire", + meaning: "読む", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "lu", + présent: ["lis", "lis", "lit", "lisons", "lisez", "lisent"], + subjonctifPrésent: ["lise", "lises", "lise", "lisions", "lisiez", "lisent"], + passéSimple: ["lus", "lus", "lut", "lûmes", "lûtes", "lurent"], + futurStem: "lir", + }, + { + id: "vivre", + infinitive: "vivre", + meaning: "生きる", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "vécu", + présent: ["vis", "vis", "vit", "vivons", "vivez", "vivent"], + subjonctifPrésent: ["vive", "vives", "vive", "vivions", "viviez", "vivent"], + passéSimple: ["vécus", "vécus", "vécut", "vécûmes", "vécûtes", "vécurent"], + futurStem: "vivr", + }, + { + id: "suivre", + infinitive: "suivre", + meaning: "ついていく、受講する", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "suivi", + présent: ["suis", "suis", "suit", "suivons", "suivez", "suivent"], + subjonctifPrésent: ["suive", "suives", "suive", "suivions", "suiviez", "suivent"], + passéSimple: ["suivis", "suivis", "suivit", "suivîmes", "suivîtes", "suivirent"], + futurStem: "suivr", + }, + { + id: "plaire", + infinitive: "plaire", + meaning: "気に入る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "plu", + présent: ["plais", "plais", "plaît", "plaisons", "plaisez", "plaisent"], + subjonctifPrésent: ["plaise", "plaises", "plaise", "plaisions", "plaisiez", "plaisent"], + passéSimple: ["plus", "plus", "plut", "plûmes", "plûtes", "plurent"], + futurStem: "plair", + }, + { + id: "rire", + infinitive: "rire", + meaning: "笑う", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "ri", + présent: ["ris", "ris", "rit", "rions", "riez", "rient"], + subjonctifPrésent: ["rie", "ries", "rie", "riions", "riiez", "rient"], + passéSimple: ["ris", "ris", "rit", "rîmes", "rîtes", "rirent"], + futurStem: "rir", + }, + { + id: "conclure", + infinitive: "conclure", + meaning: "結論づける", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "conclu", + présent: ["conclus", "conclus", "conclut", "concluons", "concluez", "concluent"], + subjonctifPrésent: ["conclue", "conclues", "conclue", "concluions", "concluiez", "concluent"], + passéSimple: ["conclus", "conclus", "conclut", "conclûmes", "conclûtes", "conclurent"], + futurStem: "conclur", + }, + { + id: "battre", + infinitive: "battre", + meaning: "打つ、負かす", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "battu", + présent: ["bats", "bats", "bat", "battons", "battez", "battent"], + subjonctifPrésent: ["batte", "battes", "batte", "battions", "battiez", "battent"], + passéSimple: ["battis", "battis", "battit", "battîmes", "battîtes", "battirent"], + futurStem: "battr", + }, + { + id: "vaincre", + infinitive: "vaincre", + meaning: "打ち負かす", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "vaincu", + présent: ["vaincs", "vaincs", "vainc", "vainquons", "vainquez", "vainquent"], + subjonctifPrésent: ["vainque", "vainques", "vainque", "vainquions", "vainquiez", "vainquent"], + passéSimple: ["vainquis", "vainquis", "vainquit", "vainquîmes", "vainquîtes", "vainquirent"], + futurStem: "vaincr", + }, + { + id: "craindre", + infinitive: "craindre", + meaning: "恐れる", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "craint", + présent: ["crains", "crains", "craint", "craignons", "craignez", "craignent"], + subjonctifPrésent: ["craigne", "craignes", "craigne", "craignions", "craigniez", "craignent"], + passéSimple: ["craignis", "craignis", "craignit", "craignîmes", "craignîtes", "craignirent"], + futurStem: "craindr", + }, + { + id: "peindre", + infinitive: "peindre", + meaning: "描く、塗る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "peint", + présent: ["peins", "peins", "peint", "peignons", "peignez", "peignent"], + subjonctifPrésent: ["peigne", "peignes", "peigne", "peignions", "peigniez", "peignent"], + passéSimple: ["peignis", "peignis", "peignit", "peignîmes", "peignîtes", "peignirent"], + futurStem: "peindr", + }, + { + id: "plaindre", + infinitive: "plaindre", + meaning: "気の毒に思う", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "plaint", + présent: ["plains", "plains", "plaint", "plaignons", "plaignez", "plaignent"], + subjonctifPrésent: ["plaigne", "plaignes", "plaigne", "plaignions", "plaigniez", "plaignent"], + passéSimple: ["plaignis", "plaignis", "plaignit", "plaignîmes", "plaignîtes", "plaignirent"], + futurStem: "plaindr", + }, + { + id: "resoudre", + infinitive: "résoudre", + meaning: "解決する", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "résolu", + présent: ["résous", "résous", "résout", "résolvons", "résolvez", "résolvent"], + subjonctifPrésent: ["résolve", "résolves", "résolve", "résolvions", "résolviez", "résolvent"], + passéSimple: ["résolus", "résolus", "résolut", "résolûmes", "résolûtes", "résolurent"], + futurStem: "résoudr", + }, + { + id: "coudre", + infinitive: "coudre", + meaning: "縫う", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "cousu", + présent: ["couds", "couds", "coud", "cousons", "cousez", "cousent"], + subjonctifPrésent: ["couse", "couses", "couse", "cousions", "cousiez", "cousent"], + passéSimple: ["cousis", "cousis", "cousit", "cousîmes", "cousîtes", "cousirent"], + futurStem: "coudr", + }, + { + id: "appeler", + infinitive: "appeler", + meaning: "呼ぶ、電話する", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "appelé", + présent: ["appelle", "appelles", "appelle", "appelons", "appelez", "appellent"], + subjonctifPrésent: ["appelle", "appelles", "appelle", "appelions", "appeliez", "appellent"], + passéSimple: ["appelai", "appelas", "appela", "appelâmes", "appelâtes", "appelèrent"], + futurStem: "appeller", + }, + { + id: "acheter", + infinitive: "acheter", + meaning: "買う", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "acheté", + présent: ["achète", "achètes", "achète", "achetons", "achetez", "achètent"], + subjonctifPrésent: ["achète", "achètes", "achète", "achetions", "achetiez", "achètent"], + passéSimple: ["achetai", "achetas", "acheta", "achetâmes", "achetâtes", "achetèrent"], + futurStem: "achèter", + }, + { + id: "preferer", + infinitive: "préférer", + meaning: "〜の方を好む", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "préféré", + présent: ["préfère", "préfères", "préfère", "préférons", "préférez", "préfèrent"], + subjonctifPrésent: ["préfère", "préfères", "préfère", "préférions", "préfériez", "préfèrent"], + passéSimple: ["préférai", "préféras", "préféra", "préférâmes", "préférâtes", "préférèrent"], + futurStem: "préférer", + }, + { + id: "envoyer", + infinitive: "envoyer", + meaning: "送る", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "envoyé", + présent: ["envoie", "envoies", "envoie", "envoyons", "envoyez", "envoient"], + subjonctifPrésent: ["envoie", "envoies", "envoie", "envoyions", "envoyiez", "envoient"], + passéSimple: ["envoyai", "envoyas", "envoya", "envoyâmes", "envoyâtes", "envoyèrent"], + futurStem: "enverr", + }, + { + id: "employer", + infinitive: "employer", + meaning: "使う、雇う", + kind: "irregular", + auxiliary: "avoir", + pastParticiple: "employé", + présent: ["emploie", "emploies", "emploie", "employons", "employez", "emploient"], + subjonctifPrésent: ["emploie", "emploies", "emploie", "employions", "employiez", "emploient"], + passéSimple: ["employai", "employas", "employa", "employâmes", "employâtes", "employèrent"], + futurStem: "emploier", + }, +]; + +// ==== -crire型・-uire型のファミリー ==== +const crireFamily: IrregularVerb[] = [ + crireVerb("écri", "ecrire", "écrire", "書く"), + crireVerb("décri", "decrire", "décrire", "描写する"), + crireVerb("inscri", "inscrire", "inscrire", "登録する、記入する"), + crireVerb("prescri", "prescrire", "prescrire", "処方する"), + crireVerb("souscri", "souscrire", "souscrire", "加入する、署名する"), + crireVerb("transcri", "transcrire", "transcrire", "書き写す"), +]; + +const uireFamily: IrregularVerb[] = [ + uireVerb("condu", "conduire", "conduire", "運転する"), + uireVerb("produ", "produire", "produire", "生産する"), + uireVerb("tradu", "traduire", "traduire", "翻訳する"), + uireVerb("constru", "construire", "construire", "建てる"), + uireVerb("détru", "detruire", "détruire", "破壊する"), + uireVerb("rédu", "reduire", "réduire", "減らす"), +]; + +// ==== 接頭辞による派生動詞 ==== +const prefixedFamily: IrregularVerb[] = [ + prefixed(venir, "re", "revenir", "revenir", "戻ってくる"), + prefixed(venir, "de", "devenir", "devenir", "〜になる"), + prefixed(venir, "pré", "prevenir", "prévenir", "警告する、予防する", { auxiliary: "avoir" }), + + prefixed(tenir, "re", "retenir", "retenir", "引き止める、覚えておく"), + prefixed(tenir, "ob", "obtenir", "obtenir", "得る"), + prefixed(tenir, "sou", "soutenir", "soutenir", "支える"), + prefixed(tenir, "con", "contenir", "contenir", "含む"), + prefixed(tenir, "appar", "appartenir", "appartenir", "〜に属する"), + prefixed(tenir, "entre", "entretenir", "entretenir", "維持する"), + + prefixed(prendre, "ap", "apprendre", "apprendre", "学ぶ"), + prefixed(prendre, "com", "comprendre", "comprendre", "理解する"), + prefixed(prendre, "sur", "surprendre", "surprendre", "驚かせる"), + prefixed(prendre, "re", "reprendre", "reprendre", "取り戻す、再開する"), + + prefixed(mettre, "per", "permettre", "permettre", "許可する"), + prefixed(mettre, "pro", "promettre", "promettre", "約束する"), + prefixed(mettre, "re", "remettre", "remettre", "戻す、延期する"), + prefixed(mettre, "sou", "soumettre", "soumettre", "従わせる、提出する"), + prefixed(mettre, "ad", "admettre", "admettre", "認める"), + + prefixed(voir, "re", "revoir", "revoir", "再び会う"), + prefixed(voir, "pré", "prevoir", "prévoir", "予測する", { futurStem: "prévoir" }), + + prefixed(dire, "re", "redire", "redire", "繰り返して言う"), + + prefixed(connaitre, "re", "reconnaitre", "reconnaître", "認識する"), + + prefixed(naitre, "re", "renaitre", "renaître", "生まれ変わる"), + + prefixed(joindre, "re", "rejoindre", "rejoindre", "合流する"), +]; + +const irregularVerbs: VerbEntry[] = [ + etre, + avoir, + aller, + faire, + pouvoir, + vouloir, + devoir, + savoir, + voir, + prendre, + venir, + tenir, + dire, + mettre, + partir, + sortir, + connaitre, + naitre, + joindre, + ...otherBaseIrregularVerbs, + ...crireFamily, + ...uireFamily, + ...prefixedFamily, +]; + +// 規則動詞: 語尾変化のルールだけで活用可能なため、原形とグループのみ保持する +const regularVerbs: VerbEntry[] = [ + { id: "parler", infinitive: "parler", meaning: "話す", kind: "regular", group: 1, auxiliary: "avoir" }, + { id: "aimer", infinitive: "aimer", meaning: "好きである、愛する", kind: "regular", group: 1, auxiliary: "avoir" }, + { id: "chanter", infinitive: "chanter", meaning: "歌う", kind: "regular", group: 1, auxiliary: "avoir" }, + { id: "finir", infinitive: "finir", meaning: "終える", kind: "regular", group: 2, auxiliary: "avoir" }, + { id: "choisir", infinitive: "choisir", meaning: "選ぶ", kind: "regular", group: 2, auxiliary: "avoir" }, + { id: "reussir", infinitive: "réussir", meaning: "成功する", kind: "regular", group: 2, auxiliary: "avoir" }, + { id: "vendre", infinitive: "vendre", meaning: "売る", kind: "regular", group: 3, auxiliary: "avoir" }, + { id: "attendre", infinitive: "attendre", meaning: "待つ", kind: "regular", group: 3, auxiliary: "avoir" }, + { id: "repondre", infinitive: "répondre", meaning: "答える", kind: "regular", group: 3, auxiliary: "avoir" }, +]; + +export const verbs: VerbEntry[] = [...irregularVerbs, ...regularVerbs]; diff --git a/app/learn/french/02/page.mdx b/app/learn/french/02/page.mdx index a3dfbb8..8c8b82e 100644 --- a/app/learn/french/02/page.mdx +++ b/app/learn/french/02/page.mdx @@ -1,66 +1,30 @@ -export const title = "基本動詞の直説法現在活用"; +export const title = "冠詞の一覧と縮約形"; -# 基本動詞の直説法現在活用 +# 冠詞の一覧と縮約形 _第1章 品詞と文型より_ -## être(be動詞) +## 冠詞の種類 -| 人称 | 活用形 | 人称 | 活用形 | -| ---------- | ------ | ---------- | ------ | -| 一人称単数 | suis | 一人称複数 | sommes | -| 二人称単数 | es | 二人称複数 | êtes | -| 三人称単数 | est | 三人称複数 | sont | +| | | | 男性形 | 女性形 | +| ------ | -------- | ------ | ---------- | ------------- | +| 可算 | 不定冠詞 | 単数形 | un | une | +| 可算 | 不定冠詞 | 複数形 | des | des | +| 可算 | 定冠詞 | 単数形 | le (l') | la (l') | +| 可算 | 定冠詞 | 複数形 | les | les | +| 不可算 | 部分冠詞 | - | du (de l') | de la (de l') | +| 不可算 | 定冠詞 | - | le (l') | la (l') | -## avoir(have) +- 括弧内は直後の名詞が母音始まりの時に用いる。 +- 「du」は「de le」の短縮版であり、部分冠詞は「de+定冠詞」で作られると考えてよい。 -| 人称 | 活用形 | 人称 | 活用形 | -| ---------- | ------ | ---------- | ------ | -| 一人称単数 | ai | 一人称複数 | avons | -| 二人称単数 | as | 二人称複数 | avez | -| 三人称単数 | a | 三人称複数 | ont | +## 冠詞の縮約形 -## faire(make, do) +- à+le → au、à+les → aux +- de+le → du、de+les → des -| 人称 | 活用形 | 人称 | 活用形 | -| ---------- | ------ | ---------- | ------- | -| 一人称単数 | fais | 一人称複数 | faisons | -| 二人称単数 | fais | 二人称複数 | faites | -| 三人称単数 | fait | 三人称複数 | font | +## 否定文における冠詞「de」 -## aller(go) +否定文において、①不定冠詞や部分冠詞が、②直接目的語に付いている場合、冠詞が「de」に変化する。 -| 人称 | 活用形 | 人称 | 活用形 | -| ---------- | ------ | ---------- | ------ | -| 一人称単数 | vais | 一人称複数 | allons | -| 二人称単数 | vas | 二人称複数 | allez | -| 三人称単数 | va | 三人称複数 | vont | - -## venir(come) - -| 人称 | 活用形 | 人称 | 活用形 | -| ---------- | ------ | ---------- | -------- | -| 一人称単数 | viens | 一人称複数 | venons | -| 二人称単数 | viens | 二人称複数 | venez | -| 三人称単数 | vient | 三人称複数 | viennent | - -## -er動詞 - -| 人称 | 語尾 | 人称 | 語尾 | -| ---------- | ---- | ---------- | ---- | -| 一人称単数 | -e | 一人称複数 | -ons | -| 二人称単数 | -es | 二人称複数 | -ez | -| 三人称単数 | -e | 三人称複数 | -ent | - -- -gerで終わる動詞(manger等)の一人称複数形は -geons -- -cerで終わる動詞(commencer等)の一人称複数形は -çons - -## -ir動詞 - -| 人称 | 語尾 | 人称 | 語尾 | -| ---------- | ---- | ---------- | ------- | -| 一人称単数 | -is | 一人称複数 | -issons | -| 二人称単数 | -is | 二人称複数 | -issez | -| 三人称単数 | -it | 三人称複数 | -issent | - -- 複数形の活用は「-iss+er動詞の活用形」の形になっている。 +- J'ai un chien. → Je n'ai pas de chien. diff --git a/app/learn/french/03/page.mdx b/app/learn/french/03/page.mdx index 3eef46d..a3dfbb8 100644 --- a/app/learn/french/03/page.mdx +++ b/app/learn/french/03/page.mdx @@ -1,64 +1,66 @@ -export const title = "基数と序数と概数"; +export const title = "基本動詞の直説法現在活用"; -# 基数と序数と概数 +# 基本動詞の直説法現在活用 _第1章 品詞と文型より_ -## 基数(0〜19) - -| 数 | 綴り | 数 | 綴り | -| --- | --------- | --- | -------- | -| 0 | zéro | 10 | dix | -| 1 | un(une) | 11 | onze | -| 2 | deux | 12 | douze | -| 3 | trois | 13 | treize | -| 4 | quatre | 14 | quatorze | -| 5 | cinq | 15 | quinze | -| 6 | six | 16 | seize | -| 7 | sept | 17 | dix-sept | -| 8 | huit | 18 | dix-huit | -| 9 | neuf | 19 | dix-neuf | - -## 基数(20の倍数) - -| 数 | 綴り | 数 | 綴り | -| --- | ------------- | --- | --------- | -| 20 | vingt | 30 | trente | -| 40 | quarante | 50 | cinquante | -| 60 | soixante | - | - | -| 80 | quatre-vingts | - | - | - -- 21〜99は上記の組み合わせで表現する:「{"A"の数字と"B"の数字の和となる基数}={"B"の基数}-{"A"の基数}」 - - 22:vingt-deux(20+2) - - 75:soixante-quinze(60+15) -- "B"として80を組み合わせに用いる場合は「quatre-vingt」(sなし)になる。 -- 基数10〜19を組み合わせで使うのは70〜79及び90〜99を表すときのみ。 -- 一桁目が1を含むとき、間に「et」を挟んでハイフンでつなぐ。 - - 31:trente-et-un - - 91:quatre-vingt-onze - -## 3桁以上の基数 - -| 桁 | 数 | 綴り | -| ------- | ----------------- | -------- | -| 3桁 | 100 | cent | -| 4桁以上 | 1.000 | mille | -| 4桁以上 | 1.000.000 | million | -| 4桁以上 | 1.000.000.000 | milliard | -| 4桁以上 | 1.000.000.000.000 | billion | - -## 序数 - -原則:基数の後ろに-ièmeをつける。 - -- deux → deuxième -- eで終わる数字はeを取ってièmeをつける(quatre → quatrième) -- 「un(une)」は不規則で「premier(première)」 -- cinq は語尾にuを加えて「cinquième」 -- neuf は語尾のfがvになり「neuvième」 - -## 概数 - -基数の語尾に-aineを付けて作る。女性名詞として扱われるため、不定冠詞「une」や定冠詞「la」を伴うことがほとんど(1000の概数millierだけ男性名詞)。後ろに名詞が続く場合は前置詞「de」が必要。 - -- vingt → vingtaine +## être(be動詞) + +| 人称 | 活用形 | 人称 | 活用形 | +| ---------- | ------ | ---------- | ------ | +| 一人称単数 | suis | 一人称複数 | sommes | +| 二人称単数 | es | 二人称複数 | êtes | +| 三人称単数 | est | 三人称複数 | sont | + +## avoir(have) + +| 人称 | 活用形 | 人称 | 活用形 | +| ---------- | ------ | ---------- | ------ | +| 一人称単数 | ai | 一人称複数 | avons | +| 二人称単数 | as | 二人称複数 | avez | +| 三人称単数 | a | 三人称複数 | ont | + +## faire(make, do) + +| 人称 | 活用形 | 人称 | 活用形 | +| ---------- | ------ | ---------- | ------- | +| 一人称単数 | fais | 一人称複数 | faisons | +| 二人称単数 | fais | 二人称複数 | faites | +| 三人称単数 | fait | 三人称複数 | font | + +## aller(go) + +| 人称 | 活用形 | 人称 | 活用形 | +| ---------- | ------ | ---------- | ------ | +| 一人称単数 | vais | 一人称複数 | allons | +| 二人称単数 | vas | 二人称複数 | allez | +| 三人称単数 | va | 三人称複数 | vont | + +## venir(come) + +| 人称 | 活用形 | 人称 | 活用形 | +| ---------- | ------ | ---------- | -------- | +| 一人称単数 | viens | 一人称複数 | venons | +| 二人称単数 | viens | 二人称複数 | venez | +| 三人称単数 | vient | 三人称複数 | viennent | + +## -er動詞 + +| 人称 | 語尾 | 人称 | 語尾 | +| ---------- | ---- | ---------- | ---- | +| 一人称単数 | -e | 一人称複数 | -ons | +| 二人称単数 | -es | 二人称複数 | -ez | +| 三人称単数 | -e | 三人称複数 | -ent | + +- -gerで終わる動詞(manger等)の一人称複数形は -geons +- -cerで終わる動詞(commencer等)の一人称複数形は -çons + +## -ir動詞 + +| 人称 | 語尾 | 人称 | 語尾 | +| ---------- | ---- | ---------- | ------- | +| 一人称単数 | -is | 一人称複数 | -issons | +| 二人称単数 | -is | 二人称複数 | -issez | +| 三人称単数 | -it | 三人称複数 | -issent | + +- 複数形の活用は「-iss+er動詞の活用形」の形になっている。 diff --git a/app/learn/french/04/page.mdx b/app/learn/french/04/page.mdx index 311db40..3eef46d 100644 --- a/app/learn/french/04/page.mdx +++ b/app/learn/french/04/page.mdx @@ -1,24 +1,64 @@ -export const title = "時刻表現"; +export const title = "基数と序数と概数"; -# 時刻表現 +# 基数と序数と概数 _第1章 品詞と文型より_ -「〜時(〜分)」は「〜heures(〜)」で表現する。 +## 基数(0〜19) -- Il est deux heures dix. (2時10分です。) +| 数 | 綴り | 数 | 綴り | +| --- | --------- | --- | -------- | +| 0 | zéro | 10 | dix | +| 1 | un(une) | 11 | onze | +| 2 | deux | 12 | douze | +| 3 | trois | 13 | treize | +| 4 | quatre | 14 | quatorze | +| 5 | cinq | 15 | quinze | +| 6 | six | 16 | seize | +| 7 | sept | 17 | dix-sept | +| 8 | huit | 18 | dix-huit | +| 9 | neuf | 19 | dix-neuf | -## 定型表現 +## 基数(20の倍数) -- 「〜時15分」は「et quart」 -- 「〜時30分」は「et demi(e)」 -- 「〜時15分前」は「moins le quart」 -- これらは「〜heures」の後に伴って表現する。 - - Il est sept heures et demie. (7時半です。) +| 数 | 綴り | 数 | 綴り | +| --- | ------------- | --- | --------- | +| 20 | vingt | 30 | trente | +| 40 | quarante | 50 | cinquante | +| 60 | soixante | - | - | +| 80 | quatre-vingts | - | - | -## 秒までの表記 +- 21〜99は上記の組み合わせで表現する:「{"A"の数字と"B"の数字の和となる基数}={"B"の基数}-{"A"の基数}」 + - 22:vingt-deux(20+2) + - 75:soixante-quinze(60+15) +- "B"として80を組み合わせに用いる場合は「quatre-vingt」(sなし)になる。 +- 基数10〜19を組み合わせで使うのは70〜79及び90〜99を表すときのみ。 +- 一桁目が1を含むとき、間に「et」を挟んでハイフンでつなぐ。 + - 31:trente-et-un + - 91:quatre-vingt-onze -時間帯を秒数まで細かく指定する場合、「et」を用いて秒数を続け、単位も記載する。表記は「heure → h、minute → min、seconde → s」とする。 +## 3桁以上の基数 -- Il est seize heures vingt et une minute et dix-sept secondes. - = Il est 16 h 21 min 17 s.(16時21分17秒です。) +| 桁 | 数 | 綴り | +| ------- | ----------------- | -------- | +| 3桁 | 100 | cent | +| 4桁以上 | 1.000 | mille | +| 4桁以上 | 1.000.000 | million | +| 4桁以上 | 1.000.000.000 | milliard | +| 4桁以上 | 1.000.000.000.000 | billion | + +## 序数 + +原則:基数の後ろに-ièmeをつける。 + +- deux → deuxième +- eで終わる数字はeを取ってièmeをつける(quatre → quatrième) +- 「un(une)」は不規則で「premier(première)」 +- cinq は語尾にuを加えて「cinquième」 +- neuf は語尾のfがvになり「neuvième」 + +## 概数 + +基数の語尾に-aineを付けて作る。女性名詞として扱われるため、不定冠詞「une」や定冠詞「la」を伴うことがほとんど(1000の概数millierだけ男性名詞)。後ろに名詞が続く場合は前置詞「de」が必要。 + +- vingt → vingtaine diff --git a/app/learn/french/05/page.mdx b/app/learn/french/05/page.mdx index e2df8af..311db40 100644 --- a/app/learn/french/05/page.mdx +++ b/app/learn/french/05/page.mdx @@ -1,11 +1,24 @@ -export const title = "日程表現"; +export const title = "時刻表現"; -# 日程表現 +# 時刻表現 _第1章 品詞と文型より_ -事細かに表す場合は「le+曜日+日+月+年」で表現する。日程を単に述べる場合、その主語は「nous」または「on」になり、動詞には「être」を用いる。 +「〜時(〜分)」は「〜heures(〜)」で表現する。 -- Nous sommes le lundi 3 janvier 2022. (2022年1月3日月曜日です。) +- Il est deux heures dix. (2時10分です。) -「〜日」は通常基数で表すが、「1日」だけ例外で序数「premier」を使う。 +## 定型表現 + +- 「〜時15分」は「et quart」 +- 「〜時30分」は「et demi(e)」 +- 「〜時15分前」は「moins le quart」 +- これらは「〜heures」の後に伴って表現する。 + - Il est sept heures et demie. (7時半です。) + +## 秒までの表記 + +時間帯を秒数まで細かく指定する場合、「et」を用いて秒数を続け、単位も記載する。表記は「heure → h、minute → min、seconde → s」とする。 + +- Il est seize heures vingt et une minute et dix-sept secondes. + = Il est 16 h 21 min 17 s.(16時21分17秒です。) diff --git a/app/learn/french/06/page.mdx b/app/learn/french/06/page.mdx index 13452e0..e2df8af 100644 --- a/app/learn/french/06/page.mdx +++ b/app/learn/french/06/page.mdx @@ -1,25 +1,11 @@ -export const title = "人称代名詞"; +export const title = "日程表現"; -# 人称代名詞 +# 日程表現 -_第3章 人称詞と指示詞より_ +_第1章 品詞と文型より_ -| | | 主語 | 直接目的語 | 間接目的語 | 強勢形 | -| ---------- | --- | ----- | ---------- | ---------- | ------ | -| 一人称単数 | | je | me | me | moi | -| 二人称単数 | | tu | te | te | toi | -| 三人称単数 | 男 | il | le | lui | lui | -| 三人称単数 | 女 | elle | la | lui | elle | -| 一人称複数 | | nous | nous | nous | nous | -| 二人称複数 | | vous | vous | vous | vous | -| 三人称複数 | 男 | ils | les | leur | eux | -| 三人称複数 | 女 | elles | les | leur | elles | +事細かに表す場合は「le+曜日+日+月+年」で表現する。日程を単に述べる場合、その主語は「nous」または「on」になり、動詞には「être」を用いる。 -- 強勢形は、前置詞の目的語や対比・強調などに用いられる。 -- 三人称の人称代名詞は非人称的な使い方で用いられることがある。 -- 目的語人称代名詞が再帰代名詞となる場合、三人称のものは「se」となる。 -- 不定主語の「on」(文法上は三人称単数)は「人は」「世間では」「一般に」「私たち」といった意味を持つ。 - - Il lit et l'on écoute. (彼が読み、人々が聞く。) - - この「l'」は母音で終わる特定の単語の後で母音の衝突を避けるために使われ、文法的な意味はない。 -- 目的語代名詞は原則動詞の前に持ってくる(不定詞などの場合でも同様)。 -- 複数の目的語代名詞が並ぶときの順序:「一・二人称間接目的語 → 三人称直接目的語 → 三人称間接目的語」。一・二人称直接目的語は他の目的語と同時には使えない。 +- Nous sommes le lundi 3 janvier 2022. (2022年1月3日月曜日です。) + +「〜日」は通常基数で表すが、「1日」だけ例外で序数「premier」を使う。 diff --git a/app/learn/french/07/page.mdx b/app/learn/french/07/page.mdx index c29b720..13452e0 100644 --- a/app/learn/french/07/page.mdx +++ b/app/learn/french/07/page.mdx @@ -1,17 +1,25 @@ -export const title = "所有形容詞"; +export const title = "人称代名詞"; -# 所有形容詞 +# 人称代名詞 _第3章 人称詞と指示詞より_ -| | 単数(男) | 単数(女) | 複数 | -| ---------- | ---------- | ---------- | ----- | -| 一人称単数 | mon | ma (mon) | mes | -| 二人称単数 | ton | ta (ton) | tes | -| 三人称単数 | son | sa (son) | ses | -| 一人称複数 | notre | notre | nos | -| 二人称複数 | votre | votre | vos | -| 三人称複数 | leur | leur | leurs | +| | | 主語 | 直接目的語 | 間接目的語 | 強勢形 | +| ---------- | --- | ----- | ---------- | ---------- | ------ | +| 一人称単数 | | je | me | me | moi | +| 二人称単数 | | tu | te | te | toi | +| 三人称単数 | 男 | il | le | lui | lui | +| 三人称単数 | 女 | elle | la | lui | elle | +| 一人称複数 | | nous | nous | nous | nous | +| 二人称複数 | | vous | vous | vous | vous | +| 三人称複数 | 男 | ils | les | leur | eux | +| 三人称複数 | 女 | elles | les | leur | elles | -- 所有形容詞は、修飾する名詞によって形が変化する。 -- 修飾する語の先頭が母音時や無音のhの時、一部の女性形は括弧内のように変化する。 +- 強勢形は、前置詞の目的語や対比・強調などに用いられる。 +- 三人称の人称代名詞は非人称的な使い方で用いられることがある。 +- 目的語人称代名詞が再帰代名詞となる場合、三人称のものは「se」となる。 +- 不定主語の「on」(文法上は三人称単数)は「人は」「世間では」「一般に」「私たち」といった意味を持つ。 + - Il lit et l'on écoute. (彼が読み、人々が聞く。) + - この「l'」は母音で終わる特定の単語の後で母音の衝突を避けるために使われ、文法的な意味はない。 +- 目的語代名詞は原則動詞の前に持ってくる(不定詞などの場合でも同様)。 +- 複数の目的語代名詞が並ぶときの順序:「一・二人称間接目的語 → 三人称直接目的語 → 三人称間接目的語」。一・二人称直接目的語は他の目的語と同時には使えない。 diff --git a/app/learn/french/08/page.mdx b/app/learn/french/08/page.mdx index 38022bd..c29b720 100644 --- a/app/learn/french/08/page.mdx +++ b/app/learn/french/08/page.mdx @@ -1,16 +1,17 @@ -export const title = "所有代名詞"; +export const title = "所有形容詞"; -# 所有代名詞 +# 所有形容詞 _第3章 人称詞と指示詞より_ -英語のmineなどにあたり、「〜のもの」と訳される。常に定冠詞とセットで、置き換える「もの」の性数によって形が変化する。 +| | 単数(男) | 単数(女) | 複数 | +| ---------- | ---------- | ---------- | ----- | +| 一人称単数 | mon | ma (mon) | mes | +| 二人称単数 | ton | ta (ton) | tes | +| 三人称単数 | son | sa (son) | ses | +| 一人称複数 | notre | notre | nos | +| 二人称複数 | votre | votre | vos | +| 三人称複数 | leur | leur | leurs | -| | 男性単数 | 女性単数 | 男性複数 | 女性複数 | -| ---------- | -------- | --------- | ---------- | ----------- | -| 一人称単数 | le mien | la mienne | les miens | les miennes | -| 二人称単数 | le tien | la tienne | les tiens | les tiennes | -| 三人称単数 | le sien | la sienne | les siens | les siennes | -| 一人称複数 | le nôtre | la nôtre | les nôtres | les nôtres | -| 二人称複数 | le vôtre | la vôtre | les vôtres | les vôtres | -| 三人称複数 | le leur | la leur | les leurs | les leurs | +- 所有形容詞は、修飾する名詞によって形が変化する。 +- 修飾する語の先頭が母音時や無音のhの時、一部の女性形は括弧内のように変化する。 diff --git a/app/learn/french/09/page.mdx b/app/learn/french/09/page.mdx index 8945c51..38022bd 100644 --- a/app/learn/french/09/page.mdx +++ b/app/learn/french/09/page.mdx @@ -1,15 +1,16 @@ -export const title = "指示形容詞"; +export const title = "所有代名詞"; -# 指示形容詞 +# 所有代名詞 _第3章 人称詞と指示詞より_ -「この」「その」「あの」に相当し、フランス語ではこれらすべてが同じ単語で表される。 +英語のmineなどにあたり、「〜のもの」と訳される。常に定冠詞とセットで、置き換える「もの」の性数によって形が変化する。 -| | 男性 | 女性 | -| ---- | -------- | ----- | -| 単数 | ce (cet) | cette | -| 複数 | ces | ces | - -- 修飾する名詞が母音始まりの男性単数形の時、発音の都合上cetを用いる。 -- 近称と遠称を区別したいときは、修飾する名詞の後ろに-ci(近称)や-là(遠称)をつける。 +| | 男性単数 | 女性単数 | 男性複数 | 女性複数 | +| ---------- | -------- | --------- | ---------- | ----------- | +| 一人称単数 | le mien | la mienne | les miens | les miennes | +| 二人称単数 | le tien | la tienne | les tiens | les tiennes | +| 三人称単数 | le sien | la sienne | les siens | les siennes | +| 一人称複数 | le nôtre | la nôtre | les nôtres | les nôtres | +| 二人称複数 | le vôtre | la vôtre | les vôtres | les vôtres | +| 三人称複数 | le leur | la leur | les leurs | les leurs | diff --git a/app/learn/french/10/page.mdx b/app/learn/french/10/page.mdx index b8e1d98..8945c51 100644 --- a/app/learn/french/10/page.mdx +++ b/app/learn/french/10/page.mdx @@ -1,21 +1,15 @@ -export const title = "指示代名詞"; +export const title = "指示形容詞"; -# 指示代名詞 +# 指示形容詞 _第3章 人称詞と指示詞より_ -## パターン1 +「この」「その」「あの」に相当し、フランス語ではこれらすべてが同じ単語で表される。 -| | これ | それ | -| -------- | ---- | -------- | -| 無強勢形 | ce | ce | -| 強勢形 | ceci | cela(ça) | +| | 男性 | 女性 | +| ---- | -------- | ----- | +| 単数 | ce (cet) | cette | +| 複数 | ces | ces | -## パターン2 - -| | 男性 | 女性 | -| ---- | ----- | ------ | -| 単数 | celui | celle | -| 複数 | ceux | celles | - -- 近称と遠称を区別したいときには、それぞれの後ろに-ci(近称)や-là(遠称)をつける。 +- 修飾する名詞が母音始まりの男性単数形の時、発音の都合上cetを用いる。 +- 近称と遠称を区別したいときは、修飾する名詞の後ろに-ci(近称)や-là(遠称)をつける。 diff --git a/app/learn/french/11/page.mdx b/app/learn/french/11/page.mdx index fbfd347..b8e1d98 100644 --- a/app/learn/french/11/page.mdx +++ b/app/learn/french/11/page.mdx @@ -1,20 +1,21 @@ -export const title = "主な否定表現"; +export const title = "指示代名詞"; -# 主な否定表現 +# 指示代名詞 -_第4章 否定文と疑問文より_ +_第3章 人称詞と指示詞より_ -| 表現 | 意味 | 例文 | -| ------------- | ------------ | ---------------------------------------- | -| ne~plus | もう〜ない | Je n'habite plus à Lyon. | -| ne~jamais | 決して〜ない | Je ne travaille jamais le samedi. | -| ne~rien | 何も〜ない | Ce n'est rien. | -| ne~personne | 誰も〜ない | Il n'y a personne. | -| ne~ni A ni B | AもBも〜ない | Je ne parle ni l'anglais ni le japonais. | +## パターン1 -## 疑問詞のない疑問文への答え方 +| | これ | それ | +| -------- | ---- | -------- | +| 無強勢形 | ce | ce | +| 強勢形 | ceci | cela(ça) | -- 肯定疑問文:Oui(はい)/Non(いいえ) -- 否定疑問文:Si(いいえ)/Non(はい) +## パターン2 -「si」は否定文への、「non」は肯定文への付加疑問文の付加要素としても同様に作用する。 +| | 男性 | 女性 | +| ---- | ----- | ------ | +| 単数 | celui | celle | +| 複数 | ceux | celles | + +- 近称と遠称を区別したいときには、それぞれの後ろに-ci(近称)や-là(遠称)をつける。 diff --git a/app/learn/french/12/page.mdx b/app/learn/french/12/page.mdx index 8b54b0c..fbfd347 100644 --- a/app/learn/french/12/page.mdx +++ b/app/learn/french/12/page.mdx @@ -1,22 +1,20 @@ -export const title = "疑問詞一覧"; +export const title = "主な否定表現"; -# 疑問詞一覧 +# 主な否定表現 _第4章 否定文と疑問文より_ -| 疑問詞 | 意味 | 例文 | -| -------- | ------ | ---------------------------------------- | -| qui | 誰 | Qui est là ? | -| que | 何 | Qu'est-ce que c'est ? | -| quand | いつ | Quand allez-vous au Japon ? | -| où | どこ | Où habites-tu ? | -| combien | いくつ | Combien d'enfants avez-vous ? | -| pourquoi | なぜ | Pourquoi pleures-tu ? | -| comment | どう | Comment allez-vous ? | -| quel | どの | Quelle voiture avez-vous achetée ? | -| lequel | どれ | Laquelle de ces voitures préférez-vous ? | -| quoi | 何 | Avec quoi mangez-vous ce plat ? | +| 表現 | 意味 | 例文 | +| ------------- | ------------ | ---------------------------------------- | +| ne~plus | もう〜ない | Je n'habite plus à Lyon. | +| ne~jamais | 決して〜ない | Je ne travaille jamais le samedi. | +| ne~rien | 何も〜ない | Ce n'est rien. | +| ne~personne | 誰も〜ない | Il n'y a personne. | +| ne~ni A ni B | AもBも〜ない | Je ne parle ni l'anglais ni le japonais. | -- quelとlequelは性数変化する。 -- quoiは前置詞の目的語部分を質問するときに使う。 -- 間接疑問文の場合「何が」は「ce qui」に、「何を」は「ce que」になる。 +## 疑問詞のない疑問文への答え方 + +- 肯定疑問文:Oui(はい)/Non(いいえ) +- 否定疑問文:Si(いいえ)/Non(はい) + +「si」は否定文への、「non」は肯定文への付加疑問文の付加要素としても同様に作用する。 diff --git a/app/learn/french/13/page.mdx b/app/learn/french/13/page.mdx index 26b7df1..8b54b0c 100644 --- a/app/learn/french/13/page.mdx +++ b/app/learn/french/13/page.mdx @@ -1,22 +1,22 @@ -export const title = "命令形の不規則活用"; +export const title = "疑問詞一覧"; -# 命令形の不規則活用 +# 疑問詞一覧 -_第5章 命令文より_ +_第4章 否定文と疑問文より_ -大半の動詞は直説法現在形と同型で命令形を作るが、以下の動詞は接続法の語形を元に命令形を作る。 +| 疑問詞 | 意味 | 例文 | +| -------- | ------ | ---------------------------------------- | +| qui | 誰 | Qui est là ? | +| que | 何 | Qu'est-ce que c'est ? | +| quand | いつ | Quand allez-vous au Japon ? | +| où | どこ | Où habites-tu ? | +| combien | いくつ | Combien d'enfants avez-vous ? | +| pourquoi | なぜ | Pourquoi pleures-tu ? | +| comment | どう | Comment allez-vous ? | +| quel | どの | Quelle voiture avez-vous achetée ? | +| lequel | どれ | Laquelle de ces voitures préférez-vous ? | +| quoi | 何 | Avec quoi mangez-vous ce plat ? | -| | 二人称単数命令形 | 二人称複数命令形 | 一人称複数命令形 | -| ------- | ---------------- | ---------------- | ---------------- | -| être | sois | soyez | soyons | -| avoir | aie | ayez | ayons | -| savoir | sache | sachez | sachons | -| vouloir | veuille | veuillez | veuillons | - -- veuilleとveuillonsについてはほとんど使用されない。 -- Ayez du courage ! (元気を出してください。) -- Soyez patient ! (我慢してください。) - -肯定命令文において人称代名詞をつける場合は、動詞の後ろにおいてハイフンで結ぶ(この場合、人称代名詞は強勢形になる)。 - -- Excusez-moi. +- quelとlequelは性数変化する。 +- quoiは前置詞の目的語部分を質問するときに使う。 +- 間接疑問文の場合「何が」は「ce qui」に、「何を」は「ce que」になる。 diff --git a/app/learn/french/14/page.mdx b/app/learn/french/14/page.mdx index 6fed985..26b7df1 100644 --- a/app/learn/french/14/page.mdx +++ b/app/learn/french/14/page.mdx @@ -1,16 +1,22 @@ -export const title = "直説法半過去形の活用"; +export const title = "命令形の不規則活用"; -# 直説法半過去形の活用 +# 命令形の不規則活用 -_第6章 時制より_ +_第5章 命令文より_ -語幹は直説法現在形の一人称複数形の語幹と同じ(êtreだけ例外でét-)。 +大半の動詞は直説法現在形と同型で命令形を作るが、以下の動詞は接続法の語形を元に命令形を作る。 -| 人称 | 語尾 | 例(donner) | -| ---------- | ------ | ------------ | -| 一人称単数 | -ais | donnais | -| 二人称単数 | -ais | donnais | -| 三人称単数 | -ait | donnait | -| 一人称複数 | -ions | donnions | -| 二人称複数 | -iez | donniez | -| 三人称複数 | -aient | donnaient | +| | 二人称単数命令形 | 二人称複数命令形 | 一人称複数命令形 | +| ------- | ---------------- | ---------------- | ---------------- | +| être | sois | soyez | soyons | +| avoir | aie | ayez | ayons | +| savoir | sache | sachez | sachons | +| vouloir | veuille | veuillez | veuillons | + +- veuilleとveuillonsについてはほとんど使用されない。 +- Ayez du courage ! (元気を出してください。) +- Soyez patient ! (我慢してください。) + +肯定命令文において人称代名詞をつける場合は、動詞の後ろにおいてハイフンで結ぶ(この場合、人称代名詞は強勢形になる)。 + +- Excusez-moi. diff --git a/app/learn/french/15/page.mdx b/app/learn/french/15/page.mdx index 1416eba..6fed985 100644 --- a/app/learn/french/15/page.mdx +++ b/app/learn/french/15/page.mdx @@ -1,20 +1,16 @@ -export const title = "直説法単純未来形の活用"; +export const title = "直説法半過去形の活用"; -# 直説法単純未来形の活用 +# 直説法半過去形の活用 _第6章 時制より_ -語幹の作り方: +語幹は直説法現在形の一人称複数形の語幹と同じ(êtreだけ例外でét-)。 -- -er動詞:原型からrをとったものを語幹にする -- -ir動詞:原型からrをとったものを語幹にする -- reで終わる動詞:原型からreを取り除いたものを語幹にする - -| 人称 | 語尾 | 例(donner) | 例(finir) | 例(être) | 例(avoir) | -| ---------- | -------- | ------------ | ----------- | ---------- | ----------- | -| 一人称単数 | -(e)rai | donnerai | finirai | serai | aurai | -| 二人称単数 | -(e)ras | donneras | finiras | seras | auras | -| 三人称単数 | -(e)ra | donnera | finira | sera | aura | -| 一人称複数 | -(e)rons | donnerons | finirons | serons | aurons | -| 二人称複数 | -(e)rez | donnerez | finirez | serez | aurez | -| 三人称複数 | -(e)ront | donneront | finiront | seront | auront | +| 人称 | 語尾 | 例(donner) | +| ---------- | ------ | ------------ | +| 一人称単数 | -ais | donnais | +| 二人称単数 | -ais | donnais | +| 三人称単数 | -ait | donnait | +| 一人称複数 | -ions | donnions | +| 二人称複数 | -iez | donniez | +| 三人称複数 | -aient | donnaient | diff --git a/app/learn/french/16/page.mdx b/app/learn/french/16/page.mdx index 8a9d7cb..1416eba 100644 --- a/app/learn/french/16/page.mdx +++ b/app/learn/french/16/page.mdx @@ -1,10 +1,20 @@ -export const title = "比較級で特殊な変化をする形容詞・副詞"; +export const title = "直説法単純未来形の活用"; -# 比較級で特殊な変化をする形容詞・副詞 +# 直説法単純未来形の活用 -_第8章 比較より_ +_第6章 時制より_ -- bonの優等比較級:meilleur(×plus bon) -- bienの優等比較級:mieux(×plus bien) -- mauvaisの優等比較級:pire(×plus mauvais) -- petitの優等比較級:moindre(×plus petit) +語幹の作り方: + +- -er動詞:原型からrをとったものを語幹にする +- -ir動詞:原型からrをとったものを語幹にする +- reで終わる動詞:原型からreを取り除いたものを語幹にする + +| 人称 | 語尾 | 例(donner) | 例(finir) | 例(être) | 例(avoir) | +| ---------- | -------- | ------------ | ----------- | ---------- | ----------- | +| 一人称単数 | -(e)rai | donnerai | finirai | serai | aurai | +| 二人称単数 | -(e)ras | donneras | finiras | seras | auras | +| 三人称単数 | -(e)ra | donnera | finira | sera | aura | +| 一人称複数 | -(e)rons | donnerons | finirons | serons | aurons | +| 二人称複数 | -(e)rez | donnerez | finirez | serez | aurez | +| 三人称複数 | -(e)ront | donneront | finiront | seront | auront | diff --git a/app/learn/french/17/page.mdx b/app/learn/french/17/page.mdx index 8821a82..8a9d7cb 100644 --- a/app/learn/french/17/page.mdx +++ b/app/learn/french/17/page.mdx @@ -1,20 +1,10 @@ -export const title = "現在分詞・過去分詞の作り方"; +export const title = "比較級で特殊な変化をする形容詞・副詞"; -# 現在分詞・過去分詞の作り方 +# 比較級で特殊な変化をする形容詞・副詞 -_第10章 分詞より_ +_第8章 比較より_ -## 現在分詞 - -動詞の1人称複数形の語幹に、-antを付ける。 - -- parler → parlant - -例外:être → étant、avoir → ayant、savoir → sachant - -## 過去分詞 - -- er動詞:語尾をéにする(manger → mangé) -- ir動詞:語尾をiにする(finir → fini) -- être → été -- avoir → eu +- bonの優等比較級:meilleur(×plus bon) +- bienの優等比較級:mieux(×plus bien) +- mauvaisの優等比較級:pire(×plus mauvais) +- petitの優等比較級:moindre(×plus petit) diff --git a/app/learn/french/18/page.mdx b/app/learn/french/18/page.mdx index a342929..8821a82 100644 --- a/app/learn/french/18/page.mdx +++ b/app/learn/french/18/page.mdx @@ -1,18 +1,20 @@ -export const title = "条件法現在形の活用"; +export const title = "現在分詞・過去分詞の作り方"; -# 条件法現在形の活用 +# 現在分詞・過去分詞の作り方 -_第11章 条件法より_ +_第10章 分詞より_ -作り方:「単純未来形の前半部分(rまで)+半過去形の語尾」 +## 現在分詞 -| 人称 | 例(donner) | 例(finir) | 例(être) | 例(avoir) | 例(faire) | -| ---------- | ------------ | ----------- | ---------- | ----------- | ----------- | -| 一人称単数 | donnerais | finirais | serais | aurais | ferais | -| 二人称単数 | donnerais | finirais | serais | aurais | ferais | -| 三人称単数 | donnerait | finirait | serait | aurait | ferait | -| 一人称複数 | donnerions | finirions | serions | aurions | ferions | -| 二人称複数 | donneriez | finiriez | seriez | auriez | feriez | -| 三人称複数 | donneraient | finiraient | seraient | auraient | feraient | +動詞の1人称複数形の語幹に、-antを付ける。 -- 条件法過去形は「avoir(être)の条件法現在形+動詞の過去分詞形」で作成可能。 +- parler → parlant + +例外:être → étant、avoir → ayant、savoir → sachant + +## 過去分詞 + +- er動詞:語尾をéにする(manger → mangé) +- ir動詞:語尾をiにする(finir → fini) +- être → été +- avoir → eu diff --git a/app/learn/french/19/page.mdx b/app/learn/french/19/page.mdx index e017fd7..a342929 100644 --- a/app/learn/french/19/page.mdx +++ b/app/learn/french/19/page.mdx @@ -1,17 +1,18 @@ -export const title = "接続法現在形の活用"; +export const title = "条件法現在形の活用"; -# 接続法現在形の活用 +# 条件法現在形の活用 -_第12章 接続法より_ +_第11章 条件法より_ -| 人称 | 語幹 | 語尾 | 特殊形(être) | 特殊形(avoir) | -| ---------- | ------------------------------------ | ----- | -------------- | --------------- | -| 一人称単数 | 直説法現在形3人称複数形の語幹と同様 | -e | sois | aie | -| 二人称単数 | 直説法現在形3人称複数形の語幹と同様 | -es | sois | aies | -| 三人称単数 | 直説法現在形3人称複数形の語幹と同様 | -e | soit | ait | -| 一人称複数 | 直説法現在形1人称複数形の語幹と同様 | -ions | soyons | ayons | -| 二人称複数 | 直説法現在形1人称複数形の語幹と同様 | -iez | soyez | ayez | -| 三人称複数 | 直説法現在形3人称複数形の語幹と同様 | -ent | soient | aient | +作り方:「単純未来形の前半部分(rまで)+半過去形の語尾」 -- 語幹は不規則でこの通りにならないものもあるが、語尾は規則的である。 -- 単数形と三人称複数形の語尾は直説法現在形と同じで、一人称複数、二人称複数の語尾は半過去形と同じである。 +| 人称 | 例(donner) | 例(finir) | 例(être) | 例(avoir) | 例(faire) | +| ---------- | ------------ | ----------- | ---------- | ----------- | ----------- | +| 一人称単数 | donnerais | finirais | serais | aurais | ferais | +| 二人称単数 | donnerais | finirais | serais | aurais | ferais | +| 三人称単数 | donnerait | finirait | serait | aurait | ferait | +| 一人称複数 | donnerions | finirions | serions | aurions | ferions | +| 二人称複数 | donneriez | finiriez | seriez | auriez | feriez | +| 三人称複数 | donneraient | finiraient | seraient | auraient | feraient | + +- 条件法過去形は「avoir(être)の条件法現在形+動詞の過去分詞形」で作成可能。 diff --git a/app/learn/french/20/page.mdx b/app/learn/french/20/page.mdx index 3471559..e017fd7 100644 --- a/app/learn/french/20/page.mdx +++ b/app/learn/french/20/page.mdx @@ -1,23 +1,17 @@ -export const title = "不定表現一覧"; +export const title = "接続法現在形の活用"; -# 不定表現一覧 +# 接続法現在形の活用 -_第13章 不定表現より_ +_第12章 接続法より_ -| 不定表現 | 意味 | 補足 | -| --------- | ---------------------------- | ------------------------------------------------------------------------ | -| aucun | いかなる(〜ない) | 否定文で用いられる/原則単数名詞を修飾する/pasと共には使えない | -| quelque | いくつかの、いくらかの/ある | 前者は可算複数形または不可算名詞、後者は可算単数形または不可算名詞を伴う | -| n'importe | 〜でも(よい) | 後ろには疑問詞が続く | -| chaque | それぞれの | 不定形容詞であり、複数形は存在しない | -| chacun | それぞれの | 不定代名詞であり、複数形は存在しない | -| tout | すべての | 後ろに定冠詞付きの名詞を伴ってそれを修飾する | +| 人称 | 語幹 | 語尾 | 特殊形(être) | 特殊形(avoir) | +| ---------- | ------------------------------------ | ----- | -------------- | --------------- | +| 一人称単数 | 直説法現在形3人称複数形の語幹と同様 | -e | sois | aie | +| 二人称単数 | 直説法現在形3人称複数形の語幹と同様 | -es | sois | aies | +| 三人称単数 | 直説法現在形3人称複数形の語幹と同様 | -e | soit | ait | +| 一人称複数 | 直説法現在形1人称複数形の語幹と同様 | -ions | soyons | ayons | +| 二人称複数 | 直説法現在形1人称複数形の語幹と同様 | -iez | soyez | ayez | +| 三人称複数 | 直説法現在形3人称複数形の語幹と同様 | -ent | soient | aient | -- Il n'y a aucune solution à ce problème. -- J'ai encore quelques questions à vous poser. -- Il a montré quelque intérêt pour ce projet. -- Vous pouvez appeler n'importe quand, je suis disponible. -- Choisissez n'importe quel livre, ils sont tous intéressants. -- Chaque étudiant doit rendre son devoir avant vendredi. -- J'ai distribué des cartes : une pour chacun. -- Toutes les fenêtres doivent rester fermées. +- 語幹は不規則でこの通りにならないものもあるが、語尾は規則的である。 +- 単数形と三人称複数形の語尾は直説法現在形と同じで、一人称複数、二人称複数の語尾は半過去形と同じである。 diff --git a/app/learn/french/21/page.mdx b/app/learn/french/21/page.mdx index eab63d7..3471559 100644 --- a/app/learn/french/21/page.mdx +++ b/app/learn/french/21/page.mdx @@ -1,17 +1,23 @@ -export const title = "中性代名詞一覧"; +export const title = "不定表現一覧"; -# 中性代名詞一覧 +# 不定表現一覧 -_第16章 中性代名詞より_ +_第13章 不定表現より_ -中性代名詞は「性数一致しない代名詞」のこと。 +| 不定表現 | 意味 | 補足 | +| --------- | ---------------------------- | ------------------------------------------------------------------------ | +| aucun | いかなる(〜ない) | 否定文で用いられる/原則単数名詞を修飾する/pasと共には使えない | +| quelque | いくつかの、いくらかの/ある | 前者は可算複数形または不可算名詞、後者は可算単数形または不可算名詞を伴う | +| n'importe | 〜でも(よい) | 後ろには疑問詞が続く | +| chaque | それぞれの | 不定形容詞であり、複数形は存在しない | +| chacun | それぞれの | 不定代名詞であり、複数形は存在しない | +| tout | すべての | 後ろに定冠詞付きの名詞を伴ってそれを修飾する | -| 中性代名詞 | 指示対象 | 例文 | -| ---------- | -------------------------- | ------------------------------------------------------------------------------------------------- | -| en | 前置詞「de」+定名詞(句) | J'en viens.(私はそこから来ました。) | -| en | 同類を表す不定名詞(句) | Avez-vous des amis ? -Oui, j'en ai.(あなたは友達がいますか。-はい、います。) | -| y | 前置詞「à」+定名詞(句) | On y compare la vie.(人生はそれにたとえられる。) | -| le | 文脈全体 | Je lui ai téléphoné. -Je l'ai fait par précaution.(私は彼に電話をした。-私は念のためそうした。) | -| le | 形容詞 | Êtes-vous fatigué ? -Oui, je le suis.(あなたは疲れていますか。-はい、疲れています。) | - -- enが同類を表す不定名詞(句)を置き換える場合で、後ろに形容詞が付いている場合は、形容詞は「de+形容詞」の形で残る。 +- Il n'y a aucune solution à ce problème. +- J'ai encore quelques questions à vous poser. +- Il a montré quelque intérêt pour ce projet. +- Vous pouvez appeler n'importe quand, je suis disponible. +- Choisissez n'importe quel livre, ils sont tous intéressants. +- Chaque étudiant doit rendre son devoir avant vendredi. +- J'ai distribué des cartes : une pour chacun. +- Toutes les fenêtres doivent rester fermées. diff --git a/app/learn/french/22/page.mdx b/app/learn/french/22/page.mdx new file mode 100644 index 0000000..eab63d7 --- /dev/null +++ b/app/learn/french/22/page.mdx @@ -0,0 +1,17 @@ +export const title = "中性代名詞一覧"; + +# 中性代名詞一覧 + +_第16章 中性代名詞より_ + +中性代名詞は「性数一致しない代名詞」のこと。 + +| 中性代名詞 | 指示対象 | 例文 | +| ---------- | -------------------------- | ------------------------------------------------------------------------------------------------- | +| en | 前置詞「de」+定名詞(句) | J'en viens.(私はそこから来ました。) | +| en | 同類を表す不定名詞(句) | Avez-vous des amis ? -Oui, j'en ai.(あなたは友達がいますか。-はい、います。) | +| y | 前置詞「à」+定名詞(句) | On y compare la vie.(人生はそれにたとえられる。) | +| le | 文脈全体 | Je lui ai téléphoné. -Je l'ai fait par précaution.(私は彼に電話をした。-私は念のためそうした。) | +| le | 形容詞 | Êtes-vous fatigué ? -Oui, je le suis.(あなたは疲れていますか。-はい、疲れています。) | + +- enが同類を表す不定名詞(句)を置き換える場合で、後ろに形容詞が付いている場合は、形容詞は「de+形容詞」の形で残る。 diff --git a/app/learn/spanish/01/page.mdx b/app/learn/spanish/01/page.mdx deleted file mode 100644 index f01e9cb..0000000 --- a/app/learn/spanish/01/page.mdx +++ /dev/null @@ -1,12 +0,0 @@ -export const title = "名詞の性の語尾による判別"; - -# 名詞の性の語尾による判別 - -_第1章 品詞と文型より_ - -ものの名詞は男性・女性を直観的に判断できないため、語尾によってある程度判別する。(例外もある) - -| 語尾 | 性 | -| ------------------------------- | -------- | -| -age、-ment、-eau、-teur | 男性名詞 | -| -tion、-sion、-té、-esse、-ette | 女性名詞 | diff --git a/app/learn/spanish/01/page.tsx b/app/learn/spanish/01/page.tsx new file mode 100644 index 0000000..163113e --- /dev/null +++ b/app/learn/spanish/01/page.tsx @@ -0,0 +1,19 @@ +"use client"; + +import { ConjugationPractice } from "@/components/verbs/ConjugationPractice"; +import { spanishConjugationConfig } from "@/lib/conjugation/es/config"; +import { verbs } from "./verbs"; + +export const title = "動詞の活用"; + +export default function SpanishVerbConjugationPage() { + return ( +
+

動詞の活用

+

動詞・時制・法を選んで、活用形を入力しながら覚えましょう。

+
+ +
+
+ ); +} diff --git a/app/learn/spanish/01/verbs.ts b/app/learn/spanish/01/verbs.ts new file mode 100644 index 0000000..9e47f83 --- /dev/null +++ b/app/learn/spanish/01/verbs.ts @@ -0,0 +1,410 @@ +import type { IrregularVerb, VerbEntry } from "@/lib/conjugation/es/types"; + +// ==== 不規則動詞 ==== +// 直説法現在・点過去・接続法現在は、規則変化のルールでは導けないため全形を直接指定する。 +// 直説法線過去はser・ir・verの3つを除き、不定詞から規則的に導出されるため指定不要。 +const irregularVerbs: IrregularVerb[] = [ + { + id: "ser", + infinitive: "ser", + meaning: "〜である", + kind: "irregular", + participio: "sido", + presente: ["soy", "eres", "es", "somos", "sois", "son"], + pretéritoIndefinido: ["fui", "fuiste", "fue", "fuimos", "fuisteis", "fueron"], + subjuntivoPresente: ["sea", "seas", "sea", "seamos", "seáis", "sean"], + futuroStem: "ser", + pretéritoImperfecto: ["era", "eras", "era", "éramos", "erais", "eran"], + }, + { + id: "estar", + infinitive: "estar", + meaning: "(状態・場所に)ある、いる", + kind: "irregular", + participio: "estado", + presente: ["estoy", "estás", "está", "estamos", "estáis", "están"], + pretéritoIndefinido: ["estuve", "estuviste", "estuvo", "estuvimos", "estuvisteis", "estuvieron"], + subjuntivoPresente: ["esté", "estés", "esté", "estemos", "estéis", "estén"], + futuroStem: "estar", + }, + { + id: "tener", + infinitive: "tener", + meaning: "持っている", + kind: "irregular", + participio: "tenido", + presente: ["tengo", "tienes", "tiene", "tenemos", "tenéis", "tienen"], + pretéritoIndefinido: ["tuve", "tuviste", "tuvo", "tuvimos", "tuvisteis", "tuvieron"], + subjuntivoPresente: ["tenga", "tengas", "tenga", "tengamos", "tengáis", "tengan"], + futuroStem: "tendr", + }, + { + id: "hacer", + infinitive: "hacer", + meaning: "する、作る", + kind: "irregular", + participio: "hecho", + presente: ["hago", "haces", "hace", "hacemos", "hacéis", "hacen"], + pretéritoIndefinido: ["hice", "hiciste", "hizo", "hicimos", "hicisteis", "hicieron"], + subjuntivoPresente: ["haga", "hagas", "haga", "hagamos", "hagáis", "hagan"], + futuroStem: "har", + }, + { + id: "ir", + infinitive: "ir", + meaning: "行く", + kind: "irregular", + participio: "ido", + presente: ["voy", "vas", "va", "vamos", "vais", "van"], + pretéritoIndefinido: ["fui", "fuiste", "fue", "fuimos", "fuisteis", "fueron"], + subjuntivoPresente: ["vaya", "vayas", "vaya", "vayamos", "vayáis", "vayan"], + futuroStem: "ir", + pretéritoImperfecto: ["iba", "ibas", "iba", "íbamos", "ibais", "iban"], + }, + { + id: "poder", + infinitive: "poder", + meaning: "〜できる", + kind: "irregular", + participio: "podido", + presente: ["puedo", "puedes", "puede", "podemos", "podéis", "pueden"], + pretéritoIndefinido: ["pude", "pudiste", "pudo", "pudimos", "pudisteis", "pudieron"], + subjuntivoPresente: ["pueda", "puedas", "pueda", "podamos", "podáis", "puedan"], + futuroStem: "podr", + }, + { + id: "saber", + infinitive: "saber", + meaning: "知っている", + kind: "irregular", + participio: "sabido", + presente: ["sé", "sabes", "sabe", "sabemos", "sabéis", "saben"], + pretéritoIndefinido: ["supe", "supiste", "supo", "supimos", "supisteis", "supieron"], + subjuntivoPresente: ["sepa", "sepas", "sepa", "sepamos", "sepáis", "sepan"], + futuroStem: "sabr", + }, + { + id: "querer", + infinitive: "querer", + meaning: "〜したい、愛する", + kind: "irregular", + participio: "querido", + presente: ["quiero", "quieres", "quiere", "queremos", "queréis", "quieren"], + pretéritoIndefinido: ["quise", "quisiste", "quiso", "quisimos", "quisisteis", "quisieron"], + subjuntivoPresente: ["quiera", "quieras", "quiera", "queramos", "queráis", "quieran"], + futuroStem: "querr", + }, + { + id: "poner", + infinitive: "poner", + meaning: "置く", + kind: "irregular", + participio: "puesto", + presente: ["pongo", "pones", "pone", "ponemos", "ponéis", "ponen"], + pretéritoIndefinido: ["puse", "pusiste", "puso", "pusimos", "pusisteis", "pusieron"], + subjuntivoPresente: ["ponga", "pongas", "ponga", "pongamos", "pongáis", "pongan"], + futuroStem: "pondr", + }, + { + id: "salir", + infinitive: "salir", + meaning: "出る、外出する", + kind: "irregular", + participio: "salido", + presente: ["salgo", "sales", "sale", "salimos", "salís", "salen"], + pretéritoIndefinido: ["salí", "saliste", "salió", "salimos", "salisteis", "salieron"], + subjuntivoPresente: ["salga", "salgas", "salga", "salgamos", "salgáis", "salgan"], + futuroStem: "saldr", + }, + { + id: "venir", + infinitive: "venir", + meaning: "来る", + kind: "irregular", + participio: "venido", + presente: ["vengo", "vienes", "viene", "venimos", "venís", "vienen"], + pretéritoIndefinido: ["vine", "viniste", "vino", "vinimos", "vinisteis", "vinieron"], + subjuntivoPresente: ["venga", "vengas", "venga", "vengamos", "vengáis", "vengan"], + futuroStem: "vendr", + }, + { + id: "decir", + infinitive: "decir", + meaning: "言う", + kind: "irregular", + participio: "dicho", + presente: ["digo", "dices", "dice", "decimos", "decís", "dicen"], + pretéritoIndefinido: ["dije", "dijiste", "dijo", "dijimos", "dijisteis", "dijeron"], + subjuntivoPresente: ["diga", "digas", "diga", "digamos", "digáis", "digan"], + futuroStem: "dir", + }, + { + id: "dar", + infinitive: "dar", + meaning: "与える", + kind: "irregular", + participio: "dado", + presente: ["doy", "das", "da", "damos", "dais", "dan"], + pretéritoIndefinido: ["di", "diste", "dio", "dimos", "disteis", "dieron"], + subjuntivoPresente: ["dé", "des", "dé", "demos", "deis", "den"], + futuroStem: "dar", + }, + { + id: "ver", + infinitive: "ver", + meaning: "見る", + kind: "irregular", + participio: "visto", + presente: ["veo", "ves", "ve", "vemos", "veis", "ven"], + pretéritoIndefinido: ["vi", "viste", "vio", "vimos", "visteis", "vieron"], + subjuntivoPresente: ["vea", "veas", "vea", "veamos", "veáis", "vean"], + futuroStem: "ver", + pretéritoImperfecto: ["veía", "veías", "veía", "veíamos", "veíais", "veían"], + }, + { + id: "traer", + infinitive: "traer", + meaning: "持ってくる", + kind: "irregular", + participio: "traído", + presente: ["traigo", "traes", "trae", "traemos", "traéis", "traen"], + pretéritoIndefinido: ["traje", "trajiste", "trajo", "trajimos", "trajisteis", "trajeron"], + subjuntivoPresente: ["traiga", "traigas", "traiga", "traigamos", "traigáis", "traigan"], + futuroStem: "traer", + }, + { + id: "oir", + infinitive: "oír", + meaning: "聞こえる", + kind: "irregular", + participio: "oído", + presente: ["oigo", "oyes", "oye", "oímos", "oís", "oyen"], + pretéritoIndefinido: ["oí", "oíste", "oyó", "oímos", "oísteis", "oyeron"], + subjuntivoPresente: ["oiga", "oigas", "oiga", "oigamos", "oigáis", "oigan"], + futuroStem: "oír", + }, + { + id: "conocer", + infinitive: "conocer", + meaning: "知っている(人・場所)", + kind: "irregular", + participio: "conocido", + presente: ["conozco", "conoces", "conoce", "conocemos", "conocéis", "conocen"], + pretéritoIndefinido: ["conocí", "conociste", "conoció", "conocimos", "conocisteis", "conocieron"], + subjuntivoPresente: ["conozca", "conozcas", "conozca", "conozcamos", "conozcáis", "conozcan"], + futuroStem: "conocer", + }, + { + id: "pensar", + infinitive: "pensar", + meaning: "考える", + kind: "irregular", + participio: "pensado", + presente: ["pienso", "piensas", "piensa", "pensamos", "pensáis", "piensan"], + pretéritoIndefinido: ["pensé", "pensaste", "pensó", "pensamos", "pensasteis", "pensaron"], + subjuntivoPresente: ["piense", "pienses", "piense", "pensemos", "penséis", "piensen"], + futuroStem: "pensar", + }, + { + id: "volver", + infinitive: "volver", + meaning: "戻る", + kind: "irregular", + participio: "vuelto", + presente: ["vuelvo", "vuelves", "vuelve", "volvemos", "volvéis", "vuelven"], + pretéritoIndefinido: ["volví", "volviste", "volvió", "volvimos", "volvisteis", "volvieron"], + subjuntivoPresente: ["vuelva", "vuelvas", "vuelva", "volvamos", "volváis", "vuelvan"], + futuroStem: "volver", + }, + { + id: "dormir", + infinitive: "dormir", + meaning: "眠る", + kind: "irregular", + participio: "dormido", + presente: ["duermo", "duermes", "duerme", "dormimos", "dormís", "duermen"], + pretéritoIndefinido: ["dormí", "dormiste", "durmió", "dormimos", "dormisteis", "durmieron"], + subjuntivoPresente: ["duerma", "duermas", "duerma", "durmamos", "durmáis", "duerman"], + futuroStem: "dormir", + }, + { + id: "sentir", + infinitive: "sentir", + meaning: "感じる", + kind: "irregular", + participio: "sentido", + presente: ["siento", "sientes", "siente", "sentimos", "sentís", "sienten"], + pretéritoIndefinido: ["sentí", "sentiste", "sintió", "sentimos", "sentisteis", "sintieron"], + subjuntivoPresente: ["sienta", "sientas", "sienta", "sintamos", "sintáis", "sientan"], + futuroStem: "sentir", + }, + { + id: "pedir", + infinitive: "pedir", + meaning: "頼む、注文する", + kind: "irregular", + participio: "pedido", + presente: ["pido", "pides", "pide", "pedimos", "pedís", "piden"], + pretéritoIndefinido: ["pedí", "pediste", "pidió", "pedimos", "pedisteis", "pidieron"], + subjuntivoPresente: ["pida", "pidas", "pida", "pidamos", "pidáis", "pidan"], + futuroStem: "pedir", + }, + { + id: "seguir", + infinitive: "seguir", + meaning: "続ける、ついていく", + kind: "irregular", + participio: "seguido", + presente: ["sigo", "sigues", "sigue", "seguimos", "seguís", "siguen"], + pretéritoIndefinido: ["seguí", "seguiste", "siguió", "seguimos", "seguisteis", "siguieron"], + subjuntivoPresente: ["siga", "sigas", "siga", "sigamos", "sigáis", "sigan"], + futuroStem: "seguir", + }, + { + id: "jugar", + infinitive: "jugar", + meaning: "遊ぶ、(スポーツを)する", + kind: "irregular", + participio: "jugado", + presente: ["juego", "juegas", "juega", "jugamos", "jugáis", "juegan"], + pretéritoIndefinido: ["jugué", "jugaste", "jugó", "jugamos", "jugasteis", "jugaron"], + subjuntivoPresente: ["juegue", "juegues", "juegue", "juguemos", "juguéis", "jueguen"], + futuroStem: "jugar", + }, + { + id: "empezar", + infinitive: "empezar", + meaning: "始める", + kind: "irregular", + participio: "empezado", + presente: ["empiezo", "empiezas", "empieza", "empezamos", "empezáis", "empiezan"], + pretéritoIndefinido: ["empecé", "empezaste", "empezó", "empezamos", "empezasteis", "empezaron"], + subjuntivoPresente: ["empiece", "empieces", "empiece", "empecemos", "empecéis", "empiecen"], + futuroStem: "empezar", + }, + { + id: "encontrar", + infinitive: "encontrar", + meaning: "見つける", + kind: "irregular", + participio: "encontrado", + presente: ["encuentro", "encuentras", "encuentra", "encontramos", "encontráis", "encuentran"], + pretéritoIndefinido: ["encontré", "encontraste", "encontró", "encontramos", "encontrasteis", "encontraron"], + subjuntivoPresente: ["encuentre", "encuentres", "encuentre", "encontremos", "encontréis", "encuentren"], + futuroStem: "encontrar", + }, + { + id: "cerrar", + infinitive: "cerrar", + meaning: "閉める", + kind: "irregular", + participio: "cerrado", + presente: ["cierro", "cierras", "cierra", "cerramos", "cerráis", "cierran"], + pretéritoIndefinido: ["cerré", "cerraste", "cerró", "cerramos", "cerrasteis", "cerraron"], + subjuntivoPresente: ["cierre", "cierres", "cierre", "cerremos", "cerréis", "cierren"], + futuroStem: "cerrar", + }, + { + id: "contar", + infinitive: "contar", + meaning: "数える、話す", + kind: "irregular", + participio: "contado", + presente: ["cuento", "cuentas", "cuenta", "contamos", "contáis", "cuentan"], + pretéritoIndefinido: ["conté", "contaste", "contó", "contamos", "contasteis", "contaron"], + subjuntivoPresente: ["cuente", "cuentes", "cuente", "contemos", "contéis", "cuenten"], + futuroStem: "contar", + }, + { + id: "entender", + infinitive: "entender", + meaning: "理解する", + kind: "irregular", + participio: "entendido", + presente: ["entiendo", "entiendes", "entiende", "entendemos", "entendéis", "entienden"], + pretéritoIndefinido: ["entendí", "entendiste", "entendió", "entendimos", "entendisteis", "entendieron"], + subjuntivoPresente: ["entienda", "entiendas", "entienda", "entendamos", "entendáis", "entiendan"], + futuroStem: "entender", + }, + { + id: "morir", + infinitive: "morir", + meaning: "死ぬ", + kind: "irregular", + participio: "muerto", + presente: ["muero", "mueres", "muere", "morimos", "morís", "mueren"], + pretéritoIndefinido: ["morí", "moriste", "murió", "morimos", "moristeis", "murieron"], + subjuntivoPresente: ["muera", "mueras", "muera", "muramos", "muráis", "mueran"], + futuroStem: "morir", + }, + { + id: "escribir", + infinitive: "escribir", + meaning: "書く", + kind: "irregular", + participio: "escrito", + presente: ["escribo", "escribes", "escribe", "escribimos", "escribís", "escriben"], + pretéritoIndefinido: ["escribí", "escribiste", "escribió", "escribimos", "escribisteis", "escribieron"], + subjuntivoPresente: ["escriba", "escribas", "escriba", "escribamos", "escribáis", "escriban"], + futuroStem: "escribir", + }, + { + id: "abrir", + infinitive: "abrir", + meaning: "開ける", + kind: "irregular", + participio: "abierto", + presente: ["abro", "abres", "abre", "abrimos", "abrís", "abren"], + pretéritoIndefinido: ["abrí", "abriste", "abrió", "abrimos", "abristeis", "abrieron"], + subjuntivoPresente: ["abra", "abras", "abra", "abramos", "abráis", "abran"], + futuroStem: "abrir", + }, + { + id: "leer", + infinitive: "leer", + meaning: "読む", + kind: "irregular", + participio: "leído", + presente: ["leo", "lees", "lee", "leemos", "leéis", "leen"], + pretéritoIndefinido: ["leí", "leíste", "leyó", "leímos", "leísteis", "leyeron"], + subjuntivoPresente: ["lea", "leas", "lea", "leamos", "leáis", "lean"], + futuroStem: "leer", + }, + { + id: "construir", + infinitive: "construir", + meaning: "建てる", + kind: "irregular", + participio: "construido", + presente: ["construyo", "construyes", "construye", "construimos", "construís", "construyen"], + pretéritoIndefinido: ["construí", "construiste", "construyó", "construimos", "construisteis", "construyeron"], + subjuntivoPresente: ["construya", "construyas", "construya", "construyamos", "construyáis", "construyan"], + futuroStem: "construir", + }, + { + id: "elegir", + infinitive: "elegir", + meaning: "選ぶ", + kind: "irregular", + participio: "elegido", + presente: ["elijo", "eliges", "elige", "elegimos", "elegís", "eligen"], + pretéritoIndefinido: ["elegí", "elegiste", "eligió", "elegimos", "elegisteis", "eligieron"], + subjuntivoPresente: ["elija", "elijas", "elija", "elijamos", "elijáis", "elijan"], + futuroStem: "elegir", + }, +]; + +// 規則動詞: 語尾変化のルールだけで活用可能なため、原形とグループのみ保持する +const regularVerbs: VerbEntry[] = [ + { id: "hablar", infinitive: "hablar", meaning: "話す", kind: "regular", group: "ar" }, + { id: "estudiar", infinitive: "estudiar", meaning: "勉強する", kind: "regular", group: "ar" }, + { id: "trabajar", infinitive: "trabajar", meaning: "働く", kind: "regular", group: "ar" }, + { id: "comer", infinitive: "comer", meaning: "食べる", kind: "regular", group: "er" }, + { id: "beber", infinitive: "beber", meaning: "飲む", kind: "regular", group: "er" }, + { id: "aprender", infinitive: "aprender", meaning: "学ぶ", kind: "regular", group: "er" }, + { id: "vivir", infinitive: "vivir", meaning: "住む、生きる", kind: "regular", group: "ir" }, + { id: "subir", infinitive: "subir", meaning: "登る、上がる", kind: "regular", group: "ir" }, + { id: "recibir", infinitive: "recibir", meaning: "受け取る", kind: "regular", group: "ir" }, +]; + +export const verbs: VerbEntry[] = [...irregularVerbs, ...regularVerbs]; diff --git a/components/verbs/AccentInput.tsx b/components/verbs/AccentInput.tsx new file mode 100644 index 0000000..77e2d14 --- /dev/null +++ b/components/verbs/AccentInput.tsx @@ -0,0 +1,145 @@ +"use client"; + +import { useMemo, useRef } from "react"; +import type { KeyboardEvent } from "react"; + +interface AccentInputProps { + value: string; + onChange: (value: string) => void; + onSubmit?: () => void; + disabled?: boolean; + placeholder?: string; + autoFocus?: boolean; + className?: string; + // 例: フランス語なら [["e","é","è","ê","ë"], ...]、スペイン語なら [["a","á"], ...] + accentCycles: string[][]; + // 入力欄の下に並べる、クリックで直接入力できるボタンの文字一覧 + toolbarChars: string[]; +} + +export function AccentInput({ + value, + onChange, + onSubmit, + disabled, + placeholder, + autoFocus, + className, + accentCycles, + toolbarChars, +}: AccentInputProps) { + const inputRef = useRef(null); + + // 大文字・小文字を問わず、どの文字がどのサイクルの何番目かを引けるように索引化する + const charIndex = useMemo(() => { + const index = new Map(); + for (const cycle of accentCycles) { + const upperCycle = cycle.map((c) => c.toUpperCase()); + cycle.forEach((ch, i) => { + index.set(ch, { cycle, index: i }); + index.set(upperCycle[i], { cycle: upperCycle, index: i }); + }); + } + return index; + }, [accentCycles]); + + const nextAccentChar = (ch: string | undefined): string | null => { + if (!ch) return null; + const entry = charIndex.get(ch); + if (!entry) return null; + const { cycle, index } = entry; + return cycle[(index + 1) % cycle.length]; + }; + + const prevAccentChar = (ch: string | undefined): string | null => { + if (!ch) return null; + const entry = charIndex.get(ch); + if (!entry) return null; + const { cycle, index } = entry; + return cycle[(index - 1 + cycle.length) % cycle.length]; + }; + + // カーソル直前の文字をサイクルさせる。切り替えられた場合はtrueを返す(呼び出し側でpreventDefaultするかどうかの判断に使う) + const applyCycledChar = (getChar: (ch: string | undefined) => string | null): boolean => { + const input = inputRef.current; + if (!input) return false; + const caret = input.selectionStart; + // 選択範囲がある場合や先頭にいる場合は、通常のカーソル移動に任せる + if (caret === null || caret !== input.selectionEnd || caret === 0) return false; + + const nextChar = getChar(value[caret - 1]); + if (!nextChar) return false; + + onChange(value.slice(0, caret - 1) + nextChar + value.slice(caret)); + requestAnimationFrame(() => { + input.setSelectionRange(caret, caret); + }); + return true; + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Enter") { + onSubmit?.(); + return; + } + if (disabled) return; + + if (e.key === "ArrowUp") { + if (applyCycledChar(nextAccentChar)) e.preventDefault(); + return; + } + if (e.key === "ArrowDown") { + if (applyCycledChar(prevAccentChar)) e.preventDefault(); + return; + } + }; + + const insertChar = (ch: string) => { + const input = inputRef.current; + const caret = input?.selectionStart ?? value.length; + onChange(value.slice(0, caret) + ch + value.slice(caret)); + requestAnimationFrame(() => { + input?.focus(); + input?.setSelectionRange(caret + 1, caret + 1); + }); + }; + + return ( +
+ onChange(e.target.value)} + onKeyDown={handleKeyDown} + readOnly={disabled} + placeholder={placeholder} + autoFocus={autoFocus} + className={className} + /> +
+ {toolbarChars.map((ch) => ( + + ))} +
+
+ ヒント: アルファベットを入力した直後に ↑キーを押すと、{accentCycles[0]?.join(" → ")} + のようにアクセント記号付きの文字へ切り替えられます(↓キーで逆順)。上のボタンから直接入力することもできます。 +
+
+ ); +} diff --git a/components/verbs/ConjugationPractice.tsx b/components/verbs/ConjugationPractice.tsx new file mode 100644 index 0000000..f0fc292 --- /dev/null +++ b/components/verbs/ConjugationPractice.tsx @@ -0,0 +1,385 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; +import { tenseKey, type SixForms, type TenseOption, type VerbLike } from "@/lib/conjugation/shared"; +import { AccentInput } from "@/components/verbs/AccentInput"; + +// 言語ごとに異なる部分(人称・時制・活用エンジン・発音・アクセント入力)をまとめた設定。 +// 新しい言語を追加するときは、この形にそって lib/conjugation//config.ts を作る +export interface ConjugationLanguageConfig { + persons: readonly string[]; + tenseOptions: TenseOption[]; + buildConjugation: (verb: V) => T; + formsFor: (table: T, mood: string, tense: string) => SixForms; + speak: (text: string, handlers: { onEnd?: () => void; onError?: () => void }) => boolean; + accentCycles: string[][]; + toolbarChars: string[]; + inputPlaceholder: string; +} + +interface Question { + verbId: string; + infinitive: string; + meaning: string; + mood: string; + tense: string; + tenseLabel: string; + personIndex: number; + answer: string; +} + +function normalize(str: string): string { + return str.trim().toLowerCase().replace(/\s+/g, " "); +} + +interface ConjugationPracticeProps { + verbs: V[]; + language: ConjugationLanguageConfig; +} + +export function ConjugationPractice({ verbs, language }: ConjugationPracticeProps) { + const { persons, tenseOptions, buildConjugation, formsFor, speak, accentCycles, toolbarChars, inputPlaceholder } = language; + + const conjugationById = useMemo(() => new Map(verbs.map((v) => [v.id, buildConjugation(v)])), [verbs, buildConjugation]); + const [phase, setPhase] = useState<"settings" | "practice">("settings"); + const [selectedVerbIds, setSelectedVerbIds] = useState>(() => new Set(verbs.map((v) => v.id))); + const [selectedTenseKeys, setSelectedTenseKeys] = useState>( + () => new Set(tenseOptions.map((t) => tenseKey(t.mood, t.tense))), + ); + + const [question, setQuestion] = useState(null); + const [answer, setAnswer] = useState(""); + const [submitted, setSubmitted] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [score, setScore] = useState({ correct: 0, total: 0 }); + const [recentKeys, setRecentKeys] = useState([]); + const [audioEnabled, setAudioEnabled] = useState(true); + const [speaking, setSpeaking] = useState(false); + + useEffect(() => { + return () => { + if (typeof window !== "undefined") window.speechSynthesis?.cancel(); + }; + }, []); + + const toggleAudio = () => setAudioEnabled((prev) => !prev); + + const selectedVerbs = useMemo(() => verbs.filter((v) => selectedVerbIds.has(v.id)), [verbs, selectedVerbIds]); + const selectedTenses = useMemo( + () => tenseOptions.filter((t) => selectedTenseKeys.has(tenseKey(t.mood, t.tense))), + [tenseOptions, selectedTenseKeys], + ); + + const toggleVerb = (id: string) => { + setSelectedVerbIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + }; + + const toggleTense = (key: string) => { + setSelectedTenseKeys((prev) => { + const next = new Set(prev); + if (next.has(key)) next.delete(key); + else next.add(key); + return next; + }); + }; + + const generateQuestion = (verbPool = selectedVerbs, tensePool = selectedTenses, history = recentKeys) => { + window.speechSynthesis?.cancel(); + setSpeaking(false); + if (verbPool.length === 0 || tensePool.length === 0) return; + + const candidates: Question[] = []; + for (const verb of verbPool) { + const table = conjugationById.get(verb.id); + if (!table) continue; + for (const t of tensePool) { + for (let personIndex = 0; personIndex < 6; personIndex++) { + candidates.push({ + verbId: verb.id, + infinitive: verb.infinitive, + meaning: verb.meaning, + mood: t.mood, + tense: t.tense, + tenseLabel: t.label, + personIndex, + answer: formsFor(table, t.mood, t.tense)[personIndex], + }); + } + } + } + if (candidates.length === 0) return; + + const keyOf = (q: Question) => `${q.verbId}.${q.mood}.${q.tense}.${q.personIndex}`; + let pool = candidates.filter((q) => !history.includes(keyOf(q))); + if (pool.length === 0) pool = candidates; + + const next = pool[Math.floor(Math.random() * pool.length)]; + setRecentKeys((prev) => [keyOf(next), ...prev].slice(0, 8)); + setQuestion(next); + setAnswer(""); + setSubmitted(false); + setIsCorrect(false); + }; + + const startPractice = () => { + setScore({ correct: 0, total: 0 }); + setRecentKeys([]); + setPhase("practice"); + generateQuestion(selectedVerbs, selectedTenses, []); + }; + + const speakAnswer = () => { + if (!question) return; + const started = speak(question.answer, { + onEnd: () => setSpeaking(false), + onError: () => setSpeaking(false), + }); + if (started) setSpeaking(true); + }; + + const handleSubmit = () => { + if (!question) return; + if (!submitted) { + const correct = normalize(answer) === normalize(question.answer); + setIsCorrect(correct); + setSubmitted(true); + setScore((prev) => ({ correct: prev.correct + (correct ? 1 : 0), total: prev.total + 1 })); + if (audioEnabled) speakAnswer(); + } else { + generateQuestion(); + } + }; + + const setAllVerbs = (on: boolean) => setSelectedVerbIds(on ? new Set(verbs.map((v) => v.id)) : new Set()); + const setAllTenses = (on: boolean) => + setSelectedTenseKeys(on ? new Set(tenseOptions.map((t) => tenseKey(t.mood, t.tense))) : new Set()); + + if (phase === "settings") { + const canStart = selectedVerbIds.size > 0 && selectedTenseKeys.size > 0; + return ( +
+
+
+
出題する動詞
+
+ + +
+
+
+ {verbs.map((v) => ( + + ))} +
+
+ +
+
+
出題する時制・法
+
+ + +
+
+
+ {tenseOptions.map((t) => { + const key = tenseKey(t.mood, t.tense); + return ( + + ); + })} +
+
+ +
+
+
+
音声読み上げ
+
+ 答え合わせのときに、正解の活用形を自動で読み上げます。 +
+
+ +
+
+ +
+ +
+ {!canStart && ( +
+ 動詞と時制・法を1つ以上選んでください。 +
+ )} +
+ ); + } + + if (!question) { + return ( +
+
出題できる組み合わせがありません。設定を見直してください。
+ +
+ ); + } + + return ( +
+
+ + + 正解: {score.correct} / {score.total} + +
+ +
+
+ + {question.tenseLabel} + +
+ +
+
{question.infinitive}
+
{question.meaning}
+
+ +
+ + {persons[question.personIndex]} + +
+ + + + {submitted && ( +
+ {isCorrect ? ( +
正解!
+ ) : ( +
不正解 — 正解は「{question.answer}」
+ )} + +
+ )} + +
+ +
+
+
+ ); +} diff --git a/lib/conjugation/es/auxiliaries.ts b/lib/conjugation/es/auxiliaries.ts new file mode 100644 index 0000000..87f295f --- /dev/null +++ b/lib/conjugation/es/auxiliaries.ts @@ -0,0 +1,18 @@ +import type { SixForms } from "./types"; + +interface HaberForms { + presente: SixForms; + pretéritoImperfecto: SixForms; + futuroSimple: SixForms; + condicionalSimple: SixForms; + subjuntivoPresente: SixForms; +} + +// スペイン語の完了時制はhaber(フランス語のavoir/êtreのような使い分けはなく常にhaber)のみを使う +export const HABER_FORMS: HaberForms = { + presente: ["he", "has", "ha", "hemos", "habéis", "han"], + pretéritoImperfecto: ["había", "habías", "había", "habíamos", "habíais", "habían"], + futuroSimple: ["habré", "habrás", "habrá", "habremos", "habréis", "habrán"], + condicionalSimple: ["habría", "habrías", "habría", "habríamos", "habríais", "habrían"], + subjuntivoPresente: ["haya", "hayas", "haya", "hayamos", "hayáis", "hayan"], +}; diff --git a/lib/conjugation/es/config.ts b/lib/conjugation/es/config.ts new file mode 100644 index 0000000..a5c602f --- /dev/null +++ b/lib/conjugation/es/config.ts @@ -0,0 +1,26 @@ +import type { ConjugationLanguageConfig } from "@/components/verbs/ConjugationPractice"; +import { speak } from "@/lib/speech"; +import { buildConjugation } from "./engine"; +import { PERSONS, TENSE_OPTIONS, formsFor, type ConjugationTable, type VerbEntry } from "./types"; + +const ACCENT_CYCLES: string[][] = [ + ["a", "á"], + ["e", "é"], + ["i", "í"], + ["o", "ó"], + ["u", "ú", "ü"], + ["n", "ñ"], +]; + +const TOOLBAR_CHARS = ["á", "é", "í", "ó", "ú", "ü", "ñ"]; + +export const spanishConjugationConfig: ConjugationLanguageConfig = { + persons: PERSONS, + tenseOptions: TENSE_OPTIONS, + buildConjugation, + formsFor, + speak: (text, handlers) => speak(text, "es-ES", handlers), + accentCycles: ACCENT_CYCLES, + toolbarChars: TOOLBAR_CHARS, + inputPlaceholder: "活用形を入力...", +}; diff --git a/lib/conjugation/es/engine.ts b/lib/conjugation/es/engine.ts new file mode 100644 index 0000000..77f19d4 --- /dev/null +++ b/lib/conjugation/es/engine.ts @@ -0,0 +1,80 @@ +import type { ConjugationTable, SixForms, VerbEntry } from "./types"; +import { HABER_FORMS } from "./auxiliaries"; + +const FUTURO_ENDINGS = ["é", "ás", "á", "emos", "éis", "án"]; +const CONDICIONAL_ENDINGS = ["ía", "ías", "ía", "íamos", "íais", "ían"]; +const IMPERFECTO_AR_ENDINGS = ["aba", "abas", "aba", "ábamos", "abais", "aban"]; +const IMPERFECTO_ER_IR_ENDINGS = ["ía", "ías", "ía", "íamos", "íais", "ían"]; + +function withEndings(stem: string, endings: string[]): SixForms { + return endings.map((e) => stem + e) as SixForms; +} + +function buildCompound(auxSix: SixForms, participio: string): SixForms { + return auxSix.map((f) => `${f} ${participio}`) as SixForms; +} + +interface IrregularBase { + presente: SixForms; + pretéritoIndefinido: SixForms; + subjuntivoPresente: SixForms; + futuroStem: string; + participio: string; + pretéritoImperfecto?: SixForms; +} + +// 規則動詞(-ar, -er, -ir)を語幹+語尾のルールから機械的に導出する +function conjugateRegularBase(infinitive: string, group: "ar" | "er" | "ir"): IrregularBase { + const stem = infinitive.slice(0, -2); + if (group === "ar") { + return { + presente: [stem + "o", stem + "as", stem + "a", stem + "amos", stem + "áis", stem + "an"], + pretéritoIndefinido: [stem + "é", stem + "aste", stem + "ó", stem + "amos", stem + "asteis", stem + "aron"], + subjuntivoPresente: [stem + "e", stem + "es", stem + "e", stem + "emos", stem + "éis", stem + "en"], + futuroStem: infinitive, + participio: stem + "ado", + }; + } + const presenteEndings = group === "er" ? ["o", "es", "e", "emos", "éis", "en"] : ["o", "es", "e", "imos", "ís", "en"]; + return { + presente: presenteEndings.map((e) => stem + e) as SixForms, + pretéritoIndefinido: [stem + "í", stem + "iste", stem + "ió", stem + "imos", stem + "isteis", stem + "ieron"], + subjuntivoPresente: [stem + "a", stem + "as", stem + "a", stem + "amos", stem + "áis", stem + "an"], + futuroStem: infinitive, + participio: stem + "ido", + }; +} + +function deriveImperfecto(infinitive: string, override?: SixForms): SixForms { + if (override) return override; + const stem = infinitive.slice(0, -2); + return infinitive.endsWith("ar") ? withEndings(stem, IMPERFECTO_AR_ENDINGS) : withEndings(stem, IMPERFECTO_ER_IR_ENDINGS); +} + +export function buildConjugation(verb: VerbEntry): ConjugationTable { + const base: IrregularBase = verb.kind === "regular" ? conjugateRegularBase(verb.infinitive, verb.group) : verb; + + const pretéritoImperfecto = deriveImperfecto(verb.infinitive, base.pretéritoImperfecto); + const futuroSimple = withEndings(base.futuroStem, FUTURO_ENDINGS); + const condicionalSimple = withEndings(base.futuroStem, CONDICIONAL_ENDINGS); + + return { + indicativo: { + presente: base.presente, + pretéritoImperfecto, + pretéritoIndefinido: base.pretéritoIndefinido, + pretéritoPerfectoCompuesto: buildCompound(HABER_FORMS.presente, base.participio), + pretéritoPluscuamperfecto: buildCompound(HABER_FORMS.pretéritoImperfecto, base.participio), + futuroSimple, + futuroCompuesto: buildCompound(HABER_FORMS.futuroSimple, base.participio), + }, + condicional: { + simple: condicionalSimple, + compuesto: buildCompound(HABER_FORMS.condicionalSimple, base.participio), + }, + subjuntivo: { + presente: base.subjuntivoPresente, + pretéritoPerfecto: buildCompound(HABER_FORMS.subjuntivoPresente, base.participio), + }, + }; +} diff --git a/lib/conjugation/es/types.ts b/lib/conjugation/es/types.ts new file mode 100644 index 0000000..ef08e17 --- /dev/null +++ b/lib/conjugation/es/types.ts @@ -0,0 +1,85 @@ +import type { SixForms, TenseOption } from "@/lib/conjugation/shared"; + +export type { SixForms }; + +export const PERSONS: readonly string[] = [ + "yo", + "tú", + "él / ella / usted", + "nosotros / nosotras", + "vosotros / vosotras", + "ellos / ellas / ustedes", +]; + +export type IndicativoTense = + | "presente" + | "pretéritoImperfecto" + | "pretéritoIndefinido" + | "pretéritoPerfectoCompuesto" + | "pretéritoPluscuamperfecto" + | "futuroSimple" + | "futuroCompuesto"; + +export type CondicionalTense = "simple" | "compuesto"; +export type SubjuntivoTense = "presente" | "pretéritoPerfecto"; + +export type Mood = "indicativo" | "condicional" | "subjuntivo"; + +export const TENSE_OPTIONS: TenseOption[] = [ + { mood: "indicativo", tense: "presente", label: "直説法 現在" }, + { mood: "indicativo", tense: "pretéritoImperfecto", label: "直説法 線過去" }, + { mood: "indicativo", tense: "pretéritoIndefinido", label: "直説法 点過去" }, + { mood: "indicativo", tense: "pretéritoPerfectoCompuesto", label: "直説法 現在完了" }, + { mood: "indicativo", tense: "pretéritoPluscuamperfecto", label: "直説法 過去完了" }, + { mood: "indicativo", tense: "futuroSimple", label: "直説法 未来" }, + { mood: "indicativo", tense: "futuroCompuesto", label: "直説法 未来完了" }, + { mood: "condicional", tense: "simple", label: "可能法 現在" }, + { mood: "condicional", tense: "compuesto", label: "可能法 過去" }, + { mood: "subjuntivo", tense: "presente", label: "接続法 現在" }, + { mood: "subjuntivo", tense: "pretéritoPerfecto", label: "接続法 現在完了" }, +]; + +export { tenseKey } from "@/lib/conjugation/shared"; + +export interface ConjugationTable { + indicativo: Record; + condicional: Record; + subjuntivo: Record; +} + +export function formsFor(table: ConjugationTable, mood: string, tense: string): SixForms { + switch (mood) { + case "indicativo": + return table.indicativo[tense as IndicativoTense]; + case "condicional": + return table.condicional[tense as CondicionalTense]; + case "subjuntivo": + return table.subjuntivo[tense as SubjuntivoTense]; + default: + throw new Error(`unknown mood: ${mood}`); + } +} + +export interface RegularVerb { + id: string; + infinitive: string; + meaning: string; + kind: "regular"; + group: "ar" | "er" | "ir"; +} + +export interface IrregularVerb { + id: string; + infinitive: string; + meaning: string; + kind: "irregular"; + participio: string; + presente: SixForms; + pretéritoIndefinido: SixForms; + subjuntivoPresente: SixForms; + futuroStem: string; + // ser・ir・verのみ、直説法線過去が語幹+語尾のルールに乗らない完全な例外形なのでここで直接指定する + pretéritoImperfecto?: SixForms; +} + +export type VerbEntry = RegularVerb | IrregularVerb; diff --git a/lib/conjugation/fr/auxiliaries.ts b/lib/conjugation/fr/auxiliaries.ts new file mode 100644 index 0000000..4381d0d --- /dev/null +++ b/lib/conjugation/fr/auxiliaries.ts @@ -0,0 +1,25 @@ +import type { SixForms } from "./types"; + +interface AuxiliaryForms { + présent: SixForms; + imparfait: SixForms; + futurSimple: SixForms; + conditionnelPrésent: SixForms; + subjonctifPrésent: SixForms; +} + +export const AVOIR_FORMS: AuxiliaryForms = { + présent: ["ai", "as", "a", "avons", "avez", "ont"], + imparfait: ["avais", "avais", "avait", "avions", "aviez", "avaient"], + futurSimple: ["aurai", "auras", "aura", "aurons", "aurez", "auront"], + conditionnelPrésent: ["aurais", "aurais", "aurait", "aurions", "auriez", "auraient"], + subjonctifPrésent: ["aie", "aies", "ait", "ayons", "ayez", "aient"], +}; + +export const ETRE_FORMS: AuxiliaryForms = { + présent: ["suis", "es", "est", "sommes", "êtes", "sont"], + imparfait: ["étais", "étais", "était", "étions", "étiez", "étaient"], + futurSimple: ["serai", "seras", "sera", "serons", "serez", "seront"], + conditionnelPrésent: ["serais", "serais", "serait", "serions", "seriez", "seraient"], + subjonctifPrésent: ["sois", "sois", "soit", "soyons", "soyez", "soient"], +}; diff --git a/lib/conjugation/fr/config.ts b/lib/conjugation/fr/config.ts new file mode 100644 index 0000000..bc9c875 --- /dev/null +++ b/lib/conjugation/fr/config.ts @@ -0,0 +1,27 @@ +import type { ConjugationLanguageConfig } from "@/components/verbs/ConjugationPractice"; +import { speak } from "@/lib/speech"; +import { buildConjugation } from "./engine"; +import { PERSONS, TENSE_OPTIONS, formsFor, type ConjugationTable, type VerbEntry } from "./types"; + +const ACCENT_CYCLES: string[][] = [ + ["e", "é", "è", "ê", "ë"], + ["a", "à", "â"], + ["i", "î", "ï"], + ["o", "ô"], + ["u", "ù", "û", "ü"], + ["c", "ç"], + ["y", "ÿ"], +]; + +const TOOLBAR_CHARS = ["é", "è", "ê", "ë", "à", "â", "î", "ï", "ô", "ù", "û", "ç"]; + +export const frenchConjugationConfig: ConjugationLanguageConfig = { + persons: PERSONS, + tenseOptions: TENSE_OPTIONS, + buildConjugation, + formsFor, + speak: (text, handlers) => speak(text, "fr-FR", handlers), + accentCycles: ACCENT_CYCLES, + toolbarChars: TOOLBAR_CHARS, + inputPlaceholder: "活用形を入力...", +}; diff --git a/lib/conjugation/fr/engine.ts b/lib/conjugation/fr/engine.ts new file mode 100644 index 0000000..f037379 --- /dev/null +++ b/lib/conjugation/fr/engine.ts @@ -0,0 +1,101 @@ +import type { Auxiliary, ConjugationTable, SixForms, VerbEntry } from "./types"; +import { AVOIR_FORMS, ETRE_FORMS } from "./auxiliaries"; + +const FUTUR_ENDINGS = ["ai", "as", "a", "ons", "ez", "ont"]; +const IMPARFAIT_ENDINGS = ["ais", "ais", "ait", "ions", "iez", "aient"]; + +function withEndings(stem: string, endings: string[]): SixForms { + return endings.map((e) => stem + e) as SixForms; +} + +function deriveImparfait(présent: SixForms, overrideStem?: string): SixForms { + const stem = overrideStem ?? présent[3].replace(/ons$/, ""); + return withEndings(stem, IMPARFAIT_ENDINGS); +} + +function deriveFuturEtConditionnel(futurStem: string): { + futurSimple: SixForms; + conditionnelPrésent: SixForms; +} { + return { + futurSimple: withEndings(futurStem, FUTUR_ENDINGS), + conditionnelPrésent: withEndings(futurStem, IMPARFAIT_ENDINGS), + }; +} + +function buildCompound(auxSix: SixForms, pastParticiple: string): SixForms { + return auxSix.map((f) => `${f} ${pastParticiple}`) as SixForms; +} + +function auxiliaryForms(auxiliary: Auxiliary) { + return auxiliary === "être" ? ETRE_FORMS : AVOIR_FORMS; +} + +interface RegularBase { + présent: SixForms; + subjonctifPrésent: SixForms; + passéSimple: SixForms; + futurStem: string; + pastParticiple: string; + imparfaitStem?: string; +} + +// 規則動詞(-er, -ir(finir型), -re(vendre型))を語幹+語尾のルールから機械的に導出する +function conjugateRegularBase(infinitive: string, group: 1 | 2 | 3): RegularBase { + if (group === 1) { + const stem = infinitive.slice(0, -2); + return { + présent: [stem + "e", stem + "es", stem + "e", stem + "ons", stem + "ez", stem + "ent"], + subjonctifPrésent: [stem + "e", stem + "es", stem + "e", stem + "ions", stem + "iez", stem + "ent"], + passéSimple: [stem + "ai", stem + "as", stem + "a", stem + "âmes", stem + "âtes", stem + "èrent"], + futurStem: infinitive, + pastParticiple: stem + "é", + }; + } + if (group === 2) { + const stem = infinitive.slice(0, -2); + return { + présent: [stem + "is", stem + "is", stem + "it", stem + "issons", stem + "issez", stem + "issent"], + subjonctifPrésent: [stem + "isse", stem + "isses", stem + "isse", stem + "issions", stem + "issiez", stem + "issent"], + passéSimple: [stem + "is", stem + "is", stem + "it", stem + "îmes", stem + "îtes", stem + "irent"], + futurStem: infinitive, + pastParticiple: stem + "i", + }; + } + const stem = infinitive.slice(0, -2); + return { + présent: [stem + "s", stem + "s", stem, stem + "ons", stem + "ez", stem + "ent"], + subjonctifPrésent: [stem + "e", stem + "es", stem + "e", stem + "ions", stem + "iez", stem + "ent"], + passéSimple: [stem + "is", stem + "is", stem + "it", stem + "îmes", stem + "îtes", stem + "irent"], + futurStem: infinitive.slice(0, -1), + pastParticiple: stem + "u", + }; +} + +export function buildConjugation(verb: VerbEntry): ConjugationTable { + const base: RegularBase = verb.kind === "regular" ? conjugateRegularBase(verb.infinitive, verb.group) : verb; + + const imparfait = deriveImparfait(base.présent, base.imparfaitStem); + const { futurSimple, conditionnelPrésent } = deriveFuturEtConditionnel(base.futurStem); + const aux = auxiliaryForms(verb.auxiliary); + + return { + indicatif: { + présent: base.présent, + imparfait, + passéSimple: base.passéSimple, + passéComposé: buildCompound(aux.présent, base.pastParticiple), + plusQueParfait: buildCompound(aux.imparfait, base.pastParticiple), + futurSimple, + futurAntérieur: buildCompound(aux.futurSimple, base.pastParticiple), + }, + conditionnel: { + présent: conditionnelPrésent, + passé: buildCompound(aux.conditionnelPrésent, base.pastParticiple), + }, + subjonctif: { + présent: base.subjonctifPrésent, + passé: buildCompound(aux.subjonctifPrésent, base.pastParticiple), + }, + }; +} diff --git a/lib/conjugation/fr/types.ts b/lib/conjugation/fr/types.ts new file mode 100644 index 0000000..e616e93 --- /dev/null +++ b/lib/conjugation/fr/types.ts @@ -0,0 +1,82 @@ +import type { SixForms, TenseOption } from "@/lib/conjugation/shared"; + +export type { SixForms }; + +export const PERSONS: readonly string[] = ["je", "tu", "il / elle / on", "nous", "vous", "ils / elles"]; + +export type IndicatifTense = + | "présent" + | "imparfait" + | "passéSimple" + | "passéComposé" + | "plusQueParfait" + | "futurSimple" + | "futurAntérieur"; + +export type ConditionnelTense = "présent" | "passé"; +export type SubjonctifTense = "présent" | "passé"; + +export type Mood = "indicatif" | "conditionnel" | "subjonctif"; + +export const TENSE_OPTIONS: TenseOption[] = [ + { mood: "indicatif", tense: "présent", label: "直説法 現在" }, + { mood: "indicatif", tense: "imparfait", label: "直説法 半過去" }, + { mood: "indicatif", tense: "passéSimple", label: "直説法 単純過去" }, + { mood: "indicatif", tense: "passéComposé", label: "直説法 複合過去" }, + { mood: "indicatif", tense: "plusQueParfait", label: "直説法 大過去" }, + { mood: "indicatif", tense: "futurSimple", label: "直説法 単純未来" }, + { mood: "indicatif", tense: "futurAntérieur", label: "直説法 前未来" }, + { mood: "conditionnel", tense: "présent", label: "条件法 現在" }, + { mood: "conditionnel", tense: "passé", label: "条件法 過去" }, + { mood: "subjonctif", tense: "présent", label: "接続法 現在" }, + { mood: "subjonctif", tense: "passé", label: "接続法 過去" }, +]; + +export { tenseKey } from "@/lib/conjugation/shared"; + +export interface ConjugationTable { + indicatif: Record; + conditionnel: Record; + subjonctif: Record; +} + +export function formsFor(table: ConjugationTable, mood: string, tense: string): SixForms { + switch (mood) { + case "indicatif": + return table.indicatif[tense as IndicatifTense]; + case "conditionnel": + return table.conditionnel[tense as ConditionnelTense]; + case "subjonctif": + return table.subjonctif[tense as SubjonctifTense]; + default: + throw new Error(`unknown mood: ${mood}`); + } +} + +export type Auxiliary = "avoir" | "être"; + +export interface RegularVerb { + id: string; + infinitive: string; + meaning: string; + kind: "regular"; + group: 1 | 2 | 3; + auxiliary: Auxiliary; +} + +export interface IrregularVerb { + id: string; + infinitive: string; + meaning: string; + kind: "irregular"; + auxiliary: Auxiliary; + pastParticiple: string; + présent: SixForms; + subjonctifPrésent: SixForms; + passéSimple: SixForms; + futurStem: string; + // 直説法半過去の語幹が「nous形の現在からonsを除いたもの」と一致しない例外(être)でのみ指定する + imparfaitStem?: string; +} + +export type VerbEntry = RegularVerb | IrregularVerb; diff --git a/lib/conjugation/shared.ts b/lib/conjugation/shared.ts new file mode 100644 index 0000000..6a28bab --- /dev/null +++ b/lib/conjugation/shared.ts @@ -0,0 +1,20 @@ +// 言語をまたいで共通する型だけを置く場所。活用ルールそのもの(時制の種類・語尾変化など)は +// 言語ごとに大きく異なるため、lib/conjugation/{fr,es}/ 以下にそれぞれ実装する。 + +export type SixForms = [string, string, string, string, string, string]; + +export interface VerbLike { + id: string; + infinitive: string; + meaning: string; +} + +export interface TenseOption { + mood: string; + tense: string; + label: string; +} + +export function tenseKey(mood: string, tense: string): string { + return `${mood}.${tense}`; +} diff --git a/lib/speech.ts b/lib/speech.ts new file mode 100644 index 0000000..de360f6 --- /dev/null +++ b/lib/speech.ts @@ -0,0 +1,67 @@ +// 学習用途のため、ブラウザ標準(1.0)よりゆっくり読み上げる +export const SPEECH_RATE = 0.8; + +// ブラウザ起動直後はgetVoices()が空配列を返すことがあるため、 +// モジュール読み込み時に一度呼んで音声リストの取得を早めに促す +if (typeof window !== "undefined" && "speechSynthesis" in window) { + window.speechSynthesis.getVoices(); +} + +// 既定の音声(OS標準の合成音声など)は抑揚が乏しく聞き取りにくいことがあるため、 +// Google/Natural/Neural系などの高品質な音声があればそちらを優先的に選ぶ +const PREFERRED_VOICE_KEYWORDS = ["google", "natural", "neural", "enhanced", "premium"]; + +function pickVoice(langPrefix: string): SpeechSynthesisVoice | undefined { + if (typeof window === "undefined" || !("speechSynthesis" in window)) return undefined; + + const matching = window.speechSynthesis.getVoices().filter((v) => v.lang.toLowerCase().startsWith(langPrefix)); + if (matching.length === 0) return undefined; + + const preferred = matching.find((v) => PREFERRED_VOICE_KEYWORDS.some((keyword) => v.name.toLowerCase().includes(keyword))); + + return preferred ?? matching[0]; +} + +// langは"fr-FR"や"es-ES"のようなBCP47タグ。音声選択は先頭の言語部分("fr"/"es")で照合する +export function createUtterance(text: string, lang: string): SpeechSynthesisUtterance { + const utterance = new SpeechSynthesisUtterance(text); + utterance.lang = lang; + utterance.rate = SPEECH_RATE; + + const voice = pickVoice(lang.split("-")[0].toLowerCase()); + if (voice) utterance.voice = voice; + + return utterance; +} + +// ChromeはSpeechSynthesisUtteranceへの参照が無いと、再生開始前にガベージコレクトされ +// エラーも出さずに無音のまま再生が失敗することがある。モジュール変数に保持して防ぐ +let activeUtterance: SpeechSynthesisUtterance | null = null; + +// 指定した言語での読み上げを開始する。開始できた場合はtrueを返す +export function speak(text: string, lang: string, handlers: { onEnd?: () => void; onError?: () => void } = {}): boolean { + if (typeof window === "undefined" || !("speechSynthesis" in window)) return false; + + window.speechSynthesis.cancel(); + + const utterance = createUtterance(text, lang); + activeUtterance = utterance; + + utterance.onend = () => { + if (activeUtterance === utterance) activeUtterance = null; + handlers.onEnd?.(); + }; + utterance.onerror = () => { + if (activeUtterance === utterance) activeUtterance = null; + handlers.onError?.(); + }; + + // Chromeはcancel()の直後に同期でspeak()すると、cancel処理と競合して新しい発話も + // 開始直後に打ち切られてしまうことがある(「再生中」表示が一瞬で消える不具合の原因)。 + // cancel()の処理が完了するのを待つため、少し遅らせてからspeak()を呼ぶ + setTimeout(() => { + if (activeUtterance === utterance) window.speechSynthesis.speak(utterance); + }, 50); + + return true; +} From 374b30e888cfd318da6a105cbcfb25969f355fc5 Mon Sep 17 00:00:00 2001 From: Tatsu723 Date: Fri, 28 Aug 2026 14:18:18 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E4=B8=AD=E5=9B=BD=E8=AA=9E=E3=81=AE?= =?UTF-8?q?=E3=83=94=E3=83=B3=E3=82=A4=E3=83=B3=E3=82=A2=E3=82=A6=E3=83=88?= =?UTF-8?q?=E3=83=97=E3=83=83=E3=83=88=E6=A9=9F=E8=83=BD=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/learn/chinese/01/characters.ts | 75 ++++++ app/learn/chinese/01/page.mdx | 12 - app/learn/chinese/01/page.tsx | 21 ++ components/pinyin/PinyinPractice.tsx | 390 +++++++++++++++++++++++++++ components/verbs/AccentInput.tsx | 43 ++- 5 files changed, 526 insertions(+), 15 deletions(-) create mode 100644 app/learn/chinese/01/characters.ts delete mode 100644 app/learn/chinese/01/page.mdx create mode 100644 app/learn/chinese/01/page.tsx create mode 100644 components/pinyin/PinyinPractice.tsx diff --git a/app/learn/chinese/01/characters.ts b/app/learn/chinese/01/characters.ts new file mode 100644 index 0000000..f742bae --- /dev/null +++ b/app/learn/chinese/01/characters.ts @@ -0,0 +1,75 @@ +// 基本的な漢字1字と、その普通話(標準中国語)でのピンイン。 +// pinyin は声調記号付きの表記(軽声は記号なし)。多音字は避け、代表的な読みが1つに定まる字だけを収録している。 + +export interface HanziEntry { + id: string; + hanzi: string; + // 声調記号付きピンイン(例: "wǒ"、軽声は "ma") + pinyin: string; + // 日本語での意味・用法 + meaning: string; +} + +export const characters: HanziEntry[] = [ + // 数字 + { id: "yi", hanzi: "一", pinyin: "yī", meaning: "1" }, + { id: "er", hanzi: "二", pinyin: "èr", meaning: "2" }, + { id: "san", hanzi: "三", pinyin: "sān", meaning: "3" }, + { id: "si", hanzi: "四", pinyin: "sì", meaning: "4" }, + { id: "wu", hanzi: "五", pinyin: "wǔ", meaning: "5" }, + { id: "liu", hanzi: "六", pinyin: "liù", meaning: "6" }, + { id: "qi", hanzi: "七", pinyin: "qī", meaning: "7" }, + { id: "ba", hanzi: "八", pinyin: "bā", meaning: "8" }, + { id: "jiu", hanzi: "九", pinyin: "jiǔ", meaning: "9" }, + { id: "shi10", hanzi: "十", pinyin: "shí", meaning: "10" }, + + // 代名詞・基本動詞・助詞 + { id: "wo", hanzi: "我", pinyin: "wǒ", meaning: "わたし" }, + { id: "ni", hanzi: "你", pinyin: "nǐ", meaning: "あなた" }, + { id: "ta-he", hanzi: "他", pinyin: "tā", meaning: "彼" }, + { id: "ta-she", hanzi: "她", pinyin: "tā", meaning: "彼女" }, + { id: "shi-be", hanzi: "是", pinyin: "shì", meaning: "〜である" }, + { id: "bu", hanzi: "不", pinyin: "bù", meaning: "〜でない(否定)" }, + { id: "you-have", hanzi: "有", pinyin: "yǒu", meaning: "ある・持つ" }, + { id: "zai", hanzi: "在", pinyin: "zài", meaning: "〜にある/〜で" }, + { id: "hao", hanzi: "好", pinyin: "hǎo", meaning: "よい" }, + { id: "hen", hanzi: "很", pinyin: "hěn", meaning: "とても" }, + { id: "ma-q", hanzi: "吗", pinyin: "ma", meaning: "〜か(疑問/軽声)" }, + { id: "de-poss", hanzi: "的", pinyin: "de", meaning: "〜の(軽声)" }, + + // 方向・大小・自然 + { id: "zhong", hanzi: "中", pinyin: "zhōng", meaning: "中・中国" }, + { id: "guo", hanzi: "国", pinyin: "guó", meaning: "国" }, + { id: "ren", hanzi: "人", pinyin: "rén", meaning: "人" }, + { id: "da", hanzi: "大", pinyin: "dà", meaning: "大きい" }, + { id: "xiao", hanzi: "小", pinyin: "xiǎo", meaning: "小さい" }, + { id: "duo", hanzi: "多", pinyin: "duō", meaning: "多い" }, + { id: "shang", hanzi: "上", pinyin: "shàng", meaning: "上" }, + { id: "xia", hanzi: "下", pinyin: "xià", meaning: "下" }, + { id: "tian", hanzi: "天", pinyin: "tiān", meaning: "空・日" }, + { id: "ri", hanzi: "日", pinyin: "rì", meaning: "日・太陽" }, + { id: "yue", hanzi: "月", pinyin: "yuè", meaning: "月" }, + { id: "nian", hanzi: "年", pinyin: "nián", meaning: "年" }, + { id: "shui", hanzi: "水", pinyin: "shuǐ", meaning: "水" }, + { id: "huo", hanzi: "火", pinyin: "huǒ", meaning: "火" }, + { id: "shan", hanzi: "山", pinyin: "shān", meaning: "山" }, + + // 人・暮らし + { id: "nu", hanzi: "女", pinyin: "nǚ", meaning: "女" }, + { id: "nan", hanzi: "男", pinyin: "nán", meaning: "男" }, + { id: "ming", hanzi: "名", pinyin: "míng", meaning: "名前" }, + { id: "zi-char", hanzi: "字", pinyin: "zì", meaning: "字" }, + { id: "shu-book", hanzi: "书", pinyin: "shū", meaning: "本" }, + { id: "jia", hanzi: "家", pinyin: "jiā", meaning: "家" }, + { id: "ai", hanzi: "爱", pinyin: "ài", meaning: "愛する" }, + { id: "xue", hanzi: "学", pinyin: "xué", meaning: "学ぶ" }, + { id: "ma-horse", hanzi: "马", pinyin: "mǎ", meaning: "馬" }, + { id: "niao", hanzi: "鸟", pinyin: "niǎo", meaning: "鳥" }, + + // 動作 + { id: "chi", hanzi: "吃", pinyin: "chī", meaning: "食べる" }, + { id: "he-drink", hanzi: "喝", pinyin: "hē", meaning: "飲む" }, + { id: "kan", hanzi: "看", pinyin: "kàn", meaning: "見る" }, + { id: "qu", hanzi: "去", pinyin: "qù", meaning: "行く" }, + { id: "lai", hanzi: "来", pinyin: "lái", meaning: "来る" }, +]; diff --git a/app/learn/chinese/01/page.mdx b/app/learn/chinese/01/page.mdx deleted file mode 100644 index f01e9cb..0000000 --- a/app/learn/chinese/01/page.mdx +++ /dev/null @@ -1,12 +0,0 @@ -export const title = "名詞の性の語尾による判別"; - -# 名詞の性の語尾による判別 - -_第1章 品詞と文型より_ - -ものの名詞は男性・女性を直観的に判断できないため、語尾によってある程度判別する。(例外もある) - -| 語尾 | 性 | -| ------------------------------- | -------- | -| -age、-ment、-eau、-teur | 男性名詞 | -| -tion、-sion、-té、-esse、-ette | 女性名詞 | diff --git a/app/learn/chinese/01/page.tsx b/app/learn/chinese/01/page.tsx new file mode 100644 index 0000000..b640852 --- /dev/null +++ b/app/learn/chinese/01/page.tsx @@ -0,0 +1,21 @@ +"use client"; + +import { PinyinPractice } from "@/components/pinyin/PinyinPractice"; +import { characters } from "./characters"; + +export const title = "漢字からピンイン"; + +export default function ChinesePinyinPage() { + return ( +
+

漢字からピンイン

+

+ 基本的な漢字を1字ずつ見て、ピンインを入力しましょう。四声は母音を入力した直後に + ↑/↓キーで選べます。 +

+
+ +
+
+ ); +} diff --git a/components/pinyin/PinyinPractice.tsx b/components/pinyin/PinyinPractice.tsx new file mode 100644 index 0000000..44cf219 --- /dev/null +++ b/components/pinyin/PinyinPractice.tsx @@ -0,0 +1,390 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; +import { AccentInput } from "@/components/verbs/AccentInput"; +import { speak } from "@/lib/speech"; +import type { HanziEntry } from "@/app/learn/chinese/01/characters"; + +// 母音ごとの声調サイクル。母音を入力した直後に ↑/↓ キーで +// 第1声→第2声→第3声→第4声(→軽声)と切り替えられる。 +const TONE_CYCLES: string[][] = [ + ["a", "ā", "á", "ǎ", "à"], + ["e", "ē", "é", "ě", "è"], + ["i", "ī", "í", "ǐ", "ì"], + ["o", "ō", "ó", "ǒ", "ò"], + ["u", "ū", "ú", "ǔ", "ù"], + ["ü", "ǖ", "ǘ", "ǚ", "ǜ"], +]; + +const TOOLBAR_CHARS = [ + "ü", + "ā", + "á", + "ǎ", + "à", + "ē", + "é", + "ě", + "è", + "ī", + "í", + "ǐ", + "ì", + "ō", + "ó", + "ǒ", + "ò", + "ū", + "ú", + "ǔ", + "ù", + "ǖ", + "ǘ", + "ǚ", + "ǜ", +]; + +// 声調記号付きの母音 → { 基本母音, 声調番号 } +const TONE_MARKS: Record = { + ā: { base: "a", tone: "1" }, + á: { base: "a", tone: "2" }, + ǎ: { base: "a", tone: "3" }, + à: { base: "a", tone: "4" }, + ē: { base: "e", tone: "1" }, + é: { base: "e", tone: "2" }, + ě: { base: "e", tone: "3" }, + è: { base: "e", tone: "4" }, + ī: { base: "i", tone: "1" }, + í: { base: "i", tone: "2" }, + ǐ: { base: "i", tone: "3" }, + ì: { base: "i", tone: "4" }, + ō: { base: "o", tone: "1" }, + ó: { base: "o", tone: "2" }, + ǒ: { base: "o", tone: "3" }, + ò: { base: "o", tone: "4" }, + ū: { base: "u", tone: "1" }, + ú: { base: "u", tone: "2" }, + ǔ: { base: "u", tone: "3" }, + ù: { base: "u", tone: "4" }, + ǖ: { base: "ü", tone: "1" }, + ǘ: { base: "ü", tone: "2" }, + ǚ: { base: "ü", tone: "3" }, + ǜ: { base: "ü", tone: "4" }, +}; + +// ピンインを「基本つづり + 声調番号」の正規形にそろえる。 +// 声調記号(wǒ)・末尾の数字(wo3)どちらの入力でも同じ形になり、 +// v / u: は ü として扱う。軽声・無声調は番号なし。 +function canonicalPinyin(raw: string): string { + let s = raw.normalize("NFC").trim().toLowerCase().replace(/\s+/g, ""); + s = s.replace(/u:/g, "ü").replace(/v/g, "ü"); + + let tone = ""; + let base = ""; + for (const ch of s) { + const mark = TONE_MARKS[ch]; + if (mark) { + base += mark.base; + tone = mark.tone; + } else { + base += ch; + } + } + + const trailing = base.match(/([0-5])$/); + if (trailing) { + base = base.slice(0, -1); + const t = trailing[1]; + tone = t === "0" || t === "5" ? "" : t; + } + + return base + tone; +} + +interface Question { + id: string; + hanzi: string; + pinyin: string; + meaning: string; +} + +interface PinyinPracticeProps { + characters: HanziEntry[]; +} + +export function PinyinPractice({ characters }: PinyinPracticeProps) { + const [phase, setPhase] = useState<"settings" | "practice">("settings"); + const [selectedIds, setSelectedIds] = useState>( + () => new Set(characters.map((c) => c.id)), + ); + const [audioEnabled, setAudioEnabled] = useState(true); + + const [question, setQuestion] = useState(null); + const [answer, setAnswer] = useState(""); + const [submitted, setSubmitted] = useState(false); + const [isCorrect, setIsCorrect] = useState(false); + const [score, setScore] = useState({ correct: 0, total: 0 }); + const [recentIds, setRecentIds] = useState([]); + const [speaking, setSpeaking] = useState(false); + + useEffect(() => { + return () => { + if (typeof window !== "undefined") window.speechSynthesis?.cancel(); + }; + }, []); + + const selectedCharacters = useMemo( + () => characters.filter((c) => selectedIds.has(c.id)), + [characters, selectedIds], + ); + + const toggleCharacter = (id: string) => { + setSelectedIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + }; + + const setAll = (on: boolean) => + setSelectedIds(on ? new Set(characters.map((c) => c.id)) : new Set()); + + const speakHanzi = (hanzi: string) => { + const started = speak(hanzi, "zh-CN", { + onEnd: () => setSpeaking(false), + onError: () => setSpeaking(false), + }); + if (started) setSpeaking(true); + }; + + const generateQuestion = (pool = selectedCharacters, history = recentIds) => { + window.speechSynthesis?.cancel(); + setSpeaking(false); + if (pool.length === 0) return; + + let candidates = pool.filter((c) => !history.includes(c.id)); + if (candidates.length === 0) candidates = pool; + + const next = candidates[Math.floor(Math.random() * candidates.length)]; + // 直近の出題を避ける。ただし少なくとも1つは候補が残るよう、選択数-1 を上限にする + setRecentIds((prev) => [next.id, ...prev].slice(0, Math.max(0, Math.min(8, pool.length - 1)))); + setQuestion({ id: next.id, hanzi: next.hanzi, pinyin: next.pinyin, meaning: next.meaning }); + setAnswer(""); + setSubmitted(false); + setIsCorrect(false); + }; + + const startPractice = () => { + setScore({ correct: 0, total: 0 }); + setRecentIds([]); + setPhase("practice"); + generateQuestion(selectedCharacters, []); + }; + + const handleSubmit = () => { + if (!question) return; + if (!submitted) { + const correct = canonicalPinyin(answer) === canonicalPinyin(question.pinyin); + setIsCorrect(correct); + setSubmitted(true); + setScore((prev) => ({ correct: prev.correct + (correct ? 1 : 0), total: prev.total + 1 })); + if (audioEnabled) speakHanzi(question.hanzi); + } else { + generateQuestion(); + } + }; + + if (phase === "settings") { + const canStart = selectedIds.size > 0; + return ( +
+
+
+
出題する漢字
+
+ + +
+
+
+ {characters.map((c) => ( + + ))} +
+
+ +
+
+
+
音声読み上げ
+
+ 答え合わせのときに、漢字の発音を自動で読み上げます。 +
+
+ +
+
+ +
+ +
+ {!canStart && ( +
+ 漢字を1つ以上選んでください。 +
+ )} +
+ ); + } + + if (!question) { + return ( +
+
出題できる漢字がありません。設定を見直してください。
+ +
+ ); + } + + return ( +
+
+ + + 正解: {score.correct} / {score.total} + +
+ +
+
+ この漢字のピンインは? +
+
+
+ {question.hanzi} +
+ {submitted && ( +
{question.meaning}
+ )} +
+ + + ヒント: 母音(a e i o u ü)を入力した直後に ↑キーを押すと、a → ā → á → ǎ → à + のように四声の記号を付けられます(↓キーで逆順)。ü は u に続けて \ + を打つと入力できます。 ü や記号は下のボタンからも入力できます。数字での声調入力(例: + hao3)も正解になります。 + + } + className={`w-full rounded-xl border-2 px-4 py-3 text-center text-xl font-medium text-zinc-800 outline-none transition-colors dark:text-zinc-100 ${ + !submitted + ? "border-zinc-200 bg-white focus:border-tealblue-400 dark:border-zinc-700 dark:bg-zinc-800" + : isCorrect + ? "border-green-500 bg-green-50 dark:bg-green-900/20" + : "border-red-500 bg-red-50 dark:bg-red-900/20" + }`} + /> + + {submitted && ( +
+ {isCorrect ? ( +
+ 正解!({question.pinyin}) +
+ ) : ( +
+ 不正解 — 正解は「{question.pinyin}」 +
+ )} + +
+ )} + +
+ +
+
+
+ ); +} diff --git a/components/verbs/AccentInput.tsx b/components/verbs/AccentInput.tsx index 77e2d14..4764441 100644 --- a/components/verbs/AccentInput.tsx +++ b/components/verbs/AccentInput.tsx @@ -1,7 +1,7 @@ "use client"; import { useMemo, useRef } from "react"; -import type { KeyboardEvent } from "react"; +import type { KeyboardEvent, ReactNode } from "react"; interface AccentInputProps { value: string; @@ -15,6 +15,11 @@ interface AccentInputProps { accentCycles: string[][]; // 入力欄の下に並べる、クリックで直接入力できるボタンの文字一覧 toolbarChars: string[]; + // 入力欄の下に表示する操作ヒント(省略時はアクセント記号向けの既定文言) + hintText?: ReactNode; + // 2文字の組み合わせを1文字に置き換える。キー: 「直前の文字 + 押したキー」、値: 置換後の文字。 + // 例: { "u\\": "ü" } なら、u を打った直後に \ を打つと ü になる + chordReplacements?: Record; } export function AccentInput({ @@ -27,6 +32,8 @@ export function AccentInput({ className, accentCycles, toolbarChars, + hintText, + chordReplacements, }: AccentInputProps) { const inputRef = useRef(null); @@ -77,6 +84,27 @@ export function AccentInput({ return true; }; + // カーソル直前の文字と押したキーの組み合わせが chordReplacements に一致すれば、 + // その2文字を置換後の文字に差し替える。差し替えた場合はtrueを返す + const applyChordReplacement = (key: string): boolean => { + if (!chordReplacements) return false; + const input = inputRef.current; + if (!input) return false; + const caret = input.selectionStart; + if (caret === null || caret !== input.selectionEnd || caret === 0) return false; + + const prev = value[caret - 1]; + const replacement = chordReplacements[prev + key]; + if (replacement === undefined) return false; + + onChange(value.slice(0, caret - 1) + replacement + value.slice(caret)); + const nextCaret = caret - 1 + replacement.length; + requestAnimationFrame(() => { + input.setSelectionRange(nextCaret, nextCaret); + }); + return true; + }; + const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Enter") { onSubmit?.(); @@ -84,6 +112,11 @@ export function AccentInput({ } if (disabled) return; + if (e.key.length === 1 && applyChordReplacement(e.key)) { + e.preventDefault(); + return; + } + if (e.key === "ArrowUp") { if (applyCycledChar(nextAccentChar)) e.preventDefault(); return; @@ -137,8 +170,12 @@ export function AccentInput({ ))}
- ヒント: アルファベットを入力した直後に ↑キーを押すと、{accentCycles[0]?.join(" → ")} - のようにアクセント記号付きの文字へ切り替えられます(↓キーで逆順)。上のボタンから直接入力することもできます。 + {hintText ?? ( + <> + ヒント: アルファベットを入力した直後に ↑キーを押すと、{accentCycles[0]?.join(" → ")} + のようにアクセント記号付きの文字へ切り替えられます(↓キーで逆順)。上のボタンから直接入力することもできます。 + + )}
); From d2f2b8d8513312e00b8bdad435e3e251d2ea732e Mon Sep 17 00:00:00 2001 From: Tatsu723 Date: Fri, 28 Aug 2026 14:31:31 +0900 Subject: [PATCH 3/3] =?UTF-8?q?format=E3=81=AE=E8=A8=82=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/learn/french/01/verbs.ts | 144 ++++++++++++++++++++--- app/learn/spanish/01/verbs.ts | 81 +++++++++++-- components/verbs/ConjugationPractice.tsx | 61 ++++++++-- lib/conjugation/es/engine.ts | 46 ++++++-- lib/conjugation/fr/engine.ts | 66 +++++++++-- lib/conjugation/fr/types.ts | 9 +- lib/speech.ts | 14 ++- 7 files changed, 364 insertions(+), 57 deletions(-) diff --git a/app/learn/french/01/verbs.ts b/app/learn/french/01/verbs.ts index 3f460e6..d7eab1c 100644 --- a/app/learn/french/01/verbs.ts +++ b/app/learn/french/01/verbs.ts @@ -40,9 +40,30 @@ function crireVerb(stem: string, id: string, infinitive: string, meaning: string kind: "irregular", auxiliary: "avoir", pastParticiple: stem + "t", - présent: [stem + "s", stem + "s", stem + "t", longStem + "ons", longStem + "ez", longStem + "ent"], - subjonctifPrésent: [longStem + "e", longStem + "es", longStem + "e", longStem + "ions", longStem + "iez", longStem + "ent"], - passéSimple: [longStem + "is", longStem + "is", longStem + "it", longStem + "îmes", longStem + "îtes", longStem + "irent"], + présent: [ + stem + "s", + stem + "s", + stem + "t", + longStem + "ons", + longStem + "ez", + longStem + "ent", + ], + subjonctifPrésent: [ + longStem + "e", + longStem + "es", + longStem + "e", + longStem + "ions", + longStem + "iez", + longStem + "ent", + ], + passéSimple: [ + longStem + "is", + longStem + "is", + longStem + "it", + longStem + "îmes", + longStem + "îtes", + longStem + "irent", + ], futurStem: stem + "r", }; } @@ -58,8 +79,22 @@ function uireVerb(stem: string, id: string, infinitive: string, meaning: string) auxiliary: "avoir", pastParticiple: stem + "it", présent: [stem + "is", stem + "is", stem + "it", stem + "isons", stem + "isez", stem + "isent"], - subjonctifPrésent: [stem + "ise", stem + "ises", stem + "ise", stem + "isions", stem + "isiez", stem + "isent"], - passéSimple: [stem + "isis", stem + "isis", stem + "isit", stem + "isîmes", stem + "isîtes", stem + "isirent"], + subjonctifPrésent: [ + stem + "ise", + stem + "ises", + stem + "ise", + stem + "isions", + stem + "isiez", + stem + "isent", + ], + passéSimple: [ + stem + "isis", + stem + "isis", + stem + "isit", + stem + "isîmes", + stem + "isîtes", + stem + "isirent", + ], futurStem: stem + "ir", }; } @@ -282,7 +317,14 @@ const connaitre: IrregularVerb = { auxiliary: "avoir", pastParticiple: "connu", présent: ["connais", "connais", "connaît", "connaissons", "connaissez", "connaissent"], - subjonctifPrésent: ["connaisse", "connaisses", "connaisse", "connaissions", "connaissiez", "connaissent"], + subjonctifPrésent: [ + "connaisse", + "connaisses", + "connaisse", + "connaissions", + "connaissiez", + "connaissent", + ], passéSimple: ["connus", "connus", "connut", "connûmes", "connûtes", "connurent"], futurStem: "connaîtr", }; @@ -419,7 +461,14 @@ const otherBaseIrregularVerbs: IrregularVerb[] = [ auxiliary: "avoir", pastParticiple: "acquis", présent: ["acquiers", "acquiers", "acquiert", "acquérons", "acquérez", "acquièrent"], - subjonctifPrésent: ["acquière", "acquières", "acquière", "acquérions", "acquériez", "acquièrent"], + subjonctifPrésent: [ + "acquière", + "acquières", + "acquière", + "acquérions", + "acquériez", + "acquièrent", + ], passéSimple: ["acquis", "acquis", "acquit", "acquîmes", "acquîtes", "acquirent"], futurStem: "acquerr", }, @@ -784,15 +833,78 @@ const irregularVerbs: VerbEntry[] = [ // 規則動詞: 語尾変化のルールだけで活用可能なため、原形とグループのみ保持する const regularVerbs: VerbEntry[] = [ - { id: "parler", infinitive: "parler", meaning: "話す", kind: "regular", group: 1, auxiliary: "avoir" }, - { id: "aimer", infinitive: "aimer", meaning: "好きである、愛する", kind: "regular", group: 1, auxiliary: "avoir" }, - { id: "chanter", infinitive: "chanter", meaning: "歌う", kind: "regular", group: 1, auxiliary: "avoir" }, - { id: "finir", infinitive: "finir", meaning: "終える", kind: "regular", group: 2, auxiliary: "avoir" }, - { id: "choisir", infinitive: "choisir", meaning: "選ぶ", kind: "regular", group: 2, auxiliary: "avoir" }, - { id: "reussir", infinitive: "réussir", meaning: "成功する", kind: "regular", group: 2, auxiliary: "avoir" }, - { id: "vendre", infinitive: "vendre", meaning: "売る", kind: "regular", group: 3, auxiliary: "avoir" }, - { id: "attendre", infinitive: "attendre", meaning: "待つ", kind: "regular", group: 3, auxiliary: "avoir" }, - { id: "repondre", infinitive: "répondre", meaning: "答える", kind: "regular", group: 3, auxiliary: "avoir" }, + { + id: "parler", + infinitive: "parler", + meaning: "話す", + kind: "regular", + group: 1, + auxiliary: "avoir", + }, + { + id: "aimer", + infinitive: "aimer", + meaning: "好きである、愛する", + kind: "regular", + group: 1, + auxiliary: "avoir", + }, + { + id: "chanter", + infinitive: "chanter", + meaning: "歌う", + kind: "regular", + group: 1, + auxiliary: "avoir", + }, + { + id: "finir", + infinitive: "finir", + meaning: "終える", + kind: "regular", + group: 2, + auxiliary: "avoir", + }, + { + id: "choisir", + infinitive: "choisir", + meaning: "選ぶ", + kind: "regular", + group: 2, + auxiliary: "avoir", + }, + { + id: "reussir", + infinitive: "réussir", + meaning: "成功する", + kind: "regular", + group: 2, + auxiliary: "avoir", + }, + { + id: "vendre", + infinitive: "vendre", + meaning: "売る", + kind: "regular", + group: 3, + auxiliary: "avoir", + }, + { + id: "attendre", + infinitive: "attendre", + meaning: "待つ", + kind: "regular", + group: 3, + auxiliary: "avoir", + }, + { + id: "repondre", + infinitive: "répondre", + meaning: "答える", + kind: "regular", + group: 3, + auxiliary: "avoir", + }, ]; export const verbs: VerbEntry[] = [...irregularVerbs, ...regularVerbs]; diff --git a/app/learn/spanish/01/verbs.ts b/app/learn/spanish/01/verbs.ts index 9e47f83..49c6e7c 100644 --- a/app/learn/spanish/01/verbs.ts +++ b/app/learn/spanish/01/verbs.ts @@ -23,7 +23,14 @@ const irregularVerbs: IrregularVerb[] = [ kind: "irregular", participio: "estado", presente: ["estoy", "estás", "está", "estamos", "estáis", "están"], - pretéritoIndefinido: ["estuve", "estuviste", "estuvo", "estuvimos", "estuvisteis", "estuvieron"], + pretéritoIndefinido: [ + "estuve", + "estuviste", + "estuvo", + "estuvimos", + "estuvisteis", + "estuvieron", + ], subjuntivoPresente: ["esté", "estés", "esté", "estemos", "estéis", "estén"], futuroStem: "estar", }, @@ -190,7 +197,14 @@ const irregularVerbs: IrregularVerb[] = [ kind: "irregular", participio: "conocido", presente: ["conozco", "conoces", "conoce", "conocemos", "conocéis", "conocen"], - pretéritoIndefinido: ["conocí", "conociste", "conoció", "conocimos", "conocisteis", "conocieron"], + pretéritoIndefinido: [ + "conocí", + "conociste", + "conoció", + "conocimos", + "conocisteis", + "conocieron", + ], subjuntivoPresente: ["conozca", "conozcas", "conozca", "conozcamos", "conozcáis", "conozcan"], futuroStem: "conocer", }, @@ -289,8 +303,22 @@ const irregularVerbs: IrregularVerb[] = [ kind: "irregular", participio: "encontrado", presente: ["encuentro", "encuentras", "encuentra", "encontramos", "encontráis", "encuentran"], - pretéritoIndefinido: ["encontré", "encontraste", "encontró", "encontramos", "encontrasteis", "encontraron"], - subjuntivoPresente: ["encuentre", "encuentres", "encuentre", "encontremos", "encontréis", "encuentren"], + pretéritoIndefinido: [ + "encontré", + "encontraste", + "encontró", + "encontramos", + "encontrasteis", + "encontraron", + ], + subjuntivoPresente: [ + "encuentre", + "encuentres", + "encuentre", + "encontremos", + "encontréis", + "encuentren", + ], futuroStem: "encontrar", }, { @@ -322,8 +350,22 @@ const irregularVerbs: IrregularVerb[] = [ kind: "irregular", participio: "entendido", presente: ["entiendo", "entiendes", "entiende", "entendemos", "entendéis", "entienden"], - pretéritoIndefinido: ["entendí", "entendiste", "entendió", "entendimos", "entendisteis", "entendieron"], - subjuntivoPresente: ["entienda", "entiendas", "entienda", "entendamos", "entendáis", "entiendan"], + pretéritoIndefinido: [ + "entendí", + "entendiste", + "entendió", + "entendimos", + "entendisteis", + "entendieron", + ], + subjuntivoPresente: [ + "entienda", + "entiendas", + "entienda", + "entendamos", + "entendáis", + "entiendan", + ], futuroStem: "entender", }, { @@ -344,7 +386,14 @@ const irregularVerbs: IrregularVerb[] = [ kind: "irregular", participio: "escrito", presente: ["escribo", "escribes", "escribe", "escribimos", "escribís", "escriben"], - pretéritoIndefinido: ["escribí", "escribiste", "escribió", "escribimos", "escribisteis", "escribieron"], + pretéritoIndefinido: [ + "escribí", + "escribiste", + "escribió", + "escribimos", + "escribisteis", + "escribieron", + ], subjuntivoPresente: ["escriba", "escribas", "escriba", "escribamos", "escribáis", "escriban"], futuroStem: "escribir", }, @@ -377,8 +426,22 @@ const irregularVerbs: IrregularVerb[] = [ kind: "irregular", participio: "construido", presente: ["construyo", "construyes", "construye", "construimos", "construís", "construyen"], - pretéritoIndefinido: ["construí", "construiste", "construyó", "construimos", "construisteis", "construyeron"], - subjuntivoPresente: ["construya", "construyas", "construya", "construyamos", "construyáis", "construyan"], + pretéritoIndefinido: [ + "construí", + "construiste", + "construyó", + "construimos", + "construisteis", + "construyeron", + ], + subjuntivoPresente: [ + "construya", + "construyas", + "construya", + "construyamos", + "construyáis", + "construyan", + ], futuroStem: "construir", }, { diff --git a/components/verbs/ConjugationPractice.tsx b/components/verbs/ConjugationPractice.tsx index f0fc292..b3224e8 100644 --- a/components/verbs/ConjugationPractice.tsx +++ b/components/verbs/ConjugationPractice.tsx @@ -37,12 +37,29 @@ interface ConjugationPracticeProps { language: ConjugationLanguageConfig; } -export function ConjugationPractice({ verbs, language }: ConjugationPracticeProps) { - const { persons, tenseOptions, buildConjugation, formsFor, speak, accentCycles, toolbarChars, inputPlaceholder } = language; - - const conjugationById = useMemo(() => new Map(verbs.map((v) => [v.id, buildConjugation(v)])), [verbs, buildConjugation]); +export function ConjugationPractice({ + verbs, + language, +}: ConjugationPracticeProps) { + const { + persons, + tenseOptions, + buildConjugation, + formsFor, + speak, + accentCycles, + toolbarChars, + inputPlaceholder, + } = language; + + const conjugationById = useMemo( + () => new Map(verbs.map((v) => [v.id, buildConjugation(v)])), + [verbs, buildConjugation], + ); const [phase, setPhase] = useState<"settings" | "practice">("settings"); - const [selectedVerbIds, setSelectedVerbIds] = useState>(() => new Set(verbs.map((v) => v.id))); + const [selectedVerbIds, setSelectedVerbIds] = useState>( + () => new Set(verbs.map((v) => v.id)), + ); const [selectedTenseKeys, setSelectedTenseKeys] = useState>( () => new Set(tenseOptions.map((t) => tenseKey(t.mood, t.tense))), ); @@ -64,7 +81,10 @@ export function ConjugationPractice({ verbs, language }: const toggleAudio = () => setAudioEnabled((prev) => !prev); - const selectedVerbs = useMemo(() => verbs.filter((v) => selectedVerbIds.has(v.id)), [verbs, selectedVerbIds]); + const selectedVerbs = useMemo( + () => verbs.filter((v) => selectedVerbIds.has(v.id)), + [verbs, selectedVerbIds], + ); const selectedTenses = useMemo( () => tenseOptions.filter((t) => selectedTenseKeys.has(tenseKey(t.mood, t.tense))), [tenseOptions, selectedTenseKeys], @@ -88,7 +108,11 @@ export function ConjugationPractice({ verbs, language }: }); }; - const generateQuestion = (verbPool = selectedVerbs, tensePool = selectedTenses, history = recentKeys) => { + const generateQuestion = ( + verbPool = selectedVerbs, + tensePool = selectedTenses, + history = recentKeys, + ) => { window.speechSynthesis?.cancel(); setSpeaking(false); if (verbPool.length === 0 || tensePool.length === 0) return; @@ -155,9 +179,12 @@ export function ConjugationPractice({ verbs, language }: } }; - const setAllVerbs = (on: boolean) => setSelectedVerbIds(on ? new Set(verbs.map((v) => v.id)) : new Set()); + const setAllVerbs = (on: boolean) => + setSelectedVerbIds(on ? new Set(verbs.map((v) => v.id)) : new Set()); const setAllTenses = (on: boolean) => - setSelectedTenseKeys(on ? new Set(tenseOptions.map((t) => tenseKey(t.mood, t.tense))) : new Set()); + setSelectedTenseKeys( + on ? new Set(tenseOptions.map((t) => tenseKey(t.mood, t.tense))) : new Set(), + ); if (phase === "settings") { const canStart = selectedVerbIds.size > 0 && selectedTenseKeys.size > 0; @@ -196,7 +223,9 @@ export function ConjugationPractice({ verbs, language }: className="h-4 w-4 accent-tealblue-600" /> - {v.infinitive} + + {v.infinitive} + {v.meaning} @@ -206,7 +235,9 @@ export function ConjugationPractice({ verbs, language }:
-
出題する時制・法
+
+ 出題する時制・法 +
-
{question.infinitive}
+
+ {question.infinitive} +
{question.meaning}
@@ -358,7 +391,9 @@ export function ConjugationPractice({ verbs, language }: {isCorrect ? (
正解!
) : ( -
不正解 — 正解は「{question.answer}」
+
+ 不正解 — 正解は「{question.answer}」 +
)}