aboutsummaryrefslogtreecommitdiffstats
path: root/routers
diff options
context:
space:
mode:
authorGary Wang <wzc782970009@gmail.com>2022-08-01 06:36:58 +0800
committerGitHub <noreply@github.com>2022-08-01 01:36:58 +0300
commitc35535ce071c221fa0e529b89c3df462df769377 (patch)
treea52e3dd8847da9553bba5b27d7518ae9005d629a /routers
parent335e918b1129ef6e0dd03a4713de90e68fbb23e3 (diff)
downloadgitea-c35535ce071c221fa0e529b89c3df462df769377.tar.gz
gitea-c35535ce071c221fa0e529b89c3df462df769377.zip
Support localized README (#20508)
* Support localized README * Slightly simplify getting the readme file and add some tests. Ensure that i18n also works for docs/ etc. Signed-off-by: Andrew Thornton <art27@cantab.net> * Update modules/markup/renderer.go * Update modules/markup/renderer.go * Update modules/markup/renderer.go Co-authored-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'routers')
-rw-r--r--routers/web/repo/view.go123
-rw-r--r--routers/web/repo/view_test.go63
2 files changed, 117 insertions, 69 deletions
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index c5657aa675..6a9c6b9bba 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -57,7 +57,7 @@ type namedBlob struct {
}
// FIXME: There has to be a more efficient way of doing this
-func getReadmeFileFromPath(commit *git.Commit, treePath string) (*namedBlob, error) {
+func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath string) (*namedBlob, error) {
tree, err := commit.SubTree(treePath)
if err != nil {
return nil, err
@@ -68,50 +68,33 @@ func getReadmeFileFromPath(commit *git.Commit, treePath string) (*namedBlob, err
return nil, err
}
- var readmeFiles [4]*namedBlob
- exts := []string{".md", ".txt", ""} // sorted by priority
+ // Create a list of extensions in priority order
+ // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
+ // 2. Txt files - e.g. README.txt
+ // 3. No extension - e.g. README
+ exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority
+ extCount := len(exts)
+ readmeFiles := make([]*namedBlob, extCount+1)
for _, entry := range entries {
if entry.IsDir() {
continue
}
- for i, ext := range exts {
- if markup.IsReadmeFile(entry.Name(), ext) {
- if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) {
- name := entry.Name()
- isSymlink := entry.IsLink()
- target := entry
- if isSymlink {
- target, err = entry.FollowLinks()
- if err != nil && !git.IsErrBadLink(err) {
- return nil, err
- }
- }
- if target != nil && (target.IsExecutable() || target.IsRegular()) {
- readmeFiles[i] = &namedBlob{
- name,
- isSymlink,
- target.Blob(),
- }
- }
- }
- }
- }
-
- if markup.IsReadmeFile(entry.Name()) {
- if readmeFiles[3] == nil || base.NaturalSortLess(readmeFiles[3].name, entry.Blob().Name()) {
+ if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok {
+ if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) {
name := entry.Name()
isSymlink := entry.IsLink()
+ target := entry
if isSymlink {
- entry, err = entry.FollowLinks()
+ target, err = entry.FollowLinks()
if err != nil && !git.IsErrBadLink(err) {
return nil, err
}
}
- if entry != nil && (entry.IsExecutable() || entry.IsRegular()) {
- readmeFiles[3] = &namedBlob{
+ if target != nil && (target.IsExecutable() || target.IsRegular()) {
+ readmeFiles[i] = &namedBlob{
name,
isSymlink,
- entry.Blob(),
+ target.Blob(),
}
}
}
@@ -151,13 +134,38 @@ func renderDirectory(ctx *context.Context, treeLink string) {
renderReadmeFile(ctx, readmeFile, readmeTreelink)
}
+// localizedExtensions prepends the provided language code with and without a
+// regional identifier to the provided extenstion.
+// Note: the language code will always be lower-cased, if a region is present it must be separated with a `-`
+// Note: ext should be prefixed with a `.`
+func localizedExtensions(ext, languageCode string) (localizedExts []string) {
+ if len(languageCode) < 1 {
+ return []string{ext}
+ }
+
+ lowerLangCode := "." + strings.ToLower(languageCode)
+
+ if strings.Contains(lowerLangCode, "-") {
+ underscoreLangCode := strings.ReplaceAll(lowerLangCode, "-", "_")
+ indexOfDash := strings.Index(lowerLangCode, "-")
+ // e.g. [.zh-cn.md, .zh_cn.md, .zh.md, .md]
+ return []string{lowerLangCode + ext, underscoreLangCode + ext, lowerLangCode[:indexOfDash] + ext, ext}
+ }
+
+ // e.g. [.en.md, .md]
+ return []string{lowerLangCode + ext, ext}
+}
+
func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) (*namedBlob, string) {
- // 3 for the extensions in exts[] in order
- // the last one is for a readme that doesn't
- // strictly match an extension
- var readmeFiles [4]*namedBlob
- var docsEntries [3]*git.TreeEntry
- exts := []string{".md", ".txt", ""} // sorted by priority
+ // Create a list of extensions in priority order
+ // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
+ // 2. Txt files - e.g. README.txt
+ // 3. No extension - e.g. README
+ exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority
+ extCount := len(exts)
+ readmeFiles := make([]*namedBlob, extCount+1)
+
+ docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/)
for _, entry := range entries {
if entry.IsDir() {
lowerName := strings.ToLower(entry.Name())
@@ -178,47 +186,24 @@ func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string)
continue
}
- for i, ext := range exts {
- if markup.IsReadmeFile(entry.Name(), ext) {
- log.Debug("%s", entry.Name())
- name := entry.Name()
- isSymlink := entry.IsLink()
- target := entry
- if isSymlink {
- var err error
- target, err = entry.FollowLinks()
- if err != nil && !git.IsErrBadLink(err) {
- ctx.ServerError("FollowLinks", err)
- return nil, ""
- }
- }
- log.Debug("%t", target == nil)
- if target != nil && (target.IsExecutable() || target.IsRegular()) {
- readmeFiles[i] = &namedBlob{
- name,
- isSymlink,
- target.Blob(),
- }
- }
- }
- }
-
- if markup.IsReadmeFile(entry.Name()) {
+ if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok {
+ log.Debug("Potential readme file: %s", entry.Name())
name := entry.Name()
isSymlink := entry.IsLink()
+ target := entry
if isSymlink {
var err error
- entry, err = entry.FollowLinks()
+ target, err = entry.FollowLinks()
if err != nil && !git.IsErrBadLink(err) {
ctx.ServerError("FollowLinks", err)
return nil, ""
}
}
- if entry != nil && (entry.IsExecutable() || entry.IsRegular()) {
- readmeFiles[3] = &namedBlob{
+ if target != nil && (target.IsExecutable() || target.IsRegular()) {
+ readmeFiles[i] = &namedBlob{
name,
isSymlink,
- entry.Blob(),
+ target.Blob(),
}
}
}
@@ -239,7 +224,7 @@ func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string)
continue
}
var err error
- readmeFile, err = getReadmeFileFromPath(ctx.Repo.Commit, entry.GetSubJumpablePathName())
+ readmeFile, err = getReadmeFileFromPath(ctx, ctx.Repo.Commit, entry.GetSubJumpablePathName())
if err != nil {
ctx.ServerError("getReadmeFileFromPath", err)
return nil, ""
diff --git a/routers/web/repo/view_test.go b/routers/web/repo/view_test.go
new file mode 100644
index 0000000000..9d5a88fca4
--- /dev/null
+++ b/routers/web/repo/view_test.go
@@ -0,0 +1,63 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "reflect"
+ "testing"
+)
+
+func Test_localizedExtensions(t *testing.T) {
+ tests := []struct {
+ name string
+ ext string
+ languageCode string
+ wantLocalizedExts []string
+ }{
+ {
+ name: "empty language",
+ ext: ".md",
+ wantLocalizedExts: []string{".md"},
+ },
+ {
+ name: "No region - lowercase",
+ languageCode: "en",
+ ext: ".csv",
+ wantLocalizedExts: []string{".en.csv", ".csv"},
+ },
+ {
+ name: "No region - uppercase",
+ languageCode: "FR",
+ ext: ".txt",
+ wantLocalizedExts: []string{".fr.txt", ".txt"},
+ },
+ {
+ name: "With region - lowercase",
+ languageCode: "en-us",
+ ext: ".md",
+ wantLocalizedExts: []string{".en-us.md", ".en_us.md", ".en.md", ".md"},
+ },
+ {
+ name: "With region - uppercase",
+ languageCode: "en-CA",
+ ext: ".MD",
+ wantLocalizedExts: []string{".en-ca.MD", ".en_ca.MD", ".en.MD", ".MD"},
+ },
+ {
+ name: "With region - all uppercase",
+ languageCode: "ZH-TW",
+ ext: ".md",
+ wantLocalizedExts: []string{".zh-tw.md", ".zh_tw.md", ".zh.md", ".md"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if gotLocalizedExts := localizedExtensions(tt.ext, tt.languageCode); !reflect.DeepEqual(gotLocalizedExts, tt.wantLocalizedExts) {
+ t.Errorf("localizedExtensions() = %v, want %v", gotLocalizedExts, tt.wantLocalizedExts)
+ }
+ })
+ }
+}