Python Fluent chooses wrong locales

Hi!

I have implemented Fluent in my Python/Kivy app. I thought it worked fine, but now it seems to ignore when I change locales. Here is the (global) initialization in my Python app:

from fluent.runtime import FluentLocalization, FluentResourceLoader
loader = FluentResourceLoader(“languages/{locale}”)
language = FluentLocalization([“en”, “es”, “da”], [“main.ftl”], loader)

Later the user can change the selection, for instance like this:

language.locales = [‘da’,‘en’,‘es’]

AFTER that change, I instantiate a class that uses this in init:

    self.label_text = language.format_value("train-ideogram-label")

So why does that result in the string from “en” and not the one from “da”?

Simply put, this works as designed.

The localization class caches the bundles it has loaded, which depends on the list of languages.

All properties are read-only after the initial construction. If you want to change them, just create a new instance.

Would it help to make that more crisp in the documentation?

OK. Thanks.
I didn’t notice anything in the doc - https://www.projectfluent.org/python-fluent/fluent.runtime/stable/usage.html - about all properties being read-only. I just assumed I could change the locales list property.

Your reply leads to a new question, which is not particularly about Fluent but a more general Python question:

I instantiated ‘language’ outside of any other class or function, so that it would be accessible globally:

language = FluentLocalization([“en”, “es”, “da”], [“main.ftl”], loader)

How can I make it equally globally accessible if I have to instantiate it inside a method where the user selects a language from a list?

This depends on the kind of app you are writing. Given that you are using Kivy, a single global for this might be fine, in which case you can use the global keyword:

from fluent.runtime import FluentLocalization, FluentResourceLoader
localization = None

def change_language(languages):
    global localization
    loader = FluentResourceLoader(“languages/{locale}”)
    localization = FluentLocalization(languages, [“main.ftl”], loader)

# Ensure `localization` is always initialized
change_language([“en”, “es”, “da”])

# Also call `change_language` from other places

In other app architectures this could be a bad way to do it, and even with Kivy you may need to worry about other threads or modules that are importing this module and could end up with stale values. It might be better to ask on Kivy forums about what the best pattern for global mutable data is.

Can ‘change_language()’ be called several times? And thus ‘re-instantiate’ ‘localization = FluentLocalization()’?

If you’re looking for a global entry point, I’d cache a global function, on languages and resources.

A global variable for the localization class isn’t a good fit with how that class is designed and implemented.

Thank you for the answer!
But can you explain or exemplify what that means? Or link to an explanation?

Sure, something like:

from functools import lru_cache
from typing import Sequence
from fluent.runtime import FluentLocalization, FluentResourceLoader

@lru_cache
def get_localization(languages: Sequence[str], resources: Sequence[str]) -> FluentLocalization:
    loader = FluentResourceLoader("languages/{locale}")
    return FluentLocalization(languages, resources, loader)

Shouldn’t that function be called SET_localization() instead of GET_localization() ?
SET would be the functionality I am looking for, as in:

language = SET_localization(languages_list, resources)

label_text = language.format_value("press-for-next")

The code snippet I showed is getting a translation class to be used in a local context, with caching of these instances, on top of the caching that happens inside the translation class.

If you’re looking for a way still to make your global work, you’ll need to replace the localization class with one that does what you need. It’s not like you’re loosing a lot, because most of the actual logic in the implementation needs to be done differently for the architecture you’d be looking at.

The built-in localization class is really an opinionated way of implementing the concept, and alternative ways should substitute the one we’re shipping.

Thanks for the suggestions.
I think I will stick to something more simple for now, like this:

from fluent.runtime import FluentLocalization, FluentResourceLoader
language = None

‘#’ Only call this ONCE:
def set_language(languages):
global language
loader = FluentResourceLoader(‘languages/{locale}’)
language = FluentLocalization(languages, [‘main.ftl’], loader)

PS: It seems I can’t write Python comment lines…