JuliaPluto/PlutoTeachingTools.jl

Internationalization

jonschumacher opened this issue · 10 comments

First of all thanks for the awesome working on making Pluto an even better tool for teaching!

We are currently setting up a (sort of) signal processing course for German-speaking students and would like to add internationalization to the box labels. Thus, I would like to start a discussion here on how we could get this into PlutoTechingTools.
I found two packages for i18n:

Do you have a preference here? How could we go forward on implementing this?

Cheers
Jonas

eford commented

Thanks for the feedback and interest.
I'm afraid that I have only a passing familiarity with internationalization issues and no experience addressing them. Do you the pros and cons of the two packages you cited? (E.g., is Gettext.jl not changing much recently because it already does what it aims to well, or because it's a dead end?)

Are you thinking that you'd like an easy way to swap out which language's words are displayed by the functions for "common" words/phrases (e.g., almost, correct, danger, hint, etc.)?

Or are you thinking bigger? E.g., instead of making two notebooks one in German and one in English, there would be text for both languages in one notebook? That would sounds more like a project for the main Pluto/PlutoUI folks, then for this pacakge.

If it's just the former, then I'm a little reluctant to create a dependancy on PyCall or a big package like ResourceBundles.jl that does way more than we need.
My first thought was that we could make a submodule for each language that contains a set variables set to their language-specific strings. Then the main PlutoTeachingTools module could import one of those submodules based on the locale id. In the main module, we'd refer to the strings by variable name (instead of hard coding them like now).
Do you see a weakness in that approach?

I am also not that deep into i18n. To me, it looks like Gettext.jl is mainly a wrapper for the Python equivalent and thus the functionalities are set and no further development has to be done.
We are thinking bigger here and would like to be able to switch whole parts of Markdown text to another language, but for the start we would like to get rid of the hard-coded strings. The submodule approach sounds good. I will check, how this can be extended without bringing all languages into the main Git and try to come up with a PR.

I tried a few approaches in order to split the translations in another module. The way I wanted to implement this is an abstract type for translations and then we can dispatch on concrete structs of this subtype. The problem is that if I want to just wrap the strings in macros/functions, the code gets replaced during precompilation and precompilation is already happening while using PlutoTeachingTools. Thus, I do not have a chance to integrate a new translation here because I already need the abstract type. Probably I am just missing a good idea here. Did you have something specific in mind when you proposed to make submodules? My plan was something like having using PlutoTeachingTools; using PlutoTeachingToolsGerman and this is automatically adding the respective subtype to the list of available translations which is then automatically used but can be switched manually using functions from PlutoTeachingTools.

eford commented
eford commented

Another potential work around would be to set the main PlutoTeachingTools with __precompile__(false) and to put as much of the code as possible to submodules that could be pre-compiled.  I'm not sure if that would add a noticeable startup cost.

eford commented

How about

module PTTAbstract
 abstract type AbstractLanguage end
 function hint_str end
 export AbstractLanguage
end


module PTTEnglish
 import ..PTTAbstract: AbstractLanguage, hint_str

 abstract type English  <: AbstractLanguage end
 struct EnglishUS <: English end

 hint_str(lang::Lang ) where { Lang <: English }  = "Hint"
end


module PTT
 import ..PTTAbstract: AbstractLanguage, hint_str
 using Markdown

 function hint(text, lang::AbstractLanguage = default_language )
   Markdown.MD(Markdown.Admonition("hint", hint_str(lang), [text]));
 end

 # default
 import ..PTTEnglish: EnglishUS
 default_language = EnglishUS()
 function set_language!(lang::AbstractLanguage)
    global default_language = lang
  end

 export set_language!, hint
end


module PTTGerman
 import ..PTTAbstract: AbstractLanguage, hint_str
 import ..PTT: hint
 struct German <: AbstractLanguage end

 hint_str(lang::Lang ) where { Lang <: German }  = "Hinweis"
 hinweis = hint
 export hinweis
end

Then use it like

using .PTT
hint("1")

using .PTTGerman
hint("2")

set_language!(PTTGerman.German())
hint("3")
set_language!(PTTEnglish.EnglishUS())
hint("4")

That solution looks like the Julian way I didn’t see because of being too focused on macros. Will try to come up with a PR.

eford commented

That would be great!
With the benefit of a fresh look, I think I could have put both each of the modules inside the PTT module.

It's not clear to me if it's a good plan for using PlutoTeachingToolsGerman to automatically change the default stored in another module. Once we have the basic setup working with at least two languages, then we could put more thought in how to initialize the default language (e.g., based on ENV["LANG"]) to avoid the manual set_language!(...) step.

eford commented

@jonschumacher I created a basic preferred_text() in v0.2.3. You may be interested in trying it out and building on it.

Looks good. Thanks for the hint!