Skip to content

Commit

Permalink
ref: reimplement preferred locale resolution (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sv443 committed Nov 23, 2024
1 parent 015ab7c commit 7dc45cc
Showing 1 changed file with 41 additions and 34 deletions.
75 changes: 41 additions & 34 deletions src/utils/misc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { compress, decompress, fetchAdvanced, openInNewTab, pauseFor, randomId, randRange } from "@sv443-network/userutils";
import { compress, decompress, fetchAdvanced, openInNewTab, pauseFor, randomId, randRange, type Prettify } from "@sv443-network/userutils";
import { marked } from "marked";
import { branch, compressionFormat, repo, sessionStorageAvailable } from "../constants.js";
import { type Domain, type NumberLengthFormat, type ResourceKey, type StringGen } from "../types.js";
Expand Down Expand Up @@ -114,18 +114,18 @@ export function sanitizeChannelId(channelId: string) {

/** Tests whether a string is a valid channel ID in the format `@User` or `UC...` */
export function isValidChannelId(channelId: string) {
return channelId.match(/^(UC|@)[\w-]+$/) !== null;
return channelId.match(/^(UC|@)[a-zA-Z0-9_-]+$/) !== null;
}

/** Quality identifier for a thumbnail - from highest to lowest res: `maxresdefault` > `sddefault` > `hqdefault` > `mqdefault` > `default` */
type ThumbQuality = `${"maxres" | "sd" | "hq" | "mq" | ""}default`;
type ThumbQuality = `${"maxres" | "sd" | "hq" | "mq"}default` | "default";

/** Returns the thumbnail URL for a video with the given watch ID and quality (defaults to "hqdefault") */
export function getThumbnailUrl(watchId: string, quality?: ThumbQuality): string
/** Returns the thumbnail URL for a video with the given watch ID and index (0 is low quality thumbnail, 1-3 are low quality frames from the video) */
export function getThumbnailUrl(watchId: string, index: 0 | 1 | 2 | 3): string
export function getThumbnailUrl(watchId: string, index?: 0 | 1 | 2 | 3): string
/** Returns the thumbnail URL for a video with either a given quality identifier or index */
export function getThumbnailUrl(watchId: string, qualityOrIndex: ThumbQuality | 0 | 1 | 2 | 3 = "maxresdefault") {
export function getThumbnailUrl(watchId: string, qualityOrIndex: Prettify<ThumbQuality | 0 | 1 | 2 | 3> = "maxresdefault") {
return `https://img.youtube.com/vi/${watchId}/${qualityOrIndex}.jpg`;
}

Expand Down Expand Up @@ -230,49 +230,56 @@ export function formatNumber(num: number, notation?: NumberLengthFormat): string
*/
export async function getResourceUrl(name: ResourceKey | "_", uncached = false) {
let url = !uncached && await GM.getResourceUrl(name);

if(!url || url.length === 0) {
const resObjOrStr = resourcesJson?.[name as keyof typeof resourcesJson];

if(typeof resObjOrStr === "object" || typeof resObjOrStr === "string") {
const pathname = typeof resObjOrStr === "object" && "path" in resObjOrStr ? resObjOrStr.path : resObjOrStr;
const ref = typeof resObjOrStr === "object" && "ref" in resObjOrStr ? resObjOrStr.ref : branch;

if(pathname && pathname.startsWith("/") && pathname.length > 1)
return `https://raw.githubusercontent.com/${repo}/${ref}${pathname}`;
else if(pathname && pathname.startsWith("http"))
return pathname;
else if(pathname && pathname.length > 0)
return `https://raw.githubusercontent.com/${repo}/${ref}/assets/${pathname}`;
const pathName = typeof resObjOrStr === "object" && "path" in resObjOrStr ? resObjOrStr.path : resObjOrStr;
const ghRef = typeof resObjOrStr === "object" && "ref" in resObjOrStr ? resObjOrStr.ref : branch;

if(pathName && pathName.startsWith("/") && pathName.length > 1)
return `https://raw.githubusercontent.com/${repo}/${ghRef}${pathName}`;
else if(pathName && pathName.startsWith("http"))
return pathName;
else if(pathName && pathName.length > 0)
return `https://raw.githubusercontent.com/${repo}/${ghRef}/assets/${pathName}`;
}

warn(`Couldn't get blob URL nor external URL for @resource '${name}', trying to use base64-encoded fallback`);
// @ts-ignore
url = await GM.getResourceUrl(name, false);
}

return url;
}

/**
* Returns the preferred locale of the user, provided it is supported by the userscript.
* Prioritizes `navigator.language`, then `navigator.languages`, then `"en-US"` as a fallback.
* Resolves the preferred locale of the user given their browser's language settings, as long as it is supported by the userscript directly or via the `altLocales` prop in `locales.json`
* Prioritizes any supported value of `navigator.language`, then `navigator.languages`, then goes over them again, trimming off the part after the hyphen, then falls back to `"en-US"`
*/
export function getPreferredLocale(): TrLocale {
const nvLangs = navigator.languages
.filter(lang => lang.match(/^[a-z]{2}(-|_)[A-Z]$/) !== null);

if(Object.entries(langMapping).find(([key]) => key === navigator.language))
return navigator.language as TrLocale;

for(const loc of nvLangs) {
if(Object.entries(langMapping).find(([key]) => key === loc))
return loc as TrLocale;
}

// if navigator.languages has entries that aren't locale codes in the format xx-XX
if(navigator.languages.some(lang => lang.match(/^[a-z]{2}$/))) {
for(const lang of nvLangs) {
const foundLoc = Object.entries(langMapping).find(([ key ]) => key.startsWith(lang))?.[0];
if(foundLoc)
return foundLoc as TrLocale;
}
const sanEq = (str1: string, str2: string) => str1.trim().toLowerCase() === str2.trim().toLowerCase();

const allNvLocs = [...new Set([navigator.language, ...navigator.languages])]
.map((v) => v.replace(/_/g, "-"));

for(const nvLoc of allNvLocs) {
const resolvedLoc = Object.entries(langMapping)
.find(([key, { altLocales }]) =>
sanEq(key, nvLoc) || altLocales.find(al => sanEq(al, nvLoc))
)?.[0];
if(resolvedLoc)
return resolvedLoc.trim() as TrLocale;

const trimmedNvLoc = nvLoc.split("-")[0];
const resolvedFallbackLoc = Object.entries(langMapping)
.find(([key, { altLocales }]) =>
sanEq(key.split("-")[0], trimmedNvLoc) || altLocales.find(al => sanEq(al.split("-")[0], trimmedNvLoc))
)?.[0];

if(resolvedFallbackLoc)
return resolvedFallbackLoc.trim() as TrLocale;
}

return "en-US";
Expand Down

0 comments on commit 7dc45cc

Please sign in to comment.