Streamlitでサイドバーに表示されるメニューを日本語化
前提
python 3.11.4
Streamlit 1.25.0
どうしよう?
PythonのフレームワークStreamlit。BtoCの華やかなWebページを~とは無縁な、どちらかというと大学や研究機関でのデータ解析や企業のデータ分析部門で使われるウェブアプリを作るのに、HTMLとCSSを書く必要が無いというフレームワーク。
ロジックやplotだけコーディングして、注目して欲しい分析結果等々を見せればいいだけだからUIはシンプル。カスタマイズも基本しない(少しできるけど。またカッコ良いUIライブラリも色々あるけど導入するとドツボにハマるのが豊潤にある)。
例えば複数ページを作成するときは、pagesフォルダ配下に[ファイル名.py]を作成すればそのファイル名がそのままサイドバーにメニューとして表示される。それでいてレシポンシブル対応。
もー簡単!
この画面はファイル構造を次のようにしただけで何もプログラムを書いていない。
my_app
├── main.py
└── pages #←フォルダ名は必ずpagesにすること
├── page_2.py
└── page_3.py
まあ英語圏ならこれでいいんだけどね。
教授から「メニュー日本語にならないの?ならない?ふーん…」なんて言われたら・・・ねぇ。
解決方法
その①
「ファイル名をメニュー表示名そのものにする」
ちょっと古い記事だが、ついにStreamlitがマルチページ(Multipage)に対応した模様に書いてあった。ファイル名を日本語メニューそのものにする。
例えば
home.py
|- pages
|- page-1.py
|- page-2.py
を、
ホーム.py
|- pages
|- ページ1.py
|- ページ2.py
という風に。
ついにStreamlitがマルチページ(Multipage)に対応した模様の記事ではうまく日本語化できて絵文字までOKとある。
これは簡単!やってみた。
結論
pages配下の「ページ1.py」「ページ2.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と堂々巡りの長時間デートになる。漫画「銃夢」でハッカーのピング・ウーが「人間の職人的カンはやっかいだぜ~」というセリフを吐くが…自分がそこまでの職人とは思ってないが。
「こういう悩みは日本人だけじゃなかろう」
と推測し「Streamlit Sidebar menu localization」で検索。
見つけたよ 同じ悩みを持った韓国人を。
さあデバッグだ。
- まずルートディレクトリにjapanese_pages.pyを作成
- そのファイルに以下のコード。
#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からインポートしたスタイルを埋め込んでいる。
# 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()を使っている。
st.set_page_config(page_title="ホーム", page_icon="🏠")
st.title("PDF文書-質問回答AIチャットWeb ホーム")
titles()
「絵文字は?」
絵文字いるか~?まあどっちでもいいか…
- 絵文字はjapanese_pages.pyのスタイルに付ければ良い。
# ・・・前略・・・
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);
}
# ・・・後略・・・
home.pyだけコード全文:
# 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では大丈夫です。
ありがたい韓国のお人↓
参考ページ:Is it possible to set different names for multipage in the sidebar and URL?