Step 6: Create the Main App
Wire everything together in a Streamlit app with proper routing.
# main.py
import streamlit as st
import os
from gws_core.streamlit import StreamlitRouter
from gws_plate_reader.cell_culture_app_core.pages import (
first_page, recipe_page, settings
)
from .my_custom_state import MyCustomState
from .pages import new_recipe_page
# Get translation folder path
current_dir = os.path.dirname(__file__)
core_dir = os.path.join(current_dir, '..', '_my_custom_core')
lang_translation_folder_path = os.path.abspath(core_dir)
# Initialize state (global variable for the module)
custom_state = MyCustomState(lang_translation_folder_path)
def display_first_page(custom_state: MyCustomState):
"""Display the first page (recipe list)."""
first_page.render_first_page(custom_state)
def add_first_page(router: StreamlitRouter, custom_state: MyCustomState):
"""Add first page to router."""
translate_service = custom_state.get_translate_service()
router.add_page(
lambda: display_first_page(custom_state),
title=translate_service.translate('page_title_analyses'),
url_path='first-page',
icon='🧬',
hide_from_sidebar=False
)
def display_new_analysis_page(custom_state: MyCustomState):
"""Display the new analysis creation page."""
new_recipe_page.render_new_recipe_page(custom_state)
def add_new_analysis_page(router: StreamlitRouter, custom_state: MyCustomState):
"""Add new analysis page to router."""
router.add_page(
lambda: display_new_analysis_page(custom_state),
title='New Analysis', # Hidden from sidebar, no need to translate
url_path='new-analysis',
icon='➕',
hide_from_sidebar=True
)
def display_analysis_page(custom_state: MyCustomState):
"""Display the analysis page (recipe details)."""
recipe_page.render_recipe_page(custom_state)
def add_analysis_page(router: StreamlitRouter, custom_state: MyCustomState):
"""Add analysis page to router."""
router.add_page(
lambda: display_analysis_page(custom_state),
title='Analysis', # Hidden from sidebar
url_path='analysis',
icon='📊',
hide_from_sidebar=True
)
def display_settings_page(custom_state: MyCustomState):
"""Display the settings page."""
settings.render_settings_page(custom_state)
def add_settings_page(router: StreamlitRouter, custom_state: MyCustomState):
"""Add settings page to router."""
translate_service = custom_state.get_translate_service()
router.add_page(
lambda: display_settings_page(custom_state),
title=translate_service.translate('page_title_settings'),
url_path='settings',
icon=':material/settings:',
hide_from_sidebar=False
)
# Create router and add all pages
router = StreamlitRouter.load_from_session()
add_first_page(router, custom_state)
add_new_analysis_page(router, custom_state)
add_analysis_page(router, custom_state)
add_settings_page(router, custom_state)
# Run the router (this renders the current page)
router.run()
Important Notes:
- Initialize state once as a module-level variable
- Use
StreamlitRouter.load_from_session() to get the router - Pages hidden from sidebar (
hide_from_sidebar=True) don't need translated titles - Call
router.run() at the end to render the current page - Each page has a display function and an add function
Step 7: Create Translation Files
Create JSON files for internationalization by combining the core framework translations with your custom translations.
Process:
- Copy the base translation files from
cell_culture_app_core:Copy gws_plate_reader/cell_culture_app_core/en.json → _my_custom_core/en.jsonCopy gws_plate_reader/cell_culture_app_core/fr.json → _my_custom_core/fr.json
- Add your custom translation keys to the copied files. The core files contain ~600 keys for all Cell Culture framework features. You need to add your application-specific keys on top.
Structure of translation files:
_my_custom_core/en.json:
{
// ========================================
// CORE CELL CULTURE APP TRANSLATIONS (~600 keys)
// ========================================
"page_title_analyses": "Analyses",
"page_title_overview": "Overview",
"page_title_selection": "Selection",
"page_title_quality_check": "Quality Check",
"page_title_table_view": "Table View",
"page_title_graph_view": "Graph View",
"page_title_medium_view": "Medium View",
"page_title_pca": "PCA Analysis",
"page_title_umap": "UMAP Analysis",
"page_title_feature_extraction": "Feature Extraction",
"page_title_pls_regression": "PLS Regression",
"page_title_random_forest": "Random Forest",
"page_title_settings": "Settings",
// ... (600+ core keys)
// ========================================
// YOUR CUSTOM TRANSLATIONS (~50-200 keys)
// ========================================
"new_recipe_my_custom": "New Custom Analysis",
"my_custom_app_title": "My Custom Fermentation Analysis",
"recipe_name_label": "Recipe Name",
"recipe_name_placeholder": "Enter recipe name...",
"upload_info_file": "Upload Info File",
"upload_medium_file": "Upload Medium File",
"upload_raw_data_file": "Upload Raw Data File",
"upload_follow_up_file": "Upload Follow-up File",
"file_upload_success": "File uploaded successfully",
"file_upload_error": "Error uploading file",
"launch_analysis_button": "Launch Analysis",
"return_to_recipes": "Return to recipes",
"analysis_type_standard": "Standard Analysis",
"analysis_type_microplate": "Microplate Analysis",
"select_existing_resources": "Select Existing Resources",
"upload_new_files": "Upload New Files",
"error_missing_files": "Please provide all required files",
"error_missing_recipe_name": "Please provide a recipe name",
"success_analysis_launched": "Analysis launched successfully"
}
_my_custom_core/fr.json:
{
// ========================================
// CORE CELL CULTURE APP TRANSLATIONS (~600 keys)
// ========================================
"page_title_analyses": "Analyses",
"page_title_overview": "Vue d'ensemble",
"page_title_selection": "Sélection",
"page_title_quality_check": "Contrôle qualité",
"page_title_table_view": "Vue tableau",
"page_title_graph_view": "Vue graphique",
"page_title_medium_view": "Vue milieu",
"page_title_pca": "Analyse PCA",
"page_title_umap": "Analyse UMAP",
"page_title_feature_extraction": "Extraction de caractéristiques",
"page_title_pls_regression": "Régression PLS",
"page_title_random_forest": "Forêt aléatoire",
"page_title_settings": "Paramètres",
// ... (600+ core keys)
// ========================================
// YOUR CUSTOM TRANSLATIONS (~50-200 keys)
// ========================================
"new_recipe_my_custom": "Nouvelle Analyse Personnalisée",
"my_custom_app_title": "Mon Analyse de Fermentation Personnalisée",
"recipe_name_label": "Nom de la recette",
"recipe_name_placeholder": "Entrez le nom de la recette...",
"upload_info_file": "Télécharger le fichier Info",
"upload_medium_file": "Télécharger le fichier Milieu",
"upload_raw_data_file": "Télécharger le fichier Données brutes",
"upload_follow_up_file": "Télécharger le fichier Suivi",
"file_upload_success": "Fichier téléchargé avec succès",
"file_upload_error": "Erreur lors du téléchargement du fichier",
"launch_analysis_button": "Lancer l'analyse",
"return_to_recipes": "Retour aux recettes",
"analysis_type_standard": "Analyse standard",
"analysis_type_microplate": "Analyse microplaque",
"select_existing_resources": "Sélectionner les ressources existantes",
"upload_new_files": "Télécharger de nouveaux fichiers",
"error_missing_files": "Veuillez fournir tous les fichiers requis",
"error_missing_recipe_name": "Veuillez fournir un nom de recette",
"success_analysis_launched": "Analyse lancée avec succès"
}
Important Notes:
- Do NOT modify the core translation keys (the ~600 keys from
cell_culture_app_core) - Only add your custom keys at the end of the files
- Keep both files synchronized: Every key in
en.json must exist in fr.json - Use consistent naming: Prefix your custom keys with a unique identifier (e.g.,
my_custom_, myapp_) - Total keys: Core (~600) + Custom (~50-200) = ~650-800 keys per file