Streamlitでサイドバーに表示されるメニューを日本語化

# Streamlitでサイドバーに表示されるメニューを日本語化

#### 前提
python 3.11.4
Streamlit 1.25.0

### どうしよう？
PythonのフレームワークStreamlit。BtoCの華やかなWebページを～:sun_with_face:とは無縁な、どちらかというと大学や研究機関でのデータ解析や企業のデータ分析部門で使われるウェブアプリを作るのに、<b>HTMLとCSSを書く必要が無い</b>というフレームワーク。

ロジックやplotだけコーディングして、注目して欲しい分析結果等々を見せればいいだけだからUIはシンプル。カスタマイズも基本しない（少しできるけど。またカッコ良いUIライブラリも色々あるけど導入するとドツボにハマるのが豊潤にある）。

例えば複数ページを作成するときは、pagesフォルダ配下に[ファイル名.py]を作成すればそのファイル名がそのままサイドバーにメニューとして表示される。それでいて<b>レシポンシブル対応</b>。
もー簡単！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/958891ab-ac6e-0bf9-e093-00a52055ec10.png)

この画面はファイル構造を次のようにしただけで何もプログラムを書いていない。
```
my_app
├── main.py
└── pages　　　　　　　#←フォルダ名は必ずpagesにすること
    ├── page_2.py
    └── page_3.py
```

まあ英語圏ならこれでいいんだけどね。

教授から「メニュー日本語にならないの？ならない？ふーん…」なんて言われたら・・・ねぇ。

## 解決方法

### その①
「ファイル名をメニュー表示名そのものにする」

ちょっと古い記事だが、[ついにStreamlitがマルチページ(Multipage)に対応した模様](https://kajiblo.com/streamlit-maltipage/)に書いてあった。<b>ファイル名を日本語メニューそのものにする</b>。

例えば
```
home.py
    |- pages
         |- page-1.py
         |- page-2.py
```
を、
```
ホーム.py
    |- pages
         |- ページ１.py
         |- ページ２.py
```
という風に。

[ついにStreamlitがマルチページ(Multipage)に対応した模様](https://kajiblo.com/streamlit-maltipage/)の記事ではうまく日本語化できて絵文字までOKとある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/3d1c3e18-0f07-a71a-6953-a6999cef7df1.png)

これは簡単！やってみた。

#### 結論
pages配下の「ページ１.py」「ページ２.py」はうまく行くが「ホーム.py」はうまく行かない。おびただしいエラー。
```
File "/usr/src/llm-streamlit/.venv/lib/python3.11/site-packages/streamlit/watcher/local_sources_watcher.py", line 55, in init
self._register_watcher(
File "/usr/src/llm-streamlit/.venv/lib/python3.11/site-packages/streamlit/watcher/local_sources_watcher.py", line 103, in _register_watcher
watcher=PathWatcher(filepath, self.on_file_changed),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/llm-streamlit/.venv/lib/python3.11/site-packages/streamlit/watcher/event_based_path_watcher.py", line 92, in init
path_watcher.watch_path(
File "/usr/src/llm-streamlit/.venv/lib/python3.11/site-packages/streamlit/watcher/event_based_path_watcher.py", line 174, in watch_path
folder_handler.add_path_change_listener(
File "/usr/src/llm-streamlit/.venv/lib/python3.11/site-packages/streamlit/watcher/event_based_path_watcher.py", line 274, in add_path_change_listener
md5 = util.calc_md5_with_blocking_retries(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
～後略～
```

これはやめとこう。

そもそもプログラムが使うファイル名を日本語に、なんてなんとなくヤバい感じもするよね。

### その②

「ChatGPTに聞いた？」

もちろんChatGPTにもClaudeにも聞いた（無料版）。
なんやらややこしいやり方を提案してきて、まあ実際にやってみたけどやっぱり駄目ね。

「たぶんこのやり方だと永遠にムリ目だな」

カンでなんとなくわかる、このままじゃClaudeと堂々巡りの長時間デートになる。漫画「銃夢」でハッカーのピング・ウーが「人間の職人的カンはやっかいだぜ～」というセリフを吐くが…自分がそこまでの職人とは思ってないが。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/d72a896c-abaf-6e77-0aa8-f2e5a4201980.png)
> <span style="color: blue;">_Copyright © 2008-2024. Kodansha Ltd. All Rights Reserved._</span> 

<br />


「こういう悩みは日本人だけじゃなかろう」


と推測し「Streamlit Sidebar menu localization」で検索。

見つけたよ:v: 同じ悩みを持った韓国人を。

さあデバッグだ。

- まずルートディレクトリにjapanese_pages.pyを作成
- そのファイルに以下のコード。
 ```python
 #japanese_page.py
 
import streamlit as st

def titles():
    return st.markdown(
        """
    <style>
:root {
  --text-color: #808495; /* Light text color */
  --bg-color: transparent; /* Dark background color */
}
a[href="http://localhost:8501/"] span:first-child {
    z-index: 1;
    color: transparent;
}
a[href="http://localhost:8501/"] span:first-child::before {
    content: "ホーム";
    left: 0;
    z-index: 2;
    color: var(--text-color);
    background-color: var(--bg-color);
}
a[href="http://localhost:8501/admin"] span:first-child {
    z-index: 1;
    color: transparent;
}
a[href="http://localhost:8501/admin"] span:first-child::before {
    content: "管理画面";
    left: 0;
    z-index: 2;
    color: var(--text-color);
    background-color: var(--bg-color);
}
a[href="http://localhost:8501/chat"] span:first-child {
    z-index: 1;
    color: transparent;
}
a[href="http://localhost:8501/chat"] span:first-child::before {
    content: "チャット";
    left: 0;
    z-index: 2;
    color: var(--text-color);
    background-color: var(--bg-color);
}
</style>
    """,
        unsafe_allow_html=True,
    )
 ```
- 要はスタイルの定義をしている。ここでは「ホーム」(home.py)、「管理画面」(admin.py)、「チャット」(chat.py)、の3ページがあることを想定して作成している。わたしの手前ミソの開発中ウェブアプリだ。
- そうしたら各ページに以下のコード。これはStreamlitお馴染みの`set_page_config()`だ。そして`titles()`でjapanese_page.pyからインポートしたスタイルを埋め込んでいる。
 ```python
# home.py
from japanese_pages import titles

def main():

    # タイトル
    st.set_page_config(page_title="ホーム", page_icon="🏠")
    st.markdown(
        "<h1 class='jp-san-serif'>PDF文書-質問回答AIチャットWeb ホーム</h1>",
        unsafe_allow_html=True,
    )
    titles()

#・・・中略・・・

if __name__ == "__main__":
    main()
 ```

- 本当は以下のようにst.title("ホーム画面タイトル")で良いのだけど、ここではタイトルにスタイル'jp-san-serif'（別途定義）を当てたいためにst.markdown()を使っている。
```python
st.set_page_config(page_title="ホーム", page_icon="🏠")
st.title("PDF文書-質問回答AIチャットWeb ホーム")
titles()
```
- admin.pyおよびchat.pyにも同様に上のコードを貼り付け、タイトル名は良きように変える。

- サイドバーのメニューを日本語化できた！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/2d7003cf-40ff-44a3-9e4a-1ef43597630b.png)

<br />

「絵文字は？」

<br />

絵文字いるか～？まあどっちでもいいか…

- 絵文字はjapanese_pages.pyのスタイルに付ければ良い。
```python
# ・・・前略・・・
a[href="http://localhost:8501/"] span:first-child::before {
    content: "🏠ホーム";
    left: 0;
    z-index: 2;
    color: var(--text-color);
    background-color: var(--bg-color);
}
a[href="http://localhost:8501/admin"] span:first-child {
    z-index: 1;
    color: transparent;
}
a[href="http://localhost:8501/admin"] span:first-child::before {
    content: "💻管理画面";
    left: 0;
    z-index: 2;
    color: var(--text-color);
    background-color: var(--bg-color);
}
a[href="http://localhost:8501/chat"] span:first-child {
    z-index: 1;
    color: transparent;
}
a[href="http://localhost:8501/chat"] span:first-child::before {
    content: "💬チャット";
    left: 0;
    z-index: 2;
    color: var(--text-color);
    background-color: var(--bg-color);
}
# ・・・後略・・・
```

ほれ、このように。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/49204c93-40fa-b216-15c1-22453f4d28e7.png)

<br />

home.pyだけコード全文：
```python
# homy.py
import streamlit as st
from japanese_pages import titles

# ページ表示用関数
def main():

    # タイトル
    st.set_page_config(page_title="ホーム", page_icon="🏠")
    st.markdown(
        "<h1 class='jp-san-serif'>PDF文書-質問回答AIチャットWeb ホーム</h1>",
        unsafe_allow_html=True,
    )
    titles()

    ##################################### タイトルのCSSを良しなに設定 ############################################
    # Google FontsからNoto Sans JPフォントをロードする
    st.markdown(
        """
        <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
        """,
        unsafe_allow_html=True,
    )

    # Robotoフォントをタイトル文字に使用するためのHTMLスタイル
    st.markdown(
        """
        <style>
        .jp-san-serif {
            font-family: 'Noto Sans JP', sans-serif;
            font-size: 1.7rem;
        }
        </style>
        """,
        unsafe_allow_html=True,
    )
    ################################## タイトルのCSSを良しなに設定～ここまで #####################################

if __name__ == "__main__":
    main()

```

この方法もStreamlitの他のバージョンでは使えないかもね～`<span>`使ってるところが`<p>`に変わってたりとかとか。とりあえずStreamlitバージョン1.25.0では大丈夫です。

<br />

ありがたい韓国のお人↓
参考ページ：[Is it possible to set different names for multipage in the sidebar and URL?](https://discuss.streamlit.io/t/is-it-possible-to-set-different-names-for-multipage-in-the-sidebar-and-url/38908)