From 1ff5940375d0efd18b199a8c97085ead5bb9b262 Mon Sep 17 00:00:00 2001 From: Blobadoodle Date: Sat, 8 Apr 2023 16:35:23 +0100 Subject: [PATCH] add language picker --- src/front/cobalt.css | 33 ++++++++++++++++++++++++++++++ src/front/cobalt.js | 32 ++++++++++++++++++++++++++++- src/localization/languages/en.json | 4 +++- src/localization/manager.js | 3 +++ src/modules/pageRender/elements.js | 13 ++++++++++++ src/modules/pageRender/page.js | 8 ++++++-- src/modules/sub/utils.js | 15 +++++++++++++- 7 files changed, 103 insertions(+), 5 deletions(-) diff --git a/src/front/cobalt.css b/src/front/cobalt.css index 5e6a5290..994305d2 100644 --- a/src/front/cobalt.css +++ b/src/front/cobalt.css @@ -862,4 +862,37 @@ input[type="checkbox"] { height: 6rem; width: 6rem; } +} +.dropdown-select { + display: inline-flex; + flex-direction: row; + align-items: center; + flex-wrap: nowrap; + /* padding: 0.55rem 1rem 0.8rem 0.7rem; */ + width: fit-content; + background: var(--accent-button-bg); + font-size: 0.9rem; +} +.dropdown-select label { + margin: 0.55rem var(--padding-1) 0.8rem 0.7rem; +} +.dropdown-select select { + background: var(--accent-button-bg); + font-family: var(--font-mono); + color: var(--accent); + line-height: 1.35rem; + height: 2.65rem; + border: none; +} +.dropdown-select select:hover { + background: var(--accent-hover); +} + +.dropdown-select select option:hover { + background: var(--accent-hover); +} + +.dropdown-select select option { + line-height: 0.9rem; + height: 2.25rem; } \ No newline at end of file diff --git a/src/front/cobalt.js b/src/front/cobalt.js index a6ac7fb3..7fc3f731 100644 --- a/src/front/cobalt.js +++ b/src/front/cobalt.js @@ -20,6 +20,7 @@ let checkboxes = ["disableTikTokWatermark", "fullTikTokAudio", "muteAudio"]; let exceptions = { // used for mobile devices "vQuality": "720" } +let dropdowns = ["language"] function eid(id) { return document.getElementById(id) @@ -30,6 +31,19 @@ function sGet(id) { function sSet(id, value) { localStorage.setItem(id, value) } +function setCookie(id, value, expire = 730, path = "/") { // by default expire in 2 years + const d = new Date(); + d.setTime(d.getTime() + (expire * 24 * 60 * 60 * 1000)); + document.cookie = `${id}=${value};expires=${d.toUTCString()};path=${path}`; +} +function getCookie(id) { + const cookies = {}; + for(let i of document.cookie.split(';')) { + const [key, value] = i.split('='); + cookies[key] = value; + } + return cookies[id]; +} function enable(id) { eid(id).dataset.enabled = "true"; } @@ -248,6 +262,15 @@ function checkbox(action) { } action === "disableChangelog" && sGet(action) === "true" ? notificationCheck("disable") : notificationCheck(); } +function dropdownSelect(action) { + let value = eid(`${action}-select`).value; + if(action === "language") { // language has to be a cookie because the server needs to read the value + setCookie(action, value); + window.location.reload(); + } else { + sSet(action, value) + } +} function loadSettings() { try { if (typeof(navigator.clipboard.readText) == "undefined") throw new Error(); @@ -268,6 +291,13 @@ function loadSettings() { for (let i in switchers) { changeSwitcher(i, sGet(i)) } + for (let i of dropdowns) { + if(i == 'language') { + eid(`${i}-select`).value = getCookie(i) || document.documentElement.lang; + } else{ + eid(`${i}-select`).value = sGet(i); + } + } } function changeButton(type, text) { switch (type) { @@ -443,4 +473,4 @@ eid("url-input-area").addEventListener("keyup", (event) => { document.onkeydown = (event) => { if (event.key === "Tab" || event.ctrlKey) eid("url-input-area").focus(); if (event.key === 'Escape') hideAllPopups(); -} +} \ No newline at end of file diff --git a/src/localization/languages/en.json b/src/localization/languages/en.json index 9600ae8d..083d226c 100644 --- a/src/localization/languages/en.json +++ b/src/localization/languages/en.json @@ -117,6 +117,8 @@ "SettingsDubDefault": "original", "SettingsDubAuto": "auto", "SettingsVimeoPrefer": "vimeo downloads type", - "SettingsVimeoPreferDescription": "progressive: direct file link to vimeo's cdn. max quality is 1080p.\ndash: video and audio are merged by {appName} into one file. max quality is 4k.\n\npick \"progressive\" if you want best editor/player/social media compatibility. if progressive download isn't available, dash is used instead." + "SettingsVimeoPreferDescription": "progressive: direct file link to vimeo's cdn. max quality is 1080p.\ndash: video and audio are merged by {appName} into one file. max quality is 4k.\n\npick \"progressive\" if you want best editor/player/social media compatibility. if progressive download isn't available, dash is used instead.", + "SettingsAccessibilitySubtitle": "accessibility", + "SettingsAccessibilityLanguage": "interface language:" } } diff --git a/src/localization/manager.js b/src/localization/manager.js index 65c048c8..14b70250 100644 --- a/src/localization/manager.js +++ b/src/localization/manager.js @@ -6,6 +6,7 @@ const locPath = './src/localization/languages' let loc = {} let languages = []; +let languageNames = []; export function loadLoc() { fs.readdir(locPath, (err, files) => { @@ -13,6 +14,7 @@ export function loadLoc() { files.forEach(file => { loc[file.split('.')[0]] = loadJson(`${locPath}/${file}`); languages.push(file.split('.')[0]) + languageNames.push({value: file.split('.')[0], name: loc[file.split('.')[0]].name }) }); }) } @@ -46,3 +48,4 @@ export default function(lang, string, replacement) { } } export const languageList = languages; +export const languagePickerNames = languageNames; \ No newline at end of file diff --git a/src/modules/pageRender/elements.js b/src/modules/pageRender/elements.js index dc016124..e24c0c63 100644 --- a/src/modules/pageRender/elements.js +++ b/src/modules/pageRender/elements.js @@ -173,3 +173,16 @@ export function celebrationsEmoji() { let dm = `${n[1]}-${n[2]}`; return Object.keys(celebrations).includes(dm) ? celebrations[dm] : "🐲"; } + +export function dropdownSelect(label, action, options) { + let items = ` + '; + return items; +} \ No newline at end of file diff --git a/src/modules/pageRender/page.js b/src/modules/pageRender/page.js index 25e785b3..c88284c6 100644 --- a/src/modules/pageRender/page.js +++ b/src/modules/pageRender/page.js @@ -1,7 +1,7 @@ -import { backdropLink, celebrationsEmoji, checkbox, collapsibleList, explanation, footerButtons, multiPagePopup, popup, popupWithBottomButtons, sep, settingsCategory, switcher, socialLink } from "./elements.js"; +import { backdropLink, celebrationsEmoji, checkbox, collapsibleList, explanation, footerButtons, multiPagePopup, popup, popupWithBottomButtons, sep, settingsCategory, switcher, socialLink, dropdownSelect } from "./elements.js"; import { services as s, appName, authorInfo, version, repo, donations, supportedAudio } from "../config.js"; import { getCommitInfo } from "../sub/currentCommit.js"; -import loc from "../../localization/manager.js"; +import loc, { languagePickerNames } from "../../localization/manager.js"; import emoji from "../emoji.js"; import changelogManager from "../changelog/changelogManager.js"; @@ -319,6 +319,10 @@ export default function(obj) { name: "miscellaneous", title: t('Miscellaneous'), body: checkbox("disableChangelog", t('SettingsDisableNotifications')) + `${!isIOS ? checkbox("downloadPopup", t('SettingsEnableDownloadPopup'), 1, t('AccessibilityEnableDownloadPopup')) : ''}` + }) + settingsCategory({ + name: "accessibility", + title: t('SettingsAccessibilitySubtitle'), + body: dropdownSelect(t('SettingsAccessibilityLanguage'), 'language', languagePickerNames) }) }], })} diff --git a/src/modules/sub/utils.js b/src/modules/sub/utils.js index 32f2618b..5bbe0133 100644 --- a/src/modules/sub/utils.js +++ b/src/modules/sub/utils.js @@ -84,11 +84,24 @@ export function cleanURL(url, host) { } return url.slice(0, 128) } +export function getCookie(req, id) { + if(!req.headers.cookie) return undefined; + let cookies = {}; + for(let i of req.headers.cookie.split(';')) { + const [key, value] = i.trim().split('='); + cookies[key] = value; + } + return cookies[id] +} export function verifyLanguageCode(code) { return RegExp(/[a-z]{2}/).test(String(code.slice(0, 2).toLowerCase())) ? String(code.slice(0, 2).toLowerCase()) : "en" } export function languageCode(req) { - return req.header('Accept-Language') ? verifyLanguageCode(req.header('Accept-Language')) : "en" + const overrideLanguage = getCookie(req, 'language'); + let language; + if(overrideLanguage) language = overrideLanguage + else language = req.header('Accept-Language'); + return language ? verifyLanguageCode(language) : 'en'; } export function unicodeDecode(str) { return str.replace(/\\u[\dA-F]{4}/gi, (unicode) => {