本文へ移動

Jisho辞書

JMdictの単語を読み、表記、英語の意味、含まれる漢字、ローマ字から引け、必要な小さなチャンクだけを取得します。初期値はセーフモードで、各語義にはJMdictの印が付き、「フィルターなし」も選べます。2,999字の漢字の学年、JLPTレベル、読み、意味、形の似た漢字の組も収め、表ごとに独立したエントリポイントがあります。

「Jisho」は「辞書」のローマ字表記です。日本語の単語と漢字を収めています。 名前について(英語)

目次

やり方

React、Vue、Svelte、Angular

React、Vue、Svelte、Angularのコンポーネントで単語を引き、サーバーで単語を描きます。

単語を引く処理はPromiseです。コンポーネントは、文字が変わったら引き始め、単語を自分の状態に持ち、文字が変わったあとに届いた答えは捨てます。単語はふつうの行なので、written、reading、meaningを文字として表示するだけでよく、安全の印を付ける必要はありません。

React

import { loadWords, type Word, wordsOf } from "@johnmorrisdotca/jisho/words";
import { useEffect, useState } from "react";

export function WordList({ words }: { words: readonly Word[] }) {
  return (
    <ul id="words" lang="ja">
      {words.slice(0, 5).map((one) => (
        <li key={`${one.written}-${one.reading}`}>
          {one.written} {one.meaning}
        </li>
      ))}
    </ul>
  );
}

export function Lookup({ text }: { text: string }) {
  const [words, setWords] = useState<readonly Word[]>([]);
  useEffect(() => {
    let current = true;
    void loadWords(text).then((loaded) => current && setWords(wordsOf(loaded, text)));
    return () => {
      current = false;
    };
  }, [text]);
  return <WordList words={words} />;
}

WordListは渡された単語を描くだけで、Lookupが単語を取得します。この2つを分けておけば、前者はサーバーでも動きます。

サーバーでの描画

import { loadWords, wordsOf } from "@johnmorrisdotca/jisho/words";
import { renderToString } from "react-dom/server";
import { WordList } from "./WordList";

const words = wordsOf(await loadWords("かみ"), "かみ");
console.log(renderToString(<WordList words={words} />));

サーバーでの描画はエフェクトを待てません。単語を先に読み込み(ルートのローダー、サーバーコンポーネント、スクリプトなど)、WordListに渡します。チャンクのファイルは実行時にnode_modulesから読みます。辞書全体がバンドルに入らないよう、サーバーのバンドルではこのパッケージを外部扱いにしてください。

Vue 3

<script setup lang="ts">
import { loadWords, type Word, wordsOf } from "@johnmorrisdotca/jisho/words";
import { ref, watchEffect } from "vue";

const props = defineProps<{ text: string }>();
const words = ref<readonly Word[]>([]);

watchEffect(async () => {
  const text = props.text;
  const loaded = await loadWords(text);
  if (text === props.text) words.value = wordsOf(loaded, text);
});
</script>

<template>
  <ul id="words" lang="ja">
    <li v-for="one in words.slice(0, 5)" :key="one.written + one.reading">{{ one.written }} {{ one.meaning }}</li>
  </ul>
</template>
import { createApp } from "vue";
import WordList from "./WordList.vue";

createApp(WordList, { text: "かみ" }).mount("#app");

Svelte

<script lang="ts">
import { loadWords, type Word, wordsOf } from "@johnmorrisdotca/jisho/words";

let { text }: { text: string } = $props();
let words = $state<readonly Word[]>([]);

$effect(() => {
  const asked = text;
  void loadWords(asked).then((loaded) => {
    if (asked === text) words = wordsOf(loaded, asked);
  });
});
</script>

<ul id="words" lang="ja">
  {#each words.slice(0, 5) as one (one.written + one.reading)}
    <li>{one.written} {one.meaning}</li>
  {/each}
</ul>

Angular

import { Component, Input, type OnChanges, type OnInit, type SimpleChanges, signal } from "@angular/core";
import { loadWords, type Word, wordsOf } from "@johnmorrisdotca/jisho/words";

@Component({
  selector: "app-word-list",
  standalone: true,
  template: `
    <ul id="words" lang="ja">
      @for (one of words(); track one.written + one.reading) {
        <li>{{ one.written }} {{ one.meaning }}</li>
      }
    </ul>
  `,
})
export class WordListComponent implements OnInit, OnChanges {
  @Input() text = "かみ";
  readonly words = signal<readonly Word[]>([]);

  ngOnInit(): void {
    void this.show();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes["text"]?.firstChange === false) void this.show();
  }

  private async show(): Promise<void> {
    const asked = this.text;
    const loaded = await loadWords(asked);
    if (asked === this.text) this.words.set(wordsOf(loaded, asked).slice(0, 5));
  }
}

このコンポーネントはシグナルと新しい制御フロー構文を使い、zoneなしで動きます。起動は、ふつうのスタンドアロンのAngularアプリと同じです。

ソースを見るGitHub で編集@johnmorrisdotca/jisho 2.11.0 のソースから作成しました。

この日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。