diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index b5184c40a..c98d5f87e 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -1,13 +1,8 @@
-<?php
+<?php namespace BookStack\Http\Controllers;
 
-namespace BookStack\Http\Controllers;
-
-use BookStack\Activity;
 use Exception;
 use Illuminate\Http\Request;
-
 use Illuminate\Http\Response;
-use BookStack\Http\Requests;
 use BookStack\Repos\UserRepo;
 use BookStack\Services\SocialAuthService;
 use BookStack\User;
@@ -152,7 +147,8 @@ class UserController extends Controller
             'name'             => 'min:2',
             'email'            => 'min:2|email|unique:users,email,' . $id,
             'password'         => 'min:5|required_with:password_confirm',
-            'password-confirm' => 'same:password|required_with:password'
+            'password-confirm' => 'same:password|required_with:password',
+            'setting'          => 'array'
         ]);
 
         $user = $this->user->findOrFail($id);
@@ -175,6 +171,13 @@ class UserController extends Controller
             $user->external_auth_id = $request->get('external_auth_id');
         }
 
+        // Save an user-specific settings
+        if ($request->has('setting')) {
+            foreach ($request->get('setting') as $key => $value) {
+                setting()->putUser($user, $key, $value);
+            }
+        }
+
         $user->save();
         session()->flash('success', trans('settings.users_edit_success'));
 
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index f1d95f5c0..c55cc9ab8 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -1,6 +1,4 @@
-<?php
-
-namespace BookStack\Http;
+<?php namespace BookStack\Http;
 
 use Illuminate\Foundation\Http\Kernel as HttpKernel;
 
@@ -30,6 +28,7 @@ class Kernel extends HttpKernel
             \Illuminate\View\Middleware\ShareErrorsFromSession::class,
             \BookStack\Http\Middleware\VerifyCsrfToken::class,
             \Illuminate\Routing\Middleware\SubstituteBindings::class,
+            \BookStack\Http\Middleware\Localization::class
         ],
         'api' => [
             'throttle:60,1',
diff --git a/app/Http/Middleware/Localization.php b/app/Http/Middleware/Localization.php
new file mode 100644
index 000000000..3624e0e01
--- /dev/null
+++ b/app/Http/Middleware/Localization.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Carbon\Carbon;
+use Closure;
+
+class Localization
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        $defaultLang = config('app.locale');
+        $locale = setting()->getUser(user(), 'language', $defaultLang);
+        app()->setLocale($locale);
+        Carbon::setLocale($locale);
+        return $next($request);
+    }
+}
diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php
index 2b3c64695..c27df7af4 100644
--- a/app/Http/Middleware/RedirectIfAuthenticated.php
+++ b/app/Http/Middleware/RedirectIfAuthenticated.php
@@ -1,6 +1,4 @@
-<?php
-
-namespace BookStack\Http\Middleware;
+<?php namespace BookStack\Http\Middleware;
 
 use Closure;
 use Illuminate\Contracts\Auth\Guard;
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 66f40569a..40a1eef3d 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -1,6 +1,5 @@
 <?php namespace BookStack\Providers;
 
-use Carbon\Carbon;
 use Illuminate\Support\ServiceProvider;
 use Validator;
 
@@ -18,8 +17,6 @@ class AppServiceProvider extends ServiceProvider
             $imageMimes = ['image/png', 'image/bmp', 'image/gif', 'image/jpeg', 'image/jpg', 'image/tiff', 'image/webp'];
             return in_array($value->getMimeType(), $imageMimes);
         });
-
-        Carbon::setLocale(config('app.locale'));
     }
 
     /**
diff --git a/app/Services/SettingService.php b/app/Services/SettingService.php
index 1dc30f8a0..40094a513 100644
--- a/app/Services/SettingService.php
+++ b/app/Services/SettingService.php
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Services;
 
 use BookStack\Setting;
+use BookStack\User;
 use Illuminate\Contracts\Cache\Repository as Cache;
 
 /**
@@ -43,6 +44,18 @@ class SettingService
         return $this->formatValue($value, $default);
     }
 
+    /**
+     * Get a user-specific setting from the database or cache.
+     * @param User $user
+     * @param $key
+     * @param bool $default
+     * @return bool|string
+     */
+    public function getUser($user, $key, $default = false)
+    {
+        return $this->get($this->userKey($user->id, $key), $default);
+    }
+
     /**
      * Gets a setting value from the cache or database.
      * Looks at the system defaults if not cached or in database.
@@ -111,6 +124,16 @@ class SettingService
         return $setting !== null;
     }
 
+    /**
+     * Check if a user setting is in the database.
+     * @param $key
+     * @return bool
+     */
+    public function hasUser($key)
+    {
+        return $this->has($this->userKey($key));
+    }
+
     /**
      * Add a setting to the database.
      * @param $key
@@ -128,6 +151,28 @@ class SettingService
         return true;
     }
 
+    /**
+     * Put a user-specific setting into the database.
+     * @param User $user
+     * @param $key
+     * @param $value
+     * @return bool
+     */
+    public function putUser($user, $key, $value)
+    {
+        return $this->put($this->userKey($user->id, $key), $value);
+    }
+
+    /**
+     * Convert a setting key into a user-specific key.
+     * @param $key
+     * @return string
+     */
+    protected function userKey($userId, $key = '')
+    {
+        return 'user:' . $userId . ':' . $key;
+    }
+
     /**
      * Removes a setting from the database.
      * @param $key
@@ -143,6 +188,16 @@ class SettingService
         return true;
     }
 
+    /**
+     * Delete settings for a given user id.
+     * @param $userId
+     * @return mixed
+     */
+    public function deleteUserSettings($userId)
+    {
+        return $this->setting->where('setting_key', 'like', $this->userKey($userId) . '%')->delete();
+    }
+
     /**
      * Gets a setting model from the database for the given key.
      * @param $key
diff --git a/app/helpers.php b/app/helpers.php
index b5be0fd11..6decb08e9 100644
--- a/app/helpers.php
+++ b/app/helpers.php
@@ -60,11 +60,12 @@ function userCan($permission, Ownable $ownable = null)
  * Helper to access system settings.
  * @param $key
  * @param bool $default
- * @return mixed
+ * @return bool|string|\BookStack\Services\SettingService
  */
-function setting($key, $default = false)
+function setting($key = null, $default = false)
 {
     $settingService = app(\BookStack\Services\SettingService::class);
+    if (is_null($key)) return $settingService;
     return $settingService->get($key, $default);
 }
 
diff --git a/readme.md b/readme.md
index fa5c48fe0..63d43e4b7 100644
--- a/readme.md
+++ b/readme.md
@@ -40,13 +40,19 @@ php artisan db:seed --class=DummyContentSeeder --database=mysql_testing
 
 Once done you can run `phpunit` in the application root directory to run all tests.
 
-## Website and Docs 
+## Translations
 
-The website and project docs are currently stored in the [BookStackApp/website](https://github.com/BookStackApp/website) repo. The docs are stored as markdown files in the `resources/docs` folder
+As part of BookStack v0.14 support for translations has been built in. All text strings can be found in the `resources/lang` folder where each language option has its own folder. To add a new language you should copy the `en` folder to an new folder (eg. `fr` for french) then go through and translate all text strings in those files, leaving the keys and file-names intact. If a language string is missing then the `en` translation will be used. To show the language option in the user preferences language drop-down you will need to add your language to the options found at the bottom of the `resources/lang/en/settings.php` file. A system-wide language can also be set in the `.env` file like so: `APP_LANG=en`.
+ 
+ Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time.
+
+## Website, Docs & Blog 
+
+The website project docs & Blog can be found in the [BookStackApp/website](https://github.com/BookStackApp/website) repo.
 
 ## License
 
-BookStack is provided under the MIT License.
+The BookStack source is provided under the MIT License.
 
 ## Attribution
 
diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php
index e61df19d9..e1b544ef9 100644
--- a/resources/lang/en/settings.php
+++ b/resources/lang/en/settings.php
@@ -101,12 +101,23 @@ return [
     'users_edit_success' => 'User successfully updated',
     'users_avatar' => 'User Avatar',
     'users_avatar_desc' => 'This image should be approx 256px square.',
+    'users_preferred_language' => 'Preferred Language',
     'users_social_accounts' => 'Social Accounts',
     'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
     'users_social_connect' => 'Connect Account',
     'users_social_disconnect' => 'Disconnect Account',
     'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
     'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+
+    // Since these labels are already localized this array does not need to be
+    // translated in the language-specific files.
+    // DELETE BELOW IF COPIED FROM EN
+    ///////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'de' => 'Deutsch'
+    ]
+    ///////////////////////////////////
 ];
 
 
diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php
index 4db00e31f..c5d512725 100644
--- a/resources/views/users/edit.blade.php
+++ b/resources/views/users/edit.blade.php
@@ -5,8 +5,6 @@
 
     @include('settings/navbar', ['selected' => 'users'])
 
-
-
     <div class="container small">
         <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="post">
             <div class="row">
@@ -42,7 +40,14 @@
                           'name' => 'image_id',
                           'imageClass' => 'avatar large'
                       ])
-
+                </div>
+                <div class="form-group">
+                    <label for="user-language">{{ trans('settings.users_preferred_language') }}</label>
+                    <select name="setting[language]" id="user-language">
+                        @foreach(trans('settings.language_select') as $lang => $label)
+                            <option @if(setting()->getUser($user, 'language') === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
+                        @endforeach
+                    </select>
                 </div>
             </div>
         </div>
diff --git a/tests/Permissions/RolesTest.php b/tests/Permissions/RolesTest.php
index 0f6a7a150..7f1895b91 100644
--- a/tests/Permissions/RolesTest.php
+++ b/tests/Permissions/RolesTest.php
@@ -583,7 +583,6 @@ class RolesTest extends TestCase
     public function test_image_delete_own_permission()
     {
         $this->giveUserPermissions($this->user, ['image-update-all']);
-//        $admin = $this->getAdmin();
         $page = \BookStack\Page::first();
         $image = factory(\BookStack\Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $this->user->id, 'updated_by' => $this->user->id]);