diff --git a/.babelrc b/.babelrc
index b5d40d62..e511eb2f 100644
--- a/.babelrc
+++ b/.babelrc
@@ -16,13 +16,66 @@
     "@babel/plugin-proposal-function-sent",
     "@babel/plugin-proposal-export-namespace-from",
     "@babel/plugin-proposal-numeric-separator",
-    "@babel/plugin-proposal-throw-expressions"
+    "@babel/plugin-proposal-throw-expressions",
+    [
+      "prismjs", {
+        "languages": [
+          "markup",
+          "css",
+          "clike",
+          "javascript",
+          "c",
+          "bash",
+          "basic",
+          "cpp",
+          "csharp",
+          "arduino",
+          "ruby",
+          "elixir",
+          "fsharp",
+          "go",
+          "graphql",
+          "handlebars",
+          "haskell",
+          "ini",
+          "java",
+          "json",
+          "kotlin",
+          "latex",
+          "less",
+          "makefile",
+          "markdown",
+          "matlab",
+          "nginx",
+          "objectivec",
+          "perl",
+          "php",
+          "powershell",
+          "pug",
+          "python",
+          "typescript",
+          "rust",
+          "scss",
+          "scala",
+          "smalltalk",
+          "sql",
+          "stylus",
+          "swift",
+          "vbnet",
+          "yaml"
+        ],
+        "plugins": ["line-numbers"],
+        "theme": "dark",
+        "css": true
+      }
+    ]
   ],
   "presets": [
     [
       "@babel/preset-env", {
         "useBuiltIns": "entry",
-        "corejs": 3
+        "corejs": 3,
+        "debug": true
       }
     ]
   ]
diff --git a/.gitignore b/.gitignore
index a35d5683..6e55106e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ npm-debug.log*
 # Generated assets
 /assets
 server/views/master.pug
+server/views/legacy.pug
 server/views/setup.pug
 
 # Webpack
diff --git a/client/components/admin/admin-locale.vue b/client/components/admin/admin-locale.vue
index 7fefc486..ec9cd9cc 100644
--- a/client/components/admin/admin-locale.vue
+++ b/client/components/admin/admin-locale.vue
@@ -52,8 +52,6 @@
                 v-toolbar(color='primary', dark, dense, flat)
                   v-toolbar-title
                     .subheading {{ $t('admin:locale.namespacing') }}
-                  v-spacer
-                  v-chip(label, color='white', small).primary--text coming soon
                 v-card-text
                   v-switch(
                     v-model='namespacing'
@@ -102,28 +100,35 @@
               v-card.animated.fadeInUp.wait-p4s
                 v-toolbar(color='teal', dark, dense, flat)
                   v-toolbar-title
-                    .subheading {{ $t('admin:locale.download') }}
-                v-list(two-line, dense)
-                  template(v-for='(lc, idx) in locales')
-                    v-list-tile(:key='lc.code')
-                      v-list-tile-avatar
-                        v-avatar.teal.white--text(size='40') {{lc.code.toUpperCase()}}
-                      v-list-tile-content
-                        v-list-tile-title(v-html='lc.name')
-                        v-list-tile-sub-title(v-html='lc.nativeName')
-                      v-list-tile-action(v-if='lc.isRTL')
-                        v-chip(label, small, :class='$vuetify.dark ? `text--lighten-5` : `text--darken-2`').caption.grey--text RTL
-                      v-list-tile-action(v-if='lc.isInstalled && lc.installDate < lc.updatedAt')
-                        v-btn(icon, @click='download(lc)')
-                          v-icon.blue--text cached
-                      v-list-tile-action(v-else-if='lc.isInstalled')
+                    .subheading {{ $t('admin:locale.downloadTitle') }}
+                v-data-table(
+                  :headers='headers',
+                  :items='locales',
+                  hide-actions,
+                  item-key='code',
+                  :rows-per-page-items='[-1]'
+                )
+                  template(v-slot:items='lc')
+                    td
+                      v-chip.white--text(label, color='teal', small) {{lc.item.code}}
+                    td
+                      strong {{lc.item.name}}
+                    td
+                      span {{ lc.item.nativeName }}
+                    td.text-xs-center
+                      v-icon(v-if='lc.item.isRTL') check
+                    td
+                      .d-flex.align-center.pl-4
+                        .caption.mr-2(:class='lc.item.availability <= 33 ? `red--text` : (lc.item.availability <= 66) ? `orange--text` : `green--text`') {{lc.item.availability}}%
+                        v-progress-circular(:value='lc.item.availability', width='2', size='20', :color='lc.item.availability <= 33 ? `red` : (lc.item.availability <= 66) ? `orange` : `green`')
+                    td.text-xs-center
+                      v-progress-circular(v-if='lc.item.isDownloading', indeterminate, color='blue', size='20', :width='2')
+                      v-btn(v-else-if='lc.item.isInstalled && lc.item.installDate < lc.item.updatedAt', icon, @click='download(lc.item)')
+                        v-icon.blue--text cached
+                      v-btn(v-else-if='lc.item.isInstalled', icon, @click='download(lc.item)')
                         v-icon.green--text check
-                      v-list-tile-action(v-else-if='lc.isDownloading')
-                        v-progress-circular(indeterminate, color='blue', size='20', :width='3')
-                      v-list-tile-action(v-else)
-                        v-btn(icon, @click='download(lc)')
-                          v-icon.grey--text cloud_download
-                    v-divider.my-0(inset, v-if='idx < locales.length - 1')
+                      v-btn(v-else, icon, @click='download(lc.item)')
+                        v-icon.grey--text cloud_download
               v-card.wiki-form.mt-3.animated.fadeInUp.wait-p5s
                 v-toolbar(color='teal', dark, dense, flat)
                   v-toolbar-title
@@ -158,6 +163,46 @@ export default {
   computed: {
     installedLocales() {
       return _.filter(this.locales, ['isInstalled', true])
+    },
+    headers() {
+      return [
+        {
+          text: this.$t('admin:locale.code'),
+          align: 'left',
+          value: 'code',
+          width: 10
+        },
+        {
+          text: this.$t('admin:locale.name'),
+          align: 'left',
+          value: 'name'
+        },
+        {
+          text: this.$t('admin:locale.nativeName'),
+          align: 'left',
+          value: 'nativeName'
+        },
+        {
+          text: this.$t('admin:locale.rtl'),
+          align: 'center',
+          value: 'isRTL',
+          sortable: false,
+          width: 10
+        },
+        {
+          text: this.$t('admin:locale.availability'),
+          align: 'center',
+          value: 'availability',
+          width: 100
+        },
+        {
+          text: this.$t('admin:locale.download'),
+          align: 'center',
+          value: 'code',
+          sortable: false,
+          width: 100
+        }
+      ]
     }
   },
   methods: {
@@ -173,6 +218,8 @@ export default {
       if (resp.succeeded) {
         lc.isDownloading = false
         lc.isInstalled = true
+        lc.updatedAt = new Date().toISOString()
+        lc.installDate = lc.updatedAt
         this.$store.commit('showNotification', {
           message: `Locale ${lc.name} has been installed successfully.`,
           style: 'success',
diff --git a/client/components/admin/admin-utilities-auth.vue b/client/components/admin/admin-utilities-auth.vue
index e697fb8b..12695a0a 100644
--- a/client/components/admin/admin-utilities-auth.vue
+++ b/client/components/admin/admin-utilities-auth.vue
@@ -3,14 +3,14 @@
     v-toolbar(flat, color='primary', dark, dense)
       .subheading {{ $t('admin:utilities.authTitle') }}
     v-card-text
-      v-subheader.pl-0 Generate New Authentication Public / Private Key Certificates
+      v-subheader.pl-0.primary--text Generate New Authentication Public / Private Key Certificates
       .body-1 This will invalidate all current session tokens and cause all users to be logged out.
       .body-1.red--text You will need to log back in after the operation.
       v-btn(outline, color='primary', @click='regenCerts', :disabled='loading').ml-0.mt-3
         v-icon(left) build
         span Proceed
       v-divider.my-3
-      v-subheader.pl-0 Reset Guest User
+      v-subheader.pl-0.primary--text Reset Guest User
       .body-1 This will reset the guest user to its default parameters and permissions.
       v-btn(outline, color='primary', @click='resetGuest', :disabled='loading').ml-0.mt-3
         v-icon(left) build
diff --git a/client/components/admin/admin-utilities-cache.vue b/client/components/admin/admin-utilities-cache.vue
index d4e3c215..8e54d1fa 100644
--- a/client/components/admin/admin-utilities-cache.vue
+++ b/client/components/admin/admin-utilities-cache.vue
@@ -3,13 +3,13 @@
     v-toolbar(flat, color='primary', dark, dense)
       .subheading {{ $t('admin:utilities.cacheTitle') }}
     v-card-text
-      v-subheader.pl-0 Flush Pages and Assets Cache
+      v-subheader.pl-0.primary--text Flush Pages and Assets Cache
       .body-1 Pages and Assets are cached to disk for better performance. You can flush the cache to force all content to be fetched from the DB again.
       v-btn(outline, color='primary', @click='flushCache', :disabled='loading').ml-0.mt-3
         v-icon(left) build
         span Proceed
       v-divider.my-3
-      v-subheader.pl-0 Flush Temporary Uploads
+      v-subheader.pl-0.primary--text Flush Temporary Uploads
       .body-1 New uploads are temporarily saved to disk while they are being processed. They are automatically deleted after processing, but you can force an immediate cleanup using this tool.
       .body-1.red--text Note that performing this action while an upload is in progress can result in a failed upload.
       v-btn(outline, color='primary', @click='flushUploads', :disabled='loading').ml-0.mt-3
diff --git a/client/components/admin/admin-utilities-content.vue b/client/components/admin/admin-utilities-content.vue
new file mode 100644
index 00000000..e836163c
--- /dev/null
+++ b/client/components/admin/admin-utilities-content.vue
@@ -0,0 +1,68 @@
+
+  v-card
+    v-toolbar(flat, color='primary', dark, dense)
+      .subheading {{ $t('admin:utilities.contentTitle') }}
+    v-card-text
+      v-subheader.pl-0.primary--text Migrate all pages to base language
+      .body-1 If you created content before selecting a different locale and activating the namespacing capabilities, you may want to transfer all content to the base locale.
+      .body-1.red--text: strong This operation is destructive and cannot be reversed! Make sure you have proper backups!
+      .body-1.mt-3 Based on your current configuration, all pages will be migrated to the locale #[v-chip(label, small): strong {{currentLocale.toUpperCase()}}]
+      .body-1.mt-3 Pages that are already in the target locale will not be touched. If a page already exists at the target, the source page will not be modified as it would create a conflict. If you want to overwrite the target content, you must first delete that page.
+      v-btn(outline, color='primary', @click='migrateToLocale', :disabled='loading').ml-0.mt-3
+        v-icon(left) build
+        span Proceed
+
+
+
+
+
diff --git a/client/components/admin/admin-utilities.vue b/client/components/admin/admin-utilities.vue
index 3e4b96a7..2b05e8bc 100644
--- a/client/components/admin/admin-utilities.vue
+++ b/client/components/admin/admin-utilities.vue
@@ -35,6 +35,7 @@
 export default {
   components: {
     UtilityAuth: () => import(/* webpackChunkName: "admin" */ './admin-utilities-auth.vue'),
+    UtilityContent: () => import(/* webpackChunkName: "admin" */ './admin-utilities-content.vue'),
     UtilityCache: () => import(/* webpackChunkName: "admin" */ './admin-utilities-cache.vue'),
     UtilityImportv1: () => import(/* webpackChunkName: "admin" */ './admin-utilities-importv1.vue'),
     UtilityTelemetry: () => import(/* webpackChunkName: "admin" */ './admin-utilities-telemetry.vue')
@@ -49,6 +50,12 @@ export default {
           i18nKey: 'auth',
           isAvailable: true
         },
+        {
+          key: 'UtilityContent',
+          icon: 'insert_drive_file',
+          i18nKey: 'content',
+          isAvailable: true
+        },
         {
           key: 'UtilityCache',
           icon: 'invert_colors',
diff --git a/client/components/common/search-results.vue b/client/components/common/search-results.vue
index ddf86a08..ee721cf5 100644
--- a/client/components/common/search-results.vue
+++ b/client/components/common/search-results.vue
@@ -91,7 +91,7 @@ export default {
       return this.response.suggestions ? this.response.suggestions : []
     },
     paginationLength() {
-      return this.response.totalHits > 0 ? 0 : Math.ceil(this.response.totalHits / 10)
+      return (this.response.totalHits > 0) ? 0 : Math.ceil(this.response.totalHits / 10)
     }
   },
   watch: {
@@ -107,7 +107,7 @@ export default {
   },
   mounted() {
     this.$root.$on('searchMove', (dir) => {
-      this.cursor += (dir === 'up' ? -1 : 1)
+      this.cursor += ((dir === 'up') ? -1 : 1)
       if (this.cursor < -1) {
         this.cursor = -1
       } else if (this.cursor > this.results.length + this.suggestions.length - 1) {
diff --git a/client/components/editor/editor-markdown.vue b/client/components/editor/editor-markdown.vue
index 658c3a44..8b697afd 100644
--- a/client/components/editor/editor-markdown.vue
+++ b/client/components/editor/editor-markdown.vue
@@ -190,7 +190,7 @@ import mdMark from 'markdown-it-mark'
 import mdImsize from 'markdown-it-imsize'
 
 // Prism (Syntax Highlighting)
-import Prism from '@/libs/prism/prism.js'
+import Prism from 'prismjs'
 
 // ========================================
 // INIT
diff --git a/client/graph/admin/locale/locale-query-list.gql b/client/graph/admin/locale/locale-query-list.gql
index 63402965..c91e2d90 100644
--- a/client/graph/admin/locale/locale-query-list.gql
+++ b/client/graph/admin/locale/locale-query-list.gql
@@ -1,6 +1,7 @@
 {
   localization {
     locales {
+      availability
       code
       createdAt
       isInstalled
diff --git a/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql b/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql
new file mode 100644
index 00000000..43fb27a2
--- /dev/null
+++ b/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql
@@ -0,0 +1,12 @@
+mutation {
+  pages {
+    migrateToLocale {
+      responseResult {
+        succeeded
+        errorCode
+        slug
+        message
+      }
+    }
+  }
+}
diff --git a/client/index-legacy.js b/client/index-legacy.js
new file mode 100644
index 00000000..65dc2027
--- /dev/null
+++ b/client/index-legacy.js
@@ -0,0 +1,3 @@
+require('./scss/legacy.scss')
+
+window.WIKI = null
diff --git a/client/index-setup.js b/client/index-setup.js
index 4edfddf1..a0c8b2cb 100644
--- a/client/index-setup.js
+++ b/client/index-setup.js
@@ -1,4 +1,5 @@
-require('@babel/polyfill')
+require('core-js/stable')
+require('regenerator-runtime/runtime')
 
 require('vuetify/src/stylus/main.styl')
 require('./scss/app.scss')
diff --git a/client/scss/app.scss b/client/scss/app.scss
index 61bd915a..b5e6e4be 100644
--- a/client/scss/app.scss
+++ b/client/scss/app.scss
@@ -18,7 +18,7 @@
 @import 'layout/md2';
 
 // @import '../libs/twemoji/twemoji-awesome';
-@import '../libs/prism/prism.css';
+// @import '../libs/prism/prism.css';
 @import '~vue-tour/dist/vue-tour.css';
 @import '~vue-status-indicator/styles.css';
 @import '~xterm/dist/xterm.css';
diff --git a/client/scss/legacy.scss b/client/scss/legacy.scss
new file mode 100644
index 00000000..1290ef4e
--- /dev/null
+++ b/client/scss/legacy.scss
@@ -0,0 +1,218 @@
+@import "global";
+
+@import "./base/fonts.scss";
+@import "./base/icons.scss";
+
+html {
+  box-sizing: border-box;
+  background-color: mc('grey', '50');
+  font-size: 14px;
+}
+*, *:before, *:after {
+  box-sizing: inherit;
+}
+* {
+  margin: 0;
+  padding: 0;
+}
+
+.is-hidden {
+  display: none;
+}
+
+body {
+  margin: 0;
+  padding: 0;
+  font-family: "Roboto",sans-serif;
+  line-height: 1.5;
+  min-height: 100vh;
+}
+
+.header {
+  background-color: #000;
+  color: #FFF;
+  height: 64px;
+  padding: 0 16px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  &-title {
+    margin: 0;
+    font-size: 16px;
+    font-weight: 500;
+    letter-spacing: .02em;
+  }
+
+  &-deprecated {
+    color: mc('red', '100');
+
+    a {
+      color: mc('pink', '400');
+    }
+  }
+
+  &-login {
+    a {
+      text-decoration: none;
+      color: #FFF;
+      transition: color .3s ease;
+
+      &:hover {
+        color: mc('blue', '500');
+      }
+    }
+  }
+}
+
+.main {
+  display: flex;
+  align-items: stretch;
+  min-height: calc(100vh - 64px);
+  height: 50vh;
+
+  &-container {
+    flex-grow: 1;
+  }
+}
+
+.sidebar {
+  width: 300px;
+  background-color: mc('blue', '700');
+  color: #FFF;
+  padding: 8px 0;
+
+  .sidebar-link {
+    height: 40px;
+    font-size: 13px;
+    display: flex;
+    align-items: center;
+    padding: 0 16px;
+    transition: background .3s cubic-bezier(.25,.8,.5,1);
+    font-weight: 400;
+    color: #FFF;
+    text-decoration: none;
+
+    &:hover {
+      background: hsla(0,0%,100%,.08);
+    }
+  }
+
+  i.material-icons {
+    width: 56px;
+    padding-left: 8px;
+  }
+
+  .sidebar-divider {
+    border-top: 1px solid hsla(0,0%,100%,.12);
+    margin: 8px 0;
+  }
+
+  .sidebar-title {
+    font-size: 13px;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    padding: 0 16px 0 24px;
+    font-weight: 500;
+    color: hsla(0,0%,100%,.7);
+  }
+}
+
+.page-header {
+  background-color: mc('grey', '100');
+  padding: 0 24px;
+  height: 90px;
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid mc('grey', '200');
+
+  h1 {
+    font-size: 24px;
+    font-weight: 400;
+    line-height: 32px;
+    color: mc('grey', '800');
+  }
+
+  h2 {
+    color: mc('grey', '600');
+    font-size: 12px;
+    font-weight: 400;
+  }
+
+  &-left {
+    flex-grow: 1;
+  }
+
+  &-right {
+    flex: 0 0 324px;
+    padding-left: 16px;
+
+    &-title {
+      color: mc('grey', '500');
+      font-size: 12px;
+    }
+    &-author {
+      color: mc('grey', '800');
+      font-weight: 500;
+    }
+    &-updated {
+      color: mc('grey', '600');
+      font-size: 12px;
+    }
+  }
+}
+
+.page-contents {
+  display: flex;
+}
+
+.toc {
+  flex: 0 0 348px;
+  background-color: mc('grey', '200');
+  padding: 4px 0;
+
+  &-title {
+    font-size: 13px;
+    height: 40px;
+    display: flex;
+    color: mc('blue', '600');
+    align-items: center;
+    font-weight: 500;
+    padding: 0 16px;
+  }
+
+  &-tile {
+    text-decoration: none;
+    height: 40px;
+    display: flex;
+    font-size: 13px;
+    align-items: center;
+    padding: 0 16px;
+    color: mc('grey', '800');
+    transition: background-color .3s ease;
+
+    &.inset {
+      padding-left: 32px;
+    }
+
+    &:hover {
+      background-color: rgba(0,0,0,.06);
+    }
+  }
+
+  &-divider {
+    border-top: 1px solid rgba(0,0,0,.12);
+    margin: 0 0 0 24px;
+
+    &.inset {
+      margin-left: 40px;
+    }
+  }
+}
+
+@import "../themes/default/scss/app.scss";
+
+.contents {
+  flex-grow: 1;
+}
diff --git a/client/themes/default/components/page.vue b/client/themes/default/components/page.vue
index e2051cd4..700ef6c9 100644
--- a/client/themes/default/components/page.vue
+++ b/client/themes/default/components/page.vue
@@ -137,7 +137,7 @@