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
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.
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.
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.
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.
@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!