Compassion Rescue
動物保護団体のための、2言語対応の公開サイト、譲渡パイプライン、シェルター運営プラットフォームです。
COMPASSION RESCUE
Where Every Life Is Curated With Compassion
- 対応言語、EN / ZH
- 2
- 統合したプログラム
- 4
- 完了したエピック
- 7/7

- クライアント
- Compassion Rescue
- 業種
- 動物保護 · 非営利団体
- 依頼内容
- フルスタックのプラットフォーム開発
- ステータス
- 進行中 — ステージング環境を納品済み
- 期間
- 2026年5月〜6月 · ステージング環境まで約6週間
- 対応範囲
- 2言語対応の公開サイト、管理プラットフォーム、ボランティアポータル
01 概要
クライアントは誰か
Compassion Rescueは、2言語を使うコミュニティを支える動物保護シェルターです。プラットフォームは英語と簡体字中国語の両方で完全に動作し、シェルター自身の既定設定(言語・タイムゾーン・通貨)も、中国語を使って運営されていることをうかがわせます。活動は4つのプログラムにわたります。ずっと暮らせる家庭へ動物を譲渡すること、動物を一時預かりボランティアに預けること、シフト制のボランティアプログラムを運営すること、そしてニュースとしあわせ報告を発信して地域とのつながりを保つことです。
02 課題
どんな課題を抱えていたか
シェルターは、こうした活動をすべて仕組みなしで運営していました。公開の保護動物一覧も、申込パイプラインも、ボランティアポータルもなく、コンテンツを変えるには開発者が必要で、しかもそれが2言語分でした。
公開の保護動物一覧がない
里親を希望する人は、いまどの子を迎えられるのかを見る手段も、種類・年齢・サイズで絞り込む手段も、連絡する前にその子のこれまでの経緯や医療歴を読む手段もありませんでした。
申込パイプラインがない
里親・一時預かり・ボランティアの申込には、受付の手順も、その先の管理の流れもありませんでした。承認待ちか承認済みかの管理は手作業で、譲渡が決まったあとに動物の掲載状況を更新するのも、忙しいと真っ先に抜け落ちる類の作業でした。
ボランティアポータルがない
シフトやお知らせ、活動時間の記録は、コーディネーターが管理していた場所にばらばらに置かれていて、ボランティア自身が確認できる仕組みもありませんでした。
変更のたびに開発者が必要、しかも2言語分
ニュースの投稿も、動物の情報も、サイトのお知らせも、コンテンツに関わる変更はすべて技術的な手助けが必要で、2言語対応のためその手間も倍になっていました。
03 解決策
どう解決したか
4つのプログラムを軸に、プラットフォーム全体を構築しました。2言語対応は後付けにせず、最初から守るべき制約として扱っています。公開サイトは、訪問者の一連の流れをすべてカバーします。トップページ、URLと連動したフィルターで絞り込める保護動物一覧、動物の詳細ページ、記事ごとのページを持つニュースです。動物の紹介文を含むすべての文章が英語と中国語の両方で用意され、選んだ言語を記憶する言語切り替えと、専用のCJK向け組版ルール(斜体なし、文字間隔の調整なし)を備えています。
申込は、里親・一時預かり・ボランティアごとに分かれた、きちんとした複数ステップの入力フローで、入力中の内容は下書き保存されます。運用面での価値が生まれるのは管理側です。ダッシュボード、動物情報の管理(登録・編集・削除)、2言語対応のコンテンツエディター、シフト管理つきの一時預かり・ボランティア管理、動物ごとの医療記録、サイト設定、そして申込が「承認待ち → 審査中 → 承認/却下」と進む譲渡パイプラインがそろっています。どの操作にも確認ダイアログが付き、実行後5秒間は取り消せます。全体をつなぐのがバックエンドのフックです。譲渡が承認されると動物のステータスが自動的に「譲渡済み」に切り替わり、一時預かりが承認されると「手続き中」に切り替わります。
- FE Next.js 16 2言語対応のフロントエンド React 19Cookie認証
- PB PocketBase Go + 組み込みSQLite カスタムのシード用コマンド承認フック
- S3 MinIO オブジェクトストレージ 自動プロビジョニングS3互換
- Next.js 16
- React 19
- TypeScript
- PocketBase
- Go
- SQLite
- MinIO (S3)
- Tailwind CSS v4
- Docker
- Dokploy
04 プロジェクトの流れ
主なマイルストーンと期間
フェーズ1 2026年5月4日〜15日
基盤構築と公開サイト
プロジェクトの構成、Docker環境、デザインシステム、そしてモックデータで組んだ公開ページ。まだつないでいない部分には「近日公開」の表示を付けています。
フェーズ2 2026年5月11日〜18日
バックエンドとスキーマ
動物、申込、投稿、ボランティア、シフト、一時預かり、医療記録、設定のためのPocketBaseコレクションを、コレクションごとのアクセスルールとともにコードで定義しました。シードデータは両言語分を用意しています。
フェーズ3 2026年5月19日〜25日
ステージング環境の構築
DokployへのデプロイとMinIOストレージの接続、そして文書化した運用手順書(ランブック)。ここで恒久的なハマりどころを2つ記録しました。ビルド時の環境変数と、DokployがComposeファイルを1つしか受け付けないという制約です。
フェーズ4 2026年6月8日〜12日
実際のバックエンドとの連携
計画書を書き、着手前に敵対的レビュー(計画の弱点をあえて突くレビュー)を行って、SDKのバージョン不一致、更新ルールの不足、設定まわりのアンチパターンを見つけました。そのうえで、すべての画面を実際のバックエンドにつなぎ込み、モックデータ表示バナーをすべて撤去しました。
フェーズ5 2026年6月12日〜16日
体系的なQA
アクセシビリティと操作性の監査、そして5つの状態(ローディング、エラー、エンプティ、パーシャル、理想)への対応チェックを、データを取得するすべての箇所に実施しました。全項目にチェックが入り、スモークテストは通り、ビルドもクリーンです。
本番リリース 2026年6月17日〜(進行中)
ドメインとストレージの移行
ステージング環境の検証と承認は完了しています。リリース計画では、ドメインとDNS、オブジェクトストレージのMinIOからクラウド事業者への移行、バックアップの保持期間、最終のセキュリティレビューが含まれます。
05 直面した壁と対処
どんな壁にぶつかり、どう乗り越えたか
サーバーは英語でレンダリングし、ブラウザーは中国語を覚えている
課題選んだ言語はブラウザーに保存されますが、先にレンダリングするのはサーバーです。そのため、中国語を選んで再訪した人でも、切り替わる前に一瞬英語が表示されてしまうことがありました。ページを移動するたびに起きるので、気が散ります。
解決策言語プロバイダーは、クライアント側で保存済みの言語を確認できるまでレンダリングを待つようになりました。その間は中立的な読み込み状態を表示するので、どのページでも、一瞬の切り替わりもちらつきも起きません。
譲渡は承認済み、掲載は古いまま
課題譲渡を承認したあとに、もうひとつ手順を覚えておく必要がありました。動物の画面に戻って、里親募集中から外す作業です。現場の忙しさの中では真っ先に忘れられがちな、典型的な手作業です。
解決策バックエンドのフックでこの抜けを塞ぎました。承認と動物のステータス変更はひとつの操作としてサーバー側で実行されるため、どの画面やクライアントから承認しても確実に動きます。
開発環境では起きない壊れ方をした本番ビルド
課題NEXT_PUBLIC_の環境変数はビルド時にJavaScriptのバンドルへ埋め込まれるため、サーバー側で値を変えて再起動しても何も変わりません。ステージング環境では、ログインはできるのに、管理画面はどのページも「failed to fetch」と表示され、デバッグにかなり時間を取られました。
解決策リポジトリに恒久的なメモを残し、運用手順書にも、URLが実際にビルド済みのバンドルへ入っているかを確認する1行のコマンドを加えました。もう、存在しないバグを追いかけることはありません。
画像のアップロードは成功したのに、あとで404になる
課題PocketBaseは、明示的に指定しないかぎりS3ストレージを使いません。新規にデプロイすると、アップロードは成功したように見えて、あとで404になります。誰かが実際に画像をクリックするまで気づけません。
解決策1回だけ実行するスクリプト化した手順(npm run configure-s3)を、初回デプロイの初期セットアップチェックリストに加え、初回デプロイの定番のハマりどころとして文書化しました。
固定されたバージョンより新しいAPIを前提に書かれたバックエンド
課題バックエンドは当初、モジュールファイルで実際に固定しているバージョンより新しいPocketBaseのAPIを前提に書かれていて、コンパイルが通りませんでした。
解決策連携計画に組み込んだ敵対的レビューが、後工程の妨げになる前にこれを見つけました。つなぎ込みを始める前に、バックエンドを正しいSDKのAPIに合わせて書き直しています。
06 Before / After
プロジェクト前後の状況の比較
| Before | After |
|---|---|
| 保護中の動物の公開一覧がない | 絞り込みに対応した2言語の一覧、詳細ページ、URLと連動して共有できる絞り込み結果 |
| 里親・一時預かり・ボランティアを希望する人の正式な受付窓口がない | 里親・一時預かり・ボランティアそれぞれの複数ステップの申込フロー(入力中の内容を下書き保存) |
| 申込の管理は手作業 | ステータス付きのパイプライン(検索、確認ダイアログ、却下理由、5秒間の取り消し) |
| 譲渡後の掲載更新が別の手作業になっていて、うっかり忘れやすい | バックエンドのフックで自動化。承認と動物のステータス更新が1回の操作で完了 |
| ボランティア向けの仕組みがなく、シフトや活動時間はコーディネーターの手元に散らばっていた | ログイン制のポータル(シフト、お知らせ、活動時間の記録)と、管理側のスケジュール管理 |
| 医療歴が電子化されていない | 管理画面で動物ごとの医療記録を管理 |
| コンテンツの変更は毎回開発者が必要で、1言語のみ | 2言語対応のエディター、ニュース公開、お知らせバナーのすべてを自分たちで運用 |
| デプロイ基盤がない | Dokploy上のDocker構成(S3ストレージ、ヘルスチェック、バックアップ、運用手順書) |

07 クライアントにもたらした成果
クライアントはどんな価値を得たか
- 完了したエピック
- 7/7
- 全タスク完了、モックデータ表示バナーを撤去
- 言語数
- 2
- 英語 · 簡体字中国語、ハイドレーションエラーなし
- 統合したプログラム
- 4
- 譲渡 · 一時預かり · ボランティア · ニュース
- 監査した画面状態
- 5
- ローディング · エラー · エンプティ · パーシャル · 理想
散らばっていた手作業を、ひとつの仕組みに
スタッフは、動物・申込・一時預かり・ボランティア・医療記録・コンテンツの管理を、技術的な手助けなしに、2言語で自分たちだけで行えます。
手作業の管理で最も痛手となる2つの失敗パターンが、起きなくなった
すべての申込について、ステータスを管理する担当者が決まっています。譲渡が承認されると公開サイトも自動で更新されるため、申込の取りこぼしも、古い掲載の放置も、もう起こりません。
本当の意味でアクセシブルな入り口
44pxのタッチターゲット、モーダルのフォーカストラップ、メインコンテンツへスキップ、レイアウトシフトゼロ、ARIAロール。監査基準は掲げただけの目標ではなく、項目ごとに検証しました。
非営利団体の予算に見合った、文書化された再現可能な仕組み
バックエンドのバイナリは1つ、サーバーも控えめな1台です。オープンソースのオブジェクトストレージをローカルで動かし、本番ではクラウドストレージへ移行する道筋も明確です。運用手順書があるので、新しくデプロイするときも、最初に構築した人の手が空いているかどうかに左右されません。
08 得られた学び
このプロジェクトから何を学んだか
作る前に失敗パターンを書き出すと、本物のバグが見つかる
連携計画に組み込んだ敵対的レビューで、SDKのバージョン不一致、アクセスルールの不足、スキーマのアンチパターンが見つかりました。どれも、後工程でデバッグの手間になる前に潰せています。手間が小さいわりに、当たりの多い習慣です。
この規模のQAは、記憶よりチェックリスト
「データを取得するすべての箇所で、ローディング・エラー・エンプティ・パーシャル・理想の各状態に対応できているか」という問いに、40以上ある画面すべてについて、記憶だけで確実に答えられる人はいません。項目ごとにチェックボックスを付けた2つの監査文書のおかげで、検証は「したつもり」ではなく、実際に確かめたものになりました。
デプロイのハマりどころは、誰かの頭の中ではなくリポジトリに残す
ビルド時の環境変数の問題とS3の設定手順は、それぞれ一度ずつ時間を取られました。どちらも今は確認コマンド付きの恒久的なメモになっていて、同じことで二度、時間を失うことはありません。
国際化は翻訳作業ではなく、設計上の制約
言語ごとに重複させたコンテンツ項目、CJK向けの組版ルール、ハイドレーションエラーを起こさない言語判定。どれも後から付け足すと全画面に手が入るため、早い段階で決める必要がありました。
人が忘れる手順は、自動化する
承認フックはバックエンドコードわずか10行で、ワークフロー全体で最も起きやすい運用ミスをなくします。
ステージング環境で稼働中 · 実際のシードデータ
モジュール
どの画面も、シェルターが日々運用している実際の業務に対応しており、稼働中のステージング環境からそのまま撮影しました。クリックすると拡大します。
















