diff --git a/.github/workflows/do-spaces-workflow.yml b/.github/workflows/do-spaces-workflow.yml
index e1e190d..2e5311e 100644
--- a/.github/workflows/do-spaces-workflow.yml
+++ b/.github/workflows/do-spaces-workflow.yml
@@ -1,4 +1,4 @@
-name: Test and deploy to DigitalOcean Spaces
+name: Deploy to DigitalOcean Spaces
 
 on: push
 
diff --git a/.github/workflows/test-workflow.yml b/.github/workflows/test-workflow.yml
index 6fa0882..bd0e490 100644
--- a/.github/workflows/test-workflow.yml
+++ b/.github/workflows/test-workflow.yml
@@ -44,3 +44,24 @@ jobs:
 
       - name: Test with sass-lint
         run: npm run test:sass-lint
+
+  i18n-packs:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v1
+
+      - name: Read .nvmrc
+        run: echo "##[set-output name=NVMRC;]$(cat .nvmrc)"
+        id: nvm
+
+      - name: Use Node.js (.nvmrc)
+        uses: actions/setup-node@v1
+        with:
+          node-version: "${{ steps.nvm.outputs.NVMRC }}"
+
+      - name: Install dependencies
+        run: npm ci
+
+      - name: Test i18n packs integrity
+        run: npm run test:i18n-packs
diff --git a/.gitignore b/.gitignore
index 07812a4..01e7234 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,9 @@
-node_modules/
-.cache/
-dist/
-dev/
-.idea/
-.vscode/
-build/
+node_modules
+.cache
+.idea
+.vscode
 .DS_Store
+
+/build/
+/dist/
+/dev/
diff --git a/package-lock.json b/package-lock.json
index faf5020..7a22fa8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -682,6 +682,26 @@
         "@babel/helper-validator-identifier": "^7.10.4",
         "chalk": "^2.0.0",
         "js-tokens": "^4.0.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "@babel/parser": {
@@ -2226,6 +2246,16 @@
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
           "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
         },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
         "cli-spinners": {
           "version": "1.3.1",
           "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz",
@@ -2251,6 +2281,14 @@
           "requires": {
             "ansi-regex": "^3.0.0"
           }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
         }
       }
     },
@@ -2473,6 +2511,17 @@
           "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
           "dev": true
         },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
         "strip-ansi": {
           "version": "5.2.0",
           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -2481,6 +2530,15 @@
           "requires": {
             "ansi-regex": "^4.1.0"
           }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
         }
       }
     },
@@ -2942,6 +3000,15 @@
             "graceful-fs": "^4.1.6"
           }
         },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
         "webpack-bundle-analyzer": {
           "version": "3.9.0",
           "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz",
@@ -2961,6 +3028,19 @@
             "mkdirp": "^0.5.1",
             "opener": "^1.5.1",
             "ws": "^6.0.0"
+          },
+          "dependencies": {
+            "chalk": {
+              "version": "2.4.2",
+              "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+              "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+              "dev": true,
+              "requires": {
+                "ansi-styles": "^3.2.1",
+                "escape-string-regexp": "^1.0.5",
+                "supports-color": "^5.3.0"
+              }
+            }
           }
         }
       }
@@ -2991,6 +3071,17 @@
           "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
           "dev": true
         },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
         "semver": {
           "version": "6.3.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -3005,6 +3096,15 @@
           "requires": {
             "ansi-regex": "^5.0.0"
           }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
         }
       }
     },
@@ -4344,13 +4444,39 @@
       "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
     },
     "chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+      "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+      "dev": true,
       "requires": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        }
       }
     },
     "char-regex": {
@@ -4678,6 +4804,26 @@
         "@types/q": "^1.5.1",
         "chalk": "^2.4.1",
         "q": "^1.1.2"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "code-point-at": {
@@ -6192,6 +6338,28 @@
         "find-root": "^1.0.0",
         "lodash": "^4.17.4",
         "semver": "^5.4.1"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "easy-stack": {
@@ -6862,6 +7030,12 @@
       "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
       "dev": true
     },
+    "esm": {
+      "version": "3.2.25",
+      "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
+      "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
+      "dev": true
+    },
     "espree": {
       "version": "7.3.1",
       "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
@@ -10856,6 +11030,28 @@
       "requires": {
         "chalk": "^2.3.0",
         "shell-quote": "^1.6.1"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "launch-editor-middleware": {
@@ -10982,6 +11178,26 @@
       "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
       "requires": {
         "chalk": "^2.0.1"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "loglevel": {
@@ -11921,6 +12137,17 @@
           "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
           "dev": true
         },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
         "strip-ansi": {
           "version": "5.2.0",
           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -11929,6 +12156,15 @@
           "requires": {
             "ansi-regex": "^4.1.0"
           }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
         }
       }
     },
@@ -12102,6 +12338,16 @@
           "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
           "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
         },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
         "dotenv": {
           "version": "5.0.1",
           "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
@@ -12126,6 +12372,14 @@
             "posthtml-render": "^1.1.5"
           }
         },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
         "terser": {
           "version": "3.17.0",
           "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz",
@@ -12399,6 +12653,26 @@
         "supports-color": "^6.1.0"
       },
       "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          },
+          "dependencies": {
+            "supports-color": {
+              "version": "5.5.0",
+              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+              "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+              "requires": {
+                "has-flag": "^3.0.0"
+              }
+            }
+          }
+        },
         "supports-color": {
           "version": "6.1.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
@@ -13345,6 +13619,28 @@
             "chalk": "^2.4.2",
             "source-map": "^0.6.1",
             "supports-color": "^6.1.0"
+          },
+          "dependencies": {
+            "chalk": {
+              "version": "2.4.2",
+              "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+              "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+              "requires": {
+                "ansi-styles": "^3.2.1",
+                "escape-string-regexp": "^1.0.5",
+                "supports-color": "^5.3.0"
+              },
+              "dependencies": {
+                "supports-color": {
+                  "version": "5.5.0",
+                  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+                  "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+                  "requires": {
+                    "has-flag": "^3.0.0"
+                  }
+                }
+              }
+            }
           }
         },
         "supports-color": {
@@ -15491,11 +15787,20 @@
       }
     },
     "supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
       "requires": {
-        "has-flag": "^3.0.0"
+        "has-flag": "^4.0.0"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        }
       }
     },
     "supports-hyperlinks": {
@@ -15543,6 +15848,26 @@
         "stable": "^0.1.8",
         "unquote": "~1.1.1",
         "util.promisify": "~1.0.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
       }
     },
     "symbol-tree": {
diff --git a/package.json b/package.json
index 7a0c0ba..4a5775f 100644
--- a/package.json
+++ b/package.json
@@ -9,22 +9,24 @@
   },
   "main": "src/nginxconfig/mount.js",
   "scripts": {
-    "build": "npm run build:clean && npm run build:template && npm run build:static && npm run build:tool",
+    "build": "npm run build:clean && npm run build:template && npm run build:prism && npm run build:static && npm run build:tool",
     "build:clean": "do-vue clean",
     "build:template": "do-vue template && node src/nginxconfig/build/template.js",
+    "build:prism": "node src/nginxconfig/build/prism.js",
     "build:static": "copyfiles --up 2 src/static/{*,**/*} dist",
     "build:tool": "vue-cli-service build src/nginxconfig/mount.js --no-clean",
-    "dev": "npm run build:template && npm run dev:tool",
+    "dev": "npm run build:template && npm run build:prism && npm run dev:tool",
     "dev:tool": "vue-cli-service serve src/nginxconfig/mount.js",
     "analyze": "npm run build:tool -- --analyze",
     "deploy:spaces:comment": "do-vue comment nginxconfig",
-    "test": "npm run test:eslint && npm run test:sass-lint && npm run test:jest",
+    "test": "npm run test:eslint && npm run test:sass-lint && npm run test:i18n-packs && npm run test:jest",
     "test:jest": "jest /test/.*.js?$",
     "test:fix": "npm run test:eslint:fix && npm run test:sass-lint:fix",
     "test:eslint": "eslint 'src/**/*.{js,vue}'",
     "test:eslint:fix": "npm run test:eslint -- --fix",
     "test:sass-lint": "sass-lint 'src/**/*.scss' --verbose --no-exit --config .sasslintrc",
-    "test:sass-lint:fix": "sass-lint-auto-fix 'src/**/*.scss'"
+    "test:sass-lint:fix": "sass-lint-auto-fix 'src/**/*.scss'",
+    "test:i18n-packs": "node -r esm src/nginxconfig/i18n/verify.js"
   },
   "jest": {
     "testRegex": "/test/.*.js?$"
@@ -65,12 +67,15 @@
     "@babel/runtime": "^7.11.2",
     "@vue/cli-service": "^4.5.9",
     "babel-eslint": "^10.1.0",
+    "chalk": "^4.1.0",
     "copyfiles": "^2.4.1",
     "core-js": "^3.6.5",
     "duplicate-package-checker-webpack-plugin": "^3.0.0",
     "eslint": "^7.15.0",
     "eslint-plugin-vue": "^7.3.0",
     "jest": "^26.6.3",
+    "esm": "^3.2.25",
+    "node-fetch": "^2.6.1",
     "sass": "^1.30.0",
     "sass-lint": "^1.13.1",
     "sass-lint-auto-fix": "^0.21.2",
diff --git a/src/nginxconfig/build/prism.js b/src/nginxconfig/build/prism.js
new file mode 100644
index 0000000..a0937d5
--- /dev/null
+++ b/src/nginxconfig/build/prism.js
@@ -0,0 +1,42 @@
+/*
+Copyright 2020 DigitalOcean
+
+This code is licensed under the MIT License.
+You may obtain a copy of the License at
+https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions :
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+const fs = require('fs').promises;
+const path = require('path');
+const fetch = require('node-fetch');
+
+const main = async () => {
+    const resp = await fetch('https://assets.digitalocean.com/prism/prism.css');
+    const text = await resp.text();
+
+    // Fix $676767 -> #676767
+    const fixed = text.replace(/:\s*\$((?:[0-9a-fA-F]{3}){1,2});/g, ':#$1;');
+
+    const buildDir = path.join(__dirname, '..', '..', '..', 'build');
+    await fs.writeFile(path.join(buildDir, 'prism.css'), fixed);
+};
+
+main().then(() => {});
diff --git a/src/nginxconfig/i18n/en/common.js b/src/nginxconfig/i18n/en/common.js
index 22d0a74..00b1786 100644
--- a/src/nginxconfig/i18n/en/common.js
+++ b/src/nginxconfig/i18n/en/common.js
@@ -35,8 +35,6 @@ export default {
 	https: 'HTTPS',
 	letsEncrypt: 'Let\'s Encrypt',
 	python: 'Python',
-	docker: 'Docker',
-	dockerCompose: 'Docker Compose',
 	wordPress: 'WordPress',
 	drupal: 'Drupal',
 	magento: 'Magento',
diff --git a/src/nginxconfig/i18n/en/templates/global_sections/docker.js b/src/nginxconfig/i18n/en/templates/global_sections/docker.js
index 8e9cecc..510111b 100644
--- a/src/nginxconfig/i18n/en/templates/global_sections/docker.js
+++ b/src/nginxconfig/i18n/en/templates/global_sections/docker.js
@@ -26,7 +26,16 @@ THE SOFTWARE.
 
 import common from '../../common';
 
+const docker = 'Docker';
+const dockerfile = 'Dockerfile';
+
 export default {
-    dockerfile: `Include Dockerfile to run nginx with ${common.docker}`,
-    dockerCompose: 'Include docker-compose to run nginx with docker-compose',
+    docker,
+    dockerfile,
+    dockerCompose: `${docker} Compose`,
+    applyDockerTweaks: `Apply ${docker} tweaks`,
+    applyDockerTweaksForNginx: `Apply configuration tweaks for running ${common.nginx} with ${docker}`,
+    applyDockerTweaksExplainer: `Updates the ${common.nginx} user to be <code class="slim">nginx</code> and the pid to <code class="slim">/var/run/nginx.pid</code>`,
+    includeDockerfile: `Include ${dockerfile} to run ${common.nginx} with ${docker}`,
+    includeDockerCompose: `Include docker-compose to run ${common.nginx} with docker-compose`,
 };
diff --git a/src/nginxconfig/i18n/verify.js b/src/nginxconfig/i18n/verify.js
new file mode 100644
index 0000000..c1e3d5d
--- /dev/null
+++ b/src/nginxconfig/i18n/verify.js
@@ -0,0 +1,93 @@
+/*
+Copyright 2020 DigitalOcean
+
+This code is licensed under the MIT License.
+You may obtain a copy of the License at
+https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions :
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+import chalk from 'chalk';
+import defaultPack from './default';
+import * as packs from './index';
+
+// Recursively get all keys in a i18n pack object fragment
+const explore = packFragment => {
+    const foundKeys = new Set();
+
+    for (const [key, value] of Object.entries(packFragment)) {
+        // If this is an actual translation, store the key
+        if (typeof value === 'string') {
+            foundKeys.add(key);
+            continue;
+        }
+
+        // Otherwise, assume this is another fragment and explore it recursively
+        explore(packFragment[key]).forEach(exploreKey => foundKeys.add(`${key}.${exploreKey}`));
+    }
+
+    return foundKeys;
+};
+
+// Get all the keys for the default "source" language pack
+const defaultKeys = explore(packs[defaultPack]);
+
+// Track if we need to exit with an error
+let hadError = false;
+
+// Work through all the packs and compare to default
+for (const [pack, packData] of Object.entries(packs)) {
+    console.log(chalk.underline(`Language pack \`${pack}\``));
+
+    // We don't need to compare default to itself
+    if (pack === defaultPack) {
+        console.log(`  Default pack, found ${defaultKeys.size.toLocaleString()} keys`);
+        console.log(chalk.reset());
+        continue;
+    }
+
+    // Get all the keys and the set differences
+    const packKeys = explore(packData);
+    const missingKeys = [...defaultKeys].filter(x => !packKeys.has(x));
+    const extraKeys = [...packKeys].filter(x => !defaultKeys.has(x));
+
+    // Missing keys are errors, extra keys are just warnings
+    const errors = missingKeys.map(key => `Missing key \`${key}\``);
+    const warnings = extraKeys.map(key => `Unexpected key \`${key}\``);
+
+    // Output the pack results
+    if (warnings.length)
+        for (const warning of warnings)
+            console.log(`  ${chalk.yellow('warning')} ${warning}`);
+    if (errors.length)
+        for (const error of errors)
+            console.log(`  ${chalk.red('error')}   ${error}`);
+    if (!errors.length && !warnings.length)
+        console.log(`  ${chalk.green('No issues, all keys present with no unexpected keys')}`);
+
+    // If we had errors, script should exit 1
+    if (errors.length) hadError = true;
+
+    // Linebreak before next pack or exit
+    console.log(chalk.reset());
+}
+
+// Exit 1 if we had errors
+if (hadError) process.exit(1);
diff --git a/src/nginxconfig/i18n/zh-cn/common.js b/src/nginxconfig/i18n/zh-cn/common.js
index 5e305b2..579b395 100644
--- a/src/nginxconfig/i18n/zh-cn/common.js
+++ b/src/nginxconfig/i18n/zh-cn/common.js
@@ -35,8 +35,6 @@ export default {
     https: 'HTTPS',
     letsEncrypt: 'Let\'s Encrypt',
     python: 'Python',
-    docker: 'Docker',
-    dockerCompose: 'Docker Compose',
     wordPress: 'WordPress',
     drupal: 'Drupal',
     magento: 'Magento',
diff --git a/src/nginxconfig/i18n/zh-cn/templates/global_sections/docker.js b/src/nginxconfig/i18n/zh-cn/templates/global_sections/docker.js
index 54b89b5..d9cac35 100644
--- a/src/nginxconfig/i18n/zh-cn/templates/global_sections/docker.js
+++ b/src/nginxconfig/i18n/zh-cn/templates/global_sections/docker.js
@@ -26,7 +26,16 @@ THE SOFTWARE.
 
 import common from '../../common';
 
+const docker = 'Docker';
+const dockerfile = 'Dockerfile';
+
 export default {
-    dockerfile: `生成Dockerfile运行${common.nginx}与${common.docker}`,
-    dockerCompose: '生成docker-compose.yaml来运行nginx docker-compose',
+    docker,
+    dockerfile,
+    dockerCompose: `${docker} Compose`, // TODO: translate
+    applyDockerTweaks: `Apply ${docker} tweaks`, // TODO: translate
+    applyDockerTweaksForNginx: `Apply configuration tweaks for running ${common.nginx} with ${docker}`, // TODO: translate
+    applyDockerTweaksExplainer: `Updates the ${common.nginx} user to be <code class="slim">nginx</code> and the pid to <code class="slim">/var/run/nginx.pid</code>`, // TODO: translate
+    includeDockerfile: `生成${dockerfile}运行${common.nginx}与${docker}`,
+    includeDockerCompose: `生成docker-compose.yaml来运行${common.nginx} docker-compose`,
 };
diff --git a/src/nginxconfig/i18n/zh-tw/common.js b/src/nginxconfig/i18n/zh-tw/common.js
index 000cd56..808cc8f 100644
--- a/src/nginxconfig/i18n/zh-tw/common.js
+++ b/src/nginxconfig/i18n/zh-tw/common.js
@@ -35,8 +35,6 @@ export default {
     https: 'HTTPS',
     letsEncrypt: 'Let\'s Encrypt',
     python: 'Python',
-    docker: 'Docker',
-    dockerCompose: 'Docker Compose',
     wordPress: 'WordPress',
     drupal: 'Drupal',
     magento: 'Magento',
diff --git a/src/nginxconfig/i18n/zh-tw/templates/global_sections/docker.js b/src/nginxconfig/i18n/zh-tw/templates/global_sections/docker.js
index 104bff8..92cc143 100644
--- a/src/nginxconfig/i18n/zh-tw/templates/global_sections/docker.js
+++ b/src/nginxconfig/i18n/zh-tw/templates/global_sections/docker.js
@@ -26,7 +26,16 @@ THE SOFTWARE.
 
 import common from '../../common';
 
+const docker = 'Docker';
+const dockerfile = 'Dockerfile';
+
 export default {
-    dockerfile: `生成Dockerfile運行${common.nginx}與${common.docker}`,
-    dockerCompose: '生成docker-compose.yaml來運行nginx docker-compose',
+    docker,
+    dockerfile,
+    dockerCompose: `${docker} Compose`, // TODO: translate
+    applyDockerTweaks: `Apply ${docker} tweaks`, // TODO: translate
+    applyDockerTweaksForNginx: `Apply configuration tweaks for running ${common.nginx} with ${docker}`, // TODO: translate
+    applyDockerTweaksExplainer: `Updates the ${common.nginx} user to be <code class="slim">nginx</code> and the pid to <code class="slim">/var/run/nginx.pid</code>`, // TODO: translate
+    includeDockerfile: `生成${dockerfile}運行${common.nginx}與${docker}`,
+    includeDockerCompose: `生成docker-compose.yaml來運行${common.nginx} docker-compose`,
 };
diff --git a/src/nginxconfig/scss/_fields.scss b/src/nginxconfig/scss/_fields.scss
index fbfc76d..4263a73 100644
--- a/src/nginxconfig/scss/_fields.scss
+++ b/src/nginxconfig/scss/_fields.scss
@@ -48,6 +48,14 @@ THE SOFTWARE.
 
     &.is-aligned-top {
       align-items: flex-start;
+
+      > p {
+        @include sailec-medium;
+
+        color: $dark-grey;
+        font-size: 14px;
+        margin: 0 .5rem;
+      }
     }
 
     + .control {
@@ -118,6 +126,14 @@ THE SOFTWARE.
       color: $dark-grey;
       padding: 0 ($margin * 1.5);
     }
+
+    &.is-tiny {
+      font-size: 14px;
+      height: auto;
+      line-height: normal;
+      margin: .2rem .25rem;
+      padding: ($margin * .5) ($margin * .75);
+    }
   }
 
   .control {
diff --git a/src/nginxconfig/scss/style.scss b/src/nginxconfig/scss/style.scss
index 8ab4c8d..e6964db 100644
--- a/src/nginxconfig/scss/style.scss
+++ b/src/nginxconfig/scss/style.scss
@@ -24,13 +24,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */
 
-@import url("https://assets.digitalocean.com/prism/prism.css");
-
 $header: #0071fe;
 $highlight: #f2c94c;
 @import "~do-bulma/src/style";
 
 .do-bulma {
+  @import "../../../build/prism";
+
   $pretty--color-dark: $primary;
   $pretty--color-default: $primary;
   @import "~pretty-checkbox/src/pretty-checkbox";
diff --git a/src/nginxconfig/templates/global_sections/docker.vue b/src/nginxconfig/templates/global_sections/docker.vue
index 1dcd388..103ffbb 100644
--- a/src/nginxconfig/templates/global_sections/docker.vue
+++ b/src/nginxconfig/templates/global_sections/docker.vue
@@ -28,7 +28,24 @@ THE SOFTWARE.
     <div>
         <div class="field is-horizontal">
             <div class="field-label">
-                <label class="label">{{ $t('common.docker') }}</label>
+                <label class="label">{{ $t('templates.globalSections.docker.docker') }}</label>
+            </div>
+            <div class="field-body">
+                <div class="field is-horizontal is-aligned-top">
+                    <a class="button is-primary is-tiny" @click="applyDockerTweaks">
+                        {{ $t('templates.globalSections.docker.applyDockerTweaks') }}
+                    </a>
+                    <p>
+                        {{ $t('templates.globalSections.docker.applyDockerTweaksForNginx') }}
+                        <br />
+                        <small v-html="$t('templates.globalSections.docker.applyDockerTweaksExplainer')"></small>
+                    </p>
+                </div>
+            </div>
+        </div>
+        <div class="field is-horizontal">
+            <div class="field-label">
+                <label class="label">{{ $t('templates.globalSections.docker.dockerfile') }}</label>
             </div>
             <div class="field-body">
                 <div class="field">
@@ -36,7 +53,7 @@ THE SOFTWARE.
                         <div class="checkbox">
                             <PrettyCheck v-model="dockerfile" class="p-default p-curve p-fill p-icon">
                                 <i slot="extra" class="icon fas fa-check"></i>
-                                {{ $t('templates.globalSections.docker.dockerfile') }}
+                                {{ $t('templates.globalSections.docker.includeDockerfile') }}
                             </PrettyCheck>
                         </div>
                     </div>
@@ -45,7 +62,7 @@ THE SOFTWARE.
         </div>
         <div v-if="dockerfile" class="field is-horizontal">
             <div class="field-label">
-                <label class="label">{{ $t('common.dockerCompose') }}</label>
+                <label class="label">{{ $t('templates.globalSections.docker.dockerCompose') }}</label>
             </div>
             <div class="field-body">
                 <div class="field">
@@ -53,7 +70,7 @@ THE SOFTWARE.
                         <div class="checkbox">
                             <PrettyCheck v-model="dockerCompose" class="p-default p-curve p-fill p-icon">
                                 <i slot="extra" class="icon fas fa-check"></i>
-                                {{ $t('templates.globalSections.docker.dockerCompose') }}
+                                {{ $t('templates.globalSections.docker.includeDockerCompose') }}
                             </PrettyCheck>
                         </div>
                     </div>
@@ -67,6 +84,7 @@ THE SOFTWARE.
     import PrettyCheck from 'pretty-checkbox-vue/check';
     import delegatedFromDefaults from '../../util/delegated_from_defaults';
     import computedFromDefaults from '../../util/computed_from_defaults';
+    import analytics from '../../util/analytics';
 
     const defaults = {
         dockerfile: {
@@ -81,7 +99,7 @@ THE SOFTWARE.
 
     export default {
         name: 'GlobalDocker',                               // Component name
-        display: 'common.docker',                           // Display name for tab (i18n key)
+        display: 'templates.globalSections.docker.docker',  // Display name for tab (i18n key)
         key: 'docker',                                      // Key for data in parent
         delegated: delegatedFromDefaults(defaults),         // Data the parent will present here
         components: {
@@ -106,5 +124,13 @@ THE SOFTWARE.
                 deep: true,
             },
         },
+        methods: {
+            applyDockerTweaks() {
+                analytics('apply_docker_tweaks', 'Presets');
+                this.$parent.setValue('nginx', 'user', 'nginx');
+                this.$parent.setValue('nginx', 'pid', '/var/run/nginx.pid');
+                this.$parent.setValue('docker', 'dockerfile', true);
+            },
+        },
     };
 </script>