くずはすくりぷとPHP+ (ksphp-plus) 言語ファイル一本化作業 changelog
記録形式：ISO 8601（分単位）、タイムゾーンはUTC
（本ファイルは ksphp-plus-handoff-2026-07-17-01.zip からの引き継ぎ以降の
  作業記録。以前の経緯は CHANGELOG_HANDOFF.txt を参照）

============================================================
2026-07-16T23:30 UTC
------------------------------------------------------------
【段階1：ja版固有の改善点をen版（正本）へ移植】
対象：sub/en/bbstree.php, sub/en/template.html

これまでja/en両サブフォルダに分かれていたロジック／テンプレートを、
将来的に英語版を正本として一本化する方針に基づき、まずja版側にしか
無かった改善点をen版に移植した。

  [1] sub/en/bbstree.php：indexthreads()最適化（2026-07-16T17:13、
      ja版のみに適用済みだったもの）を移植。ログ全体を毎スレッド毎に
      再走査していた旧O(n^2)実装を、1パスでスレッド別バケット化する
      方式に置き換え。未読判定・ページング判定のロジックもja版に
      合わせて修正。コメント・エラーメッセージは英語表記に統一。
      php -lで構文確認済み。
  [2] sub/en/template.html：{CUSTOMHEAD}プレースホルダ（ja版のみに
      存在）を、スクリプトタグ群の直後・<style>直前に追加。bbs.php
      側は既にCUSTOMHEAD変数を汎用的にセットしているため、テンプレート
      側への追加のみで動作する。patTemplate:tmplタグの開閉数（40対40）
      が一致することを確認。

  ※調査の過程で、ハニーポット（スパムボット対策の隠し入力欄）の設置
    箇所がja版とen版で異なっており（ja版はadminlogin側、en版は一般
    投稿フォーム側）、擬古猫さんに確認したところ「ハニーポット機能は
    うまく動作しないと確認済み。en側の配置を残す」との回答を得た。
    ja版側の配置は移植せず、en版の配置（一般投稿フォーム側）をそのまま
    採用した。


============================================================
2026-07-16T23:32 UTC
------------------------------------------------------------
【段階2：言語ファイル機構の実装】
対象：language/english.txt（新規）, language/japanese.txt（新規）,
      conf.php, bbs.php

sub/ja・sub/enサブフォルダ丸ごとの重複を解消するための第一歩として、
UI文言（$MSG）を専用の言語ファイルとして外出しする機構を実装した。

  [1] language/english.txt, language/japanese.txt を新規作成。
      内容はsub/en/lang.php・sub/ja/lang.phpの$MSG配列をPHP経由で
      機械的にダンプし、新パーサーで読み戻した結果が元の配列と
      完全一致すること（キー数55件、値の差異ゼロ）を確認した上で
      採用した。
      書式：KEY=値（「=」前後にスペースを入れない。「=」より後ろは
      行末まで値としてそのまま保持）、「#」「;」始まりの行はコメント、
      空行は無視、UTF-8（BOM無し）。
  [2] conf.php：新規キー'LANGUAGE_FILE'を追加（デフォルト'japanese'）。
      既存の'TEMPLATE_LANGUAGE'（サブフォルダ選択用）とは役割を分離。
      日英併記コメント付き。
  [3] bbs.php：自前パーサー関数loadLanguageFile()を追加。
      $CONF['LANGUAGE_FILE']が設定されていればlanguage/*.txtを読み込む
      新方式、未設定の場合は旧方式（sub/{TEMPLATE_LANGUAGE}/lang.php の
      require_once）に自動フォールバックする形にした。T()関数（翻訳
      ヘルパー）は変更なし。
      動作確認：日本語ファイル読み込み・英語ファイル読み込み・旧方式
      フォールバックの3パターンを個別実行し、いずれも件数55件、値も
      一致することを確認。php -lで構文エラー無しを確認。

  ※既存のsub/ja/lang.php・sub/en/lang.phpはまだ削除・変更せず、
    フォールバック用として温存している（ロジックファイル一本化が
    完了するまでの経過措置）。


============================================================
2026-07-16T23:33 UTC
------------------------------------------------------------
【バグ修正：FRAUDSTER_TAGの閉じ括弧欠落】
対象：sub/en/lang.php, language/english.txt

段階2の言語ファイル移植作業中に発見した既存不具合。sub/en/lang.phpの
'FRAUDSTER_TAG'の値が ' (fraudster'（閉じ括弧無し）になっていた
（ja版は'（騙り）'で正しく閉じている）。' (fraudster)' に修正。
移植元・移植先の両方（sub/en/lang.php, language/english.txt）に
同一の修正を適用し、今後どちらの読み込み経路（新方式／旧フォール
バック）を通っても同じ値になることを確認した。


============================================================
2026-07-16T23:40 UTC
------------------------------------------------------------
【バグ修正：$MSGを経由しないハードコード文字列（既存キーとの重複／未外部化）】
対象：sub/{ja,en}/bbsadmin.php, sub/{ja,en}/bbsimage.php,
      sub/{ja,en}/bbslog.php, sub/{ja,en}/bbstree.php,
      sub/{ja,en}/lang.php, language/english.txt, language/japanese.txt

FRAUDSTER_TAGの調査中、$MSG（T()関数）を経由せず各言語ファイルに
それぞれ独立にハードコードされていた文言が他にも見つかったため、
まとめて洗い出し・修正した。ja/en間で将来内容が食い違う潜在リスクと
なるため、優先度の高いバグ修正として対応した。

  [1] 既存キーとの重複（新規キー不要、T()呼び出しへの置き換えのみ）：
      - bbsadmin.php 2箇所：'Failed to load message'（ja:'メッセージ
        読み込みに失敗しました'）→ T('FAILED_TO_READ_MESSAGE')
        （既存キー。文言はFAILED_TO_READ_MESSAGE側の英語表記
        'Failed to read message'に統一）
      - bbslog.php 1箇所：'$filename was unable to be opened.'
        （ja:'$filenameを開けませんでした。'）
        → sprintf(T('FAILED_TO_OPEN_LOG'), $filename)（既存キー）
      - bbstree.php 1箇所：'There are no parameters.'
        （ja:'パラメータがありません。'）→ T('NO_PARAMETERS')（既存キー）

  [2] 新規キー追加（$MSGに存在しなかったため、language/*.txt・
      sub/{ja,en}/lang.php双方に追加）：
      NO_PASSWORD_SET, FILE_SIZE_OVER_LIMIT, FILE_UPLOAD_FAILED,
      FAILED_TO_LOAD_UPLOAD_ID, IMAGE_WIDTH_EXCEEDED,
      INVALID_FILE_FORMAT, DIRECTORY_OPEN_FAILED, TOO_MANY_KEYWORDS
      （bbsadmin.php, bbsimage.php, bbslog.phpの計8箇所を対応する
      T()呼び出しに置き換え。%sプレースホルダを使う2件はsprintf()
      でラップ）

php -lで全対象ファイルの構文エラー無しを確認。language/*.txtと
sub/{ja,en}/lang.phpの内容が新パーサー経由で完全一致することを
再確認した（キー数63件）。

  ※sub/en/bbsadmin.php内、327行目付近のコメントアウト済みprterror
    呼び出し（#$this->prterror(...)）は無効なコード（実行されない）
    のため、今回の対応範囲からは除外した。


============================================================
2026-07-17T00:05 UTC
------------------------------------------------------------
【段階3：sub/ja・sub/enの物理統合（ロジック／テンプレート一本化）】
対象：sub/ 以下全体（削除・移動）, bbs.php, conf.php, docs/permissions.md,
      language/english.txt, language/japanese.txt

段階1・2でen版（正本）にja版の改善点を移植し、UI文言を言語ファイルへ
外部化した上で、sub/ja・sub/enサブフォルダの重複を解消する物理統合を
実施した。

  [1] sub/en/{bbsadmin.php, bbsimage.php, bbslog.php, bbstree.php,
      template.html, tmpladmin.html, tmpllog.html, tmpltree.html,
      login.html, index.html} を sub/ 直下へ移動。
  [2] sub/ja/ ・ sub/en/ ディレクトリを削除（lang.php含む。言語文言は
      language/*.txt に完全移行済みのため）。
  [3] bbs.php：$tmpl_lang/$SUBDIR分岐（'./sub/' . TEMPLATE_LANGUAGE . '/'）
      を廃止し、$SUBDIRを'./sub/'固定に変更。LANGUAGE_FILE未設定時の
      sub/{lang}/lang.phpへのフォールバックも、当該フォルダ自体が
      無くなったため削除（LANGUAGE_FILE未設定時は既定で'english'を
      使用する形に変更）。
  [4] conf.php：'TEMPLATE_LANGUAGE'キーを廃止（日英併記の説明コメントに
      置き換え）。言語切り替えは'LANGUAGE_FILE'キーのみで行う。
  [5] docs/permissions.md：sub/ja・sub/enのlang.phpを参照していた
      切り分け手順の記述を、language/*.txtを参照する形に更新。

  【移動作業中に発見・修正した不具合（優先対応）】
  ファイル移動に伴い、以下の不具合を発見し、その場で修正した。
    - sub/{bbsadmin,bbsimage,bbslog,bbstree}.php・sub/index.html内の
      INCLUDED_FROM_BBSガード等が参照する相対パス「../../bbs.php」が、
      旧構成（sub/en/等、階層が1段深い）を前提にしていたため、新構成
      （sub/直下）では階層が合わなくなっていた。「../bbs.php」に修正。
    - 【重大】sub/ja/{bbsadmin,bbslog,bbstree}.php・bbs.phpには、
      $MSG（T()関数）を経由しない日本語タイトル文字列（管理メニュー、
      パスワード設定画面、過去ログ検索、ツリービュー、エラー、
      お知らせ　等、計11箇所）がハードコードされていた。sub/ja削除に
      伴いこれらがそのまま失われ、日本語設定（LANGUAGE_FILE=japanese）
      でもタイトルが英語表示になってしまうところだった。該当11箇所を
      すべてT()呼び出しに置き換え、対応する新規キー11件を
      language/english.txt・language/japanese.txtに追加した。

  【動作確認】
    - 変更対象全ファイルをphp -lで構文確認（エラー無し）。
    - PHP内蔵サーバー（php -S）でbbs.phpを実際に起動し、HTTP 200での
      応答、および初回起動時パスワード設定画面のタイトルが
      LANGUAGE_FILE=japaneseで「パスワード設定画面」、
      LANGUAGE_FILE=englishで「Password settings page」と、
      それぞれ正しく切り替わることを確認した。
    - 「../../bbs.php」等の残存参照が無いことを全ファイル横断で
      grep確認済み。

  【現在の構成】
  sub/ 直下に bbsadmin.php, bbsimage.php, bbslog.php, bbstree.php,
  template.html, tmpladmin.html, tmpllog.html, tmpltree.html,
  login.html, index.html, patTemplate.php, patTemplate/, phpzip.inc.php
  が一本化されている。sub/ja・sub/enは存在しない。UI文言は
  language/english.txt・language/japanese.txtの2ファイルのみで管理。


============================================================
2026-07-17T00:10 UTC
------------------------------------------------------------
【全体バグ精査：PHPStan（Level 5）による静的解析＋手動grep調査】
対象：bbs.php, conf.php, sub/{bbsadmin,bbsimage,bbslog,bbstree,
      patTemplate,phpzip.inc}.php

擬古猫さんの指示により、typo・括弧/波括弧の閉じ忘れ・構文的に機能
しないコードの洗い出しを実施。PHPStan Level 5を全PHPファイルに対して
実行し、検出された全項目を1件ずつ精査した。

  【実際のバグとして修正した項目】
  [1] conf.php：'HANDLENAMES'設定例に、キー'騙り'の重複があった
      （'騙り' => '管理人' と '騙り' => '管理入'）。後者は「管理人」の
      誤字（人→入）で、PHP配列では後勝ちのため誤字入りの値で上書き
      されていた。誤字の行を削除し、正しい'騙り' => '管理人'のみを
      残した。
  [2] bbs.php：Func::getdatestr()内、曜日配列$wdaysをキャッシュする
      意図と思われるisset()チェックがあったが、'static'宣言が
      抜けていたため、実際には毎回配列を再構築しており、キャッシュが
      一度も機能していなかった（動作結果自体は正しいが、意図した
      最適化が無効化されていた）。static宣言を追加。
  [3] コメント内の英語スペルミス3件を修正（機能への影響なし）：
      conf.php・sub/patTemplate.php内の'seperated'→'separated'
      （2箇所）、sub/patTemplate.php内の'teh'→'the'（1箇所）。

  【調査したが「バグではない」と判断し、変更しなかった項目】
  PHPStanはLevel 5で多数の警告を出したが、大半は以下のいずれかに
  分類される、機能に影響しない既存のPHPDoc精度の問題だった：
    - 関数のPHPDoc（@return等）が実際の戻り値の型と一致していない
      （例：Func::fgetline()は実際にはFALSEを返しうるが、PHPDocには
      Stringとしか書かれていない）ことに起因する「常にtrue/false」
      「到達不能コード」という誤検出。実際の制御フロー（EOF判定の
      while ($x !== FALSE)ループ等）は正しく機能しており、動作
      確認でも問題は再現しなかった。同様のパターンがbbs.php・
      sub/bbsadmin.php・sub/bbslog.php・sub/bbstree.php全体で
      多数該当。
    - patTemplate.php全体：PHP4/5時代の動的プロパティ宣言スタイルに
      よる「未定義プロパティへのアクセス」警告多数。既存の設計方針
      であり、今回の指示（typo・括弧忘れ等）の範囲外と判断。
    - $this->fの動的キーアクセスに起因する型不整合の警告多数
      （元々全ての値が文字列として扱われる$_GET/$_POST由来のため）。

  ※PHPDoc自体の精度向上（@return型の是正など）は、機能面のバグでは
    ないため今回は対象外としたが、まとまった量があるため、ご希望が
    あれば別途対応可能。

  【動作確認】
  修正対象全ファイルをphp -lで構文確認（エラー無し）。PHP内蔵サーバー
  でbbs.phpを再度起動し、HTTP 200・タイトル正常表示を確認した。


============================================================
2026-07-17T01:58 UTC
------------------------------------------------------------
【重大バグ修正：テンプレートHTML内のハードコード文言をLANGUAGE_FILEに接続】
対象：sub/template.html, sub/tmpladmin.html, sub/tmpllog.html,
      sub/tmpltree.html, sub/login.html, bbs.php,
      language/english.txt, language/japanese.txt

擬古猫さんより、LANGUAGE_FILE=japaneseに設定しているにも関わらず、
実機（qptns.com/test/bbs.php）でボタンラベル等が英語表示のままである
との報告を受け調査。原因は、$MSG（T()関数）経由の文言は正しく言語
切り替えされる一方、5つのHTMLテンプレートファイル内のボタン・ラベル・
見出し等が直接ハードコードされており、$MSGを一切経由していなかった
ことによる（段階1の時点ではテンプレートの構造差分のみ解消し、文言の
外部化は未着手だった）。

  【対応方式】
  patTemplateライブラリのaddGlobalVars()を利用し、$MSGの全キーを
  リクエスト単位で全テンプレートに自動的にグローバル変数として注入する
  方式を採用。個々のテンプレート描画箇所ごとにaddVar()を書く必要が
  無くなり、以後$MSGに追加されたキーは自動的に{KEY}としてどの
  テンプレートからも参照可能になる。

  [1] bbs.php: Webapp::refcustom()内の`$tmp = array_merge($this->c, $this->s)`
      に`$GLOBALS['MSG']`を追加。メイン処理経路（Bbs/Bbsadmin/Bbslog/
      Bbstree/Imagebbs全クラス共通）で有効。
  [2] bbs.php: script_run()内の管理者ログイン画面表示処理（GET/POST
      それぞれ）でも同様に$MSGをテンプレート変数に合流。実装時、
      script_run()が関数スコープであることを見落とし、初回は
      `$MSG`を裸で参照してしまい「Undefined variable」からの
      Fatal error（array_merge(): Argument #2 must be of type array,
      null given）を引き起こしたため、`$GLOBALS['MSG']`に修正して
      解消（動作確認で発見・即修正）。
  [3] sub/template.html: ボタン・ラベル・見出し・案内文等、約73箇所の
      ハードコード文言を{PLACEHOLDER}化。
  [4] sub/tmpladmin.html: 管理メニュー・削除モード・パスワード設定
      画面等、約25箇所を{PLACEHOLDER}化。
  [5] sub/tmpllog.html: 過去ログ検索・トピック一覧・ZIPアーカイブ
      画面等、約35箇所を{PLACEHOLDER}化。日付範囲検索フォームの
      日/時ラベル（sd/sh/ed/eh各select要素）はja/en間で語順が
      異なる構造だったため、FROM_DAY_LABEL/HOUR_LABEL/TO_DAY_LABEL/
      MINUTE_LABELとして整理し直した。
  [6] sub/tmpltree.html: ナビゲーション・ボタン類、約10箇所を
      {PLACEHOLDER}化。
  [7] sub/login.html: タイトル・ラベル・ボタン、3箇所を{PLACEHOLDER}化。
      あわせてbbs.php内でハードコードされていたログインエラー文言
      （'Invalid username or password.'）もT('LOGIN_ERROR')に変更。
  [8] {OG_LOCALE}を新設し、<meta property="og:locale">がLANGUAGE_FILEに
      連動するよう修正（従来はen_US固定だった）。

  新規追加キー：204件（既存キーからの再利用7件を含む、実質約150件が
  今回の新規追加）。language/english.txt・language/japanese.txtの
  キー数が一致すること、重複キーが無いことをスクリプトで確認済み。

  【動作確認】
  変更対象全ファイルをphp -lで構文確認（エラー無し）。PHP内蔵サーバーで
  以下の全経路をLANGUAGE_FILE=japanese／englishの両方で実行し、HTTP
  200・{PLACEHOLDER}の描画残り無し・該当言語での正しい文言表示を確認：
  掲示板メイン（m省略）、過去ログ検索（m=g）、ツリー表示（m=tree）、
  管理者ログイン（m=login、GET/POST両方、パスワード誤り時のエラー
  文言含む）、個人設定画面（setup=1）、管理メニュー（m=ad）。


============================================================
2026-07-17T02:17 UTC
------------------------------------------------------------
【重大バグ修正：$MSG内のプレースホルダのネスト置換が機能しない問題】
対象：bbs.php, sub/bbslog.php, language/english.txt, language/japanese.txt

擬古猫さんの実機確認（qptns.com/test/bbs.php）により、カウンター表示
行「{COUNTDATE} から {COUNTER} (こわれにくさレベル : {COUNTLEVEL})」等が
すべて空欄で表示される不具合が発覚。原因を調査したところ、patTemplate
ライブラリのstripUnusedVars()が、変数置換後の出力バッファに対して
「未解決の{……}パターンを一括除去する」処理を行っており、$MSGの値の
中に{COUNTDATE}のような別プレースホルダを埋め込んでも、patTemplateは
一度置換した文字列を再走査しない（＝ネストした置換は行われない）ため、
埋め込んだトークンがすべて空文字列に消されていたことが判明した。

  【対象範囲の再調査】
  同じパターンで$MSGの値に{……}が埋め込まれている箇所を全キーに対して
  機械的に検索し、以下11件を特定した（今回のテンプレート作業で追加した
  8件に加え、それより前の段階2で追加していた3件も同様の不具合を抱えて
  いたことが判明。後者は今回のテンプレート作業で初めて実際にレンダリング
  され、発覚した）：
    COUNTER_TEXT, MBRCOUNT_TEXT, LOGSAVE_TEXT, FORM_CONTENTS_HELP_SIMPLE,
    FORM_CONTENTS_HELP_IMAGE, IMAGE_UPLOAD_HELP, PAGE_GEN_TIME_TEXT,
    UNABLE_TO_OPEN_FILE_TEXT, NAME_TOO_LONG, EMAIL_TOO_LONG, TITLE_TOO_LONG
  （POSTS_RANGE_NEWEST_TO_OLDEST は元々str_replace()でPHP側解決済みの
  ため対象外。既存の対応済みパターン（FILE_AUTOCREATED, FAILED_TO_OPEN_LOG
  等）は元から%sベースのsprintf()方式だったため無事だった。）

  【対応方針】
  上記11キーの言語ファイル側の値を{BRACE}形式からsprintf()の%s形式に
  変更し、対応するPHP側の呼び出し箇所で、実際の値が確定した時点で
  sprintf(T('KEY'), ...)により文字列を完成させてからaddVar()/prterror()
  に渡す方式に統一した。

  [1] bbs.php: COUNTER/MBRCOUNT算出直後（`counter()`/`mbrcount()`呼び出し
      箇所）でCOUNTER_TEXT/MBRCOUNT_TEXTをsprintf()で解決。
  [2] bbs.php: Webapp::refcustom()内でLOGSAVE_TEXT・
      FORM_CONTENTS_HELP_SIMPLE・FORM_CONTENTS_HELP_IMAGE・
      IMAGE_UPLOAD_HELPを、リクエスト開始時点で判明している$CONF値から
      sprintf()で解決し、$MSGマージ後の値を上書き。MAX_IMAGE*系は
      BBSMODE_IMAGE=1の時のみ存在するため??で安全にフォールバック。
  [3] bbs.php: prthtmlfoot()内のDURATION算出直後でPAGE_GEN_TIME_TEXTを
      sprintf()で解決。
  [4] sub/bbslog.php: 過去ログファイルのfopen失敗検知箇所で
      UNABLE_TO_OPEN_FILE_TEXTをsprintf()で解決。
  [5] bbs.php: chkmessage()内のNAME_TOO_LONG/EMAIL_TOO_LONG/
      TITLE_TOO_LONGの各prterror()呼び出しをsprintf()経由に変更
      （これは段階2からの既存不具合で、対応する文字数上限が常に
      空欄表示になっていた）。

  【動作確認】
  language/*.txt全体を機械的に走査し、{……}を含む$MSG値が上記の
  意図的なもの（POSTS_RANGE_NEWEST_TO_OLDEST、PHP側でstr_replace
  済み）以外に残っていないことを確認。実機相当のテスト環境で
  カウンター行・投稿フォーム説明文・実行時間表示を実際にレンダリング
  し、すべて実際の数値が正しく埋め込まれること、{PLACEHOLDER}の
  描画残りが無いことを確認した。NAME_TOO_LONG等はsprintf()の出力を
  直接検証（PHP CLIでの単体確認）。全経路（メイン・過去ログ検索・
  ツリー表示・管理者ログイン・個人設定・管理メニュー）をHTTP 200・
  プレースホルダ残り無しで再確認済み。
