はじめに
こんにちは。
個人でアプリ開発をしているjunです。
この記事では、私が個人で開発・リリースした英語学習アプリ 「WordTapNews」 の開発背景から技術構成、実装の工夫、そしてリリース後の振り返りまでをまとめます。
アプリ概要:「WordTapNews」とは?
WordTapNews は、
「英語ニュースを読みながら、わからない単語をタップするだけで意味を確認できる」
英語リーディング支援アプリです。



主な特徴
- 英語ニュースサイト(BBC / NHK World / Reuters など)をアプリ内で読める
- 英単語をタップすると即座に意味がポップアップ表示
- 翻訳にはAI(OpenAI API / Google Translate API)を利用
- 将来的には単語帳・学習履歴機能を追加予定
「ニュース × 辞書 × 習慣化」
をコンセプトに、毎日の英語学習を自然に続けられる体験を目指しています。
開発のきっかけ
私は以前から英語学習を習慣にしているのですが、
「英語ニュースを読みたい」と思っても、知らない単語で詰まることが多くありました。
ニュースアプリを開いても、
- 辞書アプリをいちいち開くのが面倒
- 単語の意味を調べても、また記事に戻るのが手間
- 調べた単語を記録するのを忘れる
という問題があり、気づくと学習が続かない。
そこで、
“ニュースを読む流れの中で、そのまま英語を学べるアプリを作りたい”
と思い立ち、開発をスタートしました。
技術スタック
分類 | 使用技術 |
---|---|
フレームワーク | Flutter (Dart) |
言語 | Dart / JavaScript (WebView側) |
データベース | Firestore(予定) / SharedPreferences(ローカル) |
翻訳API | Google Translate API |
AI補助 | OpenAI API(辞書生成 / 自然な訳語生成) |
UIデザイン | Figma |
配信 | App Store(iOS向け) |
アーキテクチャ概要
アプリはシンプルな2画面構成です。
-
HomeScreen
- ニュースソース(BBC, NHK, Reuters, etc)を選択
- URLをWebViewに渡して開く
-
WebViewScreen
- WebView内で英語ニュースを表示
- 各単語をクリックできるようにJSをインジェクト
- クリックされた単語をFlutterに送信
- Flutter側で辞書APIを叩いて意味を表示
WebView 内で単語をクリック可能にする仕組み
FlutterのInAppWebView
を利用し、
JavaScriptを挿入してすべての英単語を <span>
で囲み、クリックイベントを設定しています。
function wrapWords() {
const textNodes = document.evaluate(
'//text()[not(ancestor::script) and not(ancestor::style)]',
document.body,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
null
);
for (let i = 0; i < textNodes.snapshotLength; i++) {
const node = textNodes.snapshotItem(i);
const text = node.nodeValue;
const words = text.split(/(\b[a-zA-Z']+\b)/g);
const fragment = document.createDocumentFragment();
words.forEach(word => {
if (word.match(/\b[a-zA-Z']+\b/)) {
const span = document.createElement('span');
span.textContent = word;
span.style.cursor = 'pointer';
span.onclick = () => window.flutter_inappwebview.callHandler('WordTap', word);
fragment.appendChild(span);
} else {
fragment.appendChild(document.createTextNode(word));
}
});
node.parentNode.replaceChild(fragment, node);
}
}
単語の意味取得と翻訳の流れ
クリックされた単語は Flutter 側に送信され、
_fetchWordDefinition()
→ _translateToJapanese()
の順に処理します。
Future<Map<String, dynamic>?> _fetchWordDefinition(String word) async {
final response = await http.get(
Uri.parse('https://wordsapiv1.p.rapidapi.com/words/$word'),
headers: {
'x-rapidapi-host': 'wordsapiv1.p.rapidapi.com',
'x-rapidapi-key': dotenv.env['WORDS_API_KEY'] ?? '',
},
);
return response.statusCode == 200 ? json.decode(response.body) : null;
}
Future<String> _translateToJapanese(String text) async {
final translator = GoogleTranslator();
final translation = await translator.translate(text, from: 'en', to: 'ja');
return translation.text;
}
表示UI
取得した結果を英和辞書のように整形して表示します。
英語+日本語訳+例文を組み合わせてユーザーに提示します。
_showWordMeaning(String word) async {
final definition = await _fetchWordDefinition(word);
final meaningWidgets = <Widget>[];
for (var result in definition?['results'] ?? []) {
final pos = result['partOfSpeech'] ?? 'その他';
final engDef = result['definition'] ?? '';
final jaDef = await _translateToJapanese(engDef);
meaningWidgets.add(Text('$pos: $jaDef'));
}
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text(word),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: meaningWidgets,
),
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: Text('閉じる'))
],
),
);
}
今後の機能予定
機能 | 状況 | 目的 |
---|---|---|
単語帳(ローカル保存) | 実装中 | よく調べた単語を復習 |
Firebaseログ収集 | 導入予定 | UX分析・改善 |
アカウントログイン | 検討中 | データ同期・端末間共有 |
習慣化リマインダー | 検討中 | 英語学習継続支援 |
プレミアムプラン(広告除去) | 検討中 | マネタイズ |
リリース後の課題と気づき
リリース後すぐに感じたのは 「アプリを簡単には知ってもらえないこと」 ことでした。
改善のために取り組んだこと:
- X(旧Twitter)で英語ニュース+単語投稿を開始
- 英語学習系コミュニティで意見交換
結果として、開発者仲間や英語学習者から反応をもらえるようになりました。
振り返りと学び
- Flutter は UI 構築が非常に早く、個人開発に最適
- WebView + JavaScript 連携は癖があるが慣れると強力
- 技術的完成度よりも 「ユーザーがすぐ価値を感じる体験」 が重要
- SNS を通じた 「開発過程の発信」 がプロモーションになる
関連リンク
まとめ
英語学習は「続けること」が一番難しい。
でも、ニュースを読むだけで英語が身につくなら、続けられると思う。
WordTapNews は、
「英語を読む習慣を自然に続けられるアプリ」を目指してこれからも改善していきます。
個人開発でも、アイデアと粘り強さがあればリリースまで到達できる。
この記事が同じように挑戦する人の参考になれば嬉しいです。