Просмотр исходного кода

Merge branch 'i18n'

Conflicts:
	app/coffee/Features/User/UserPagesController.coffee
	app/views/layout.jade
	app/views/project/editor/dropbox.jade
	app/views/project/editor/file-tree.jade
	app/views/project/editor/pdf.jade
	app/views/project/list/project-list.jade
	app/views/user/settings.jade
Henry Oswald 12 лет назад
Родитель
Сommit
9c6f0faff6
55 измененных файлов с 498 добавлено и 484 удалено
  1. 2 2
      services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
  2. 1 1
      services/web/app/coffee/Features/Errors/ErrorController.coffee
  3. 3 3
      services/web/app/coffee/Features/PasswordReset/PasswordResetController.coffee
  4. 1 1
      services/web/app/coffee/Features/Project/ProjectController.coffee
  5. 1 1
      services/web/app/coffee/Features/Referal/ReferalController.coffee
  6. 5 5
      services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
  7. 1 1
      services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee
  8. 2 2
      services/web/app/coffee/Features/Templates/TemplatesWebController.coffee
  9. 5 1
      services/web/app/coffee/Features/User/UserController.coffee
  10. 3 4
      services/web/app/coffee/Features/User/UserPagesController.coffee
  11. 1 1
      services/web/app/coffee/Features/User/UserUpdater.coffee
  12. 1 0
      services/web/app/coffee/infrastructure/ExpressLocals.coffee
  13. 6 2
      services/web/app/coffee/infrastructure/Server.coffee
  14. 2 2
      services/web/app/views/general/404.jade
  15. 11 4
      services/web/app/views/layout.jade
  16. 6 5
      services/web/app/views/layout/navbar.jade
  17. 9 14
      services/web/app/views/project/editor.jade
  18. 2 2
      services/web/app/views/project/editor/binary-file.jade
  19. 4 5
      services/web/app/views/project/editor/chat.jade
  20. 12 15
      services/web/app/views/project/editor/dropbox.jade
  21. 1 1
      services/web/app/views/project/editor/editor.jade
  22. 30 30
      services/web/app/views/project/editor/file-tree.jade
  23. 5 5
      services/web/app/views/project/editor/hotkeys.jade
  24. 25 25
      services/web/app/views/project/editor/left-menu.jade
  25. 21 23
      services/web/app/views/project/editor/pdf.jade
  26. 11 11
      services/web/app/views/project/editor/publish-template.jade
  27. 26 26
      services/web/app/views/project/editor/share.jade
  28. 17 16
      services/web/app/views/project/editor/track-changes.jade
  29. 3 0
      services/web/app/views/project/list.jade
  30. 28 28
      services/web/app/views/project/list/modals.jade
  31. 17 17
      services/web/app/views/project/list/project-list.jade
  32. 28 29
      services/web/app/views/project/list/side-bar.jade
  33. 20 20
      services/web/app/views/referal/bonus.jade
  34. 20 20
      services/web/app/views/subscriptions/dashboard.jade
  35. 2 2
      services/web/app/views/subscriptions/edit-billing-details.jade
  36. 9 9
      services/web/app/views/subscriptions/group_admin.jade
  37. 2 2
      services/web/app/views/subscriptions/new.jade
  38. 49 49
      services/web/app/views/subscriptions/plans.jade
  39. 11 12
      services/web/app/views/subscriptions/successful_subscription.jade
  40. 1 1
      services/web/app/views/templates/index.jade
  41. 1 1
      services/web/app/views/templates/tag.jade
  42. 3 3
      services/web/app/views/templates/template.jade
  43. 6 6
      services/web/app/views/user/login.jade
  44. 5 5
      services/web/app/views/user/passwordReset.jade
  45. 13 12
      services/web/app/views/user/register.jade
  46. 2 2
      services/web/app/views/user/restricted.jade
  47. 5 5
      services/web/app/views/user/setPassword.jade
  48. 35 38
      services/web/app/views/user/settings.jade
  49. 12 0
      services/web/config/settings.defaults.coffee
  50. 0 1
      services/web/public/coffee/main.coffee
  51. 3 3
      services/web/public/coffee/main/project-list.coffee
  52. 5 4
      services/web/test/UnitTests/coffee/Authentication/AuthenticationControllerTests.coffee
  53. 2 0
      services/web/test/UnitTests/coffee/PasswordReset/PasswordResetControllerTests.coffee
  54. 0 7
      services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee
  55. 2 0
      services/web/test/UnitTests/coffee/helpers/MockRequest.coffee

+ 2 - 2
services/web/app/coffee/Features/Authentication/AuthenticationController.coffee

@@ -18,7 +18,7 @@ module.exports = AuthenticationController =
 				res.statusCode = 429
 				return res.send
 					message:
-						text: 'This account has had too many login requests. Please wait 2 minutes before trying to log in again',
+						text: req.i18n.translate("to_many_login_requests_2_mins"),
 						type: 'error'
 			AuthenticationManager.authenticate email: email, password, (error, user) ->
 				return next(error) if error?
@@ -33,7 +33,7 @@ module.exports = AuthenticationController =
 					AuthenticationController._recordFailedLogin()
 					logger.log email: email, "failed log in"
 					res.send message:
-						text: 'Your email or password were incorrect. Please try again',
+						text: req.i18n.translate("email_or_password_wrong_try_again"),
 						type: 'error'
 
 	getAuthToken: (req, res, next = (error) ->) ->

+ 1 - 1
services/web/app/coffee/Features/Errors/ErrorController.coffee

@@ -2,4 +2,4 @@ module.exports = ErrorController =
 	notFound: (req, res)->
 		res.statusCode = 404
 		res.render 'general/404',
-			title: "Page Not Found"
+			title: "page_not_found"

+ 3 - 3
services/web/app/coffee/Features/PasswordReset/PasswordResetController.coffee

@@ -6,7 +6,7 @@ module.exports =
 
 	renderRequestResetForm: (req, res)->
 		res.render "user/passwordReset", 
-			title:"Reset Password"
+			title:"reset_password"
 
 	requestReset: (req, res)->
 		email = req.body.email.trim().toLowerCase()
@@ -17,7 +17,7 @@ module.exports =
 			throttle: 6
 		RateLimiter.addCount opts, (err, canCompile)->
 			if !canCompile
-				return res.send 500, { message: "Rate limit hit. Please wait a while before retrying" }
+				return res.send 500, { message: req.i18n.translate("rate_limit_hit_wait")}
 			PasswordResetHandler.generateAndEmailResetToken email, (err)->
 				if err?
 					res.send 500, {message:err?.message}
@@ -26,7 +26,7 @@ module.exports =
 
 	renderSetPasswordForm: (req, res)->
 		res.render "user/setPassword", 
-			title:"Set Password"
+			title:"set_password"
 			passwordResetToken:req.query.passwordResetToken
 
 	setNewUserPassword: (req, res)->

+ 1 - 1
services/web/app/coffee/Features/Project/ProjectController.coffee

@@ -157,7 +157,7 @@ module.exports = ProjectController =
 	loadEditor: (req, res, next)->
 		timer = new metrics.Timer("load-editor")
 		if !Settings.editorIsOpen
-			return res.render("general/closed", {title:"updating site"})
+			return res.render("general/closed", {title:"updating_site"})
 
 		if req.session.user?
 			user_id = req.session.user._id 

+ 1 - 1
services/web/app/coffee/Features/Referal/ReferalController.coffee

@@ -5,6 +5,6 @@ module.exports =
 	bonus: (req, res)->
 		ReferalHandler.getReferedUserIds req.session.user._id, (err, refered_users)->
 			res.render "referal/bonus",
-				title: "Bonus - Please recommend us"
+				title: "bonus_please_recommend_us"
 				refered_users: refered_users
 				refered_user_count: (refered_users or []).length

+ 5 - 5
services/web/app/coffee/Features/Subscription/SubscriptionController.coffee

@@ -21,7 +21,7 @@ module.exports = SubscriptionController =
 		viewName = "subscriptions/plans"
 		logger.log viewName:viewName, "showing plans page"
 		res.render viewName,
-			title: "Plans and Pricing"
+			title: "plans_and_pricing"
 			plans: plans
 			baseUrl: baseUrl
 
@@ -41,7 +41,7 @@ module.exports = SubscriptionController =
 					}, (error, signature) ->
 						return next(error) if error?
 						res.render "subscriptions/new",
-							title      : "Subscribe"
+							title      : "subscribe"
 							plan_code: req.query.planCode
 							recurlyConfig: JSON.stringify
 								currency: "USD"
@@ -74,7 +74,7 @@ module.exports = SubscriptionController =
 						logger.log user: user, subscription:subscription, hasSubOrFreeTrial:hasSubOrFreeTrial, "showing subscription dashboard"
 						plans = SubscriptionViewModelBuilder.buildViewModel()
 						res.render "subscriptions/dashboard",
-							title: "Your Subscription"
+							title: "your_subscription"
 							plans: plans
 							subscription: subscription
 							subscriptionTabActive: true
@@ -92,7 +92,7 @@ module.exports = SubscriptionController =
 					}, (error, signature) ->
 						return next(error) if error?
 						res.render "subscriptions/edit-billing-details",
-							title      : "Update Billing Details"
+							title      : "update_billing_details"
 							recurlyConfig: JSON.stringify
 								currency: "USD"
 								subdomain: Settings.apis.recurly.subdomain
@@ -115,7 +115,7 @@ module.exports = SubscriptionController =
 		SecurityManager.getCurrentUser req, (error, user) =>
 			SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel user, (error, subscription) ->
 				res.render "subscriptions/successful_subscription",
-					title: "Thank you!"
+					title: "thank_you"
 					subscription:subscription
 
 	cancelSubscription: (req, res, next) ->

+ 1 - 1
services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee

@@ -29,6 +29,6 @@ module.exports =
 				return res.redirect("/")
 			SubscriptionGroupHandler.getPopulatedListOfMembers user_id, (err, users)->
 				res.render "subscriptions/group_admin",
-					title: 'Group Admin'
+					title: 'group_admin'
 					users: users
 					subscription: subscription

+ 2 - 2
services/web/app/coffee/Features/Templates/TemplatesWebController.coffee

@@ -10,7 +10,7 @@ module.exports = TemplatesWebController =
 			if err? or !data?
 				logger.err err:err, "something went wrong in renderTemplatesIndexPage"
 				return res.send 500
-			data.title = "LaTeX Templates"
+			data.title = "latex_templates"
 			res.render "templates/index", data
 
 	renerTemplateInTag: (req, res)->
@@ -60,7 +60,7 @@ module.exports = TemplatesWebController =
 			if err?
 				logger.err err:err, user_id:user_id, "something went wrong in _renderCanonicalPage"
 				return res.send 500
-			data.title = "All Templates"
+			data.title = "all_templates"
 			res.render "templates/tag", data
 
 	_renderTagPage:  (req, res)->

+ 5 - 1
services/web/app/coffee/Features/User/UserController.coffee

@@ -63,7 +63,11 @@ module.exports =
 					UserUpdater.changeEmailAddress user_id, newEmail, (err)->
 						if err?
 							logger.err err:err, user_id:user_id, newEmail:newEmail, "problem updaing users email address"
-							return res.send 500, {message:err?.message}
+							if err.message == "alread_exists"
+								message = req.i18n.translate("alread_exists")
+							else
+								message = req.i18n.translate("problem_changing_email_address")
+							return res.send 500, {message:message}
 						res.send(200)
 
 	logout : (req, res)->

+ 3 - 4
services/web/app/coffee/Features/User/UserPagesController.coffee

@@ -16,16 +16,15 @@ module.exports =
 			newTemplateData.templateName = req.session.templateData.templateName
 
 		res.render 'user/register',
-			title: 'Register'
+			title: 'register'
 			redir: req.query.redir
 			sharedProjectData: sharedProjectData
 			newTemplateData: newTemplateData
 			new_email:req.query.new_email || ""
 
 	loginPage : (req, res)->
-		console.info req
 		res.render 'user/login',
-			title: 'Login',
+			title: 'login',
 			redir: req.query.redir
 
 	settingsPage : (req, res)->
@@ -34,7 +33,7 @@ module.exports =
 			dropboxHandler.getUserRegistrationStatus user._id, (err, status)->
 				userIsRegisteredWithDropbox = !err? and status.registered
 				res.render 'user/settings',
-					title:'Account Settings',
+					title:'account_settings',
 					userHasDropboxFeature: user.features.dropbox
 					userIsRegisteredWithDropbox: userIsRegisteredWithDropbox
 					user: user,

+ 1 - 1
services/web/app/coffee/Features/User/UserUpdater.coffee

@@ -19,7 +19,7 @@ module.exports = UserUpdater =
 		logger.log user_id:user_id, newEmail:newEmail, "updaing email address of user"
 		UserLocator.findByEmail newEmail, (error, user) ->
 			if user?
-				return callback({message:"User with that email already exists."})
+				return callback({message:"alread_exists"})
 			self.updateUser user_id.toString(), {
 				$set: { "email": newEmail},
 			}, (err) ->

+ 1 - 0
services/web/app/coffee/infrastructure/ExpressLocals.coffee

@@ -51,6 +51,7 @@ module.exports = (app)->
 
 	app.use (req, res, next)->
 		res.locals.translate = req.i18n.translate
+		res.locals.currentUrl = req.originalUrl
 		next()
 
 	app.use (req, res, next)->

+ 6 - 2
services/web/app/coffee/infrastructure/Server.coffee

@@ -18,7 +18,7 @@ oneDayInMilliseconds = 86400000
 ReferalConnect = require('../Features/Referal/ReferalConnect')
 RedirectManager = require("./RedirectManager")
 OldAssetProxy = require("./OldAssetProxy")
-translations = require "translations-sharelatex"
+translations = require("translations-sharelatex").setup(Settings.i18n)
 
 metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongojs/node_modules/mongodb"), logger)
 metrics.mongodb.monitor(Path.resolve(__dirname + "/../../../node_modules/mongoose/node_modules/mongodb"), logger)
@@ -40,6 +40,8 @@ ignoreCsrfRoutes = []
 app.ignoreCsrf = (method, route) ->
 	ignoreCsrfRoutes.push new express.Route(method, route)
 
+
+
 app.configure () ->
 	if Settings.behindProxy
 		app.enable('trust proxy')
@@ -49,10 +51,12 @@ app.configure () ->
 	app.use express.bodyParser(uploadDir: Settings.path.uploadFolder)
 	app.use express.bodyParser(uploadDir: __dirname + "/../../../data/uploads")
 	app.use translations.expressMiddlewear
+	app.use translations.setLangBasedOnDomainMiddlewear
 	app.use cookieParser
 	app.use express.session
 		proxy: Settings.behindProxy
 		cookie:
+			domain: Settings.cookieDomain
 			maxAge: cookieSessionLength
 			secure: Settings.secureCookie
 		store: sessionStore
@@ -90,7 +94,7 @@ app.use (req, res, next)->
 app.use (req, res, next) ->
 	if !Settings.editorIsOpen
 		res.status(503)
-		res.render("general/closed", {title:"Maintenance"})
+		res.render("general/closed", {title:"maintenance"})
 	else
 		next()
 

+ 2 - 2
services/web/app/views/general/404.jade

@@ -6,8 +6,8 @@ block content
 			.row
 				.col-md-8.col-md-offset-2.text-center
 					.page-header
-						h2 Sorry, we can't find the page you are looking for.
+						h2 #{translate("cant_find_page")}
 					p
 						a(href="/")
 							i.fa.fa-arrow-circle-o-left
-							|  Take me home!
+							|  #{translate("take_me_home")}

+ 11 - 4
services/web/app/views/layout.jade

@@ -3,14 +3,21 @@ html(itemscope, itemtype='http://schema.org/Product')
 	block vars
 
 	head
-		-if (typeof(title) == "undefined")
-			title ShareLaTeX, the Online LaTeX Editor
-		-else
-			title= title + ' - ShareLaTeX, the Online LaTeX Editor'
+
+		- if (typeof(priority_title) !== "undefined" && priority_title)
+			title= title + ' - '+ translate("online_latex_editor")
+		- else
+			title=  translate("online_latex_editor") +' ShareLaTeX - ' +translate(title)
+			title= translate(title) + ' - ShareLaTeX, '+
+
 		link(rel="icon", href="/favicon.ico")
 		link(rel='stylesheet', href='/stylesheets/style.css?fingerprint='+fingerprint('/stylesheets/style.css'))
 		link(href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css",rel="stylesheet")
 
+		if settings.i18n.subdomainLang
+			each subdomainDetails in settings.i18n.subdomainLang
+				link(rel="alternate", href=subdomainDetails.url+currentUrl, hreflang=subdomainDetails.lngCode)
+
 		meta(itemprop="name" ,content="ShareLaTeX - Real Time Online LaTeX Collaborative Editor in Your Browser")
 		meta(itemprop="description", content="Online LaTeX editor for collaborative editing, great for Maths or Sciences. You don't need to install LaTeX so it's great for beginners too.")
 		meta(itemprop="image", content="https://www.sharelatex.com/favicon.ico")

+ 6 - 5
services/web/app/views/layout/navbar.jade

@@ -6,13 +6,14 @@ nav.navbar.navbar-default
 			a(href='/').navbar-brand
 
 		.navbar-collapse.collapse(collapse="navCollapsed")
+
 			ul.nav.navbar-nav.navbar-right
 				each item in nav.header
 					if ((item.only_when_logged_in && session && session.user) || (item.only_when_logged_out && (!session || !session.user)) || (!item.only_when_logged_out && !item.only_when_logged_in))
 						if item.dropdown
 							li.dropdown(class=item.class)
 								a.dropdown-toggle(href)
-									| !{item.text} 
+									| !{translate(item.text)}
 									b.caret
 								ul.dropdown-menu
 									each child in item.dropdown
@@ -21,14 +22,14 @@ nav.navbar.navbar-default
 										else
 											li
 												if child.url
-													a(href=child.url, class=child.class) !{child.text}
+													a(href=child.url, class=child.class) !{translate(child.text)}
 												else
-													| !{child.text}
+													| !{translate(child.text)}
 						else
 							li(class=item.class)
 								if item.url
-									a(href=item.url, class=item.class) !{item.text}
+									a(href=item.url, class=item.class) !{translate(item.text)}
 								else
-									| !{item.text}
+									| !{translate(item.text)}
 
 

+ 9 - 14
services/web/app/views/project/editor.jade

@@ -10,30 +10,25 @@ block content
 	.editor(ng-controller="IdeController").full-size
 		.loading-screen(ng-show="state.loading")
 			.container
-				h3 Loading...
+				h3 #{translate("loading")}...
 				.progress
 					.progress-bar(style="width: 20%", ng-style="{'width': state.load_progress + '%'}")
 
 		.global-alerts(ng-cloak)
 			.alert.alert-danger.small(ng-if="connection.forced_disconnect")
-				strong Disconnected
-				|  Please refresh the page to continue.
+				strong #{translate("disconnected")}
+				|  #{translate("please_refresh")}
 
 			.alert.alert-warning.small(ng-if="connection.reconnection_countdown")
-				strong Lost Connection.
-				|  Reconnecting in {{ connection.reconnection_countdown }} secs. 
-				a.pull-right(href, ng-click="tryReconnectNow()") Try Now
+				strong #{translate("lost_connection")}.
+				| #{translate("reconnecting_in_x_secs", {seconds:"{{ connection.reconnection_countdown }}"})}.
+				a.pull-right(href, ng-click="tryReconnectNow()") #{translate("try_now")}
 
 			.alert.alert-warning.small(ng-if="connection.reconnecting")
-				strong Reconnecting...
+				strong #{translate("reconnecting")}...
 
 			.div(ng-controller="SavingNotificationController")
-				.alert.alert-warning.small(
-					ng-repeat="(doc_id, state) in docSavingStatus"
-					ng-if="state.unsavedSeconds > 3"
-				)
-					| Saving {{ state.doc.name }}... ({{ state.unsavedSeconds }} seconds of unsaved changes)
-
+				.alert.alert-warning.small( ng-repeat="(doc_id, state) in docSavingStatus" ng-if="state.unsavedSeconds > 3") #{translate("saving_notification_with_seconds", {docname:"{{ state.doc.name }}", seconds:"{{ state.unsavedSeconds }}"})}
 
 		include ./editor/left-menu
 
@@ -78,7 +73,7 @@ block content
 			h3 {{ title }}
 		.modal-body {{ message }}
 		.modal-footer
-			button.btn.btn-info(ng-click="done()") OK
+			button.btn.btn-info(ng-click="done()") #{translate("ok")}
 
 	script(src='/socket.io/socket.io.js')
 

+ 2 - 2
services/web/app/views/project/editor/binary-file.jade

@@ -13,7 +13,7 @@ div.binary-file.full-size(
 	)
 	p.no-preview(
 		ng-if="['png', 'jpg', 'jpeg', 'gif', 'pdf', 'eps'].indexOf(extension(openFile)) == -1"
-	) Sorry, no preview is available.
+	) #{translate("no_preview_available")}
 	a.btn.btn-info(
 		ng-href="/project/{{ project_id }}/file/{{ openFile.id }}"
-	) Download {{ openFile.name }}
+	) #{translate("download")} {{ openFile.name }}

+ 4 - 5
services/web/app/views/project/editor/chat.jade

@@ -12,11 +12,11 @@ aside.chat(
 		.infinite-scroll-inner
 			.loading(ng-show="chat.loading")
 				i.fa.fa-fw.fa-spin.fa-refresh
-				|   Loading...
+				|   #{translate("loading")}...
 			.no-messages.text-center.small(ng-show='!chat.loading && chat.messages.length == 0')
-				| No messages
+				| #{translate("no_messages")}
 			.first-message.text-center(ng-show='!chat.loading && chat.messages.length == 0')
-				| Send your first message
+				| #{translate("send_first_message")}
 				br
 				i.fa.fa-arrow-down
 			ul.list-unstyled(
@@ -27,8 +27,7 @@ aside.chat(
 					ng-controller="ChatMessageController"
 					ng-class="{'self': message.user.id == user.id }"
 				)
-					div.date(ng-if="$index == 0 || (message.timestamp - chat.messages[$index - 1].timestamp) > 5 * 60 * 1000")
-						{{ message.timestamp | formatDate:'h:mm a' }} {{ message.timestamp | relativeDate }}
+					div.date(ng-if="$index == 0 || (message.timestamp - chat.messages[$index - 1].timestamp) > 5 * 60 * 1000") {{ message.timestamp | formatDate:'h:mm a' }} {{ message.timestamp | relativeDate }}
 					span.avatar
 						img(ng-src="{{message.user.gravatar_url}}?d=mm&s=50")
 					div.message-wrapper

+ 12 - 15
services/web/app/views/project/editor/dropbox.jade

@@ -5,44 +5,41 @@ script(type="text/ng-template", id="dropboxModalTemplate")
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) ×
-		h3 Dropbox Sync
+		h3 #{translate("dropbox_sync")}
 	.modal-body.modal-body-share
 
 		div(ng-show="dbState.gotLinkStatus")
 			div(ng-hide="dbState.userIsLinkedToDropbox || !dbState.hasDropboxFeature")
 
-				span(ng-hide="dbState.startedLinkProcess") Your account is not linked to dropbox
+				span(ng-hide="dbState.startedLinkProcess") #{translate("account_not_linked_to_dropbox")}
 					|    
-					a(ng-click="linkToDropbox()").btn.btn-info Update Dropbox Settings
+					a(ng-click="linkToDropbox()").btn.btn-info #{translate("update_dropbox_settings")}
 
 				p.small.text-center(ng-show="dbState.startedLinkProcess")
-					| Please refresh this page after starting your free trial.
+					| #{translate("refresh_page_after_starting_free_trial")}
 
 
 			div(ng-show="dbState.hasDropboxFeature && dbState.userIsLinkedToDropbox")
 				progressbar.progress-striped.active(value='dbState.percentageLeftTillNextPoll', type="info")
 				p
-					strong {{dbState.minsTillNextPoll}} minutes 
-					span until Dropbox is next checked for changes.
+					strong {{dbState.minsTillNextPoll}} #{translate("minutes")} 
+					span #{translate("until_db_checked_for_changes")}
 				p.small
-					| This project will appear in your Dropbox folder at 
+					| #{translate("this_project_will_appear_in_your_dropbox_folder_at")}
 					strong Dropbox/sharelatex/{{ project.name }}
-
 			div.text-center(ng-hide="dbState.hasDropboxFeature")
-				p You need to upgrade your account to link to dropbox.
+				p #{translate("need_to_upgrade_for_dropbox")}
 				p
-					a.btn.btn-info(ng-click="startFreeTrial('dropbox')") Start Free Trial
+					a.btn.btn-info(ng-click="startFreeTrial('dropbox')") #{translate("start_free_trial")}
 				p.small(ng-show="startedFreeTrial")
-					| Please refresh this page after starting your free trial.
+					| #{translate("refresh_page_after_starting_free_trial")}
 
 		div(ng-hide="dbState.gotLinkStatus")
 			i.fa.fa-refresh.fa-spin 
-			span.small   Checking dropbox status
-
-
+			span.small   #{translate("checking_dropbox_status")}
 
 	.modal-footer()
 		button.btn.btn-default(
 			ng-click="cancel()",
 		)
-			span Dismiss
+			span #{translate("dismiss")}

+ 1 - 1
services/web/app/views/project/editor/editor.jade

@@ -10,7 +10,7 @@ div.full-size(
 	.ui-layout-center
 		.loading-panel(ng-show="!editor.sharejs_doc || editor.opening")
 			i.fa.fa-spin.fa-refresh
-			|   Loading...
+			|   #{translate("loading")}...
 
 		#editor(
 			ace-editor="editor",

+ 30 - 30
services/web/app/views/project/editor/file-tree.jade

@@ -137,13 +137,13 @@ script(type='text/ng-template', id='entityListItemTemplate')
 								href
 								ng-click="startRenaming()"
 								right-click="startRenaming()"
-							) Rename
+							) #{translate("rename")}
 						li
 							a(
 								href
 								ng-click="openDeleteModal()"
 								right-click="openDeleteModal()"
-							) Delete
+							) #{translate("delete")}
 
 				div.dropdown.context-menu(
 					id="context-menu-{{ entity.id }}",
@@ -155,13 +155,13 @@ script(type='text/ng-template', id='entityListItemTemplate')
 								href
 								ng-click="startRenaming()"
 								right-click="startRenaming()"
-							) Rename
+							) #{translate("rename")}
 						li
 							a(
 								href
 								ng-click="openDeleteModal()"
 								right-click="openDeleteModal()"
-							) Delete
+							) #{translate("delete")}
 
 
 		.entity(ng-if="entity.type == 'folder'", ng-controller="FileTreeFolderController")
@@ -219,32 +219,32 @@ script(type='text/ng-template', id='entityListItemTemplate')
 									href
 									ng-click="startRenaming()"
 									right-click="startRenaming()"
-								) Rename
+								) #{translate("rename")}
 							li
 								a(
 									href
 									ng-click="openDeleteModal()"
 									right-click="openDeleteModal()"
-								) Delete
+								) #{translate("delete")}
 							li.divider
 							li
 								a(
 									href
 									ng-click="openNewDocModal()"
 									right-click="openNewDocModal()"
-								) New File
+								) #{translate("new_file")}
 							li
 								a(
 									href
 									ng-click="openNewFolderModal()"
 									right-click="openNewFolderModal()"
-								) New Folder
+								) #{translate("new_folder")}
 							li
 								a(
 									href
 									ng-click="openUploadFileModal()"
 									right-click="openUploadFileModal()"
-								) Upload File
+								) #{translate("upload_file")}
 
 					.dropdown.context-menu(
 						ng-if="permissions.write"
@@ -256,32 +256,32 @@ script(type='text/ng-template', id='entityListItemTemplate')
 									href
 									ng-click="startRenaming()"
 									right-click="startRenaming()"
-								) Rename
+								) #{translate("rename")}
 							li
 								a(
 									href
 									ng-click="openDeleteModal()"
 									right-click="openDeleteModal()"
-								) Delete
+								) #{translate("delete")}
 							li.divider
 							li
 								a(
 									href
 									ng-click="openNewDocModal()"
 									right-click="openNewDocModal()"
-								) New File
+								) #{translate("new_file")} 
 							li
 								a(
 									href
 									ng-click="openNewFolderModal()"
 									right-click="openNewFolderModal()"
-								) New Folder
+								) #{translate("new_folder")}
 							li
 								a(
 									href
 									ng-click="openUploadFileModal()"
 									right-click="openUploadFileModal()"
-								) Upload File
+								) #{translate("upload_file")}
 
 			ul.list-unstyled(
 				ng-if="entity.type == 'folder'"
@@ -298,7 +298,7 @@ script(type='text/ng-template', id='entityListItemTemplate')
 
 script(type='text/ng-template', id='newDocModalTemplate')
 	.modal-header
-		h3 New File
+		h3 #{translate("new_file")}
 	.modal-body
 		form(novalidate, name="newDocForm")
 			input.form-control(
@@ -313,17 +313,17 @@ script(type='text/ng-template', id='newDocModalTemplate')
 		button.btn.btn-default(
 			ng-disabled="state.inflight"
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-primary(
 			ng-disabled="newDocForm.$invalid || state.inflight"
 			ng-click="create()"
 		)
-			span(ng-hide="state.inflight") Create
-			span(ng-show="state.inflight") Creating...
+			span(ng-hide="state.inflight") #{translate("create")}
+			span(ng-show="state.inflight") #{translate("creating")}...
 
 script(type='text/ng-template', id='newFolderModalTemplate')
 	.modal-header
-		h3 New Folder
+		h3 #{translate("new_folder")}
 	.modal-body
 		form(novalidate, name="newFolderForm")
 			input.form-control(
@@ -338,17 +338,17 @@ script(type='text/ng-template', id='newFolderModalTemplate')
 		button.btn.btn-default(
 			ng-disabled="state.inflight"
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")} 
 		button.btn.btn-primary(
 			ng-disabled="newFolderForm.$invalid || state.inflight"
 			ng-click="create()"
 		)
-			span(ng-hide="state.inflight") Create
-			span(ng-show="state.inflight") Creating...
+			span(ng-hide="state.inflight") #{translate("create")}
+			span(ng-show="state.inflight") #{translate("creating")}...
 
 script(type="text/ng-template", id="uploadFileModalTemplate")
 	.modal-header
-		h3 Upload File(s)
+		h3 #{translate("upload_files")}
 	.modal-body(
 		fine-upload
 		endpoint="/project/{{ project_id }}/upload"
@@ -362,23 +362,23 @@ script(type="text/ng-template", id="uploadFileModalTemplate")
 		on-upload-callback="onUpload"
 		params="{'folder_id': parent_folder_id}"
 	)
-		span Upload file(s)
+		span #{translate("upload_files")}
 	.modal-footer
-		button.btn.btn-default(ng-click="cancel()") Cancel
+		button.btn.btn-default(ng-click="cancel()") #{translate("cancel")}
 
 script(type='text/ng-template', id='deleteEntityModalTemplate')
 	.modal-header
-		h3 Delete {{ entity.name }}
+		h3 #{translate("delete")} {{ entity.name }}
 	.modal-body
-		p Are you sure you want to permanently delete <strong>{{ entity.name }}</strong>?
+		p #{translate("sure_you_want_to_delete")}
 	.modal-footer
 		button.btn.btn-default(
 			ng-disabled="state.inflight"
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-danger(
 			ng-disabled="state.inflight"
 			ng-click="delete()"
 		)
-			span(ng-hide="state.inflight") Delete
-			span(ng-show="state.inflight") Deleting...
+			span(ng-hide="state.inflight") #{translate("delete")}
+			span(ng-show="state.inflight") #{translate("deleting")}...

+ 5 - 5
services/web/app/views/project/editor/hotkeys.jade

@@ -5,9 +5,9 @@ script(type="text/ng-template", id="hotkeysModalTemplate")
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Hotkeys
+		h3 #{translate("hotkeys")}
 	.modal-body.modal-hotkeys
-		h3 Common
+		h3 #{translate("common")}
 		.row
 			.col-xs-6
 				.hotkey
@@ -24,7 +24,7 @@ script(type="text/ng-template", id="hotkeysModalTemplate")
 					span.combination {{ctrl}} + Y
 					span.description Redo
 
-		h3 Navigation
+		h3 #{translate("navigation")}
 		.row
 			.col-xs-6
 				.hotkey
@@ -38,7 +38,7 @@ script(type="text/ng-template", id="hotkeysModalTemplate")
 					span.combination {{ctrl}} + L
 					span.description Go To Line
 
-		h3 Editing
+		h3 #{translate("editing")}
 		.row
 			.col-xs-6
 				.hotkey
@@ -63,4 +63,4 @@ script(type="text/ng-template", id="hotkeysModalTemplate")
 	.modal-footer
 		button.btn.btn-default(
 			ng-click="cancel()"
-		) OK
+		) #{translate("ok")}

+ 25 - 25
services/web/app/views/project/editor/left-menu.jade

@@ -2,7 +2,7 @@ aside#left-menu.full-size(
 	ng-class="{ 'shown': ui.leftMenuShown }"
 	ng-cloak
 )
-	h4 Download
+	h4 #{translate("download")}
 
 	ul.list-unstyled.nav.nav-downloads.text-center
 		li
@@ -12,7 +12,7 @@ aside#left-menu.full-size(
 			)
 				i.fa.fa-file-archive-o.fa-2x
 				br
-				| Source
+				| #{translate("source")}
 		li
 			a(
 				ng-href="{{pdf.url}}"
@@ -32,7 +32,7 @@ aside#left-menu.full-size(
 				| PDF
 				
 	span(ng-show="!anonymous")
-		h4 Actions
+		h4 #{translate("Actions")}
 		ul.list-unstyled.nav
 			li(ng-controller="CloneProjectController")
 				a(
@@ -40,26 +40,26 @@ aside#left-menu.full-size(
 					ng-click="openCloneProjectModal()"
 				)
 					i.fa.fa-fw.fa-copy
-					| &nbsp;&nbsp;Copy Project
+					| &nbsp;&nbsp;#{translate("copy_project")}
 			li(ng-controller="TemplatesController", ng-show="permissions.admin")
 				a(ng-click="openPublishTemplateModal()")
 					i.fa.fa-external-link.fa-fw 
-					| &nbsp;&nbsp;Publish as Template
+					| &nbsp;&nbsp; #{translate("publish_as_template")}
 
 
 	span(ng-controller="DropboxController", ng-show="permissions.admin")
-		h4() Sync
+		h4() #{translate("sync")}
 		ul.list-unstyled.nav()
 			li
 				a(ng-click="openDropboxModal()")
 					i.fa.fa-dropbox.fa-fw
 					| &nbsp;&nbsp; Dropbox
 
-	h4(ng-show="!anonymous") Settings
+	h4(ng-show="!anonymous") #{translate("settings")}
 	form.settings(ng-controller="SettingsController", ng-show="!anonymous")
 		.containter-fluid
 			.form-controls(ng-show="permissions.write")
-				label(for="compiler") Compiler
+				label(for="compiler") #{translate("compiler")}
 				select(
 					name="compiler"
 					ng-model="project.compiler"
@@ -70,7 +70,7 @@ aside#left-menu.full-size(
 					option(value='lualatex') LuaLaTeX
 					
 			.form-controls(ng-show="permissions.write")
-				label(for="rootDoc_id") Main document
+				label(for="rootDoc_id") #{translate("main_document")}
 				select(
 					name="rootDoc_id",
 					ng-model="project.rootDoc_id",
@@ -78,12 +78,12 @@ aside#left-menu.full-size(
 				)
 
 			.form-controls
-				label(for="spellCheckLanguage") Spell Check
+				label(for="spellCheckLanguage") #{translate("spell_check")}
 				select(
 					name="spellCheckLanguage"
 					ng-model="project.spellCheckLanguage"
 				)
-					option(value="") Off
+					option(value="") #{translate("off")}
 					optgroup(label="Language")
 						for language in languages
 							option(
@@ -91,7 +91,7 @@ aside#left-menu.full-size(
 							)= language.name
 
 			.form-controls
-				label(for="autoComplete") Auto-Complete
+				label(for="autoComplete") #{translate("auto_complete")}
 				select(
 					name="autoComplete"
 					ng-model="settings.autoComplete"
@@ -99,7 +99,7 @@ aside#left-menu.full-size(
 				)
 
 			.form-controls
-				label(for="theme") Theme
+				label(for="theme") #{translate("theme")}
 				select(
 					name="theme"
 					ng-model="settings.theme"
@@ -108,7 +108,7 @@ aside#left-menu.full-size(
 						option(value=theme) #{theme.replace(/_/g, ' ')}
 
 			.form-controls(ng-show="!anonymous")
-				label(for="mode") Keybindings
+				label(for="mode") #{translate("keybindings")}
 				select(
 					name="mode"
 					ng-model="settings.mode"
@@ -118,7 +118,7 @@ aside#left-menu.full-size(
 					option(value='emacs') Emacs
 
 			.form-controls
-				label(for="fontSize") Font Size
+				label(for="fontSize") #{translate("font_size")}
 				select(
 					name="fontSize"
 					ng-model="settings.fontSize"
@@ -127,20 +127,20 @@ aside#left-menu.full-size(
 						option(value=size) #{size}px
 
 			.form-controls
-				label(for="pdfViewer") PDF Viewer
+				label(for="pdfViewer")
 				select(
 					name="pdfViewer"
 					ng-model="settings.pdfViewer"
 				)
-					option(value="pdfjs") Built-In
-					option(value="native") Native
+					option(value="pdfjs") #{translate("built_in")}
+					option(value="native") #{translate("native")}
 
-	h4 Hotkeys
+	h4 #{translate("hotkeys")}
 	ul.list-unstyled.nav
 		li(ng-controller="HotkeysController")
 			a(ng-click="openHotkeysModal()")
 				i.fa.fa-keyboard-o.fa-fw
-				| &nbsp;&nbsp; Show Hotkeys
+				| &nbsp;&nbsp; #{translate("show_hotkeys")}
 
 #left-menu-mask(
 	ng-show="ui.leftMenuShown",
@@ -150,11 +150,11 @@ aside#left-menu.full-size(
 
 script(type='text/ng-template', id='cloneProjectModalTemplate')
 	.modal-header
-		h3 Copy Project
+		h3 #{translate("copy_project")}
 	.modal-body
 		form(name="cloneProjectForm", novalidate)
 			.form-group
-				label New Name
+				label #{translate("new_name")}
 				input.form-control(
 					type="text",
 					placeholder="New Project Name",
@@ -167,10 +167,10 @@ script(type='text/ng-template', id='cloneProjectModalTemplate')
 		button.btn.btn-default(
 			ng-disabled="state.inflight"
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-primary(
 			ng-disabled="cloneProjectForm.$invalid || state.inflight"
 			ng-click="clone()"
 		)
-			span(ng-hide="state.inflight") Copy
-			span(ng-show="state.inflight") Copying...
+			span(ng-hide="state.inflight") #{translate("copy")}
+			span(ng-show="state.inflight") #{translate("copying")}...

+ 21 - 23
services/web/app/views/project/editor/pdf.jade

@@ -9,8 +9,8 @@ div.full-size.pdf(ng-controller="PdfController")
 				ng-class="{'fa-spin': pdf.compiling }"
 			)
 			| &nbsp;&nbsp;
-			span(ng-show="!pdf.compiling") Recompile
-			span(ng-show="pdf.compiling") Compiling...
+			span(ng-show="!pdf.compiling") #{translate("recompile")}
+			span(ng-show="pdf.compiling") #{translate("compiling")}...
 		a.log-btn(
 			href
 			ng-click="toggleLogs()"
@@ -76,29 +76,28 @@ div.full-size.pdf(ng-controller="PdfController")
 	.pdf-uncompiled(ng-show="pdf.uncompiled && !pdf.compiling")
 		| &nbsp;
 		i.fa.fa-level-up.fa-flip-horizontal.fa-2x
-		| &nbsp;&nbsp;Click here to preview your work as a PDF.
+		| &nbsp;&nbsp;#{translate("click_here_to_preview_pdf")}
 
 	.pdf-errors(ng-show="pdf.timedout || pdf.error")
 		.alert.alert-danger(ng-show="pdf.error")
-			strong Server Error. 
-			span Sorry, something went wrong and your project could not be compiled. Please try again in a few moments.
+			strong #{translate("server_error")}
+			span #{translate("somthing_went_wrong_compiling")}
 
-		.alert.alert-danger(ng-show="pdf.timedout") 
+		.alert.alert-danger(ng-show="pdf.timedout")
 			p
-				strong Your compile timed out.
-				|  Sorry, your compile was taking too long and timed out.
-				| This may be due to a problem with your LaTeX code, or too many high-res images.
+				strong #{translate("timedout")}.
+				span #{translate("proj_timed_out_reason")}
 			p
 				a.text-info(href="https://www.sharelatex.com/learn/Debugging_Compilation_timeout_errors", target="_blank")
-					| Please see our help guide for more information.
+					| #{translate("please_see_help_for_more_info")}
 
 	.pdf-logs(ng-show="(pdf.view == 'logs' || pdf.failure) && !pdf.error && !pdf.timeout && !pdf.uncompiled")
 		.alert.alert-success(ng-show="pdf.logEntries.all.length == 0")
-			| No errors, good job!
+			| #{translate("no_errors_good_job")}
 
 		.alert.alert-danger(ng-show="pdf.failure")
-			strong Compile Error. 
-			span Sorry, your LaTeX code couldn't compile for some reason. Please check the errors below for details, or view the raw log.
+			strong #{translate("compile_error")}. 
+			span #{translate("generic_failed_compile_message")}.
 
 		div(ng-repeat="entry in pdf.logEntries.all", ng-controller="PdfLogEntryController")
 			.alert(
@@ -131,7 +130,7 @@ div.full-size.pdf(ng-controller="PdfController")
 						href
 						dropdown-toggle
 					)
-						| Other logs &amp; files 
+						| #{translate("other_logs_and_files")} 
 						span.caret
 					ul.dropdown-menu.dropdown-menu-right
 						li(ng-repeat="file in pdf.outputFiles")
@@ -141,26 +140,25 @@ div.full-size.pdf(ng-controller="PdfController")
 								ng-click="openOutputFile(file)"
 							) {{ file.name }}
 			a.btn.btn-info.btn-sm(href, ng-click="toggleRawLog()")
-				span(ng-show="!pdf.showRawLog") View Raw Logs
-				span(ng-show="pdf.showRawLog") Hide Raw Logs
+				span(ng-show="!pdf.showRawLog") #{translate("view_raw_logs")}
+				span(ng-show="pdf.showRawLog") #{translate("hide_raw_logs")}
 
 		pre(ng-bind="pdf.rawLog", ng-show="pdf.showRawLog")
 
 script(type='text/ng-template', id='clearCacheModalTemplate')
 	.modal-header
-		h3 Clear cache?
+		h3 #{translate("clear_cache")}?
 	.modal-body
-		p This will clear all hidden LaTeX files (.aux, .bbl, etc) from our compile server. 
-			| You generally don't need to do this unless you're having trouble with references. 
-		p Your project files will not be deleted or changed.
+		p #{translate("clear_cache_explanation")}
+		p #{translate("clear_cache_is_safe")}
 	.modal-footer
 		button.btn.btn-default(
 			ng-click="cancel()"
 			ng-disabled="state.inflight"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-info(
 			ng-click="clear()"
 			ng-disabled="state.inflight"
 		) 
-			span(ng-show="!state.inflight") Clear cache
-			span(ng-show="state.inflight") Clearing...
+			span(ng-show="!state.inflight") #{translate("clear_cache")}
+			span(ng-show="state.inflight") #{translate("clearing")}...

+ 11 - 11
services/web/app/views/project/editor/publish-template.jade

@@ -5,11 +5,11 @@ script(type="text/ng-template", id="publishProjectAsTemplateModalTemplate")
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Publish as Template
+		h3 #{translate("publish_as_template")}
 	.modal-body.modal-body-share
 		span(ng-hide="problemTalkingToTemplateApi")
 			form()
-				label(for='Description') Template Description
+				label(for='Description') #{translate("template_description")}
 				.form-group
 					textarea.form-control(
 					rows=5,
@@ -19,11 +19,11 @@ script(type="text/ng-template", id="publishProjectAsTemplateModalTemplate")
 					value=""
 				)
 			div(ng-show="templateDetails.exists").text-center.templateDetails
-				|  Your project was last published at 
+				|  #{translate("project_last_published_at")} 
 				strong {{templateDetails.publishedDate}}. 
-				a(ng-href="{{templateDetails.canonicalUrl}}") View it in template gallery.
+				a(ng-href="{{templateDetails.canonicalUrl}}") #{translate("view_in_template_gallery")}.
 
-		span(ng-show="problemTalkingToTemplateApi") There is a problem with our publishing service, please try again in a few minutes.
+		span(ng-show="problemTalkingToTemplateApi") #{translate("problem_talking_to_publishing_service")}.
 
 
 
@@ -33,20 +33,20 @@ script(type="text/ng-template", id="publishProjectAsTemplateModalTemplate")
 			ng-click="cancel()",
 			ng-disabled="state.publishInflight || state.unpublishInflight"
 		)
-			span Cancel
+			span #{translate("cancel")}
 
 		button.btn.btn-info(
 			ng-click="unpublishTemplate()",
 			ng-disabled="state.publishInflight || state.unpublishInflight"
 			ng-show="templateDetails.exists"
 		) 
-			span(ng-show="!state.unpublishInflight") Unpublish
-			span(ng-show="state.unpublishInflight") Unpublishing...
+			span(ng-show="!state.unpublishInflight") #{translate("unpublish")}
+			span(ng-show="state.unpublishInflight") #{translate("unpublishing")}...
 
 		button.btn.btn-primary(
 			ng-click="publishTemplate()",
 			ng-disabled="state.publishInflight || state.unpublishInflight"
 		)
-			span(ng-show="!state.publishInflight && !templateDetails.exists") Publish
-			span(ng-show="!state.publishInflight && templateDetails.exists") Republish
-			span(ng-show="state.publishInflight") Publishing...
+			span(ng-show="!state.publishInflight && !templateDetails.exists") #{translate("publish")}
+			span(ng-show="!state.publishInflight && templateDetails.exists") #{translate("republish")}
+			span(ng-show="state.publishInflight") #{translate("publishing")}...

+ 26 - 26
services/web/app/views/project/editor/share.jade

@@ -5,26 +5,26 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Share Project
+		h3 #{translate("share_project")}
 	.modal-body.modal-body-share
 		.container-fluid
 			.row.public-access-level(ng-show="project.publicAccesLevel == 'private'")
 				.col-xs-12.text-center
-					| This project is private and can only be accessed by the people below.
+					| #{translate("this_project_is_private")}
 					| &nbsp;&nbsp;
 					a(
 						href
 						ng-click="openMakePublicModal()"
-					) Make Public
+					) #{translate("make_public")}
 			.row.public-access-level(ng-show="project.publicAccesLevel != 'private'")
 				.col-xs-12.text-center
-					strong(ng-if="project.publicAccesLevel == 'readAndWrite'") This project is public and can be edited by anyone with the URL.
-					strong(ng-if="project.publicAccesLevel == 'readOnly'") This project is public and can be viewed by anyone with the URL.
+					strong(ng-if="project.publicAccesLevel == 'readAndWrite'") #{translate("this_project_is_public")}
+					strong(ng-if="project.publicAccesLevel == 'readOnly'") #{translate("this_project_is_public")}
 					| &nbsp;&nbsp;
 					a(
 						href
 						ng-click="openMakePrivateModal()"
-					) Make Private
+					) #{translate("make_private")}
 			.row.project-member
 				.col-xs-8 {{ project.owner.email }}
 				.text-right(
@@ -33,8 +33,8 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
 			.row.project-member(ng-repeat="member in project.members")
 				.col-xs-8 {{ member.email }}
 				.col-xs-3.text-right
-					span(ng-show="member.privileges == 'readAndWrite'") Can Edit
-					span(ng-show="member.privileges == 'readOnly'") Read Only
+					span(ng-show="member.privileges == 'readAndWrite'") #{translate("can_edit")}
+					span(ng-show="member.privileges == 'readOnly'") #{translate("read_only")}
 				.col-xs-1
 					a(
 						href
@@ -45,7 +45,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
 						i.fa.fa-times
 			.row.invite-controls
 				form(ng-show="canAddCollaborators")
-					.small Share with your collaborators
+					.small #{translate("share_with_your_collabs")}
 					.form-group
 						input.form-control(
 							type="email"
@@ -59,19 +59,19 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
 								ng-model="inputs.privileges"
 								name="privileges"
 							)
-								option(value="readAndWrite") Can Edit
-								option(value="readOnly") Read Only
+								option(value="readAndWrite") #{translate("can_edit")}
+								option(value="readOnly") #{translate("read_only")}
 							| &nbsp;&nbsp;
 							button.btn.btn-info(
 								type="submit"
 								ng-click="addMember()"
-							) Share
+							) #{translate("share")}
 				div.text-center(ng-hide="canAddCollaborators")
-					p You need to upgrade your account to add more collaborators.
+					p #{translate("need_to_upgrade_for_more_collabs")}.
 					p
-						a.btn.btn-info(href, ng-click="startFreeTrial('projectMembers')") Start Free Trial
+						a.btn.btn-info(href, ng-click="startFreeTrial('projectMembers')") #{translate("start_free_trial")}
 					p.small(ng-show="startedFreeTrial")
-						| Please refresh this page after starting your free trial.
+						| #{translate("refresh_page_after_starting_free_trial")}.
 
 	.modal-footer
 		.modal-footer-left
@@ -79,7 +79,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
 			span.text-danger.error(ng-show="state.error") {{ state.error }}
 		button.btn.btn-primary(
 			ng-click="done()"
-		) Done
+		) #{translate("done")}
 
 script(type="text/ng-template", id="makePublicModalTemplate")
 	.modal-header
@@ -88,23 +88,23 @@ script(type="text/ng-template", id="makePublicModalTemplate")
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Make project public?
+		h3 #{translate("make_project_public")}?
 	.modal-body.modal-body-share
-		p If you make your project public then anyone with the URL will be able to access it.
+		p #{translate("make_project_public_consequences")}
 		p
 			select.form-control(
 				ng-model="inputs.privileges"
 				name="privileges"
 			)
-				option(value="readAndWrite") Allow public editing
-				option(value="readOnly")     Allow public read only access
+				option(value="readAndWrite") #{translate("allow_public_editing")}
+				option(value="readOnly")     #{translate("allow_public_read_only")}
 	.modal-footer
 		button.btn.btn-default(
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-info(
 			ng-click="makePublic()"
-		) Make public
+		) #{translate("make_public")}
 
 script(type="text/ng-template", id="makePrivateModalTemplate")
 	.modal-header
@@ -113,13 +113,13 @@ script(type="text/ng-template", id="makePrivateModalTemplate")
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Make project private?
+		h3 #{translate("make_project_private")}?
 	.modal-body.modal-body-share
-		p If you make your project private then only the people you choose to share it with will have access.
+		p #{translate("make_project_private_consequences")}
 	.modal-footer
 		button.btn.btn-default(
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-info(
 			ng-click="makePrivate()"
-		) Make private
+		) #{translate("make_private")}

+ 17 - 16
services/web/app/views/project/editor/track-changes.jade

@@ -1,15 +1,15 @@
 div#trackChanges(ng-show="ui.view == 'track-changes'")
 	.upgrade-prompt(ng-show="!project.features.versioning")
 		.message(ng-show="project.owner._id == user.id")
-			p You need to upgrade your account to use the History feature.
+			p #{translate("need_to_upgrade_for_history")}
 			p
 				a.btn.btn-info(
 					href
 					ng-click="startFreeTrial('track-changes')"
-				) Start Free Trial
-			p.small(ng-show="startedFreeTrial") Please refresh the page after starting your free trial.
+				) #{translate("start_free_trial")}
+			p.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
 		.message(ng-show="project.owner._id != user.id")
-			p Please ask the project owner to upgrade to use the History feature.
+			p #{translate("ask_proj_owner_to_upgrade_for_history")}
 
 	aside.change-list(
 		ng-controller="TrackChangesListController"
@@ -73,11 +73,11 @@ div#trackChanges(ng-show="ui.view == 'track-changes'")
 								.name(ng-if="update_user.id == user.id") You
 							div.user(ng-if="update.meta.users.length == 0")
 								.color-square(style="background-color: hsl(100, 100%, 50%)")
-								span Anonymous
+								span #{translate("anonymous")}
 
 			.loading(ng-show="trackChanges.loading")
 				i.fa.fa-spin.fa-refresh
-				| &nbsp;&nbsp;Loading...
+				| &nbsp;&nbsp; #{translate("loading")}...
 
 	.diff-panel.full-size(ng-controller="TrackChangesDiffController")
 		.diff(
@@ -98,7 +98,7 @@ div#trackChanges(ng-show="ui.view == 'track-changes'")
 					a.btn.btn-danger.btn-sm(
 						href,
 						ng-click="openRestoreDiffModal()"
-					) Restore to before these changes
+					) #{translate("restore_to_before_these_changes")}
 			.diff-editor.hide-ace-cursor(
 				ace-editor="track-changes",
 				theme="settings.theme",
@@ -112,17 +112,18 @@ div#trackChanges(ng-show="ui.view == 'track-changes'")
 		.diff-deleted.text-centered(
 			ng-show="trackChanges.diff.deleted"
 		)
-			p.text-serif {{ trackChanges.diff.doc.name }} has been deleted.
+			p.text-serif #{translate("file_has_been_deleted", {filename:"{{ trackChanges.diff.doc.name }} "})}
+
 			p
 				a.btn.btn-primary.btn-lg(
 					href,
 					ng-click="restoreDeletedDoc()"
-				) Restore
+				) #{translate("restore")}
 		.loading-panel(ng-show="trackChanges.diff.loading")
 			i.fa.fa-spin.fa-refresh
-			| &nbsp;&nbsp;Loading...
+			| &nbsp;&nbsp;#{translate("loading")}...
 		.error-panel(ng-show="trackChanges.diff.error")
-			.alert.alert-danger Sorry, something went wrong :(
+			.alert.alert-danger #{translate("generic_something_went_wrong")}
 
 script(type="text/ng-template", id="trackChangesRestoreDiffModalTemplate")
 	.modal-header
@@ -131,17 +132,17 @@ script(type="text/ng-template", id="trackChangesRestoreDiffModalTemplate")
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Restore {{diff.doc.name}}
+		h3 #{translate("restore")} {{diff.doc.name}}
 	.modal-body.modal-body-share
-		p Are you sure you want to restore <strong>{{diff.doc.name}}</strong> to before the changes on {{diff.start_ts | formatDate}}?
+		p #{translate("sure_you_want_to_restore_before", {filename:"<strong>{{diff.doc.name}}</strong>", date:"{{diff.start_ts | formatDate}}"})}
 	.modal-footer
 		button.btn.btn-default(
 			ng-click="cancel()",
 			ng-disabled="state.inflight"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-danger(
 			ng-click="restore()",
 			ng-disabled="state.inflight"
 		)
-			span(ng-show="!state.inflight") Restore
-			span(ng-show="state.inflight") Restoring...
+			span(ng-show="!state.inflight") #{translate("restore")}
+			span(ng-show="state.inflight") #{translate("restoring")} ...

+ 3 - 0
services/web/app/views/project/list.jade

@@ -3,6 +3,9 @@ extends ../layout
 block content
 	//- We need to do .replace(/\//g, '\\/') do that '</script>' -> '<\/script>'
 	//- and doesn't prematurely end the script tag.
+
+
+
 	script(type="text/javascript").
 		window.data = {
 			projects: !{JSON.stringify(projects).replace(/\//g, '\\/')},

+ 28 - 28
services/web/app/views/project/list/modals.jade

@@ -5,7 +5,7 @@ script(type='text/ng-template', id='newTagModalTemplate')
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Create New Folder
+		h3 #{translate("create_new_folder")}
 	.modal-body
 		form(name="newTagForm", novalidate)
 			input.form-control(
@@ -23,12 +23,12 @@ script(type='text/ng-template', id='newTagModalTemplate')
 		button.btn.btn-default(
 			ng-click="cancel()"
 			stop-propagation="click"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-primary(
 			ng-disabled="newTagForm.$invalid"
 			ng-click="create()"
 			stop-propagation="click"
-		) Create
+		) #{translate("create")}
 
 script(type='text/ng-template', id='renameProjectModalTemplate')
 	.modal-header
@@ -37,7 +37,7 @@ script(type='text/ng-template', id='renameProjectModalTemplate')
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Rename Project
+		h3 #{translate("rename_project")}
 	.modal-body
 		form(name="renameProjectForm", novalidate)
 			input.form-control(
@@ -49,11 +49,11 @@ script(type='text/ng-template', id='renameProjectModalTemplate')
 				focus-on="open"
 			)
 	.modal-footer
-		button.btn.btn-default(ng-click="cancel()") Cancel
+		button.btn.btn-default(ng-click="cancel()") #{translate("cancel")}
 		button.btn.btn-primary(
 			ng-click="rename()",
 			ng-disabled="renameProjectForm.$invalid"
-		) Rename
+		) #{translate("rename")}
 
 script(type='text/ng-template', id='cloneProjectModalTemplate')
 	.modal-header
@@ -62,11 +62,11 @@ script(type='text/ng-template', id='cloneProjectModalTemplate')
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Copy Project
+		h3 #{translate("copy_project")}
 	.modal-body
 		form(name="cloneProjectForm", novalidate)
 			.form-group
-				label New Name
+				label #{translate("new_name")}
 				input.form-control(
 					type="text",
 					placeholder="New Project Name",
@@ -79,13 +79,13 @@ script(type='text/ng-template', id='cloneProjectModalTemplate')
 		button.btn.btn-default(
 			ng-disabled="state.inflight"
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-primary(
 			ng-disabled="cloneProjectForm.$invalid || state.inflight"
 			ng-click="clone()"
 		)
-			span(ng-hide="state.inflight") Copy
-			span(ng-show="state.inflight") Copying...
+			span(ng-hide="state.inflight") #{translate("copy")}
+			span(ng-show="state.inflight") #{translate("copying")} ...
 
 script(type='text/ng-template', id='newProjectModalTemplate')
 	.modal-header
@@ -94,7 +94,7 @@ script(type='text/ng-template', id='newProjectModalTemplate')
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 New Project
+		h3 #{translate("new_project")}
 	.modal-body
 		form(novalidate, name="newProjectForm")
 			input.form-control(
@@ -109,13 +109,13 @@ script(type='text/ng-template', id='newProjectModalTemplate')
 		button.btn.btn-default(
 			ng-disabled="state.inflight"
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-primary(
 			ng-disabled="newProjectForm.$invalid || state.inflight"
 			ng-click="create()"
 		)
-			span(ng-hide="state.inflight") Create
-			span(ng-show="state.inflight") Creating...
+			span(ng-hide="state.inflight") #{translate("create")}
+			span(ng-show="state.inflight") #{translate("creating")} ...
 
 script(type='text/ng-template', id='deleteProjectsModalTemplate')
 	.modal-header
@@ -124,22 +124,22 @@ script(type='text/ng-template', id='deleteProjectsModalTemplate')
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 {{action}} Projects
+		h3 {{action}} #{translate("projects")}
 	.modal-body
 		div(ng-show="projectsToDelete.length > 0")
-			p You are about to delete the following projects:
+			p #{translate("about_to_delete_projects")}
 			ul
 				li(ng-repeat="project in projectsToDelete | orderBy:'name'")
 					strong {{project.name}}
 		div(ng-show="projectsToLeave.length > 0")
-			p You are about to leave the following projects:
+			p #{translate("about_to_leave_projects")}
 			ul
 				li(ng-repeat="project in projectsToLeave | orderBy:'name'")
 					strong {{project.name}}
 	.modal-footer
 		button.btn.btn-default(
 			ng-click="cancel()"
-		) Cancel
+		) #{translate("cancel")}
 		button.btn.btn-danger(
 			ng-click="delete()"
 		) {{action}}
@@ -151,7 +151,7 @@ script(type="text/ng-template", id="uploadProjectModalTemplate")
 			data-dismiss="modal"
 			ng-click="cancel()"
 		) &times;
-		h3 Upload Zipped Project
+		h3 #{translate("upload_zipped_project")}
 	.modal-body(
 		fine-upload
 		endpoint="/project/new/upload"
@@ -163,9 +163,9 @@ script(type="text/ng-template", id="uploadProjectModalTemplate")
 		allowed-extensions="['zip']"
 		on-complete-callback="onComplete"
 	)
-		span Upload a zipped project
+		span #{translate("upload_a_zipped_project")}
 	.modal-footer
-		button.btn.btn-default(ng-click="cancel()") Cancel
+		button.btn.btn-default(ng-click="cancel()") #{translate("cancel")}
 
 script(type="text/ng-template", id="userProfileModalTemplate")
 	.modal-header
@@ -174,12 +174,12 @@ script(type="text/ng-template", id="userProfileModalTemplate")
 			data-dismiss="modal"
 			ng-click="done()"
 		) &times;
-		h3 Your Profile
+		h3 #{translate("your_profile")}
 	.modal-body
 		form(enctype='multipart/form-data', method='post')
 
 			.form-group
-				label(for="first_name") First Name
+				label(for="first_name") #{translate("first_name")}
 				input.form-control(
 					type='text',
 					name='first_name',
@@ -189,7 +189,7 @@ script(type="text/ng-template", id="userProfileModalTemplate")
 				)
 
 			.form-group
-				label(for="last_name") Last Name
+				label(for="last_name") #{translate("last_name")}
 				input.form-control(
 					type='text',
 					name='last_name',
@@ -198,7 +198,7 @@ script(type="text/ng-template", id="userProfileModalTemplate")
 				)
 
 			.form-group.user_details_auto_complete
-				label(for="institution") Institution
+				label(for="institution") #{translate("institution")}
 				autocomplete(
 					ng-model="userInfoForm.institution",
 					name="institution",
@@ -209,7 +209,7 @@ script(type="text/ng-template", id="userProfileModalTemplate")
 				)
 
 			.form-group.user_details_auto_complete
-				label(for="role") Role
+				label(for="role") #{translate("role")}
 				autocomplete(
 					ng-model="userInfoForm.role",
 					name="role",
@@ -220,4 +220,4 @@ script(type="text/ng-template", id="userProfileModalTemplate")
 
 
 	.modal-footer
-		button.btn.btn-info(ng-click="done()") Done
+		button.btn.btn-info(ng-click="done()") #{translate("done")}

+ 17 - 17
services/web/app/views/project/list/project-list.jade

@@ -52,7 +52,7 @@
 						role="menu"
 						ng-controller="TagListController"
 					)
-						li.dropdown-header Add to folder
+						li.dropdown-header #{translate("add_to_folder")}
 						li(
 							ng-repeat="tag in tags | filter:nonEmpty | orderBy:'name'",
 							ng-controller="TagDropdownItemController"
@@ -68,25 +68,25 @@
 								|  {{tag.name}}
 						li.divider
 						li
-							a(href="#", ng-click="openNewTagModal()", stop-propagation="click") Create New Folder
+							a(href="#", ng-click="openNewTagModal()", stop-propagation="click") #{translate("create_new_folder")}
 
 				.btn-group(ng-hide="selectedProjects.length != 1").dropdown
 					a.btn.btn-default.dropdown-toggle(
 						href='#',
 						data-toggle="dropdown"
-					) More 
+					) #{translate("more")}
 						span.caret
 					ul.dropdown-menu.dropdown-menu-right(role="menu")
 						li(ng-show="getFirstSelectedProject().accessLevel == 'owner'")
 							a(
 								href='#',
 								ng-click="openRenameProjectModal()"
-							) Rename
+							) #{translate("rename")}
 						li
 							a(
 								href='#',
 								ng-click="openCloneProjectModal()"
-							) Make a copy
+							) #{translate("make_copy")}
 
 			.btn-toolbar(ng-show="filter == 'archived'")
 				.btn-group(ng-hide="selectedProjects.length < 1")
@@ -96,7 +96,7 @@
 						data-toggle="tooltip",
 						data-placement="bottom",
 						ng-click="restoreSelectedProjects()"
-					) Restore
+					) #{translate("restore")}
 
 				.btn-group(ng-hide="selectedProjects.length < 1")
 					a.btn.btn-danger(
@@ -105,7 +105,7 @@
 						data-toggle="tooltip",
 						data-placement="bottom",
 						ng-click="openDeleteProjectsModal()"
-					) Delete Forever
+					) #{translate("delete_forever")}
 
 .row.row-spaced
 	.col-xs-12
@@ -123,13 +123,13 @@
 								select-all,
 								type="checkbox"
 							)
-							span.header.clickable(ng-click="changePredicate('name')") Title
+							span.header.clickable(ng-click="changePredicate('name')") #{translate("title")}
 								i.tablesort.fa(ng-class="getSortIconClass('name')")
 						.col-xs-2
-							span.header.clickable(ng-click="changePredicate('accessLevel')") Owner
+							span.header.clickable(ng-click="changePredicate('accessLevel')") #{translate("owner")}
 								i.tablesort.fa(ng-class="getSortIconClass('accessLevel')")
 						.col-xs-4
-							span.header.clickable(ng-click="changePredicate('lastUpdated')") Last Modified
+							span.header.clickable(ng-click="changePredicate('lastUpdated')") #{translate("last_modified")}
 								i.tablesort.fa(ng-class="getSortIconClass('lastUpdated')")
 				li.project_entry.container-fluid(
 					ng-repeat="project in visibleProjects | orderBy:predicate:reverse",
@@ -162,13 +162,13 @@
 				)
 					.row
 						.col-xs-12.text-centered
-							small No projects
+							small #{translate("no_projects")}
 			div.welcome.text-centered(ng-if="projects.length == 0", ng-cloak)
-				h2 Welcome to ShareLaTeX!
-				p New to LaTeX? Start by having a look at our
-					a(href="/templates")  templates
-					|  or
-					a(href="/learn")  help guides
+				h2 #{translate("welcome_to_sl")}
+				p #{translate("new_to_latex_look_at")}
+					a(href="/templates") #{translate("templates").toLowerCase()}
+					|   #{translate("or")}
+					a(href="/learn")  #{translate("latex_help_guide")}
 					| ,
 					br 
-					| or create your first project on the left.
+					|  #{translate("or_create_project_left")}

+ 28 - 29
services/web/app/views/project/list/side-bar.jade

@@ -2,54 +2,56 @@
 	a.btn.btn-primary.dropdown-toggle(
 		href="#",
 		data-toggle="dropdown"
-	) New Project
+	)
+		| #{translate("new_project")}
+
 	ul.dropdown-menu(role="menu")
 		li
 			a(
 				href,
 				ng-click="openCreateProjectModal()"
-			) Blank Project
+			) #{translate("blank_project")}
 		li
 			a(
 				href,
 				ng-click="openCreateProjectModal('example')"
-			) Example Project
+			) #{translate("example_project")}
 		li
 			a(
 				href,
 				ng-click="openUploadProjectModal()"
-			) Upload Project
+			) #{translate("upload_project")}
 		li.divider
-		li.dropdown-header Templates
+		li.dropdown-header #{translate("templates")}
 		li
-			a.menu-indent(href="/templates/cv") CV or Resume
+			a.menu-indent(href="/templates/cv") #{translate("cv_or_resume")}
 		li
-			a.menu-indent(href="/templates/cover-letters") Cover Letter
+			a.menu-indent(href="/templates/cover-letters") #{translate("cover_letter")}
 		li
-			a.menu-indent(href="/templates/journals") Journal Article
+			a.menu-indent(href="/templates/journals") #{translate("journal_article")}
 		li
-			a.menu-indent(href="/templates/presentations") Presentation
+			a.menu-indent(href="/templates/presentations") #{translate("presentation")}
 		li
-			a.menu-indent(href="/templates/thesis") Thesis
+			a.menu-indent(href="/templates/thesis") #{translate("thesis")}
 		li
-			a.menu-indent(href="/templates/bibliographies") Bibliographies
+			a.menu-indent(href="/templates/bibliographies") #{translate("bibliographies")}
 		li
-			a.menu-indent(href="/templates") View All »
+			a.menu-indent(href="/templates") #{translate("view_all")} »
 
 .row-spaced(ng-if="projects.length > 0", ng-cloak)
 	ul.list-unstyled.folders-menu(
 		ng-controller="TagListController"
 	)
 		li(ng-class="{active: (filter == 'all')}")
-			a(href, ng-click="filterProjects('all')") All projects
+			a(href, ng-click="filterProjects('all')") #{translate("all_projects")}
 		li(ng-class="{active: (filter == 'owned')}")
-			a(href, ng-click="filterProjects('owned')") Your projects
+			a(href, ng-click="filterProjects('owned')") #{translate("your_projects")}
 		li(ng-class="{active: (filter == 'shared')}")
-			a(href, ng-click="filterProjects('shared')") Shared with you
+			a(href, ng-click="filterProjects('shared')") #{translate("shared_with_you")}
 		li(ng-class="{active: (filter == 'archived')}")
-			a(href, ng-click="filterProjects('archived')") Deleted projects
+			a(href, ng-click="filterProjects('archived')") #{translate("deleted_projects")}
 		li
-			h2 Folders
+			h2 #{translate("folders")}
 		li(
 			ng-repeat="tag in tags | filter:nonEmpty",
 			ng-class="{active: tag.selected}",
@@ -67,14 +69,14 @@
 		li(ng-cloak)
 			a.tag(href, ng-click="openNewTagModal()")
 				i.fa.fa-fw.fa-plus
-				span.name New Folder
+				span.name #{translate("new_folder")}
 
 .row-spaced(ng-if="projects.length == 0", ng-cloak)
 	.first-project
 		div
 			i.fa.fa-arrow-up.fa-2x
 		div
-			strong Create your first project!
+			strong #{translate("create_your_first_project")}
 
 - if (showUserDetailsArea)
 	.row-spaced#userProfileInformation(ng-if="projects.length > 0", ng-cloak)
@@ -84,22 +86,19 @@
 				.progress
 					.progress-bar.progress-bar-info(ng-style="{'width' : (percentComplete+'%')}")
 
-				p.small
-					| Your profile is 
-					strong {{percentComplete}}% 
-					| complete
-
+				p.small #{translate("profile_complete_percentage_test", {percentval:"{{percentComplete}}"})}
+				
 				button#completeUserProfileInformation.btn.btn-info(
 					ng-hide="formVisable",
 					ng-click="openUserProfileModal()"
-				) Complete
+				) #{translate("complete")}
 
 -if (settings.enableSubscriptions && !hasSubscription)
 	.row-spaced(ng-if="projects.length > 0", ng-cloak).text-centered
 		hr
-		p.small You are using the free version of ShareLaTeX.
+		p.small #{translate("on_free_sl")}
 		p
-			a(href="/user/subscription/plans").btn.btn-primary Upgrade
+			a(href="/user/subscription/plans").btn.btn-primary #{translate("upgrade")}
 		p.small
-			| or unlock some free bonus features by 
-			a(href="/user/bonus") sharing ShareLaTeX.
+			| #{translate("or_unlock_features_bonus")} 
+			a(href="/user/bonus") #{translate("sharing_sl")} .

+ 20 - 20
services/web/app/views/referal/bonus.jade

@@ -10,12 +10,12 @@ block content
 							.row
 								.col-md-12
 									.page-header
-										h1 Help us spread the word about ShareLaTeX.
+										h1 #{translate("help_us_spread_word")}.
 
 
 							.row
 								.col-md-10.col-md-offset-1
-									h2 Share ShareLaTeX with your friends and colleagues and unlock the rewards below
+									h2 #{translate("share_sl_to_get_rewards")}
 
 							.row
 								.col-md-8.col-md-offset-2.bonus-banner
@@ -29,31 +29,31 @@ block content
 							.row
 								.col-md-8.col-md-offset-2.bonus-banner
 									.title
-										a(href='#', onclick='postToFeed(); return false;').facebook Post on Facebook
+										a(href='#', onclick='postToFeed(); return false;').facebook #{translate("post_on_facebook")}
 
 							.row
 								.col-md-8.col-md-offset-2.bonus-banner
 									.title
-										a(href="https://plus.google.com/share?url=#{encodeURIComponent(buildReferalUrl('gp'))}", onclick="javascript:window.open(this.href, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;").google-plus Share us on Google+
+										a(href="https://plus.google.com/share?url=#{encodeURIComponent(buildReferalUrl('gp'))}", onclick="javascript:window.open(this.href, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;").google-plus #{translate("share_us_on_googleplus")}
 									
 							.row
 								.col-md-8.col-md-offset-2.bonus-banner
 									.title
-										a(href='mailto:?subject=Online LaTeX editor you may like &body=Hey, I have been using the online LaTeX editor ShareLaTeX recently and thought you might like to check it out. #{encodeURIComponent(buildReferalUrl("e"))}', title='Share by Email').email Email us to your friends
+										a(href='mailto:?subject=Online LaTeX editor you may like &body=Hey, I have been using the online LaTeX editor ShareLaTeX recently and thought you might like to check it out. #{encodeURIComponent(buildReferalUrl("e"))}', title='Share by Email').email #{translate("email_us_to_your_friends")}
 
 							.row
 								.col-md-8.col-md-offset-2.bonus-banner
 									.title
-										a(href='#link-modal', data-toggle="modal", ng-click="openLinkToUsModal()").link Link to us from your website
+										a(href='#link-modal', data-toggle="modal", ng-click="openLinkToUsModal()").link #{translate("link_to_us")}
 
 							.row
 								.col-md-10.col-md-offset-1.bonus-banner
-									h2.direct-link Direct Link
+									h2.direct-link #{translate("direct_link")}
 									pre.text-centered #{buildReferalUrl("d")}
 
 							.row.ab-bonus
 								.col-md-10.col-md-offset-1.bonus-banner
-									p.thanks When someone starts using ShareLaTeX after your recommendation we'll give you some <strong>free stuff</strong> to say thanks! Check your progress below.
+									p.thanks #{translate("sl_gives_you_free_stuff_see_progress_below")}
 							.row.ab-bonus
 								.col-md-10.col-md-offset-1.bonus-banner(style="position: relative; height: 30px; margin-top: 20px;")
 									- for (var i = 0; i <= 10; i++) {
@@ -67,24 +67,24 @@ block content
 								.col-md-10.col-md-offset-1.bonus-banner
 									.progress
 										- if (refered_user_count == 0)
-											div(style="text-align: center; padding: 4px;") Spread the word and fill this bar up
+											div(style="text-align: center; padding: 4px;") #{translate("spread_the_word_and_fill_bar")}
 										.progress-bar.progress-bar-info(style="width: #{refered_user_count}0%")
 
 							.row.ab-bonus
 								.col-md-10.col-md-offset-1.bonus-banner(style="position: relative; height: 70px;")
-									.perk(style="left: 10%;", class = refered_user_count >= 1 ? "active" : "") One free collaborator
-									.perk(style="left: 30%;", class = refered_user_count >= 3 ? "active" : "") Three free collaborators
-									.perk(style="left: 60%;", class = refered_user_count >= 6 ? "active" : "") Free Dropbox and History
-									.perk(style="left: 90%;", class = refered_user_count >= 9 ? "active" : "") Free Professional account
+									.perk(style="left: 10%;", class = refered_user_count >= 1 ? "active" : "") #{translate("one_free_collab")}
+									.perk(style="left: 30%;", class = refered_user_count >= 3 ? "active" : "") #{translate("three_free_collab")}
+									.perk(style="left: 60%;", class = refered_user_count >= 6 ? "active" : "") #{translate("free_dropbox_and_history")}
+									.perk(style="left: 90%;", class = refered_user_count >= 9 ? "active" : "") #{translate("free_prof_account")}
 
 							.row.ab-bonus
 								.col-md-10.col-md-offset-1.bonus-banner
 									- if (refered_user_count == 0)
-										p.thanks You've not introduced anyone to ShareLaTeX yet. Get sharing!
+										p.thanks #{translate("you_not_introed_anyone_to_sl")}
 									- else if (refered_user_count == 1)
-										p.thanks You've introduced <strong>#{refered_user_count}</strong> person to ShareLaTeX. Good job, but can you get some more?
+										p.thanks #{translate("you_introed_small_number", {numberOfPeople:"<strong>#{refered_user_count}</strong>"})}
 									- else
-										p.thanks You've introduced <strong>#{refered_user_count}</strong> people to ShareLaTeX. Good job!
+										p.thanks #{translate("you_introed_high_number", {numberOfPeople:"<strong>#{refered_user_count}</strong>"})}
 						
 						script(type="text/ng-template", id="BonusLinkToUsModal")
 							.modal-header
@@ -93,22 +93,22 @@ block content
 									data-dismiss="modal"
 									ng-click="cancel()"
 								) &times;
-								h3 Link to ShareLaTeX
+								h3 #{translate("link_to_sl")}
 							.modal-body.modal-body-share.link-modal
 
-								p You can link to ShareLaTeX with the following HTML:
+								p #{translate("can_link_to_sl_with_html")}
 								p
 									textarea.col-md-12(readonly=true)
 										<a href="#{buildReferalUrl("d")}">Online LaTeX Editor ShareLaTeX</a>
 
-								p Thanks!
+								p #{translate("thanks")}!
 
 
 							.modal-footer()
 								button.btn.btn-default(
 									ng-click="cancel()",
 								)
-									span Close
+									span #{translate("close")}
 
 
 

+ 20 - 20
services/web/app/views/subscriptions/dashboard.jade

@@ -9,14 +9,14 @@ mixin printPlan(plan)
 				strong #{plan.name}
 			td
 				-if (plan.annual)
-					| $#{plan.price / 100} / year
+					| $#{plan.price / 100} / #{translate("year")}
 				-else
-					| $#{plan.price / 100} / month	
+					| $#{plan.price / 100} / #{translate("month")}	
 			td
 				-if (subscription.state == "free-trial")
-					a(href="/user/subscription/new?planCode=#{plan.planCode}").btn.btn-success Subscribe to this plan
+					a(href="/user/subscription/new?planCode=#{plan.planCode}").btn.btn-success #{translate("subscribe_to_this_plan")}
 				-else if (plan.planCode == subscription.planCode)
-					button.btn.disabled Your plan
+					button.btn.disabled #{translate("your_plan")}
 				-else
 					form(action="/user/subscription/update",method="post")
 						input(type="hidden", name="_csrf", value=csrfToken)
@@ -34,44 +34,44 @@ block content
 				.col-md-8.col-md-offset-2
 					.card
 						.page-header
-							h1 Your Subscription
+							h1 #{translate("your_subscription")}
 						case subscription.state
 							when "free-trial"
-								p You are currently using a free trial which expires on <strong>#{subscription.expiresAt}</strong>.
-								p Choose a plan below to subscribe to.
+								p #{translate("on_free_trial_expiring_at", {expiresAt:"<strong>#{subscription.expiresAt}</strong>"})}
+								p #{translate("choose_a_plan_below")}
 							when "active"
-								p You are currently subscribed to the <strong>#{subscription.name}</strong> plan. 
-									a(href, ng-click="changePlan = true") Change Plan.
-								p The next payment of <strong>#{subscription.price}</strong> will be collected on <strong>#{subscription.nextPaymentDueAt}</strong>
+								p #{translate("currently_subscribed_to_plan", {planName:"<strong>#{subscription.name}</strong>"})}
+									a(href, ng-click="changePlan = true") #{translate("change_plan")}.
+								p #{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>#{subscription.price}</strong>", collectionDate:"<strong>#{subscription.nextPaymentDueAt}</strong>"})}
 								p.pull-right
 
 								p: form(action="/user/subscription/cancel",method="post")
 									input(type="hidden", name="_csrf", value=csrfToken)
-									a(href="/user/subscription/billing-details/edit").btn.btn-info Update your billing details
+									a(href="/user/subscription/billing-details/edit").btn.btn-info #{translate("update_your_billing_details")}
 									| &nbsp;
 									input(type="submit", value="Cancel your subscription").btn.btn-primary#cancelSubscription
 							when "canceled"
-								p You are currently subscribed to the <strong>#{subscription.name}</strong> plan.
-								p Your subscription has been canceled and will terminate on <strong>#{subscription.nextPaymentDueAt}</strong>. No further payments will be taken.
+								p #{translate("currently_subscribed_to_plan", {planName:"<strong>#{subscription.name}</strong>"})}
+								p #{translate("subscription_canceled_and_terminate_on_x", {terminateDate:"<strong>#{subscription.nextPaymentDueAt}</strong>"})}
 								p: form(action="/user/subscription/reactivate",method="post")
 									input(type="hidden", name="_csrf", value=csrfToken)
 									input(type="submit",value="Reactivate your subscription").btn.btn-success
 							when "expired"
-								p Your subscription has expired.	
-									a(href="/user/subscription/plans") Create New Subscription
+								p #{translate("your_subscription_has_expired")}	
+									a(href="/user/subscription/plans") #{translate("create_new_subscription")}
 							default
-								p There is a problem with your subscription. Please contact us for more information.
+								p #{translate("problem_with_subscription_contact_us")}
 
 						-if(subscription.groupPlan)
-							a(href="/subscription/group").btn.btn-success Manage Group
+							a(href="/subscription/group").btn.btn-success #{translate("manage_group")}
 
 						div(ng-show="changePlan", ng-cloak)
 							hr
-							h2 Change plan
+							h2 #{translate("change_plan")}
 							p: table.table
 								tr
-									th Name
-									th Price
+									th #{translate("name")}
+									th #{translate("price")}
 									th
 								mixin printPlans(plans.studentAccounts)
 								mixin printPlans(plans.individualMonthlyPlans)

+ 2 - 2
services/web/app/views/subscriptions/edit-billing-details.jade

@@ -11,8 +11,8 @@ block content
 				.col-md-6.col-md-offset-3
 					.card
 						.page-header
-							h1.text-centered Update Your Billing Details
-						#billingDetailsForm Loading billing details form...
+							h1.text-centered #{translate("update_your_billing_details")}
+						#billingDetailsForm #{translate("loading_billing_form")}...
 	
 
 	script(type="text/javascript").

+ 9 - 9
services/web/app/views/subscriptions/group_admin.jade

@@ -8,13 +8,13 @@ block content
 					.card(ng-controller="GroupMembersController")
 						.page-header
 							.pull-right(ng-cloak)
-								small(ng-show="selectedUsers.length == 0") You have added <strong>{{ users.length }}</strong> of <strong>{{ groupSize }}</strong> available members
+								small(ng-show="selectedUsers.length == 0") #{translate("you_have_added_x_of_group_size_y", {addedUsersSize:"<strong>{{ users.length }}</strong>", groupSize:"<strong>{{ groupSize }}</strong>"})}
 								a.btn.btn-danger(
 									href,
 									ng-show="selectedUsers.length > 0"
 									ng-click="removeMembers()"
-								) Remove from group
-							h1 Group Account
+								) #{translate("remove_from_group")}
+							h1 #{translate("group_account")}
 
 						.row-spaced-small
 							ul.list-unstyled.structured-list(
@@ -28,11 +28,11 @@ block content
 												select-all,
 												type="checkbox"
 											)
-											span.header Email
+											span.header #{translate("email")}
 										.col-md-5
-											span.header Name
+											span.header #{translate("name")}
 										.col-md-2
-											span.header Registered
+											span.header #{translate("registered")}
 								li.container-fluid(
 									ng-repeat="user in users | orderBy:'email':true",
 									ng-controller="GroupMemberListItemController"
@@ -57,12 +57,12 @@ block content
 								)
 									.row
 										.col-md-12.text-centered
-											small No members
+											small #{translate("no_members")}
 
 						div(ng-if="users.length < groupSize", ng-cloak)
 							hr
 							p
-								.small Add more members
+								.small #{translate("add_more_members")}
 								form.form
 									.row
 										.col-xs-6
@@ -74,7 +74,7 @@ block content
 												on-enter="addMembers()"
 											)
 										.col-xs-6
-											button.btn.btn-primary(ng-click="addMembers()") Add
+											button.btn.btn-primary(ng-click="addMembers()") #{translate("add")}
 
 	script(type="text/javascript").
 		window.users = !{JSON.stringify(users)};

+ 2 - 2
services/web/app/views/subscriptions/new.jade

@@ -11,8 +11,8 @@ block content
 				.col-md-6.col-md-offset-3
 					.card
 						.page-header
-							h1.text-centered New Subscription
-						#subscribeForm Loading subscription form...
+							h1.text-centered #{translate("new_subscription")}
+						#subscribeForm #{translate("loading_billing_form")}...
 
 	script(type="text/javascript")
 		ga('send', 'event', 'pageview', 'payment_form', "#{plan_code}")

+ 49 - 49
services/web/app/views/subscriptions/plans.jade

@@ -7,11 +7,11 @@ block content
 				.row
 					.col-md-12
 						.page-header.centered.plans-header.text-centered
-							h1 Start Your 30-Day Free Trial Today!
+							h1 #{translate("start_30_day_trial")}
 
 				.row
 					.col-md-8.col-md-offset-2
-						p.text-centered ShareLaTeX is the world's easiest to use LaTeX editor. Stay up to date with your collaborators, keep track of all changes to your work, and use our LaTeX environment from anywhere in the world.
+						p.text-centered #{translate("sl_benefits_plans")}
 
 				.row(ng-cloak)
 					.col-md-12
@@ -20,55 +20,55 @@ block content
 								a(
 									href,
 									ng-click="switchToMonthly()"
-								) Monthly
+								) #{translate("monthly")}
 							li(ng-class="{'active': ui.view == 'annual'}")
 								a(
 									href
 									ng-click="switchToAnnual()"
-								) Annual
+								) #{translate("annual")}
 							li(ng-class="{'active': ui.view == 'student'}")
 								a(
 									href,
 									ng-click="switchToStudent()"
-								) Half Price Student Plans
+								) #{translate("half_price_student")} 
 
 				.row(ng-cloak)
 					.col-md-12
 						.card-group.text-centered(ng-if="ui.view == 'monthly' || ui.view == 'annual'")
 							.card
 								.card-header
-									h2 Personal
-								.circle Free
+									h2 #{translate("personal")} 
+								.circle #{translate("free")} 
 								ul.list-unstyled
-									li Only one collaborator
+									li #{translate("one_collaborator")}
 									li &nbsp;
 									li &nbsp;
 									li
 										br
-										a.btn.btn-info(href="/register") Sign up now
+										a.btn.btn-info(href="/register") #{translate("sign_up_now")}
 							.card.highlighted
 								.card-header
-									h2 Collaborator
+									h2 #{translate("collaborator")}
 								.circle
 									span(ng-if="ui.view == 'monthly'")
 										| $15
 										span.small /mo
 									span(ng-if="ui.view == 'annual'")
 										| $180
-										span.small/yr
+										span.small /yr
 								ul.list-unstyled
 									li 
-										strong 10 collaborators per project
-									li Full document history
-									li Sync to Dropbox
+										strong #{translate("collabs_per_proj", {collabcount:10})}
+									li #{translate("full_doc_history")}
+									li #{translate("sync_to_dropbox")}
 									li
 										br
 										a.btn.btn-info(
 											ng-href="#{baseUrl}/user/subscription/new?planCode=collaborator{{ ui.view == 'annual' && '-annual' || ''}}_free_trial", ng-click="signUpNowClicked('collaborator')"
-										) Start Free Trial!
+										) #{translate("start_free_trial")}
 							.card
 								.card-header
-									h2 Professional
+									h2 #{translate("professional")}
 								.circle 
 									span(ng-if="ui.view == 'monthly'")
 										| $30
@@ -78,91 +78,91 @@ block content
 										span.small /yr
 								ul.list-unstyled
 									li
-										strong Unlimited collaborators
-									li Full document history
-									li Sync to Dropbox
+										strong #{translate("unlimited_collabs")}
+									li #{translate("full_doc_history")}
+									li #{translate("sync_to_dropbox")}
 									li
 										br
 										a.btn.btn-info(
 											ng-href="#{baseUrl}/user/subscription/new?planCode=professional{{ ui.view == 'annual' && '-annual' || ''}}_free_trial", ng-click="signUpNowClicked('professional')"
-										) Start Free Trial!
+										) #{translate("start_free_trial")}
 
 						.card-group.text-centered(ng-if="ui.view == 'student'")
 							.card
 								.card-header
-									h2 Personal
-								.circle Free
+									h2 #{translate("personal")}
+								.circle #{translate("free")}
 								ul.list-unstyled
-									li Only one collaborator
+									li #{translate("one_collaborator")}
 									li &nbsp;
 									li &nbsp;
 									li
 										br
-										a.btn.btn-info(href="/register") Sign up now
+										a.btn.btn-info(href="/register") #{translate("sign_up_now")}
 							.card.highlighted
 								.card-header
-									h2 Student
+									h2 #{translate("student")}
 								.circle
 									span
 										| $8
 										span.small /mo
 								ul.list-unstyled
 									li
-										strong 6 collaborators per project
-									li Full document history
-									li Sync to Dropbox
+										strong #{translate("collabs_per_proj", {collabcount:6})}
+									li #{translate("full_doc_history")}
+									li #{translate("sync_to_dropbox")}
 									li
 										br
 										a.btn.btn-info(
 											ng-href="#{baseUrl}/user/subscription/new?planCode=student_free_trial", ng-click="signUpNowClicked('student')"
-										) Start Free Trial!
+										) #{translate("start_free_trial")}
 							.card
 								.card-header
-									h2 Student (Annual)
+									h2 #{translate("student")} (#{translate("annual")})
 								.circle
 									span
 										| $80
 										span.small /yr
 								ul.list-unstyled
 									li
-										strong 6 collaborators per project
-									li Full document history
-									li Sync to Dropbox
+										strong #{translate("collabs_per_proj", {collabcount:6})}
+									li #{translate("full_doc_history")}
+									li #{translate("sync_to_dropbox")}
 									li
 										br
 										a.btn.btn-info(
 											ng-href="#{baseUrl}/user/subscription/new?planCode=stud-ann_free_trial", ng-click="signUpNowClicked('student')"
-										) Start Free Trial!
+										) #{translate("start_free_trial")}
 
 				.row(ng-cloak)
-					p.text-centered Choose the plan that works for you with our 30-day free trial. Cancel at any time.
+					p.text-centered #{translate("start_free_trial")} #{translate("choose_plan_works_for_you")}
 					
 				.row(ng-cloak)
 					.col-md-8.col-md-offset-2
 						.alert.alert-info.text-centered
-							| Interested in using ShareLaTeX with a group, team or department wide account?
+							| #{translate("interested_in_group_licence")}
 							br
-							a(href, ng-click="openGroupPlanModal()") Get in touch for details!
+							a(href, ng-click="openGroupPlanModal()") #{translate("get_in_touch_for_details")}
 						
 					script(type="text/ng-template", id="groupPlanModalTemplate")
 						.modal-header
-							h3 Group Plan Enquiry
+							h3 #{translate("group_plan_enquiry")}
 						.modal-body
 							form(name='form1', autocomplete='off', enctype='multipart/form-data', method='post', novalidate='', action='https://sharelatex.wufoo.com/forms/z7x3p3/#public', _lpchecked='1')
 								.form-group
-									label(for='Field9') Name
+									label(for='Field9') #{translate("name")}
 									input.form-control(name='Field9', type='text', value='', maxlength='255', tabindex='1', onkeyup='')
 								
 								.form-group
-									label(for='Field11') Email
+									label(for='Field11') #{translate("email")}
 									input.form-control(name='Field11', type='email', spellcheck='false', value='', maxlength='255', tabindex='2')
 								
 								.form-group
-									label(for='Field12') University
+									label(for='Field12') #{translate("university")}
 									input.form-control(name='Field12', type='text', value='', maxlength='255', tabindex='3', onkeyup='')
 									
 								.form-group
-									label(for='Field13') Position
+									label(for='Field13') #{translate("position")}
 									input.form-control(name='Field13', type='text', value='', maxlength='255', tabindex='4', onkeyup='')
 								
 								.form-group
@@ -176,19 +176,19 @@ block content
 				.row
 					.col-md-12
 						.page-header.plans-header.plans-subheader.text-centered
-							h2 Enjoy all of these great features
+							h2 #{translate("enjoy_these_features")}
 					.col-md-4
 						.card.features.text-centered
 							i.fa.fa-file-text-o.fa-5x
-							h4 Unlimited projects
-							p Create as many projects as you need.
+							h4 #{translate("unlimited_projects")}
+							p #{translate("create_unlimited_projects")}
 					.col-md-4
 						.card.features.text-centered
 							i.fa.fa-clock-o.fa-5x
-							h4 Full document history
-							p Never lose a step, we've got your back.
+							h4 #{translate("full_doc_history")}
+							p #{translate("never_loose_work")}
 					.col-md-4
 						.card.features.text-centered
 							i.fa.fa-dropbox.fa-5x
-							h4 Sync to Dropbox
-							p Access your projects everywhere.
+							h4 #{translate("sync_to_dropbox")}
+							p #{translate("access_projects_anywhere")}

+ 11 - 12
services/web/app/views/subscriptions/successful_subscription.jade

@@ -7,23 +7,22 @@ block content
 				.col-md-8.col-md-offset-2
 					.card
 						.page-header
-							h2 Thanks for subscribing!
+							h2 #{translate("thanks_for_subscribing")}
 						.alert.alert-success
-							p Your card will be charged soon.
-							p The next payment of <strong>#{subscription.price}</strong> will be collected on <strong>#{subscription.nextPaymentDueAt}</strong>.
-							p If you do not want to be charged again 
-								a(href="/user/subscription") click here to cancel.
+							p #{translate("your_card_will_be_charged_soon")}
+							p #{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"<strong>#{subscription.price}</strong>", collectionDate:"<strong>#{subscription.nextPaymentDueAt}</strong>"})}
+							p #{translate("if_you_dont_want_to_be_charged")}
+								a(href="/user/subscription") #{translate("click_here_to_cancel")}.
 
 						p
 							- if (subscription.groupPlan == true)
-								a.btn.btn-success.btn-large(href="/subscription/group") Add your first group members now
+								a.btn.btn-success.btn-large(href="/subscription/group") #{translate("add_your_first_group_member_now")}
 						p.letter-from-founders
-							p Thank you for subscribing to the #{subscription.name} plan. It's support from people like yourself that allows ShareLaTeX to continue to grow and improve.
-
-							p If there is anything you ever need please feel free to contact us directly at 
+							p #{translate("thanks_for_subscribing_you_help_sl", {planName:subscription.name})}
+							p #{translate("need_anything_contact_us_at")} 
 								a(href='mailto:support@sharelatex.com') support@sharelatex.com
-								| . It goes straight to both our inboxes.
-							p Regards,
+								| . #{translate("goes_straight_to_our_inboxes")}.
+							p #{translate("regards")},
 								br
 								| Henry and James
 							.portraits
@@ -33,4 +32,4 @@ block content
 								span.img-circle
 									img(src="/img/about/james_allen.jpg") 
 						p
-							a.btn.btn-primary(href="/project") &lt; Back to your projects
+							a.btn.btn-primary(href="/project") &lt; #{translate("back_to_your_projects")}

+ 1 - 1
services/web/app/views/templates/index.jade

@@ -7,7 +7,7 @@ block content
 				
 				.col-md-2
 					h2
-						a(href="/templates") Templates
+						a(href="/templates") #{translate("templates")}
 				.col-md-8
 					form.project-search.form-horizontal(role="form")
 						.form-group.has-feedback.has-feedback-left.col-md-12

+ 1 - 1
services/web/app/views/templates/tag.jade

@@ -6,7 +6,7 @@ block content
 			.row
 				.page-header
 					h2
-						a(href="/templates") Templates
+						a(href="/templates") #{translate("templates")}
 						|  ›
 						a(href=tag.tagPagePath)  #{tag.name}
 				.row

+ 3 - 3
services/web/app/views/templates/template.jade

@@ -6,7 +6,7 @@ block content
 			.row
 				.page-header
 					h2
-						a(href="/templates") Templates
+						a(href="/templates") #{translate("templates")}
 						|  ›
 						- if(tag)
 							a(href=tag.tagPagePath)  #{tag.name}
@@ -22,7 +22,7 @@ block content
 
 					.col-md-6
 						.template-details-section
-							h3 About
+							h3 #{translate("about")}
 							div !{template.description}
 							div(ng-controller="openInSlController").download-buttons
 								a.btn.btn-primary.btn-large(href=template.open_in_sharelatex_url, ng-click='open()', ng-disabled="isDisabled", rel='nofollow') {{openInSlText}}
@@ -47,7 +47,7 @@ block content
 								a.addthis_button_compact
 							script(type='text/javascript', src='//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-517c16586439faa7')
 
-						h3 Comment
+						h3 #{translate("comment")}
 						#disqus_thread
 						script(type='text/javascript').
 							/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */

+ 6 - 6
services/web/app/views/user/login.jade

@@ -7,7 +7,7 @@ block content
 				.col-md-6.col-md-offset-3.col-lg-4.col-lg-offset-4
 					.card
 						.page-header
-							h1 Log In
+							h1 #{translate("log_in")}
 						form(async-form="login", name="loginForm", action='/login', ng-cloak)
 							input(name='_csrf', type='hidden', value=csrfToken)
 							input(name='redir', type='hidden', value=redir)
@@ -23,7 +23,7 @@ block content
 									focus="true"
 								)
 								span.small.text-primary(ng-show="loginForm.email.$invalid && loginForm.email.$dirty")
-									| Must be an email address
+									| #{translate("must_be_email_address")}
 							.form-group
 								input.form-control(
 									type='password',
@@ -33,12 +33,12 @@ block content
 									ng-model="password"
 								)
 								span.small.text-primary(ng-show="loginForm.password.$invalid && loginForm.password.$dirty")
-									| Required
+									| #{translate("required")}
 							.actions
 								button.btn-primary.btn(
 									type='submit',
 									ng-disabled="loginForm.inflight"
 								)
-									span(ng-show="!loginForm.inflight") Login
-									span(ng-show="loginForm.inflight") Logging in...
-								a.pull-right(href='/user/password/reset') Forgot your password?
+									span(ng-show="!loginForm.inflight") #{translate("login")}
+									span(ng-show="loginForm.inflight") #{translate("logging_in")}...
+								a.pull-right(href='/user/password/reset') #{translate("forgot_your_password")}?

+ 5 - 5
services/web/app/views/user/passwordReset.jade

@@ -7,7 +7,7 @@ block content
 				.col-md-6.col-md-offset-3.col-lg-4.col-lg-offset-4
 					.card
 						.page-header
-							h1 Password Reset
+							h1 #{translate("password_reset")}
 						.messageArea
 						form(
 							async-form="password-reset-request",
@@ -18,9 +18,9 @@ block content
 							input(type="hidden", name="_csrf", value=csrfToken)
 							form-messages(for="passwordResetForm")
 								.alert.alert-success(ng-show="passwordResetForm.response.success")
-									| You have been sent an email to complete your password reset.
+									| #{translate("password_reset_email_sent")}
 							.form-group
-								label(for='email') Please enter your email address 
+								label(for='email') #{translate("please_enter_email")}
 								input.form-control(
 									type='email',
 									name='email',
@@ -31,9 +31,9 @@ block content
 								)
 								span.small.text-primary(
 									ng-show="passwordResetForm.email.$invalid && passwordResetForm.email.$dirty"
-								) Must be a valid email address
+								) #{translate("must_be_email_address")}
 							.actions
 								button.btn.btn-primary(
 									type='submit',
 									ng-disabled="passwordResetForm.$invalid"
-								) Request password reset
+								) #{translate("request_password_reset")}

+ 13 - 12
services/web/app/views/user/register.jade

@@ -6,24 +6,25 @@ block content
 			.row
 				.registration_message
 					if sharedProjectData.user_first_name !== undefined
-						h1 #{sharedProjectData.user_first_name} would like you to view '#{sharedProjectData.project_name}'
-						div Join ShareLaTeX to view this project
+						h1  #{translate("user_wants_you_to_see_project", {username:sharedProjectData.user_first_name, projectname:sharedProjectData.project_name})}
+						div #{translate("join_sl_to_view_project")}
 					else if newTemplateData.templateName !== undefined
-						h1 Please register to edit the '#{newTemplateData.templateName}' template
-						div Already have a ShareLaTeX account? 
-							a(href="/login") Login here
+						h1 #{translate("register_to_edit_template", {templatename:newTemplateData.templateName})}
+
+						div #{translate("already_have_sl_account")}
+							a(href="/login") #{translate("login_here")}
 
 			.row
 				.col-md-6.col-md-offset-3.col-lg-4.col-lg-offset-4
 					.card
 						.page-header
-							h1 Register
+							h1 #{translate("register")}
 						form(async-form="register", name="registerForm", action="/register", ng-cloak)
 							input(name='_csrf', type='hidden', value=csrfToken)
 							input(name='redir', type='hidden', value=redir)
 							form-messages(for="registerForm")
 							.form-group
-								label(for='email') Email
+								label(for='email') #{translate("email")}
 								input.form-control(
 									type='email',
 									name='email',
@@ -35,9 +36,9 @@ block content
 									focus="true"
 								)
 								span.small.text-primary(ng-show="registerForm.email.$invalid && registerForm.email.$dirty")
-									| Must be an email address
+									| #{translate("must_be_email_address")}
 							.form-group
-								label(for='password') Password
+								label(for='password') #{translate("password")}
 								input.form-control(
 									type='password',
 									name='password',
@@ -46,11 +47,11 @@ block content
 									ng-model="password"
 								)
 								span.small.text-primary(ng-show="registerForm.password.$invalid && registerForm.password.$dirty")
-									| Required
+									| #{translate("required")}
 							.actions
 								button.btn-primary.btn(
 									type='submit'
 									ng-disabled="registerForm.inflight"
 								)
-									span(ng-show="!registerForm.inflight") Register
-									span(ng-show="registerForm.inflight") Registering...
+									span(ng-show="!registerForm.inflight") #{translate("register")}
+									span(ng-show="registerForm.inflight") #{translate("registering")}...

+ 2 - 2
services/web/app/views/user/restricted.jade

@@ -6,8 +6,8 @@ block content
 			.row
 				.col-md-8.col-md-offset-2.text-center
 					.page-header
-						h2 Restricted, sorry you don't have permission to load this page.
+						h2 #{translate("restricted_no_permission")}
 					p
 						a(href="/")
 							i.fa.fa-arrow-circle-o-left
-							|  Take me home!
+							|  #{translate("take_me_home")}

+ 5 - 5
services/web/app/views/user/setPassword.jade

@@ -7,7 +7,7 @@ block content
 				.col-md-6.col-md-offset-3.col-lg-4.col-lg-offset-4
 					.card
 						.page-header
-							h1 Reset your password
+							h1 #{translate("reset_your_password")}
 						form(
 							async-form="password-reset",
 							name="passwordResetForm",
@@ -17,8 +17,8 @@ block content
 							input(type="hidden", name="_csrf", value=csrfToken)
 							form-messages(for="passwordResetForm")
 								.alert.alert-success(ng-show="passwordResetForm.response.success")
-									| Your password has been reset. 
-									a(href='/login') Login here
+									| #{translate("password_has_been_reset")}
+									a(href='/login') #{translate("login_here")}
 
 							.form-group
 								input.form-control(
@@ -31,7 +31,7 @@ block content
 								)
 								span.small.text-primary(
 									ng-show="passwordResetForm.password.$invalid && passwordResetForm.password.$dirty"
-								) Required
+								) #{translate("required")}
 								input(
 									type="hidden",
 									name="passwordResetToken",
@@ -41,4 +41,4 @@ block content
 								button.btn.btn-primary(
 									type='submit',
 									ng-disabled="passwordResetForm.$invalid"
-								) Set new password
+								) #{translate("set_new_password")}

+ 35 - 38
services/web/app/views/user/settings.jade

@@ -7,20 +7,20 @@ block content
 				.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
 					.card
 						.page-header
-							h1 Account Settings
+							h1  #{translate("account_settings")}
 						.account-settings(ng-controller="AccountSettingsController", ng-cloak)
 							form-messages(for="settingsForm")
 								.alert.alert-success(ng-show="settingsForm.response.success")
-									| Thanks, your settings have been updated.
+									|  #{translate("thanks_settings_updated")}
 							form-messages(for="changePasswordForm")
 							.container-fluid
 								.row
 									.col-md-5
-										h3 Update Account Info
+										h3  #{translate("update_account_info")}
 										form(async-form="settings", name="settingsForm", action="/user/settings", novalidate)
 											input(type="hidden", name="_csrf", value=csrfToken)
 											.form-group
-												label(for='email') Email
+												label(for='email')  #{translate("email")}
 												input.form-control(
 													type='email',
 													name='email',
@@ -31,16 +31,16 @@ block content
 													ng-model-options="{ updateOn: 'blur' }"
 												)
 												span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
-													| Must be an email address
+													|  #{translate("must_be_email_address")}
 											.form-group
-												label(for='firstName').control-label First Name
+												label(for='firstName').control-label #{translate("first_name")}
 												input.form-control(
 													type='text',
 													name='first_name',
 													value=user.first_name
 												)
 											.form-group
-												label(for='lastName').control-label Last Name 
+												label(for='lastName').control-label #{translate("last_name")}
 												input.form-control(
 													type='text',
 													name='last_name',
@@ -50,14 +50,14 @@ block content
 												button.btn.btn-primary(
 													type='submit',
 													ng-disabled="settingsForm.$invalid"
-												) Update
+												)  #{translate("update")}
 
 									.col-md-5.col-md-offset-1
-										h3 Change Password
+										h3  #{translate("change_password")}
 										form(async-form="changepassword", name="changePasswordForm", action="/user/password/update", novalidate)
 											input(type="hidden", name="_csrf", value=csrfToken)
 											.form-group
-												label(for='currentPassword') Current Password
+												label(for='currentPassword') #{translate("current_password")}
 												input.form-control(
 													type='password',
 													name='currentPassword',
@@ -66,9 +66,9 @@ block content
 													required
 												)
 												span.small.text-primary(ng-show="changePasswordForm.currentPassword.$invalid && changePasswordForm.currentPassword.$dirty")
-													| Required
+													| #{translate("required")}
 											.form-group
-												label(for='newPassword1') New Password
+												label(for='newPassword1') #{translate("new_password")}
 												input.form-control(
 													id='newPassword1',
 													type='password',
@@ -78,9 +78,9 @@ block content
 													required
 												)
 												span.small.text-primary(ng-show="changePasswordForm.newPassword1.$invalid && changePasswordForm.newPassword1.$dirty")
-													| Required
+													| #{translate("required")}
 											.form-group
-												label(for='newPassword2') Confirm New Password
+												label(for='newPassword2') #{translate("confirm_new_password")}
 												input.form-control(
 													type='password',
 													name='newPassword2',
@@ -89,60 +89,57 @@ block content
 													equals="newPassword1"
 												)
 												span.small.text-primary(ng-show="changePasswordForm.newPassword2.$invalid && changePasswordForm.newPassword2.$dirty")
-													| Doesn't match
+													| #{translate("doesnt_match")}
 											.actions
 												button.btn.btn-primary(
 													type='submit',
 													ng-disabled="changePasswordForm.$invalid"
-												) Change
+												) #{translate("change")}
 
 							hr.soften
 
-							h3 Dropbox Integration 
+							h3 #{translate("dropbox_integration")} 
 								span.small
-									a(href='/help/kb/dropbox-2') (Learn more)
+									a(href='/help/kb/dropbox-2') (#{translate("learn_more")})
 							- if(!userHasDropboxFeature)
-										.alert.alert-info Dropbox sync is a premium feature &nbsp; &nbsp;
-											a.btn.btn-info(href='/user/subscription/plans') Upgrade
+										.alert.alert-info #{translate("dropbox_is_premium")} &nbsp; &nbsp;
+											a.btn.btn-info(href='/user/subscription/plans') #{translate("upgrade")}
 									- else if(userIsRegisteredWithDropbox)
-										.alert.alert-success Account is linked!
+										.alert.alert-success #{translate("account_is_linked")}
 										row
-											a(href='/dropbox/unlink').btn Unlink Dropbox
+											a(href='/dropbox/unlink').btn #{translate("unlink_dropbox")}
 									- else
-										a.btn.btn-info(href='/dropbox/beginAuth') Link to dropbox
+										a.btn.btn-info(href='/dropbox/beginAuth') #{translate("link_to_dropbox")}
 
 							hr.soften
 
 							p.small
-								| Every few months we send a newsletter out summarizing the new features available.
-								| If you would prefer not to receive this email then you can unsubscribe at any time: 
+								| #{translate("newsletter_info_and_unsubscribe")} 
 								a(
 									href,
 									ng-click="unsubscribe()",
 									ng-show="subscribed && !unsubscribing"
-								) Unsubscribe
+								) #{translate("unsubscribe")}
 								span(
 									ng-show="unsubscribing"
 								)
-									i.fa.fa-spin.fa-refresh 
-									|  Unsubscribing
+									i.fa.fa-spin.fa-refresh
+									| #{translate("unsubscribing")}
 								span.text-success(
 									ng-show="!subscribed"
 								)
 									i.fa.fa-check
-									|  Unsubscribed
+									| #{translate("unsubscribed")}
 
-							p Need to leave?
-								a(href, ng-click="deleteAccount()")  Delete your account
+							p #{translate("need_to_leave")} 
+								a(href, ng-click="deleteAccount()")  #{translate("delete_your_account")}
 
 
 	script(type='text/ng-template', id='deleteAccountModalTemplate')
 		.modal-header
-			h3 Delete Account
+			h3 #{translate("delete_account")}
 		.modal-body
-			p
-				| You are about to permanently <strong>delete all of your account data</strong>, including your projects
-				| and settings. Please type DELETE into the box below to proceed.
+			p !{translate("delete_account_warning_message")}
 			form(novalidate, name="deleteAccountForm")
 				input.form-control(
 					type="text",
@@ -154,11 +151,11 @@ block content
 		.modal-footer
 			button.btn.btn-default(
 				ng-click="cancel()"
-			) Cancel
+			) #{translate("cancel")}
 			button.btn.btn-danger(
 				ng-disabled="!state.isValid || state.inflight"
 				ng-click="delete()"
 			)
-				span(ng-hide="state.inflight") Delete
-				span(ng-show="state.inflight") Deleting...
+				span(ng-hide="state.inflight") #{translate("delete")}
+				span(ng-show="state.inflight") #{translate("deleting")}...
 

+ 12 - 0
services/web/config/settings.defaults.coffee

@@ -107,6 +107,11 @@ module.exports =
 	# that are sent out, generated links, etc.
 	siteUrl : 'http://localhost:3000'
 
+	# cooke domain
+	# use full domain for cookies to only be accesabble from that domain,
+	# replace subdomain with dot to have them accessable on all subdomains
+	# cookieDomain: ".sharelatex.dev"
+
 	# Same, but with http auth credentials.
 	httpAuthSiteUrl: 'http://#{httpAuthUser}:#{httpAuthPass}@localhost:3000'
 
@@ -134,6 +139,13 @@ module.exports =
 		features: defaultFeatures
 	}]
 
+
+	# i18n
+	# ------
+	# 
+	i18n:
+		defaultLng:"en-US"
+
 	# Spelling languages
 	# ------------------
 	#

+ 0 - 1
services/web/public/coffee/main.coffee

@@ -18,6 +18,5 @@ define [
 	"directives/selectAll"
 	"directives/maxHeight"
 	"filters/formatDate"
-
 ], () ->
 	angular.bootstrap(document.body, ["SharelatexApp"])

+ 3 - 3
services/web/public/coffee/main/project-list.coffee

@@ -1,7 +1,9 @@
 define [
 	"base"
 ], (App) ->
-	App.factory "queuedHttp", ["$http", "$q", ($http, $q) ->
+
+
+	App.factory "queuedHttp", ($http, $q) ->
 		pendingRequests = []
 		inflight = false
 
@@ -48,8 +50,6 @@ define [
 
 		return queuedHttp
 
-	]
-
 	App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout) ->
 		$scope.projects = window.data.projects
 		$scope.tags = window.data.tags

+ 5 - 4
services/web/test/UnitTests/coffee/Authentication/AuthenticationControllerTests.coffee

@@ -96,10 +96,11 @@ describe "AuthenticationController", ->
 				@AuthenticationController.login(@req, @res)
 
 			it "should return an error", ->
-				expect(@res.body).to.deep.equal
-					message:
-						text: 'Your email or password were incorrect. Please try again',
-						type: 'error'
+				# @res.body.should.exist
+				expect(@res.body.message).to.exist
+					# message:
+					# 	text: 'Your email or password were incorrect. Please try again',
+					# 	type: 'error'
 
 			it "should not establish a session", ->
 				@AuthenticationController._establishUserSession.called.should.equal false

+ 2 - 0
services/web/test/UnitTests/coffee/PasswordReset/PasswordResetControllerTests.coffee

@@ -30,6 +30,8 @@ describe "PasswordResetController", ->
 				email:@email
 				passwordResetToken:@token
 				password:@password
+			i18n:
+				translate:->
 
 		@res = {}
 

+ 0 - 7
services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee

@@ -78,7 +78,6 @@ describe "Subscription controller sanboxed", ->
 
 			it "should set the correct variables for the template", ->
 				should.exist @res.renderedVariables.signature
-				@res.renderedVariables.title.should.equal "Update Billing Details"
 				@res.renderedVariables.successURL.should.equal "#{Settings.siteUrl}/user/subscription/update"
 				@res.renderedVariables.user.id.should.equal @user.id
 
@@ -137,9 +136,6 @@ describe "Subscription controller sanboxed", ->
 			@res.callback = done
 			@SubscriptionController.successful_subscription @req, @res
 
-		it "should render the thank you page", ->
-			@res.renderedVariables.title.should.equal "Thank you!"
-
 	describe "userSubscriptionPage", ->
 		describe "with a user without a subscription", ->
 			beforeEach (done) ->
@@ -164,7 +160,6 @@ describe "Subscription controller sanboxed", ->
 				done()
 			
 			it "should set the correct subscription details", ->
-				@res.renderedVariables.title.should.equal "Your Subscription"
 				@res.renderedVariables.subscription.should.deep.equal @activeRecurlySubscription
 
 		describe "with a user with a free trial", ->
@@ -175,11 +170,9 @@ describe "Subscription controller sanboxed", ->
 				@SubscriptionController.userSubscriptionPage @req, @res
 
 			it "should render the dashboard", ->
-				@res.rendered.should.equal true
 				@res.renderedTemplate.should.equal "subscriptions/dashboard"
 			
 			it "should set the correct subscription details", ->
-				@res.renderedVariables.title.should.equal "Your Subscription"
 				@res.renderedVariables.subscription.should.deep.equal @activeRecurlySubscription
 
 	describe "createSubscription", ->

+ 2 - 0
services/web/test/UnitTests/coffee/helpers/MockRequest.coffee

@@ -5,6 +5,8 @@ class MockRequest
 
 	params: {}
 	query: {}
+	i18n:
+		translate:->
 	
 module.exports = MockRequest