Strange behavior updating session variable with callback after radio button change

I have this code which is part of an app:

import streamlit as st
import gettext
from streamlit import session_state


def translate():
    """
    Instantiate a translation function for the German language.

    Retrieves the German translation object and sets the global translation function (_)
    to perform text content translation to German.

    Returns:
        function: The gettext translation function tailored for the German language.
    """
    de = gettext.translation('base', localedir='locale', languages=['de'])
    de.install()
    _ = de.gettext
    return _


def update_language_in_session(translation_func):
    """
    Updates the translation function in the session state.

    This function ensures that the text displayed in the application is translated
    according to the currently selected language by updating the translation function
    used throughout the application dynamically.

    Parameters:
        translation_func (function): The translation function to be used for text content.
    """
    session_state['_'] = translation_func


class LanguageManager:
    """
    Manages the language selection and setup for the application by providing functionality
    to initialize the default language, change languages, and render language selection controls.
    """

    def __init__(self):
        """
        Initializes the LanguageManager with supported languages.
        """
        self.languages = {"English": "en", "Deutsch": "de"}
        self.current_index = 1  # Stores the current language's index for optimization

    @staticmethod
    def initialize_language():
        """
        Initializes the application language settings.

        Sets the application's language to German (de) by default if no language
        selection has been explicitly made by the user. This default setting ensures
        that the application has a consistent language setting upon initial use.
        """
        # If no language is chosen yet set it to German
        if 'selected_language' not in session_state:
            session_state['_'] = translate()

    def get_language(self):
        """
        Retrieves the current language code from the session state based on the user's selection.

        Returns:
            str: The current language code if set (e.g., 'en', 'de'), with 'de' as the default value.
        """
        selected_language = st.session_state.get('selected_language')
        if not selected_language:
            return "de"

        return self.languages.get(selected_language)

    def change_language(self):
        """
        Changes the application language based on user selection and updates session state accordingly.

        This method updates both the translation function and the current language index in the session
        state to reflect the user's choice. Also handles updating the language selection control in the UI.
        """
        print("On change", session_state['selected_language'], session_state['language_index'])
        selected_language = session_state['selected_language']
        if selected_language == 'English':
            update_language_in_session(gettext.gettext)
        else:
            update_language_in_session(translate())

        # Update the index in session state when it changes
        if selected_language != list(self.languages.keys())[session_state['language_index']]:
            session_state['language_index'] = list(self.languages.keys()).index(selected_language)
        print("On change", session_state['selected_language'], session_state['language_index'])

    def language_controls(self):
        """
        Creates language selection controls in the application sidebar.

        Renders a radio button interface for language selection in the sidebar, allowing the user
        to choose between supported languages. This selection automatically updates the application's
        displayed language through a callback on change.
        """
        # Use cached index if available, else find it
        if 'language_index' not in session_state:
            current_language = session_state.get('selected_language')
            if current_language and current_language in self.languages:
                self.current_index = list(self.languages.keys()).index(current_language)
            session_state['language_index'] = self.current_index

        with st.sidebar:
            st.radio(
                "Language",
                options=list(self.languages),
                horizontal=True,
                index=session_state['language_index'],
                key='selected_language',
                on_change=self.change_language,
                label_visibility='hidden'
            )

        print("Main",session_state['selected_language'])

When I first run the app I get the output:

Main Deutsch

which is correct.

Then I change language to English and I get as output:

On change English 1
On change English 0
Main English

which is also correct.

But then I change back to Deutsch and I get:

On change Deutsch 0
On change Deutsch 1
Main English

and then I turn back to English and the output is just:

Main English

which means the callback wasn’t even called on the fourth time

I’m completely puzzled why the third line of the third output shows Main English instead of Main Deutsch. I thought callbacks were always called first and then the whole app is executed? So during the third output the callback is called and the session variable is updated correctly in the callback but then it doesn’t show the change in the Main app run and the fourth time I change the radio button the callback isn’t even called when I click on the radio button as seen in the output. Any idea what could be happening?

I managed to make this work by changing the change_language function to this:

    def change_language(self):
        # Directly fetch the currently selected language and compare it against the current session state.
        selected_language = st.session_state.get('selected_language')
        current_language_index = st.session_state.get('language_index')

        # Determine and set the appropriate translation function based on the selection.
        if selected_language == 'English':
            update_language_in_session(gettext.gettext)
        else:
            update_language_in_session(translate())

        # Proceed only if there is a real change in the selected language.
        if selected_language and (self.languages[selected_language] != self.get_language()):

            # Update the language index in session state if necessary.
            new_index = list(self.languages.keys()).index(selected_language)
            if current_language_index != new_index:
                st.session_state['language_index'] = new_index

Not really sure why but it works now :smile:

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.

Hi @Odrec

Glad to hear that it worked now, and thanks for sharing your update and solution!