i18n support for Golang Web application HTML templates - go

Does anyone has a good idea to localize HTML templates in Golang Web application? Now I'm using Gin and go-i18n, but I will use other frameworks if they can localize.
If possible, I want to define the localized messages in property (or JSON/yaml/toml,...) files for each language:
label.password = パスワード # Password in Japanese
and write localized html like Thymeleaf:
<label th:text="#{label.password}"></label>

I chose to use i18next instead of go-i18n because the message keys can be embedded in HTML and replaced with language JSON files.

Spreak supports template localization by passing a localizer as template data.
It can also automatically extract the strings to be translated.
The process is similar to that of go-i18n.
Create your templates:
<label>{{.T.Get "Password"}}</label>
Load the translations:
bundle, err := spreak.NewBundle(
spreak.WithSourceLanguage(language.English),
// Set the path from which the translations should be loaded
spreak.WithDomainPath(spreak.NoDomain, "locale"),
// Specify the languages you want to load
spreak.WithLanguage(language.Japanese, language.German),
)
Create a localizer for a request and pass it as template data.
func(c *gin.Context) {
accept := c.GetHeader("Accept-Language")
localizer := spreak.NewLocalizer(bundle, accept)
c.HTML(http.StatusOK, "template.html", gin.H{
"T": localizer,
})
}
Use xspreak to extract the strings to be translated.
go install github.com/vorlif/xspreak#latest
xspreak -D path/to/code -o path/to/code/locale/base.pot --template-prefix "T" -t "templates/*.html"
A directory locale with a file base.pot is created.
The file is the template for new translations and contains the strings to be translated in po-format. The structure of the file follows the structure:
#: ../template.html:8
#, go-template
msgid "Password"
msgstr ""
For editing, it is best to use an editor like PoEdit, but there are also many good alternatives and online platforms.
Open the file base.pot with an editor and create your .po files with the translations.
In your example, this would be the file locale/ja.po with the structure
#: ../template.html:8
#, go-template
msgid "Password"
msgstr "パスワード"
Start the application and the matching translations will be used. The original text will be displayed if there are no matching translations.
I also created a full example using .i18n.Tr as translation method in the templates and using multiple languages.
Note: I am the author of spreak.

Related

json tag in .proto file

I am trying to generate go structs incl. json tags.
I can do:
string name = 1 [json_name="item.name, omitempty"];
But that only generates - json=item.name and only json:"name,omitempty"
Name string `protobuf:"bytes,1,opt,name=name,json=item.name, omitempty,proto3" json:"name,omitempty"`
But I need:- json:"item.name,omitempty" Note the tag is missing "item.".
And it looks like no matter what you put in json_name it is not reflected in "json:"
Du you know how to set json tags?
Not supported. The proto file json_name is not used to generate the code.
https://github.com/golang/protobuf/issues/52
I also thought it was a bug
https://github.com/golang/protobuf/issues/998

Golang templates won't load

I started to write a Gin application and my project tree looks like
-assets
--css
---{bootstrap}
-templates
--layouts
---footer.html
---head.html
---header.html
--book.html
-main.go
In main.go I load templates and there is no error
router.LoadHTMLGlob("./templates/layouts/*.html")
I define templates
{{ define "head" }}
<head>
//Head
</head>
{{ end }}
And I nest them
{{ define "header" }}
{{ template "head.html" . }}
//HTML
{{ end }}
But when I try to use them, I get empty output
{{ template "header" . }}
<h1>{{ .Title}}</h1>
<h3>{{ .Author.Fullname}}</h3>
[Edit] Function that executes the template:
func getBook(c *gin.Context) {
//DB stuff
var book models.Book
t, err := template.ParseFiles("templates/book.html")
if err != nil {
log.Println(err)
}
t.Execute(c.Writer, book)
}
Full-code can be found on github
router.LoadHTMLGlob and template.ParseFiles are two separate approaches to deal with templates. The template returned by ParseFiles has no knowledge of the templates loaded by LoadHTMLGlob. Once you decide to use LoadHTMLGlob you should then use c.HTML to render your templates. And the name argument to this c.HTML method would be either the name specified in a {{define "name"}} action or the base name of the template file (including the extention I believe).
So in your case you should probably do something like this:
c.HTML(http.StatusOK, "book.html", book)
More examples can be found here: https://gin-gonic.com/docs/examples/html-rendering/
Keep in mind that LoadHTMLGlob relies on template.ParseGlob which states:
When parsing multiple files with the same name in different
directories, the last one mentioned will be the one that results.
That means that if you want all of your templates to be accessible through c.HTML you need to make sure that they either have unique base names or they need to contain the {{ define "name"}} action.
Moving from the default templating system where 'everything simply worked' to Gin is a bit confusing, namely, there seem to be some naming restrictions when using files for templates. I have no idea if this is the case, but, in my setup, I had to make sure that:
The name of the template (the define keyword) needs to be the filename — at least when that's the only define in the template (I haven't tested with multiple defines) — i.e. if you're using ./templates/book.html as a template, you need to have {{ define "book.html" }} at the top of that file (this is true for templates included in other templates; I didn't experiment with blocks or other more esoteric ways to jinx templates together)
Similarly, when calling c.HTML(http.StatusOK, "book.html", book), you have to put the full name of the file containing that template (as shown!)
In other words, although the manual says otherwise, and #mkopriva confirms what the manual says, I have had a different experience: it was only I started matching the filename with the define and the c.HTML() call that I stopped getting blank pages...
Also, while running from the console (in debug mode), it was clear that my programme was 'finding' far more templates than it should — namely, almost every template was duplicated (one copy for the filename, another copy for the define, etc.). This confused not only me but the application itself...

Setting variable from file content in dockerize golang template

I want to use the jsonQuery syntax in dockerize to parse traefik's acme.json and emit cert/key files for TLS settings in another service.
jsonQuery accepts a string, which the example gives as an environment variable {{ .Env.myJson }}
How might I get the string contents of a file:
{{with $myJsonContent := <insert magic here> }}
# extract key to file
{{end}}
Go's text/template doesn't natively support that. It looks like the dockerize tool provides a couple of extension functions but none of them allow this either.
(The nearest thing I can think of is that kubernetes-helm supports reading a file from a Helm chart, but that's implemented at the Go level by injecting a special accessor object that can provides the file-access API to template code, and it's intentionally limited to files physically located within the Helm chart directory.)
I can't see any way to add a function to the template, as dockerize doesn't expose the addition of functions to the template prior to parsing. So you'll either have to (1) get the contents of acme.json into an environment variable, or (2) modify dockerize to include a jsonFileQuery function in the templates.
Add the contents of acme.json to the environment variables before running dockerize - then access as in the example. This could be done with a small go program, added to the container and run via CMD prior to the CMD dockerize
Fork dockerize and change jsonQuery: Fork dockerize and change line 83 of template.go:
from: parser, err := gojq.NewStringQuery(jsonObj)
to: parser, err := gojq.NewFileQuery(jsonObj)
Then use:
{{with $myJsonContent := jsonQuery "/opt/traefik/acme.json" "toplevelobject" }}
# extract key to file
{{end}}
gojq.NewStringQuery() is a function behind the jsonQuery template function. The gojq.NewFileQuery() version has the same signature as the StringQuery but reads the file at the path in the input string instead of using the input string as json.
OR
Merge new jsonFileQuery template function into dockerize: submit an issue to dockerize to add jsonFileQuery to the template functions. Seems like it could be set up the same as jsonQuery but with the small difference above. In template.go, add the jsonFileQuery function and assign it to jsonFileQuery in the template.FuncMap{} within generateFile().

How to use SassDoc as parser for single file without generating full documentation files

Question relates to http://sassdoc.com package
I would like to parse each *.scss file in ./source folder, but instead of generating sassdoc folder i would like to create partial-html for each parsed file. For example:
parse: variables.scss and receive variables.html, without page header, sidebar - pure content, even without html and body tags.
My current code:
var gulp = require('gulp'),
sassdoc = require('sassdoc');
var paths = {
scss: [
'source/**/*.scss'
]
};
gulp.task('sassdoc', function () {
console.log("sassdoc task finished");
return gulp.src(paths.scss)
.pipe(sassdoc());
});
It's not possible with SassDoc' default theme. So you'd need to build your own theme to acheive this.
http://sassdoc.com/using-your-own-theme
Each item is given a file key in resulting data, so I would leverage that and do some merging.
That could potentially end up in a sassdoc-extra custom filter.
http://sassdoc.com/extra-tools
EDIT:
Actually your question is quite misleanding, you want a variable.html file but with no html ...
If all that you want is the raw JSON data from SassDoc, without any kind of theme processing, then the parse method is what you're looking for.
But again, unless you call SassDoc on each file separately, you'll get all files together, meaning post data processing to split them, that's why a custom theme (even with no html output) is the way to go.

How to set locale for errors.po?

How to set a locale in Phoenix, to get to use priv/gettext/{lang}/LC_MESSAGES/errors.po?
As a test, I built the locale file with mix gettext.merge priv/gettext --locale ja, and translated some words in it.
It works if I explicitly call put_locale/2 in web/views/error_helpers.ex and <%= translate_error(message) %> in a template file, but it's a bit ugly, in a point of DRY.
def translate_error(msg) do
Gettext.put_locale(LoginStudy.Gettext, "ja")
Gettext.dgettext(LoginStudy.Gettext, "errors", msg)
end
Is there any better way to set the default locale? I specified the default_locale in config/config.ex, but it doesn't work.
config :login_study, LoginStudy.Endpoint, default_locale: "ja",
Best Regards,
Great questions #hykw! Since Gettext support in Phoenix is new, documentation is just now starting to show up.
A good starting point is this excellent blog post by Rebecca Skinner: http://sevenseacat.net/2015/12/20/i18n-in-phoenix-apps.html
For example, if you want to set the locale to japanese only for some of your web requests, you can define a plug, as she did with MyApp.Locale, and run it at the beginning of your request lifecycle. I just don't advise to store the locale in the session but keep it rather as part of the URL or some other parameter.
However, if you want the locale to always be japanese, you can write in your config file:
config :my_app, MyApp.Gettext, default_locale: "ja"
You can find more information about this on the Gettext docs: http://hexdocs.pm/gettext/Gettext.html

Resources