reflex icon indicating copy to clipboard operation
reflex copied to clipboard

Internationalization (I18N) and localization (L10N) (Feature Request)

Open karndeb opened this issue 1 year ago • 2 comments

Feature Request Hi team, Great work and library. I think with internationalization the use cases and reflex adoption will greatly improve. I have two ways in my mind that we can add this. One is quick and easy which is through the middleware route and using the gettext module or adding i18n to the core jinja extensions. Will probably need help on the latter. Let me know if this is in the roadmap. Thanks

karndeb avatar Dec 29 '23 16:12 karndeb

Internationalization is important to the team and on the roadmap, but it's not currently a focus for any particular developer. We don't have a particular strategy around this area, but something which can enable translation support and also enable the CMS workflow would be ideal.

I'm imagining something like State, except all users accessing the site share the same values for a given key and the set of values can be swapped out by some identifier (language, for example).

masenf avatar Jan 02 '24 21:01 masenf

There is a great translation framework currently in development by the Mozilla Foundation, called project fluent. I think it is beautiful, because

  • It allows for better translations (not every language can map 1:1 to an English original, e.g. in English there is only one plural to express "more than one". However, some languages have additional plural forms to differentiate between "few" and "many").
  • It removes any language specific logic from your application code.
  • I find it easier to learn and use than gettext: It uses only simple text files and does not require specialized extraction tools.

Existing implementations of the fluent language can be found here.

I think it would be great to have it implemented as a component so that you can write:

def headline():
    return rx.text(
        rx.translation("my_special_headline"), 
        color="blue"
    )

where "my_special_headline" is the ID of the translation string to use. In addition, variables may be passed to the translation system as shown below. Sticking with the example given on the fluent page (showing a warning before closing all open tabs in a browser), one could write:

def warning():
    return rx.text(
        rx.translation("tabs-close-warning", MyState.number_of_open_tabs), 
        color="red"
    )

For a language like Polish which has more than one plural form, fluent would then select the appropriate translation depending on the number of open tabs:

  • number_of_open_tabs = 1: Zostanie zamkniętych ⁨1⁩ kart. Czy chcesz kontynuować?
  • number_of_open_tabs = 2: Zostaną zamknięte ⁨2⁩ karty. Czy chcesz kontynuować?
  • number_of_open_tabs > 4: Zostanie zamkniętych ⁨5⁩ kart. Czy chcesz kontynuować?

woernerm avatar Mar 29 '24 20:03 woernerm

I tried something with @masenf suggestion if I interpreted it correctly https://github.com/paoloemilioserra/reflex-i18n/tree/master any feedback is much appreciated

paoloemilioserra avatar May 26 '24 10:05 paoloemilioserra

@paoloemilioserra thanks for sharing! It seems like you forgot to make the repository public?

benedikt-bartscher avatar May 26 '24 12:05 benedikt-bartscher

@benedikt-bartscher you should be able to see it now

paoloemilioserra avatar May 26 '24 22:05 paoloemilioserra

@masenf

I'm imagining something like State, except all users accessing the site share the same values for a given key and the set of values can be swapped out by some identifier (language, for example).

This reminds me of the tiny feature request https://github.com/reflex-dev/reflex/issues/2771 Maybe it could go like this:

class AppState(rx.State):  
  @rx.var
  def user_language(self) -> str:
     ...
   ...

@rx.scope(var=AppState.user_language)
class T(AppState):
  ...
  # vars are added at compile time using po files.

# Somewhere else
@rx.page
def index():
  return rx.text(f"{T.hello_user} {AppState.user_name}")

The benefit would be: One state per used language on the server only.

Maybe it is wrong to stack one dream API :rainbow: on top of the other :cloud:

But when thinking about it the requirements might become more clear:

  • We don't want all users to have all languages on their individual states.
  • Language features should be added at compile time.
  • Only the used language should be send to the user.
  • Don't compile the frontend for each language (which might also be a possibility when giving it a thought).

abulvenz avatar Jun 17 '24 19:06 abulvenz