🚢

英語学習サイト ✏️eigopencil.com をリリースしました。

2025/01/02に公開

アプリケーションの説明

eigopencil.comは、英語のライティングができるスペースを提供します。

なぜこれを作ったか

自分の英語力向上のために開発しました。

サイトURL,README

https://eigopencil.com/question
https://github.com/KazumaWada/eigopencil#eigopencilcom

使用技術

フロントエンド: HTML/CSS/Tailwind CSS
バックエンド: Ruby on Rails
データベース: PostgreSQL
インフラ: Docker
デプロイに使用したソフトウェア: Render

実装した機能一覧

  • CRUD機能
  • Cookie
  • 投稿時の太字、改行
  • 投稿日をカレンダーで確認 (追記: デザインをシンプルにするために削除しました)
  • 下書き保存
  • リッチテキストエディタ(https://quilljs.com/)
  • xシェア機能 (作成した投稿をワンクリックでXに共有)
  • 手書き認識機能 (手書きの文字を画像から認識し、テキスト化 (rtesseract / MiniMagick 使用))

こだわったところ

フレンドリーなURL:

通常のルート: eigopencil.com/users/1
フレンドリーURL: eigopencil.com/kazuma
ユーザーがプロファイルをシェアすることを想定して、friendly_id gem を活用し見やすく親しみやすいURLにカスタマイズしました。

直感的なUI/UX:

Xの青丸い投稿ボタンを参考にして、自分のアプリにも取り入れました。

画像処理の工夫:

rtesseractだけでは画像を文字列に変えたときに上手く読み込めていなかったので、MiniMagick を用いて処理前の画像の補正を行い、手書き認識率を向上。

実装中に起こった問題とその解決策

ドメイン名

作り始めた当初は、一日一行英語をアウトプットするというコンセプトで、onedayoneline.comというドメインを取得しようとしていました。しかし、その名前がしっくりきませんでした。

なぜかというと、英語をアウトプットする場所を提供するアプリなので、EnglishやEigo、pen, write、jornalなどのワードが入っていた方がユーザーがパッとみた時に「何となくこんなアプリなんだろうな」と想像できた方が、ユーザーがすぐに理解して試してくれると思ったからです。onedayonelineと言う文字を見ても使う側はどんなアプリかイメージが湧かないだろうなと考えていました。

考えた結果、eigoとpencilを繋げてeigopencil.comにすることにしました。それに合わせてアプリのアイコンも視覚的にパッとみてすぐわかるような"✏️"にしました。

この変更に伴い、1行(one line)ということでPostモデルのcontentの最大文字数を50字ほどに制限していたのですが、それを無くしてユーザーがもっと自由にアウトプットできるように最大文字数を1000字にしました。1000字という基準は自分が使う場合は、それくらいの文字数で足りたからです。
https://eigopencil.com/how_i_use

画像認識機能の実装

rtesseract(OCR)を使用して画像を認識しましたが、あまりにも精度が良くなかったので、画像処理の前にMiniMagickで補正してからrtesseractにデータを投げることにしました。
しかし、mini_magicで補正されたファイルをrtesseractに投げるとなぜかファイルが消えてしまうという事態が発生しました。

定義したpathが悪かったのか、.jpgや.webpなどの形式がよくなかったのかなど仮説を立てて解決しようとしましたが、原因はそれらではありませんでした。

ChatGPTに「mini_magicで一度に色んな加工(画像のサイズ変更、ノイズ除去、傾き補正など)を画像に施しているから処理に時間がかかったり中断したりして上手くいかないのでは?」と言われいくつかの処理が終わるごとに一度変数に格納してから、次の処理へというように直してみると、無事データがrtesseractに渡すことができました。

  def analyze
    uploaded_file = params[:image]
  
    if uploaded_file.nil?
      flash[:alert] = "画像をアップロードしてください。"
      redirect_to current_user
      return
    end

    #app/tmpにユーザー画像を保存
    saved_tmp_path = Rails.root.join('tmp', uploaded_file.original_filename).to_s
    File.open(saved_tmp_path, 'wb') do |file|
      file.write(uploaded_file.read)
    end

    #処理するファイルのpathを定義 app/tmp
    tmp_path = Rails.root.join('tmp', "processed_#{SecureRandom.hex(8)}.png").to_s
    processed_image_path = HandwritingRecognizer.preprocess_image(saved_tmp_path, tmp_path)
    #lib/handwriting_recognizer.rbでpre処理(RTesseractで画像を処理しやすくする)
    image = RTesseract.new(processed_image_path, lang: 'eng')
    ocr_result = image.to_s
    #render json: { text: ocr_result } 
    #render html: "<p>#{ocr_result}</p>".html.safe
    redirect_to camera_path(slug: current_user.slug, ocr_result: ocr_result)
  end
require 'mini_magick'

class HandwritingRecognizer
  def self.preprocess_image(input_path, output_path)
    Rails.logger.debug "Starting image preprocessing..."
    image = MiniMagick::Image.open(input_path)
  
    # サイズ縮小
    image.resize '1200x1200>'
    image.format('png')
    image.write(output_path)
  
    # ノイズ除去
    image = MiniMagick::Image.open(output_path)
    image.morphology 'Erode', 'Diamond'
  
    # 傾き補正
    image.deskew '40%'
    image.morphology 'Close', 'Octagon'
  
    # 最終出力
    image.write(output_path)
    Rails.logger.debug "Image preprocessing completed. Output: #{output_path}"
    output_path
  end
end

bootstrapからTailwindCSSへの切り替え

「今までbootstrapを使っていて慣れていたから」という理由でbootstrapを使っていましたが、今風のモダンなデザインのアプリと比べて見た目が古いと思い、TailwindCSSに途中から切り替えました。開発に余計に時間がかかってしまうのが嫌だったので、CDNで取り込みました。

普通のformタグからリッチなテキストエディタ(trix)の追加をしようとしたが、

「ただ文字を投稿するのではなく、ユーザーが画像を挿入できたり、太字にできたりできたら便利そう」と思い、追加しようと思いました。

そして、今までJavaScriptを使わずに一通り完成させたのですが、Rails7でリッチなテキストエディタを実装するにはhotwire(turboとstimulus)を活用してJavaScriptを取り込む必要がありました。3日間使ってもJavaScriptをブラウザに表示させることができず苦戦していました。

さらに調べたところ、画像や文字の装飾ができるリッチなデータをDBで管理するには、無料枠のrenderだけではできない事が分かりました。

「このリッチなテキストエディタの実装にこれ以上時間を割けない」、「その後の管理も理解していないと大変そう」「デプロイに新しい設定をすると余計に時間がかかる」と予想し、自分でリッチなテキストエディタをviewのファイルに直接scriptタグで書いて行く事にしました。(と言っても太字だけです。)

得た学び

まずはシンプルなCRUDを完成させてリリースしてみる

アプリケーションを開発していると、「この機能も欲しい」「もっとこういうデザインの方が使いやすいかも」という考えが半永久的に出てきます。しかし、まずはリリースしないと先に進まないので、リッチテキストやRails7での推奨されているJavaScriptの導入方法など時間がかかりそうなものは一旦飛ばしました。

また、自分はこうした方が良いと思ったデザインや機能に凝って時間を使っても、ユーザーが「必要ない」と感じていたら本末転倒だと感じたので、とりあえず簡単ですぐに実装できるやり方で機能を実装しました。

構成を決めても、書いてみないとどうなるか分からない

作り始めの段階で自分の技術力や時間を考慮して「どんな機能をつけるのか」や「デザインするツールは何を使うのか」などを決めていたのですが、作り始めてみると経験不足のためなのか、そもそも自分が考慮していなかった問題が出てきました。

  • デフォルトのユーザーパスが/users/1になっていたり(users/1と書かれているサイトは今までみた事がない。/users.nameに変える必要があるなとか。)
  • 「下書き機能はどうやって実装するんだろう」と思っても@post.publishedと@post.draftで分ければ意外とすんなりと実装できたり、
  • 「カメラ認識機能なんてどうやって実装するんだろう」と思いましたが、公式を落ち着いて見れば先人が数行のコードで実装できるようにしていてくれたり、
  • Rails7にJavaScriptを推奨されている方法(Hotwire)でインストールするのが、自分にとってここまで難しいと感じるのかなど、

「作り始めるときには全てを考慮してから実装するのは難しいから、とりあえず作り始めてから考えよう」と思うようになりました。

自分に必要なアプリだとリリースまで粘れる。継続してメンテナンスをしたいと思うようになる。

自分が欲しいツールをアプリとして落とし込んで作成したので、今やっている事に納得感を持ってリリースまで辿り着けました。

その他に学んだことのノート(https://scrapbox.io/kazdb/eigopencil)

これから実装/改善する機能

ユーザーが増えてきたら考えます。

ポートフォリオ

https://kazumawada.com

Discussion