Skip to content

Commit fe1fd27

Browse files
committed
feat: add translation mechanism for file list page
- detect client preferred language by request header `Accept-Language` - try to find a suitable language according to `Accept-Language` - attach chosen language translation to response data structure - output translation field instead of plain text in html template
1 parent 2e26dd4 commit fe1fd27

File tree

6 files changed

+171
-17
lines changed

6 files changed

+171
-17
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ root = true
33
[*]
44
end_of_line = lf
55
insert_final_newline = true
6-
max_line_length = 140
6+
max_line_length = 200
77
indent_style = tab
88
indent_size = 4
99
charset = utf-8

src/i18n/dictionary.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package i18n
2+
3+
type Dictionary struct {
4+
Lang string
5+
Trans *Translation
6+
}
7+
8+
var Dictionaries = [...]Dictionary{
9+
{"en-us", &translationEnUs},
10+
{"en", &translationEnUs},
11+
{"zh-cn", &translationZhSimp},
12+
{"zh-tw", &translationZhTrad},
13+
{"zh-hk", &translationZhTrad},
14+
{"zh", &translationZhSimp},
15+
}
16+
17+
var LanguageTags []string
18+
19+
func init() {
20+
count := len(Dictionaries)
21+
LanguageTags = make([]string, count)
22+
for i := 0; i < count; i++ {
23+
LanguageTags[i] = Dictionaries[i].Lang
24+
}
25+
}

src/i18n/translation.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package i18n
2+
3+
type Translation struct {
4+
MkdirLabel string
5+
6+
UploadFilesLabel string
7+
UploadFilesHint string
8+
UploadDirLabel string
9+
UploadDirHint string
10+
UploadDirContentsLabel string
11+
UploadDirContentsHint string
12+
UploadLabel string
13+
UploadingLabel string
14+
15+
ListDirLabel string
16+
ListNameLabel string
17+
ListTypeLabel string
18+
ListSizeLabel string
19+
ListTimeLabel string
20+
21+
FilterLabel string
22+
23+
DeleteConfirm string
24+
25+
Error403 string
26+
Error404 string
27+
Error500 string
28+
}
29+
30+
var translationEnUs = Translation{
31+
MkdirLabel: "Create dir",
32+
33+
UploadFilesLabel: "Files",
34+
UploadFilesHint: "Upload files",
35+
UploadDirLabel: "Dir",
36+
UploadDirHint: "Upload Directory itself",
37+
UploadDirContentsLabel: "Dir contents",
38+
UploadDirContentsHint: "Upload contents of directory",
39+
UploadLabel: "Upload",
40+
UploadingLabel: "Uploading...",
41+
42+
ListDirLabel: "Dir",
43+
ListNameLabel: "Name",
44+
ListTypeLabel: "Type",
45+
ListSizeLabel: "Size",
46+
ListTimeLabel: "Time",
47+
48+
FilterLabel: "filter...",
49+
50+
DeleteConfirm: "Delete?",
51+
52+
Error403: "403 resource is forbidden",
53+
Error404: "404 resource not found",
54+
Error500: "500 potential issue occurred",
55+
}
56+
57+
var translationZhSimp = Translation{
58+
MkdirLabel: "建目录",
59+
60+
UploadFilesLabel: "文件",
61+
UploadFilesHint: "上传文件",
62+
UploadDirLabel: "目录",
63+
UploadDirHint: "上传目录自身",
64+
UploadDirContentsLabel: "目录内容",
65+
UploadDirContentsHint: "上传目录下的内容",
66+
UploadLabel: "上传",
67+
UploadingLabel: "上传中……",
68+
69+
ListDirLabel: "目录",
70+
ListNameLabel: "名称",
71+
ListTypeLabel: "类型",
72+
ListSizeLabel: "大小",
73+
ListTimeLabel: "时间",
74+
75+
FilterLabel: "筛选……",
76+
77+
DeleteConfirm: "确认删除吗?",
78+
79+
Error403: "403 禁止访问资源",
80+
Error404: "404 资源不存在",
81+
Error500: "500 发生潜在错误",
82+
}
83+
84+
var translationZhTrad = Translation{
85+
MkdirLabel: "建目錄",
86+
87+
UploadFilesLabel: "檔案",
88+
UploadFilesHint: "上傳檔案",
89+
UploadDirLabel: "目錄",
90+
UploadDirHint: "上傳目錄自身",
91+
UploadDirContentsLabel: "目錄內容",
92+
UploadDirContentsHint: "上傳目錄下的內容",
93+
UploadLabel: "上傳",
94+
UploadingLabel: "上傳中……",
95+
96+
ListDirLabel: "目錄",
97+
ListNameLabel: "名稱",
98+
ListTypeLabel: "類型",
99+
ListSizeLabel: "大小",
100+
ListTimeLabel: "時間",
101+
102+
FilterLabel: "篩選……",
103+
104+
DeleteConfirm: "確認刪除嗎?",
105+
106+
Error403: "403 禁止訪問資源",
107+
Error404: "404 資源不存在",
108+
Error500: "500 發生潛在錯誤",
109+
}

src/serverHandler/page.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package serverHandler
22

33
import (
4+
"../acceptHeaders"
5+
"../i18n"
46
tplutil "../tpl/util"
7+
"../util"
58
"html/template"
69
"io"
710
"net/http"
@@ -55,12 +58,25 @@ func updateSubItemsHtml(data *responseData) {
5558
}
5659
}
5760

61+
func updateTranslation(r *http.Request, data *responseData) {
62+
accepts := acceptHeaders.ParseAccepts(util.AsciiToLowerCase(r.Header.Get("Accept-Language")))
63+
index, _, ok := accepts.GetPreferredValue(i18n.LanguageTags)
64+
if !ok {
65+
index = 0
66+
}
67+
data.Lang = i18n.LanguageTags[index]
68+
data.Trans = i18n.Dictionaries[index].Trans
69+
}
70+
5871
func (h *handler) page(w http.ResponseWriter, r *http.Request, data *responseData) {
5972
header := w.Header()
6073
header.Set("X-Content-Type-Options", "nosniff")
6174
header.Set("Content-Type", "text/html; charset=utf-8")
6275
header.Set("Cache-Control", "public, max-age=0")
6376

77+
updateTranslation(r, data)
78+
header.Set("Content-Language", data.Lang)
79+
6480
if !needResponseBody(r.Method) {
6581
w.WriteHeader(data.Status)
6682
return

src/serverHandler/responseData.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package serverHandler
22

33
import (
4+
"../i18n"
45
"../util"
56
"html/template"
67
"net/http"
@@ -59,6 +60,9 @@ type responseData struct {
5960
IsDelete bool
6061
IsMutate bool
6162
WantJson bool
63+
64+
Lang string
65+
Trans *i18n.Translation
6266
}
6367

6468
func isSlash(c rune) bool {

src/tpl/frontend/index.html

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!DOCTYPE html>
2-
<html lang="">
2+
<html lang="{{.Lang}}">
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
55
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
@@ -24,23 +24,23 @@
2424
<div class="panel mkdir">
2525
<form method="POST" action="{{.SubItemPrefix}}?mkdir">
2626
<input type="text" autocomplete="off" name="name" class="name"/>
27-
<input type="submit" value="mkdir" class="submit"/>
27+
<input type="submit" value="{{.Trans.MkdirLabel}}" class="submit"/>
2828
</form>
2929
</div>
3030
{{end}}
3131

3232
{{if .CanUpload}}
3333
<div class="tab upload-type">
34-
<label class="file active" tabindex="0" role="button" title="Upload files">Files</label>
35-
{{if .CanMkdir}}<label class="dirfile hidden" tabindex="0" role="button" title="Upload Directory itself">Dir</label>{{end}}
36-
<label class="innerdirfile hidden" tabindex="0" role="button" title="Upload contents of directory">Dir contents</label>
34+
<label class="file active" tabindex="0" role="button" title="{{.Trans.UploadFilesHint}}">{{.Trans.UploadFilesLabel}}</label>
35+
{{if .CanMkdir}}<label class="dirfile hidden" tabindex="0" role="button" title="{{.Trans.UploadDirHint}}">{{.Trans.UploadDirLabel}}</label>{{end}}
36+
<label class="innerdirfile hidden" tabindex="0" role="button" title="{{.Trans.UploadDirContentsHint}}">{{.Trans.UploadDirContentsLabel}}</label>
3737
</div>
3838
<div class="panel upload">
3939
<form method="POST" action="{{.SubItemPrefix}}?upload" enctype="multipart/form-data">
4040
<input type="file" name="file" multiple="multiple" class="file"/>
4141
<button type="submit" class="submit">
4242
<span class="progress"></span>
43-
<span class="if-enabled">Upload</span><span class="if-disabled">Uploading ...</span>
43+
<span class="if-enabled">{{.Trans.UploadLabel}}</span><span class="if-disabled">{{.Trans.UploadingLabel}}</span>
4444
</button>
4545
</form>
4646
</div>
@@ -57,7 +57,7 @@
5757
{{if .SubItemsHtml}}
5858
<div class="panel filter" id="panel-filter">
5959
<div class="form">
60-
<input type="text" accesskey="r" placeholder="filter..." name="filter-text" class="filter-text"/>
60+
<input type="text" accesskey="r" placeholder="{{.Trans.FilterLabel}}" name="filter-text" class="filter-text"/>
6161
</div>
6262
</div>
6363
{{end}}
@@ -67,7 +67,7 @@
6767
function confirmDelete(el) {
6868
var href = el.href;
6969
var name = decodeURIComponent(href.substr(href.lastIndexOf('=') + 1));
70-
return confirm('Delete?\n' + name);
70+
return confirm('{{.Trans.DeleteConfirm}}\n' + name);
7171
}
7272
</script>
7373
{{end}}
@@ -76,11 +76,11 @@
7676
{{if not .IsDownload}}
7777
<li class="header">{{$dirSort := .SortState.DirSort}}{{$sortKey := .SortState.Key}}
7878
<span class="detail">
79-
<a class="field dir" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextDirSort}}">Dir{{if eq $dirSort -1}}&uarr;{{else if eq $dirSort 1}}&darr;{{end}}</a>
80-
<a class="field name" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextNameSort}}">Name{{if eq $sortKey "n"}}&uarr;{{else if eq $sortKey "N"}}&darr;{{end}}</a>
81-
<a class="field type" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextTypeSort}}">Type{{if eq $sortKey "e"}}&uarr;{{else if eq $sortKey "E"}}&darr;{{end}}</a>
82-
<a class="field size" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextSizeSort}}">Size{{if eq $sortKey "s"}}&uarr;{{else if eq $sortKey "S"}}&darr;{{end}}</a>
83-
<a class="field time" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextTimeSort}}">Time{{if eq $sortKey "t"}}&uarr;{{else if eq $sortKey "T"}}&darr;{{end}}</a>
79+
<a class="field dir" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextDirSort}}">{{.Trans.ListDirLabel}}{{if eq $dirSort -1}}&uarr;{{else if eq $dirSort 1}}&darr;{{end}}</a>
80+
<a class="field name" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextNameSort}}">{{.Trans.ListNameLabel}}{{if eq $sortKey "n"}}&uarr;{{else if eq $sortKey "N"}}&darr;{{end}}</a>
81+
<a class="field type" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextTypeSort}}">{{.Trans.ListTypeLabel}}{{if eq $sortKey "e"}}&uarr;{{else if eq $sortKey "E"}}&darr;{{end}}</a>
82+
<a class="field size" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextSizeSort}}">{{.Trans.ListSizeLabel}}{{if eq $sortKey "s"}}&uarr;{{else if eq $sortKey "S"}}&darr;{{end}}</a>
83+
<a class="field time" href="{{.SubItemPrefix}}{{.Context.QueryStringOfSort .SortState.NextTimeSort}}">{{.Trans.ListTimeLabel}}{{if eq $sortKey "t"}}&uarr;{{else if eq $sortKey "T"}}&darr;{{end}}</a>
8484
</span>
8585
</li>
8686
<li class="dir parent">
@@ -104,11 +104,11 @@
104104
</ul>
105105

106106
{{if eq .Status 403}}
107-
<div class="error">403 resource is forbidden</div>
107+
<div class="error">{{.Trans.Error403}}</div>
108108
{{else if eq .Status 404}}
109-
<div class="error">404 resource not found</div>
109+
<div class="error">{{.Trans.Error404}}</div>
110110
{{else if eq .Status 500}}
111-
<div class="error">500 potential issue occurred</div>
111+
<div class="error">{{.Trans.Error500}}</div>
112112
{{end}}
113113

114114
<script type="text/javascript" src="{{.RootRelPath}}?asset=index.js" defer="defer" async="async"></script>

0 commit comments

Comments
 (0)