Anagura Hubサイト(itako999.com/anagura/)で、メインコンテンツのグリッドカードがクリックできないという問題が発生した。各カードには見出し内に<a>タグがあるが、カード全体をクリック可能にしたかった。 最初、CSS-onlyソリューション(::before疑似要素で透明なオーバーレイを作成)を試みたが、これが既存のyurt-style.cssのグリッドレイアウトを破壊してしまった。CSSの特異性(specificity)とカスケードの問題で、意図しないスタイルが適用されていた。 最終的に、JavaScriptによる解決策を採用した。anagura-nav.jsにクリックリスナーを追加し、.hub-card要素をクリックしたら、その内部の<h2> <a>のリンク先にナビゲートする。 この実装には重要なチェックが含まれている。!e.target.closest(‘a’)により、ユーザーが直接<a>タグをクリックした場合は、イベントが重複して発火しないようにしている。 もう一つの問題は、動的に生成されるナビゲーションバーのリンクが正しく機能していなかったことだ。これはブラウザキャッシュが原因だった。JavaScriptファイルを更新してもブラウザが古いバージョンをキャッシュしていたため、変更が反映されなかった。HTMLの<script>タグにバージョンパラメータ(?v=2)を追加することで、キャッシュバスティングを実現した。 現在、Anagura Hubのすべてのリンクは正しく機能している。動的ナビゲーションバーもグリッドカードも、ルート相対パス(/linguistics/など)を使用し、どのページからアクセスしても正しいURLに遷移する。
辞書データベースサイト(Dict-DB)で、クライアントサイドJavaScriptによる動的フィルタリングを実装した。サーバーサイドのページリロードなしで、即座にフィルタリング結果が表示される。 データ構造: フィルタリングロジック: 3つのドロップダウン(Language、Category、Decade)の組み合わせでフィルタリング: 結果の表示: フィルタリング結果をカードレイアウトで表示: 初期ソート: デフォルトでは、最新の辞書が最初に表示される(逆時系列): パフォーマンス最適化: 420件以上のエントリでも、フィルタリングは瞬時に完了する。配列のフィルタ操作はJavaScriptエンジンで最適化されており、このサイズのデータセットでは十分高速だ。 さらに大規模なデータセットでは、以下の最適化を検討できる: 現在、Dict-DBのフィルタリングは快適に動作している。ユーザーはドロップダウンを変更すると、即座に結果が更新されるのを見ることができる。
Anaguraプロジェクトの複数サイト(Hub、Blog、Dict-DB、Khokh Sudar、Linguistics)で、ナビゲーションとフッターを統一するため、JavaScriptによる動的生成システムを実装した。 課題: 各サイトで手動でナビゲーションリンクを管理していたため、リンク追加や変更のたびに、すべてのHTMLファイルを編集する必要があった。これは非効率で、エラーも発生しやすい。 解決策: anagura-nav.jsに、すべてのナビゲーションデータを一元管理する配列を作成: このデータから、各ページのナビゲーションバーとフッターのリンクを動的に生成する。言語も自動検出される(<html lang=”ja”>属性から)。 関連サイトリストの自動生成: サイドバーの「関連サイト」リストも、同じデータから生成される。現在のページを除外し、残りのサイトをリスト表示: フッターの著作権表示の統一: すべてのサイトで、同じフッターテキストをJavaScriptで生成: これにより、年号が自動的に更新され、機械可読性も向上する。 Sticky/Scrolling Footer問題の解決: フッターが短いページでは画面下部に固定され、長いページではコンテンツの最後に表示されるようにする必要があった。Flexboxで解決: FOUT(Flash of Unstyled Content)の解決: Webフォントの読み込み中、システムフォントが表示されてしまう問題を、フォントプリロードで解決: 現在、すべてのAnaguraサイトは統一されたナビゲーションシステムを使用している。新しいサイトを追加する際は、NAVIGATION_DATA配列に1つのエントリを追加するだけで、すべてのサイトに自動的に反映される。
辞書プロジェクトでは、複数のAIペルソナ(Claude、Taavi、Gemini、Prof. Erdenebileg、Naranchimeg)がそれぞれ作業ログを記録している。これらのログを一元管理するため、Logbook自動化システムを構築した。 当初は、各ペルソナごとに個別のステージングフォルダを持つ設計だったが、これが管理の複雑化を招いた。そこで、単一のSTAGING_LOGBOOK_IDに統合し、すべてのペルソナがここに一時的にログを書き込む方式に変更した。 次の課題は、Google Docsの複数のLogbookファイルを自動的にフォーマットすることだった。見出しレベルの統一、アルファベット順の挿入位置の決定、完璧なMarkdown変換などが必要だった。特に、Google DocsのH2見出しをMarkdownの##に、本文の段落を適切に改行付きで変換する処理には注意が必要だった。 最終的には、別々に動作していたLogbook-SyncManagerとLogbook-Formatterを単一のGASプロジェクトに統合した。これにより、単一のメニューインターフェースから両方の機能にアクセスできるようになった。 さらに、FormatterをDocument IDのハードコードから解放し、LOGBOOKS_FOLDER_ID内のすべてのファイルを動的に処理するように改良した。これにより、新しいLogbookファイルを追加しても、コードの変更が不要になった。 現在、このシステムは時間トリガーで自動実行されており、各ペルソナのログは適切なLogbookファイルのアルファベット順の位置に自動的に挿入される。Google DriveのLogbookが「単一の真実の源(single source of truth)」となり、GitHubへの同期も自動化されている。
モンゴル系文字(伝統モンゴル文字、パスパ文字、ソヨンボ文字)およびモンゴル語の借用語の語源としてチベット文字やディーバナーガリー文字(サンスクリット用)を入力するためのWebベースのキーボードツール、KhamagBichigを開発した。Google Apps Scriptで動作し、ブラウザから直接アクセスできる。 最大の技術的課題は、Shiftキーの状態管理だった。物理キーボードのShiftキー、画面上の仮想SHIFTボタン、そして「Unshifted/Shifted」表示ボタンの3つを完全に同期させる必要があったからだ。 当初の実装では、これらが独立して動作してしまい、Shiftキーを押しても仮想キーボードの表示が切り替わらない、または逆に仮想ボタンをクリックしても入力が変わらないという問題が発生した。 解決策は、すべてのShift状態を唯一の情報源(single source of truth)として管理することだった。shiftStateという変数を定義し、すべてのShift関連イベント(キーボード、ボタンクリック、表示切替)がこの変数を参照・更新するように実装した。 もう一つの課題は、各文字体系の文字マッピングデータの整備だった。scriptMappingsとscriptLegendsという2つのオブジェクトに、すべての文字体系の完全なキーマップと凡例を格納する必要があった。データの欠損があると、一部のキーが機能しないという問題が起きる。 現在、KhamagBichigは安定稼働している。伝統モンゴル文字だけでなく、パスパ文字、ソヨンボ文字、チベット文字やディーバナーガリー文字の入力にも対応しており、モンゴル系文字研究者にとって便利なツールになった。
辞書プロジェクトで複数のWebアプリケーション(ダッシュボード、SystemHub、TeamHub、Logbookビューアなど)を開発するうちに、デザインの一貫性を保つことが課題になってきた。そこで、StyleCoreという統一デザインライブラリを構築することにした。 StyleCoreの設計思想は、スタイリングを3つの明確なレイヤーに分離することだ。第一層のCore(getThemeCss())は色、タイポグラフィ、スペーシングなどの基本変数を定義する。第二層のLayout(getFullpageLayoutCss()、getSidebarLayoutCss())はページ全体のレイアウトパターンを提供する。第三層のLocalはアプリ固有のスタイルを記述する。 この分離により、大きなメリットが得られた。たとえばダッシュボードのカラースキームを変更したい場合、CoreのCSS変数を一箇所修正するだけで、すべてのコンポーネント(ボタン、カード、モーダル、フォームなど)に変更が反映される。 実装にあたっては、Google Apps Scriptの特殊な環境を考慮する必要があった。通常のWebアプリと違い、GASではCSSファイルを.css.htmlという拡張子で保存し、<?!= include(‘StyleCore’) ?>という特殊な構文でインクルードする。この仕組みを理解するまで、試行錯誤を重ねた。 現在、StyleCoreはダッシュボード、SystemHub、TeamHubで実装済みだ。次はLogbookビューアとTraditional Mongolian Script Viewerにも適用する予定だ。デザインの統一は、単なる見た目の問題ではなく、開発効率の向上とメンテナンスコストの削減に直結する。
ResourcePadコンポーネント(Google Sitesに埋め込まれる研究リソースのリスト)で、持続的で非常にフラストレーティングなレイアウト問題が発生した。 問題は、Google Sitesのiframe内でコンポーネントが表示される際、レイアウトが崩れることだった。単独のページでは正しく表示されるが、埋め込まれると壊れる。 最初、iframeの問題だと思い、様々なiframe設定(幅、高さ、スクロール設定)を試した。しかし、どれも効果がなかった。次に、キャッシュの問題を疑い、デプロイメント設定やバージョン番号を変更した。それも無駄だった。 真の原因は、コンポーネント自体のCSSにあった。複数のCSSルールが衝突し、Google Sitesの環境でのみ問題を引き起こしていた。 具体的には、親要素のmax-width設定と子要素のwidth: 100%の組み合わせが、iframeの制約と相互作用して、予期しない結果を生んでいた。 解決策は、より具体的なレイアウトコンテナを使用することだった。Flexboxレイアウトを適用し、明示的な幅の制約を削除した。 このバグの診断が困難だったのは、問題が環境依存だったからだ。ローカルテストでは再現せず、Google Sitesの特定の条件下でのみ発生した。最終的には、ブラウザの開発者ツールで実際のレンダリング結果(outerHTML)を調べることで、真の原因を特定できた。 現在、ResourcePadはGoogle Sites内で正しく表示される。この経験から、環境依存の問題には、実際の環境でのデバッグが不可欠だということを学んだ。
モンゴル語辞書の編纂作業で、Google Sheetsに蓄積された大量のデータを効率的に処理する必要が出てきた。そこで、Google Apps Script(GAS)を使った専用ツールを開発することにした。 当初はシンプルなスクリプトを考えていたが、実際に作業を進めるうちに、データのクリーニング、重複チェック、フォーマット統一など、複数の機能が必要になってきた。最終的には、サイドバーUIを持つ統合ツールとして仕上げることができた。 特に苦労したのは、モンゴル語(キリル文字)と日本語が混在するデータの処理だ。文字コードの問題で、GitHub経由でCSVをやり取りする際に文字化けが発生することがあった。UTF-8エンコーディングの徹底と、適切なヘッダー処理の実装で解決できたが、多言語データの扱いは思った以上に注意が必要だと実感した。 また、処理対象のデータが数万行に及ぶ場合、実行時間の制限に引っかかることもあった。バッチ処理の実装や、処理の段階分けなどの工夫が必要だった。 現在は、辞書データの各処理段階(S02からS06まで)に対応した専用ツールが稼働している。作業効率は以前と比べて格段に向上した。今後はさらなる自動化を進めていく予定だ。
大量の辞書データを扱う上で避けて通れないのが、データの品質管理だ。特に、複数の情報源から集めたデータを統合する際には、重複チェックや内容の妥当性検証が不可欠になる。 当初は人力でExcelシートを見ながらチェックしていたが、データ量が数万件を超えると現実的ではない。そこで、段階的な自動検証システムを構築することにした。 まず、基本的な重複チェックツールを作成した。見出し語の完全一致だけでなく、表記の揺れ(大文字小文字、全角半角など)も考慮する必要がある。また、モンゴル語の場合、格変化した形が重複して登録されていないかも確認する。 次に、分類(CAT)の妥当性チェックだ。これにはGemini APIを活用した。各エントリに対して「この見出し語の分類は妥当か」を判定させ、疑義のあるものにフラグを立てる。ただし、AIの判定が100%正確とは限らないので、最終的には人間が確認する。 さらに高度な検証として、Claudeを使った内容検証も実装した。Google Sheetsからデータを抽出し、GitHub Gist経由でClaudeに渡す仕組みだ。Claudeは語義の説明が適切か、用例が正しいかなどを細かくチェックしてくれる。 この一連のワークフローを「S06データ監査ツール」として統合し、Google Apps Scriptで実装した。ピボットレポート、ファイルリストチェッカー、差分トラッカーなどの機能を持つサイドバーUIとして仕上げた。 完璧なシステムではないが、以前と比べて検証作業の効率は大幅に向上した。人間とAIの協働による品質管理の一例として、他のプロジェクトの参考になれば幸いだ。
辞書データ処理ツール(S04)のGoogle Apps Scriptが巨大化し、メンテナンスが困難になってきた。すべてのツール機能が単一のファイルに詰め込まれており、関数が1000行を超えていた。 リファクタリングの目的は、コードを論理的に整理し、各ツールの責任を明確にすることだった。新しい構造は3つのレイヤーに分けられた: 第一層:Core UI/Helpers 第二層:Tool-Specific Functions 各ツールはさらに2つのセクションに分割: 第三層:Data Access この構造により、関数の配置が明確になった。例えば、Expression & PN Toolでは: リファクタリング中、関数の配置ミスをいくつか発見した。例えば、「Duplicate Finder」ツールの内部ロジック関数が、「CAT Checker」のセクションに紛れ込んでいた。新しい構造では、各関数が論理的な場所に配置されている。 コメントブロックも標準化した: 現在、S04スクリプトは明確に整理されている。新しいツールを追加する際も、どこに配置すべきか一目瞭然だ。各ツールは独立しており、相互に干渉しない。この経験から、早期のモジュール化の重要性を学んだ。