Browse Source

feat: Views localization

pull/95/head
NGPixel 7 years ago
parent
commit
40c4ff80f4
19 changed files with 128 additions and 69 deletions
  1. 16
      server/libs/entries.js
  2. 2
      server/libs/git.js
  3. 2
      server/libs/local.js
  4. 4
      server/libs/uploads-agent.js
  5. 8
      server/libs/uploads.js
  6. 2
      server/locales/en/auth.json
  7. 49
      server/locales/en/common.json
  8. 8
      server/locales/en/errors.json
  9. 6
      server/locales/en/git.json
  10. 6
      server/models/user.js
  11. 8
      server/views/common/header.pug
  12. 20
      server/views/pages/admin/_layout.pug
  13. 10
      server/views/pages/all.pug
  14. 8
      server/views/pages/create.pug
  15. 6
      server/views/pages/edit.pug
  16. 4
      server/views/pages/history.pug
  17. 10
      server/views/pages/source.pug
  18. 22
      server/views/pages/view.pug
  19. 6
      server/views/pages/welcome.pug

16
server/libs/entries.js

@ -155,7 +155,7 @@ module.exports = {
return false
}
}).catch((err) => { // eslint-disable-line handle-callback-err
throw new Promise.OperationalError('Entry ' + entryPath + ' does not exist!')
throw new Promise.OperationalError(lang.t('errors:notexist', { path: entryPath }))
})
},
@ -184,11 +184,11 @@ module.exports = {
}
})
} else {
return Promise.reject(new Error('Parent entry is not a valid file.'))
return Promise.reject(new Error(lang.t('errors:parentinvalid')))
}
})
} else {
return Promise.reject(new Error('Parent entry is root.'))
return Promise.reject(new Error(lang.t('errors:parentisroot')))
}
},
@ -212,11 +212,11 @@ module.exports = {
})
})
} else {
return Promise.reject(new Error('Entry does not exist!'))
return Promise.reject(new Error(lang.t('errors:notexist', { path: entryPath }))
}
}).catch((err) => {
winston.error(err)
return Promise.reject(new Error('Failed to save document.'))
return Promise.reject(new Error(lang.t('errors:savefailed')))
})
},
@ -316,11 +316,11 @@ module.exports = {
})
})
} else {
return Promise.reject(new Error('Entry already exists!'))
return Promise.reject(new Error(lang.t('errors:alreadyexists')))
}
}).catch((err) => {
winston.error(err)
return Promise.reject(new Error('Something went wrong.'))
return Promise.reject(new Error(lang.t('errors:generic')))
})
},
@ -352,7 +352,7 @@ module.exports = {
let self = this
if (_.isEmpty(entryPath) || entryPath === 'home') {
return Promise.reject(new Error('Invalid path!'))
return Promise.reject(new Error(lang.t('errors:invalidpath')))
}
return git.moveDocument(entryPath, newEntryPath).then(() => {

2
server/libs/git.js

@ -206,7 +206,7 @@ module.exports = {
let out = cProc.stdout.toString()
return _.includes(out, gitFilePath)
}).then((isTracked) => {
commitMsg = (isTracked) ? 'Updated ' + gitFilePath : 'Added ' + gitFilePath
commitMsg = (isTracked) ? lang.t('git:updated', { path: gitFilePath }) : lang.t('git:added', { path: gitFilePath })
return self._git.add(gitFilePath)
}).then(() => {
let commitUsr = securityHelper.sanitizeCommitUser(author)

2
server/libs/local.js

@ -163,7 +163,7 @@ module.exports = {
let fpath = path.resolve(this._uploadsPath, fld, f)
return fs.statAsync(fpath).then((s) => {
throw new Error('File ' + f + ' already exists.')
throw new Error(lang.t('errors:fileexists', { path: f }))
}).catch((err) => {
if (err.code === 'ENOENT') {
return f

4
server/libs/uploads-agent.js

@ -59,14 +59,14 @@ module.exports = {
return self.processFile(pInfo.folder, pInfo.filename).then((mData) => {
return db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true })
}).then(() => {
return git.commitUploads('Uploaded ' + p)
return git.commitUploads(lang.t('git:uploaded', { path: p }))
})
})
// -> Remove upload file
self._watcher.on('unlink', (p) => {
return git.commitUploads('Deleted/Renamed ' + p)
return git.commitUploads(lang.t('git:deleted', { path: p }))
})
},

8
server/libs/uploads.js

@ -168,7 +168,7 @@ module.exports = {
return upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
if (!destFolderPath) {
return Promise.reject(new Error('Invalid Folder'))
return Promise.reject(new Error(lang.t('errors:invalidfolder')))
}
return lcdata.validateUploadsFilename(fUrlFilename, destFolder).then((destFilename) => {
@ -192,7 +192,7 @@ module.exports = {
rq.abort()
destFileStream.destroy()
fs.remove(destFilePath)
reject(new Error('Remote file is too large!'))
reject(new Error(lang.t('errors:remotetoolarge')))
}
}).on('error', (err) => {
destFileStream.destroy()
@ -243,7 +243,7 @@ module.exports = {
// -> Check for invalid operations
if (sourceFilePath === destFilePath) {
return Promise.reject(new Error('Invalid Operation!'))
return Promise.reject(new Error(lang.t('errors:invalidoperation')))
}
// -> Delete DB entry
@ -271,7 +271,7 @@ module.exports = {
})
})
} else {
return Promise.reject(new Error('Invalid Destination Folder'))
return Promise.reject(new Error(lang.t('errors:invaliddestfolder')))
}
})
}

2
server/locales/en/auth.json

@ -12,7 +12,9 @@
"errors": {
"invalidlogin": "Invalid Login",
"invalidloginmsg": "The email or password is invalid.",
"invaliduseremail": "Invalid User Email",
"loginerror": "Login error",
"notyetauthorized": "You have not been authorized to login to this site yet.",
"toomanyattempts": "Too many attempts!",
"toomanyattemptsmsg": "You've made too many failed attempts in a short period of time, please try again {{time}}.",
"usernotfound": "User not found"

49
server/locales/en/common.json

@ -1,11 +1,54 @@
{
"wiki": "Wiki",
"headers": {
"overview": "Overview"
"header": {
"overview": "Overview",
"createdoc": "Create New Document"
},
"footer": {
"poweredby": "Powered by",
"home": "Home",
"top": "Return to top"
},
"search": {
"placeholder": "Search...",
"results": "Search Results",
"nomatch": "No results matching your query",
"didyoumean": "Did you mean...?"
},
"sidebar": {
"nav": "NAV",
"navigation": "Navigation",
"pagecontents": "Page Contents",
"pastversions": "Past Versions"
},
"nav": {
"home": "Home",
"account": "Account",
"myprofile": "My Profile",
"stats": "Stats",
"syssettings": "System Settings",
"users": "Users",
"logout": "Logout",
"create": "Create",
"edit": "Edit",
"history": "History",
"source": "Source",
"move": "Move",
"allpages": "All Pages",
"login": "Login",
"normalview": "Normal View",
"viewlatest": "View Latest",
"discard": "Discard",
"savechanges": "Save Changes",
"savedocument": "Save Document"
},
"welcome": {
"title": "Welcome to your wiki!",
"subtitle": "Let's get started and create the home page.",
"createhome": "Create Home Page"
},
"loading": {
"source": "Loading source...",
"editor": "Loading editor..."
}
}
}

8
server/locales/en/errors.json

@ -1,17 +1,25 @@
{
"alreadyexists": "This entry already exists!",
"debugmsg": "Detailed debug trail",
"fileexists": "File {{path}} already exists.",
"forbidden": "Forbidden",
"forbiddendetail": "Sorry, you don't have the necessary permissions to access this page.",
"generic": "Oops, something went wrong",
"invalidaction": "Invalid Action.",
"invaliddestfolder": "Invalid Destination Folder!",
"invalidfiletype": "Invalid File Type.",
"invalidfolder": "Invalid Folder.",
"invalidoperation": "Invalid Operation!",
"invalidpath": "Invalid page path.",
"invaliduserid": "Invalid User Id",
"newpasswordtooshort": "New password is too short!",
"notexist": "Entry {{path}} does not exist!",
"notexistdetail": "Would you like to create this entry?",
"parentinvalid": "Parent entry is not a valid file.",
"parentisroot": "Parent entry is root.",
"remotetoolarge": "Remote file is too large!",
"reservedname": "You cannot create a document with this name as it is reserved by the system.",
"savefailed": "Failed to save document",
"starterfailed": "Could not load starter content!",
"unauthorized": "Unauthorized",
"actions": {

6
server/locales/en/git.json

@ -0,0 +1,6 @@
{
"added": "Added {{path}}",
"deleted": "Deleted/Renamed {{path}}",
"updated": "Updated {{path}}",
"uploaded": "Uplodated {{path}}"
}

6
server/models/user.js

@ -55,7 +55,7 @@ userSchema.statics.processProfile = (profile) => {
} else if (profile.user && profile.user.email && profile.user.email.length > 5) {
primaryEmail = profile.user.email
} else {
return Promise.reject(new Error('Invalid User Email'))
return Promise.reject(new Error(lang.t('auth:errors.invaliduseremail')))
}
profile.provider = _.lowerCase(profile.provider)
@ -89,7 +89,7 @@ userSchema.statics.processProfile = (profile) => {
}
return db.User.create(nUsr)
}
return user || Promise.reject(new Error('You have not been authorized to login to this site yet.'))
return user || Promise.reject(new Error(lang.t('auth:errors:notyetauthorized')))
})
}
@ -99,7 +99,7 @@ userSchema.statics.hashPassword = (rawPwd) => {
userSchema.methods.validatePassword = function (rawPwd) {
return bcrypt.compare(rawPwd, this.password).then((isValid) => {
return (isValid) ? true : Promise.reject(new Error('Invalid Login'))
return (isValid) ? true : Promise.reject(new Error(lang.t('auth:errors:invalidlogin')))
})
}

8
server/views/common/header.pug

@ -11,7 +11,7 @@
block rootNavCenter
.nav-item
p.control(v-bind:class='{ "is-loading": searchload > 0 }')
input.input#search-input(type='text', v-model='searchq', @keyup.esc='closeSearch', @keyup.down='moveDownSearch', @keyup.up='moveUpSearch', @keyup.enter='moveSelectSearch', debounce='400', placeholder='Search...')
input.input#search-input(type='text', v-model='searchq', @keyup.esc='closeSearch', @keyup.down='moveDownSearch', @keyup.up='moveUpSearch', @keyup.enter='moveSelectSearch', debounce='400', placeholder=t('search.placeholder'))
span.nav-toggle
span
span
@ -22,13 +22,13 @@
transition(name='searchresults-anim', enter-active-class='slideInDown', leave-active-class='fadeOutUp')
.searchresults.animated(v-show='searchactive', v-cloak, style={'display':'none'})
p.searchresults-label Search Results
p.searchresults-label= t('search.results')
ul.searchresults-list
li(v-if='searchres.length === 0')
a: em No results matching your query
a: em= t('search.nomatch')
li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres.entryPath }')
a(v-bind:href='"/" + sres.entryPath') {{ sres.title }}
p.searchresults-label(v-if='searchsuggest.length > 0') Did you mean...?
p.searchresults-label(v-if='searchsuggest.length > 0')= t('search.didyoumean')
ul.searchresults-list(v-if='searchsuggest.length > 0')
li(v-for='sug in searchsuggest', v-bind:class='{ "is-active": searchmovekey === "sug." + sug }')
a(v-on:click='useSuggestion(sug)') {{ sug }}

20
server/views/pages/admin/_layout.pug

@ -1,14 +1,14 @@
extends ../../layout.pug
block rootNavCenter
h2.nav-item Account
h2.nav-item= t('nav.account')
block rootNavRight
i.nav-item#notifload
.nav-item
a.button.btn-edit-discard(href='/')
i.icon-home
span Home
span= t('nav.home')
block content
@ -20,38 +20,38 @@ block content
aside
.sidebar-label
span Navigation
span= t('sidebar.navigation')
ul.sidebar-menu
li
a(href='/')
i.icon-home
span Home
span= t('nav.home')
aside
.sidebar-label
span Account
span= t('nav.account')
ul.sidebar-menu
li
a(href='/admin/profile')
i.icon-user
span My Profile
span= t('nav.myprofile')
li
a(href='/admin/stats')
i.icon-bar-graph-2
span Stats
span= t('nav.stats')
if rights.manage
li
a(href='/admin/users')
i.icon-users
span Users
span= t('nav.users')
li
a(href='/admin/settings')
i.icon-cog
span System Settings
span= t('nav.syssettings')
li
a(href='/logout')
i.icon-delete2
span Logout
span= t('nav.logout')
.column
block adminContent

10
server/views/pages/all.pug

@ -10,22 +10,22 @@ block content
.sidebar.is-collapsed
aside
.sidebar-label
span NAV
span= t('sidebar.nav')
ul.sidebar-menu
li
a(href='/')
i.icon-home
span Home
span= t('nav.home')
if !isGuest
li
a(href='/admin')
i.icon-head
span Account
span= t('nav.account')
else
li
a(href='/login')
i.icon-unlock
span Login
span= t('nav.login')
ul.collapsable-nav(v-for='treeItem in tree', :class='{ "has-children": treeItem.hasChildren }', v-cloak)
li(v-for='page in treeItem.pages', :class='{ "is-active": page.isActive }')
a(v-on:click='mainAction(page)')
@ -34,7 +34,7 @@ block content
span {{ page.title }}
template(v-else)
i.icon-home
span Home
span= t('nav.home')
a.is-pagelink(v-if='page.isDirectory && page.isEntry', v-on:click='goto(page._id)')
i.icon-file-text-o
i.icon-arrow-right2

8
server/views/pages/create.pug

@ -1,17 +1,17 @@
extends ../layout.pug
block rootNavCenter
h2.nav-item Create New Document
h2.nav-item= t('header.createdoc')
block rootNavRight
i.nav-item#notifload
span.nav-item
a.button.is-outlined.btn-create-discard
i.icon-cross
span Discard
span= t('nav.discard')
a.button.btn-create-save
i.icon-check
span Save Document
span= t('nav.savedocument')
block content
@ -29,4 +29,4 @@ block content
block outside
#page-loader
i
span Loading editor...
span= t('loading.editor')

6
server/views/pages/edit.pug

@ -8,10 +8,10 @@ block rootNavRight
span.nav-item
a.button.is-outlined.btn-edit-discard
i.icon-cross
span Discard
span= t('nav.discard')
a.button.btn-edit-save
i.icon-check
span Save Changes
span= t('nav.savechanges')
block content
@ -29,4 +29,4 @@ block content
block outside
#page-loader
i
span Loading editor...
span= t('loading.editor')

4
server/views/pages/history.pug

@ -5,7 +5,7 @@ block rootNavRight
.nav-item
a.button(href='/' + pageData.meta._id)
i.icon-circle-check
span View Latest
span= t('nav.viewlatest')
block content
@ -17,7 +17,7 @@ block content
aside.stickyscroll
.sidebar-label
span Past versions
span= t('sidebar.pastversions')
ul.sidebar-menu
each item, index in pageData.history
- var itemDate = moment(item.date)

10
server/views/pages/source.pug

@ -9,17 +9,17 @@ block rootNavRight
if rights.write
a.button.is-outlined.btn-move-prompt.is-hidden
i.icon-shuffle
span Move
span= t('nav.move')
a.button.is-outlined(href='/' + pageData.meta.path)
i.icon-loader
span Normal View
span= t('nav.normalview')
if rights.write
a.button.is-orange(href='/edit/' + pageData.meta.path)
i.fa.fa-edit
span Edit
span= t('nav.edit')
a.button.is-blue.btn-create-prompt
i.fa.fa-plus
span Create
span= t('nav.create')
block content
@ -33,4 +33,4 @@ block content
block outside
#page-loader
i
span Loading source...
span= t('loading.source')

22
server/views/pages/view.pug

@ -14,20 +14,20 @@ block rootNavRight
if rights.write
a.button.is-outlined.btn-move-prompt.is-hidden
i.icon-shuffle
span Move
span= t('nav.move')
a.button.is-outlined(href='/source/' + pageData.meta.path)
i.icon-loader
span Source
span= t('nav.source')
a.button.is-outlined(href='/hist/' + pageData.meta.path)
i.icon-clock
span History
span= t('nav.history')
if rights.write
a.button(href='/edit/' + pageData.meta.path)
i.icon-document-text
span Edit
span= t('nav.edit')
a.button.btn-create-prompt
i.icon-plus
span Create
span= t('nav.create')
block content
@ -39,16 +39,16 @@ block content
aside
.sidebar-label
span Navigation
span= t('sidebar.navigation')
ul.sidebar-menu
li
a(href='/')
i.icon-home
span Home
span= t('nav.home')
li
a(href='/all')
i.icon-paper
span All Pages
span= t('nav.allpages')
if pageData.parent
li
a(href='/' + pageData.parent.path)
@ -58,15 +58,15 @@ block content
li
a(href='/admin')
i.icon-head
span Account
span= t('nav.account')
else
li
a(href='/login')
i.icon-unlock
span Login
span= t('nav.login')
aside.stickyscroll
.sidebar-label
span Page Contents
span= t('sidebar.pagecontents')
ul.sidebar-menu
li.is-hidden-until-scroll: a(href='#root', title='Top of Page')
i.icon-arrow-up2

6
server/views/pages/welcome.pug

@ -11,6 +11,6 @@ block content
.container
.welcome
img(src='/images/logo.png', alt='Wiki.js')
h1 Welcome to your wiki!
h2 Let's get started and create the home page.
a.button.is-indigo(href='/create/home') Create Home Page
h1= t('welcome.title')
h2= t('welcome.subtitle')
a.button.is-indigo(href='/create/home')= t('welcome.createhome')
Loading…
Cancel
Save