From cc6f670815f4b6d5372afad9b00b2813cfbf89a8 Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Tue, 3 May 2022 21:05:21 +0900 Subject: [PATCH 1/2] fixed template renderer to make correct aliases especially for lang files --- render/template.go | 18 +------ render/template_test.go | 115 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 16 deletions(-) diff --git a/render/template.go b/render/template.go index 370e8d5f5..1aaec4a66 100644 --- a/render/template.go +++ b/render/template.go @@ -79,22 +79,8 @@ func (s *templateRenderer) updateAliases() error { return nil } - base := filepath.Base(path) - dir := filepath.Dir(path) - - var exts []string - sep := strings.Split(base, ".") - if len(sep) >= 1 { - base = sep[0] - } - if len(sep) > 1 { - exts = sep[1:] - } - - for _, ext := range exts { - pn := filepath.Join(dir, base+"."+ext) - s.aliases.Store(pn, path) - } + shortcut := strings.Replace(path, ".plush.", ".", 1) + s.aliases.Store(shortcut, path) return nil }) diff --git a/render/template_test.go b/render/template_test.go index 1c7f66676..5ff80adff 100644 --- a/render/template_test.go +++ b/render/template_test.go @@ -105,3 +105,118 @@ func Test_AssetPathNoManifestCorrupt(t *testing.T) { r.NotEqual(expected, strings.TrimSpace(bb.String())) } } + +/* test if i18n files (both with plush mid-extension and latecy) proceeded correctly. + */ +func Test_Template_resolve_DefaultLang_Plush(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.plush.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"es", "en"}})) + r.Equal("default Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_UserLang_Plush(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.plush.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko-KR", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_DefaultLang_Legacy(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"es", "en"}})) + r.Equal("default Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_UserLang_Legacy(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko-KR", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_DefaultLang_Mixed(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + // `buffalo fix` renames templates but does not fix actions + // in this case, aliases will be used for template matching + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"es", "en"}})) + r.Equal("default Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_UserLang_Mixed(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + // `buffalo fix` renames templates but does not fix actions + // in this case, aliases will be used for template matching + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko-KR", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + From 4b5b29e0a162dab953401f242cc46d9096ca9412 Mon Sep 17 00:00:00 2001 From: Yonghwan SO Date: Thu, 5 May 2022 20:49:59 +0900 Subject: [PATCH 2/2] reduce function calls, also support short locale such as 'ko' --- render/template.go | 67 +++++++++++++++++++++---- render/template_test.go | 108 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 9 deletions(-) diff --git a/render/template.go b/render/template.go index 1aaec4a66..e0fc62d1b 100644 --- a/render/template.go +++ b/render/template.go @@ -12,6 +12,7 @@ import ( "sync" "github.com/sirupsen/logrus" + "golang.org/x/text/language" ) type templateRenderer struct { @@ -79,9 +80,30 @@ func (s *templateRenderer) updateAliases() error { return nil } + // index.plush.ko-kr.html as index.ko-kr.html shortcut := strings.Replace(path, ".plush.", ".", 1) s.aliases.Store(shortcut, path) + // register short version (lang only) of shortcut + words := strings.Split(filepath.Base(shortcut), ".") + if len(words) > 2 { + for _, w := range words[1 : len(words)-1] { + if tag, err := language.Parse(w); err == nil { + base, confidence := tag.Base() + if confidence == language.Exact || confidence == language.High { + // index.plush.ko-kr.html as index.ko.html + shortcut := strings.Replace(shortcut, w, base.String(), 1) + s.aliases.Store(shortcut, path) + + // index.plush.ko-kr.html as index.plush.ko.html + shortcut = strings.Replace(path, w, base.String(), 1) + s.aliases.Store(shortcut, path) + } + } + + } + } + return nil }) } @@ -120,15 +142,7 @@ func (s *templateRenderer) exec(name string, data Data) (template.HTML, error) { name = fixExtension(name, ct) - // Try to use localized version - templateName := s.localizedName(name, data) - - // Set current_template to context - if _, ok := data["current_template"]; !ok { - data["current_template"] = templateName - } - - source, err := s.resolve(templateName) + source, err := s.localizedResolve(name, data) if err != nil { return "", err } @@ -162,6 +176,7 @@ func (s *templateRenderer) exec(name string, data Data) (template.HTML, error) { return template.HTML(body), nil } +// next step, deprecate if this is no longer required. func (s *templateRenderer) localizedName(name string, data Data) string { templateName := name @@ -193,6 +208,40 @@ func (s *templateRenderer) localizedName(name string, data Data) string { return templateName } +func (s *templateRenderer) localizedResolve(name string, data Data) ([]byte, error) { + languages, ok := data["languages"].([]string) + if !ok || len(languages) == 0 { + return s.resolve(name) + } + + defaultLang := languages[len(languages)-1] // default language + ext := filepath.Ext(name) + rawName := strings.TrimSuffix(name, ext) + + for _, lang := range languages { + if lang == defaultLang { + break + } + + fullLower := strings.ToLower(lang) // forms of ko-kr or ko + short := strings.Split(fullLower, "-")[0] // form of ko + + fullLocale := rawName + "." + fullLower + ext + if source, err := s.resolve(fullLocale); err == nil { + return source, nil + } + + if fullLower != short { + langOnly := rawName + "." + short + ext + if source, err := s.resolve(langOnly); err == nil { + return source, nil + } + } + } + + return s.resolve(name) +} + func (s *templateRenderer) exts(name string) []string { exts := []string{} for { diff --git a/render/template_test.go b/render/template_test.go index 5ff80adff..b1e364fdd 100644 --- a/render/template_test.go +++ b/render/template_test.go @@ -220,3 +220,111 @@ func Test_Template_resolve_UserLang_Mixed(t *testing.T) { r.Equal("korean Paul", strings.TrimSpace(bb.String())) } +// support short language-only version of template e.g. index.plush.ko.html +func Test_Template_resolve_FullLocale_ShortFile(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.plush.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko-KR", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_LangOnly_FullFile(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.plush.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_FullLocale_ShortFile_Legacy(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.ko.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko-KR", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_LangOnly_FullFile_Legacy(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_FullLocale_ShortFile_Mixed(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko-KR", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +} + +func Test_Template_resolve_LangOnly_FullFile_Mixed(t *testing.T) { + r := require.New(t) + + rootFS := memfs.New() + r.NoError(rootFS.WriteFile("index.plush.html", []byte("default <%= name %>"), 0644)) + r.NoError(rootFS.WriteFile("index.plush.ko-kr.html", []byte("korean <%= name %>"), 0644)) + + e := NewEngine() + e.TemplatesFS = rootFS + + re := e.Template("foo/bar", "index.html") + r.Equal("foo/bar", re.ContentType()) + + bb := &bytes.Buffer{} + r.NoError(re.Render(bb, Data{"name": "Paul", "languages": []string{"ko", "en"}})) + r.Equal("korean Paul", strings.TrimSpace(bb.String())) +}