1
0

Introduce i18n. Теперь переведу сайт на русский!

This commit is contained in:
Alexander Andreev 2024-09-14 18:34:22 +04:00
parent 4b351ed7a9
commit 50ba6bb74b
Signed by: Arav
GPG Key ID: 25969B23DCB5CA34
5 changed files with 98 additions and 13 deletions

View File

@ -15,8 +15,10 @@ import (
"git.arav.su/Arav/dwelling-home/internal/version" "git.arav.su/Arav/dwelling-home/internal/version"
mfsqlite "git.arav.su/Arav/dwelling-home/pkg/mindflow/database/sqlite" mfsqlite "git.arav.su/Arav/dwelling-home/pkg/mindflow/database/sqlite"
"git.arav.su/Arav/dwelling-home/web" "git.arav.su/Arav/dwelling-home/web"
"git.arav.su/Arav/dwelling-home/web/locales"
"git.arav.su/Arav/httpr" "git.arav.su/Arav/httpr"
gb "git.arav.su/Arav/justguestbook" gb "git.arav.su/Arav/justguestbook"
"github.com/invopop/ctxi18n"
) )
var ( var (
@ -50,6 +52,10 @@ func main() {
} }
defer mindflowDB.Close() defer mindflowDB.Close()
if err := ctxi18n.LoadWithDefault(locales.Content, "en"); err != nil {
log.Fatalln("Failed to load i18n:", err)
}
hand := dwhttp.NewHandlers(*captchaExpiry, owner, *guestbookPageSize, guestbookDB, mindflowDB) hand := dwhttp.NewHandlers(*captchaExpiry, owner, *guestbookPageSize, guestbookDB, mindflowDB)
r := httpr.New() r := httpr.New()
@ -101,7 +107,7 @@ func main() {
s.Handler(http.MethodPatch, "/category/:id", mindflowApi.EditCategory) s.Handler(http.MethodPatch, "/category/:id", mindflowApi.EditCategory)
s.Handler(http.MethodDelete, "/category/:id", mindflowApi.DeleteCategory) s.Handler(http.MethodDelete, "/category/:id", mindflowApi.DeleteCategory)
srv := dwhttp.NewHttpServer(r) srv := dwhttp.NewHttpServer(I18nMiddleware(r))
if err := srv.Start(*listenAddress); err != nil { if err := srv.Start(*listenAddress); err != nil {
log.Fatalln(err) log.Fatalln(err)
@ -117,3 +123,13 @@ func main() {
<-doneSignal <-doneSignal
} }
func I18nMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx, err := ctxi18n.WithLocale(r.Context(), r.Header.Get("Accept-Language"))
if err != nil {
log.Println("i18nmw:", err)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}

View File

@ -1,7 +1,6 @@
package http package http
import ( import (
"context"
"log" "log"
"math" "math"
"net/http" "net/http"
@ -36,15 +35,15 @@ func NewHandlers(captchaExpire time.Duration, owner string, gbPageSize int64,
} }
func (h *Handlers) Index(w http.ResponseWriter, r *http.Request) { func (h *Handlers) Index(w http.ResponseWriter, r *http.Request) {
web.Index(r).Render(context.Background(), w) web.Index(r).Render(r.Context(), w)
} }
func (h *Handlers) Privacy(w http.ResponseWriter, r *http.Request) { func (h *Handlers) Privacy(w http.ResponseWriter, r *http.Request) {
web.Privacy(r).Render(context.Background(), w) web.Privacy(r).Render(r.Context(), w)
} }
func (h *Handlers) Stuff(w http.ResponseWriter, r *http.Request) { func (h *Handlers) Stuff(w http.ResponseWriter, r *http.Request) {
web.Stuff(r).Render(context.Background(), w) web.Stuff(r).Render(r.Context(), w)
} }
func (h *Handlers) Mindflow(w http.ResponseWriter, r *http.Request) { func (h *Handlers) Mindflow(w http.ResponseWriter, r *http.Request) {
@ -62,7 +61,7 @@ func (h *Handlers) Mindflow(w http.ResponseWriter, r *http.Request) {
return return
} }
web.Mindflow(posts, categories, r).Render(context.Background(), w) web.Mindflow(posts, categories, r).Render(r.Context(), w)
} }
func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) { func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) {
@ -78,7 +77,7 @@ func (h *Handlers) MindflowAdmin(w http.ResponseWriter, r *http.Request) {
return return
} }
web.MindflowAdmin(posts, categories, r).Render(context.Background(), w) web.MindflowAdmin(posts, categories, r).Render(r.Context(), w)
} }
func (h *Handlers) About(w http.ResponseWriter, r *http.Request) { func (h *Handlers) About(w http.ResponseWriter, r *http.Request) {
@ -96,7 +95,7 @@ func (h *Handlers) About(w http.ResponseWriter, r *http.Request) {
} }
} }
web.About(&lst, r).Render(context.Background(), w) web.About(&lst, r).Render(r.Context(), w)
} }
func (h *Handlers) Article(w http.ResponseWriter, r *http.Request) { func (h *Handlers) Article(w http.ResponseWriter, r *http.Request) {
@ -107,7 +106,7 @@ func (h *Handlers) Article(w http.ResponseWriter, r *http.Request) {
return return
} }
web.Article(artcl.Title, artcl.Description, string(artcl.Body), name, artcl.Date, r).Render(context.Background(), w) web.Article(artcl.Title, artcl.Description, string(artcl.Body), name, artcl.Date, r).Render(r.Context(), w)
} }
func (h *Handlers) Guestbook(w http.ResponseWriter, r *http.Request) { func (h *Handlers) Guestbook(w http.ResponseWriter, r *http.Request) {
@ -144,7 +143,7 @@ func (h *Handlers) Guestbook(w http.ResponseWriter, r *http.Request) {
dwc := dwcaptcha.NewDwellingCaptcha(h.captchaExpire) dwc := dwcaptcha.NewDwellingCaptcha(h.captchaExpire)
_, id := inmemdb.New(r.RemoteAddr, dwc) _, id := inmemdb.New(r.RemoteAddr, dwc)
web.Guestbook(string(id), h.owner, entries, pageCount, page, r).Render(context.Background(), w) web.Guestbook(string(id), h.owner, entries, pageCount, page, r).Render(r.Context(), w)
} }
func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) { func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) {
@ -158,7 +157,7 @@ func (h *Handlers) GuestbookAdmin(w http.ResponseWriter, r *http.Request) {
entry.Message = strings.ReplaceAll(entry.Message, "\\n", "\n") entry.Message = strings.ReplaceAll(entry.Message, "\\n", "\n")
} }
web.GuestbookAdmin(h.owner, entries, r).Render(context.Background(), w) web.GuestbookAdmin(h.owner, entries, r).Render(r.Context(), w)
} }
func (h *Handlers) RSS(w http.ResponseWriter, r *http.Request) { func (h *Handlers) RSS(w http.ResponseWriter, r *http.Request) {
@ -172,7 +171,7 @@ func (h *Handlers) RSS(w http.ResponseWriter, r *http.Request) {
scheme = "http" scheme = "http"
} }
web.RSS(scheme+"://"+r.Host, h.owner, posts, r).Render(context.Background(), w) web.RSS(scheme+"://"+r.Host, h.owner, posts, r).Render(r.Context(), w)
} }
func ServeAsset(path string) func(http.ResponseWriter, *http.Request) { func ServeAsset(path string) func(http.ResponseWriter, *http.Request) {
@ -184,7 +183,7 @@ func ServeAsset(path string) func(http.ResponseWriter, *http.Request) {
func Error(w http.ResponseWriter, code int, reason, message string, r *http.Request) { func Error(w http.ResponseWriter, code int, reason, message string, r *http.Request) {
w.WriteHeader(code) w.WriteHeader(code)
web.ErrorXXX(code, reason, message, r).Render(context.Background(), w) web.ErrorXXX(code, reason, message, r).Render(r.Context(), w)
} }
func cleanupNewlines(text string) (out string) { func cleanupNewlines(text string) (out string) {

30
web/locales/en/en.yaml Normal file
View File

@ -0,0 +1,30 @@
en:
base:
rss-feed: "RSS"
rss-feed-title: "Stay up to date on what's going on."
copy-author: "Alexander \"Arav\" Andreev"
privacy-statements-link: "Privacy statements"
menu:
home: "Home"
stuff: "Stuff"
mindflow: "Mindflow"
about: "About"
guestbook: "Guestbook"
privacy: "Privacy"
article:
go-back: "Back to a list"
guestbook:
description: "This is my guestbook. Welcome."
form:
name: "Name (Anonymous if left blank)"
website: "Website (optional)"
message: "Your message"
hide-website: "Hide website <small>(only I can see if set)</small>"
quote-usage: "Use > to make a quote."
captcha-validity: "Valid for <b>10</b> minutes."
send-post: "Send"
post:
by: "by"
on: "on"
reply: "Reply by"
no-posts: "No posts."

9
web/locales/locales.go Normal file
View File

@ -0,0 +1,9 @@
package locales
import (
"embed"
)
//go:embed en
//go:embed ru
var Content embed.FS

31
web/locales/ru/ru.yaml Normal file
View File

@ -0,0 +1,31 @@
ru:
base:
rss-feed: "RSS"
rss-feed-title: "Будь в курсе происходящего."
copy-author: "Александр «Arav» Андреев"
privacy-statements-link: "О приватности"
section:
home: "Домой"
stuff: "Всякое"
mindflow: "Блог"
about: "О..."
guestbook: "Гостевая"
privacy: "Приватность"
article:
go-back: "К списку"
guestbook:
description: "Моя гостевая. Добро пожаловать."
form:
name: "Имя (Аноним если оставить пустым)"
website: "Вебсайт (необязательно)"
message: "Твоё сообщение"
hide-website-1: "Скрыть вебсайт"
hide-website-2: "(будет видно только мне)"
quote-usage: "Используй &gt; для цитаты."
captcha-validity: "Действительна <b>10</b> минут."
send-post: "Отправить"
post:
by: "от"
on: " "
reply: "Ответ"
no-posts: "Нет постов."