mirror of
https://github.com/PretendoNetwork/website.git
synced 2026-08-23 17:17:02 -05:00
Merge pull request #46 from montylion/dev
Add auto locale detection and custom locale selection (preference saved to cookie)
This commit is contained in:
33
package-lock.json
generated
33
package-lock.json
generated
@@ -10,8 +10,10 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"colors": "^1.4.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"express": "^4.17.1",
|
||||
"express-handlebars": "^4.0.4",
|
||||
"express-locale": "^2.0.0",
|
||||
"fs-extra": "^9.1.0",
|
||||
"ioredis": "^4.26.0",
|
||||
"morgan": "^1.10.0",
|
||||
@@ -156,6 +158,18 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
|
||||
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
|
||||
"dependencies": {
|
||||
"cookie": "0.4.0",
|
||||
"cookie-signature": "1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
@@ -287,6 +301,11 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/express-locale": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-locale/-/express-locale-2.0.0.tgz",
|
||||
"integrity": "sha512-SUwsgOTV8lplAkgyfKSwKH3ZVSKUrAa+b48acPGXpwWX211WZ/BYWX2WcZO57+xLOqPYb6rSNpWCFnhGrt0moA=="
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||
@@ -1114,6 +1133,15 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||
},
|
||||
"cookie-parser": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
|
||||
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
|
||||
"requires": {
|
||||
"cookie": "0.4.0",
|
||||
"cookie-signature": "1.0.6"
|
||||
}
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
@@ -1224,6 +1252,11 @@
|
||||
"promise": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"express-locale": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-locale/-/express-locale-2.0.0.tgz",
|
||||
"integrity": "sha512-SUwsgOTV8lplAkgyfKSwKH3ZVSKUrAa+b48acPGXpwWX211WZ/BYWX2WcZO57+xLOqPYb6rSNpWCFnhGrt0moA=="
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
"homepage": "https://github.com/PretendoNetwork/website#readme",
|
||||
"dependencies": {
|
||||
"colors": "^1.4.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"express": "^4.17.1",
|
||||
"express-handlebars": "^4.0.4",
|
||||
"express-locale": "^2.0.0",
|
||||
"fs-extra": "^9.1.0",
|
||||
"ioredis": "^4.26.0",
|
||||
"morgan": "^1.10.0",
|
||||
|
||||
106
public/assets/css/dropdown.css
Normal file
106
public/assets/css/dropdown.css
Normal file
@@ -0,0 +1,106 @@
|
||||
.select-box {
|
||||
display: flex;
|
||||
width: 200px;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.select-box > * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.select-box .options-container {
|
||||
max-height: 0;
|
||||
width: calc(100% - 12px);
|
||||
opacity: 0;
|
||||
transition: all 0.4s;
|
||||
overflow: hidden;
|
||||
border-radius: 5px;
|
||||
background-color: var(--btn-secondary);
|
||||
order: 1;
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
}
|
||||
|
||||
.selected-locale {
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
width: 188px;
|
||||
height: 45px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--btn-secondary);
|
||||
color: white;
|
||||
order: 0;
|
||||
}
|
||||
|
||||
.selected-locale::after {
|
||||
content: "";
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
background: url("/assets/icons/down-arrow.svg");
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 50%;
|
||||
transition: transform 150ms;
|
||||
transform: translateY(-50%);
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.select-box .option .item {
|
||||
color: #afb5dd;
|
||||
}
|
||||
|
||||
.select-box .lang {
|
||||
width: 1.3rem;
|
||||
height: 1rem;
|
||||
margin-right: .2rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.select-box .options-container.active {
|
||||
max-height: 240px;
|
||||
opacity: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.select-box .options-container.active + .selected-locale::after {
|
||||
transform: translateY(-50%) rotateX(180deg);
|
||||
}
|
||||
|
||||
.select-box .options-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background: #0d141f;
|
||||
background: #81878f;
|
||||
background: #f1f2f3;
|
||||
border-radius: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
.select-box .options-container::-webkit-scrollbar-thumb {
|
||||
background: #525861;
|
||||
background: #81878f;
|
||||
border-radius: 0 5px 5px 0;
|
||||
}
|
||||
.select-box .option,
|
||||
.selected-locale {
|
||||
padding: 12px 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.select-box .option:hover {
|
||||
background: #2f345b;
|
||||
}
|
||||
.select-box .option:hover .item {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.select-box label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.select-box .option .radio {
|
||||
display: none;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
--text: white;
|
||||
--text-secondary: #A1A8D9;
|
||||
--btn: #673DB6;
|
||||
--btn-secondary: #333960;
|
||||
--theme: var(--btn);
|
||||
--text-secondary-2: #8990C1;
|
||||
}
|
||||
@@ -80,6 +81,10 @@ header nav a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.locale-dropdown {
|
||||
margin-left: auto;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Misc */
|
||||
.purple-circle {
|
||||
@@ -114,7 +119,7 @@ header nav a {
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
margin-left: -40px;
|
||||
z-index: 3;
|
||||
z-index: 1;
|
||||
}
|
||||
.light-purple-circle .deco {
|
||||
position: absolute;
|
||||
@@ -124,6 +129,15 @@ header nav a {
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
/* Twemoji */
|
||||
|
||||
img.emoji {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin: 0 .05em 0 .1em;
|
||||
vertical-align: -0.1em;
|
||||
}
|
||||
|
||||
/* Title business */
|
||||
.sect .title[id] {
|
||||
padding-top: 100px;
|
||||
@@ -217,7 +231,7 @@ header nav a {
|
||||
background: var(--btn);
|
||||
}
|
||||
.button.secondary {
|
||||
background: #333960;
|
||||
background: var(--btn-secondary);
|
||||
}
|
||||
.button.secondary.discord svg {
|
||||
margin-bottom: -1px;
|
||||
|
||||
1
public/assets/icons/down-arrow.svg
Normal file
1
public/assets/icons/down-arrow.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 261 B |
56
public/assets/js/locale-dropdown-handler.js
Normal file
56
public/assets/js/locale-dropdown-handler.js
Normal file
@@ -0,0 +1,56 @@
|
||||
function setDefaultDropdownLocale(localeString) {
|
||||
const selected = document.querySelector(".selected-locale");
|
||||
selected.innerHTML = document.querySelector(`label[for=${localeString}`).innerHTML
|
||||
}
|
||||
|
||||
function localeDropdownHandler(selectedLocale) {
|
||||
document.cookie = `preferredLocale=${selectedLocale};max-age=31536000`;
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
const dropdowns = document.querySelectorAll("*[data-dropdown]");
|
||||
const dropdownOptions = document.querySelectorAll(
|
||||
"*[data-dropdown] .options-container"
|
||||
);
|
||||
|
||||
dropdowns.forEach((el) => {
|
||||
const selected = el.querySelector(".selected-locale");
|
||||
const optionsContainer = el.querySelector(".options-container");
|
||||
const optionsList = el.querySelectorAll(".option");
|
||||
|
||||
// click dropdown element will open dropdown
|
||||
selected.addEventListener("click", () => {
|
||||
optionsContainer.classList.toggle("active");
|
||||
});
|
||||
|
||||
// clicking on any option will close dropdown and change value
|
||||
optionsList.forEach((option) => {
|
||||
option.addEventListener("click", () => {
|
||||
selected.innerHTML = option.querySelector("label").innerHTML;
|
||||
optionsContainer.classList.remove("active");
|
||||
const selectedLocale = option.querySelector("label").getAttribute("for");
|
||||
localeDropdownHandler(selectedLocale);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// close all dropdowns on scroll
|
||||
document.addEventListener("scroll", () => {
|
||||
dropdownOptions.forEach((el) => el.classList.remove("active"));
|
||||
});
|
||||
|
||||
// click outside of dropdown will close all dropdowns
|
||||
document.addEventListener("click", (e) => {
|
||||
const targetElement = e.target;
|
||||
|
||||
// check if target is from a dropdown
|
||||
let found = false;
|
||||
dropdowns.forEach((v) => {
|
||||
if (v == targetElement || v.contains(targetElement)) found = true;
|
||||
});
|
||||
|
||||
if (found) return;
|
||||
|
||||
// click outside of dropdowns
|
||||
dropdownOptions.forEach((el) => el.classList.remove("active"));
|
||||
});
|
||||
@@ -5,12 +5,16 @@ const router = new Router();
|
||||
const { getTrelloCache } = require('../trello');
|
||||
|
||||
router.get('/', async (request, response) => {
|
||||
const tmpLocale = util.getLocale('US', 'en');
|
||||
|
||||
const reqLocale = request.locale
|
||||
const locale = util.getLocale(reqLocale.region, reqLocale.language);
|
||||
|
||||
const cache = await getTrelloCache();
|
||||
|
||||
response.render('home', {
|
||||
layout: 'main',
|
||||
locale: tmpLocale,
|
||||
locale,
|
||||
localeString: reqLocale.toString(),
|
||||
featuredFeatureList: cache.sections[0]
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,12 +5,18 @@ const router = new Router();
|
||||
const { getTrelloCache } = require('../trello');
|
||||
|
||||
router.get('/', async (request, response) => {
|
||||
const tmpLocale = util.getLocale('US', 'en');
|
||||
|
||||
const reqLocale = request.locale
|
||||
const locale = util.getLocale(reqLocale.region, reqLocale.language);
|
||||
|
||||
const localeString = reqLocale.toString()
|
||||
|
||||
const cache = await getTrelloCache();
|
||||
|
||||
response.render('progress', {
|
||||
layout: 'main',
|
||||
locale: tmpLocale,
|
||||
locale,
|
||||
localeString,
|
||||
progressLists: cache
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,8 @@ process.title = 'Pretendo - Website';
|
||||
const express = require('express');
|
||||
const handlebars = require('express-handlebars');
|
||||
const morgan = require('morgan');
|
||||
const expressLocale = require('express-locale');
|
||||
const cookieParser = require('cookie-parser')
|
||||
const logger = require('./logger');
|
||||
const util = require('./util');
|
||||
const config = require('../config.json');
|
||||
@@ -23,6 +25,28 @@ const routers = {
|
||||
progress: require('./routers/progress')
|
||||
};
|
||||
|
||||
app.use(cookieParser())
|
||||
|
||||
// Locale express middleware setup
|
||||
app.use(expressLocale({
|
||||
"priority": ["cookie", "accept-language", "map", "default"],
|
||||
cookie: {name: 'preferredLocale'},
|
||||
// fallbacks for browsers that don't send a region code, but only a language
|
||||
map: {
|
||||
de: 'de-DE',
|
||||
es: 'es-ES',
|
||||
fr: 'fr-FR',
|
||||
it: 'it-IT',
|
||||
ko: 'ko-KR',
|
||||
ru: 'ru-RU',
|
||||
tr: 'tr-TR'
|
||||
},
|
||||
"default": "en-US"
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
app.use('/', routers.home);
|
||||
app.use('/faq', routers.faq);
|
||||
app.use('/progress', routers.progress);
|
||||
|
||||
@@ -28,11 +28,20 @@
|
||||
<link rel="shortcut icon" href="/assets/icons/favicon.ico"><title>Pretendo</title>
|
||||
<!-- css files -->
|
||||
<link rel="stylesheet" href="/assets/css/main.css">
|
||||
<link rel="stylesheet" href="/assets/css/dropdown.css" />
|
||||
<!-- favicon -->
|
||||
<link rel="shortcut icon" href="/assets/icons/favicon.ico">
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Twemoji setup -->
|
||||
<script src="https://twemoji.maxcdn.com/v/latest/twemoji.min.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
twemoji.parse(document.body, {folder: 'svg', ext: '.svg'});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-body">
|
||||
@@ -40,5 +49,8 @@
|
||||
</div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.0.2/chart.min.js"></script>
|
||||
<script src="/assets/js/progress-charts.js"></script>
|
||||
<script src="/assets/js/locale-dropdown-handler.js"></script>
|
||||
<script>setDefaultDropdownLocale("{{localeString}}")</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -30,4 +30,45 @@
|
||||
<a href="/#credits">{{ locale.nav.credits }}</a>
|
||||
<a href="/progress" class="keep-on-mobile">{{ locale.nav.progress }}</a>
|
||||
</nav>
|
||||
|
||||
<div class="select-box locale-dropdown" data-dropdown>
|
||||
<div class="options-container">
|
||||
<div class="option">
|
||||
<input type="radio" class="radio" id="en-US" name="category" />
|
||||
<label for="en-US">
|
||||
<div class="item"><span class="lang">🇺🇸</span>English</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="option">
|
||||
<input type="radio" class="radio" id="es-ES" name="category" />
|
||||
<label for="es-ES">
|
||||
<div class="item"><span class="lang">🇪🇸</span>Español</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="option">
|
||||
<input type="radio" class="radio" id="it-IT" name="category" />
|
||||
<label for="it-IT">
|
||||
<div class="item"><span class="lang">🇮🇹</span>Italiano</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="option">
|
||||
<input type="radio" class="radio" id="ru-RU" name="category" />
|
||||
<label for="ru-RU">
|
||||
<div class="item"><span class="lang">🇷🇺</span>Pусский</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="option">
|
||||
<input type="radio" class="radio" id="tr-TR" name="category" />
|
||||
<label for="tr-TR">
|
||||
<div class="item"><span class="lang">🇹🇷</span>Türkçe</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="selected-locale">
|
||||
<div class="item"><span class="lang"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</header>
|
||||
Reference in New Issue
Block a user