How to make Panel app multi-language? Parameters with variable labels

Probably this problem is more related to my poor understanding of Python classes than Panel itself. However, I would really appreciate your help to get a cleaner and better code.

I would like to change the language of the app at instantiation within init. The problem in my real app is that if I setup the values of the widgets inside init (e.g. self.param[‘continent’].label/objects=… and self.continent=…) I trigger one of the many @param.depends below and I get an error since other widgets are not yet defined. Thus, it becomes impossible use init to initialize the values of the widgets and I need to initialize them in the class variables section. But this implies that I cannot supply the parameter lang in order to change the language of the app during instantiation.

Is there any pythonic way to improve the code below?


import param
import panel as pn

pn.extension()

class Test(param.Parameterized):
    
    lang = 'en'

    #In my real app I use gettext with .po/.mo files instead of hard-coded dictionaries
    #t = gettext.translation(lang, localedir='translations', languages=[lang], fallback=True)
    #_ = t.gettext
    #For example -> default = _('Asia')


    continent = param.ObjectSelector(
        default={'en': 'Asia', 'it': 'Asia_it'}[lang],
        objects={'en': ['Africa', 'Asia', 'Europe'], 'it': ['Africa_it', 'Asia_it', 'Europe_it']}[lang])
    
    country = param.ObjectSelector(
        default={'en': 'China', 'it': 'China_it'}[lang],
        objects={'en': ['China', 'Thailand', 'Japan'], 'it': ['China_it', 'Thailand_it', 'Japan_it']}[lang])
    
    _countries = {'en': {'Africa': ['Ghana', 'Togo', 'South Africa', 'Tanzania'],
                  'Asia'  : ['China', 'Thailand', 'Japan'],
                  'Europe': ['Austria', 'Bulgaria', 'Greece', 'Portugal', 'Switzerland']},
                  'it': {'Africa_it': ['Ghana_it', 'Togo_it', 'South Africa_it', 'Tanzania_it'],
                  'Asia_it'  : ['China_it', 'Thailand_it', 'Japan_it'],
                  'Europe_it': ['Austria_it', 'Bulgaria_it', 'Greece_it', 'Portugal_it', 'Switzerland_it']}
                  }[lang]
    
    # In my real app I have to load data and display plots by init.
    # Ideally, I would like to pass 'lang' as a variable during instantiation within init and not as a class variable.
    #def __init__(self, **params):
    #    super().__init__(**params)

    @param.depends('continent', watch=True)
    def _update_countries(self):
        countries = self._countries[self.continent]
        self.param['country'].objects = countries
        self.country = countries[0]

# This is a very dummy solution, since a big chunk of code (all the class variables) is repeated only because 'lang' changes to a new language.
class TestIT(Test):
    
    lang = 'it'

    continent = param.ObjectSelector(
        default={'en': 'Asia', 'it': 'Asia_it'}[lang],
        objects={'en': ['Africa', 'Asia', 'Europe'], 'it': ['Africa_it', 'Asia_it', 'Europe_it']}[lang])
    
    country = param.ObjectSelector(
        default={'en': 'China', 'it': 'China_it'}[lang],
        objects={'en': ['China', 'Thailand', 'Japan'], 'it': ['China_it', 'Thailand_it', 'Japan_it']}[lang])
    
    _countries = {'en': {'Africa': ['Ghana', 'Togo', 'South Africa', 'Tanzania'],
                  'Asia'  : ['China', 'Thailand', 'Japan'],
                  'Europe': ['Austria', 'Bulgaria', 'Greece', 'Portugal', 'Switzerland']},
                  'it': {'Africa_it': ['Ghana_it', 'Togo_it', 'South Africa_it', 'Tanzania_it'],
                  'Asia_it'  : ['China_it', 'Thailand_it', 'Japan_it'],
                  'Europe_it': ['Austria_it', 'Bulgaria_it', 'Greece_it', 'Portugal_it', 'Switzerland_it']}
                  }[lang]
 
# I would like to get something like viewer = Test(lang='it') instead of the code below
viewer = TestIT()
pn.Row(viewer.param).show()

2 Likes