Files
drop/i18n/scripts/utils.ts
DecDuck 63ac2b8ffc Depot API & v4 (#298)
* feat: nginx + torrential basics & services system

* fix: lint + i18n

* fix: update torrential to remove openssl

* feat: add torrential to Docker build

* feat: move to self hosted runner

* fix: move off self-hosted runner

* fix: update nginx.conf

* feat: torrential cache invalidation

* fix: update torrential for cache invalidation

* feat: integrity check task

* fix: lint

* feat: move to version ids

* fix: client fixes and client-side checks

* feat: new depot apis and version id fixes

* feat: update torrential

* feat: droplet bump and remove unsafe update functions

* fix: lint

* feat: v4 featureset: emulators, multi-launch commands

* fix: lint

* fix: mobile ui for game editor

* feat: launch options

* fix: lint

* fix: remove axios, use $fetch

* feat: metadata and task api improvements

* feat: task actions

* fix: slight styling issue

* feat: fix style and lints

* feat: totp backend routes

* feat: oidc groups

* fix: update drop-base

* feat: creation of passkeys & totp

* feat: totp signin

* feat: webauthn mfa/signin

* feat: launch selecting ui

* fix: manually running tasks

* feat: update add company game modal to use new SelectorGame

* feat: executor selector

* fix(docker): update rust to rust nightly for torrential build (#305)

* feat: new version ui

* feat: move package lookup to build time to allow for deno dev

* fix: lint

* feat: localisation cleanup

* feat: apply localisation cleanup

* feat: potential i18n refactor logic

* feat: remove args from commands

* fix: lint

* fix: lockfile

---------

Co-authored-by: Aden Lindsay <140392385+AdenMGB@users.noreply.github.com>
2026-01-13 15:32:39 +11:00

123 lines
3.5 KiB
TypeScript

import path from "node:path";
import fs from "node:fs";
import prettier from "prettier";
const prettierConfig = JSON.parse(
fs.readFileSync("./.prettierrc.json", "utf-8"),
);
const paths = ["./components", "./layouts", "./pages", "./server"];
const constPaths = ["error.vue", "app.vue"];
const extensions = [".vue", ".ts"];
function recursiveFindFiles(root: string): string[] {
const results = [];
const subpaths = fs.readdirSync(root);
for (const subpath of subpaths) {
const absPath = path.join(root, subpath);
if (extensions.some((v) => absPath.endsWith(v))) {
results.push(absPath);
continue;
}
const stat = fs.statSync(absPath);
if (stat.isDirectory()) {
results.push(...recursiveFindFiles(absPath));
continue;
}
}
return [...results, ...constPaths];
}
/**
* Fetches the paths of all files available to be localised
*/
export function allLocalisableFiles(): string[] {
const files = paths.map((k) => recursiveFindFiles(k)).flat();
return files;
}
const I18N_UTIL_REGEX = /(?<=[^a-zA-Z]t\(\s*?["']).*?(?=["'])/g;
const I18N_KEYPATH_REGEX = /(?<=keypath=["']).*?(?=["'])/g;
/**
* Uses regex to match all i18n keys in content
* @param content The file content to match against
*/
export function keysFromContent(content: string): string[] {
const matches = [
...content.matchAll(I18N_UTIL_REGEX),
...content.matchAll(I18N_KEYPATH_REGEX),
];
return matches.map((v) => v[0]);
}
export type Localisation = { [key: string]: Localisation | string };
export function flattenLocalisation(localisation: Localisation) {
const map = new Map<string, string>();
flattenLocalisationRecursive(map, [], localisation);
return map;
}
function flattenLocalisationRecursive(
map: Map<string, string>,
key: string[],
localisationBranch: Localisation | string,
) {
if (typeof localisationBranch === "string") {
map.set(key.join("."), localisationBranch);
return;
}
for (const [subKey, value] of Object.entries(localisationBranch)) {
const newKey = [...key, subKey];
flattenLocalisationRecursive(map, newKey, value);
}
}
export function deleteLocalisation(localisation: Localisation, key: string) {
const parts = key.split(".");
let current: Localisation | string = localisation;
for (const part of parts.slice(0, -1)) {
if (typeof current === "string")
throw new Error(`${key} not found in localisation`);
current = current[part];
}
if (typeof current === "string")
throw new Error(`${key} not found in localisation`);
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete current[parts.at(-1)!];
}
export function fetchLocalisation(
localisation: Localisation,
key: string,
): string {
const parts = key.split(".");
let current: Localisation | string = localisation;
for (const part of parts.slice(0, -1)) {
if (typeof current === "string")
throw new Error(`${key} not found in localisation`);
current = current[part];
}
if (typeof current === "string")
throw new Error(`${key} not found in localisation`);
return current[parts.at(-1)!] as string;
}
export async function writeJSON<T>(path: string, object: T) {
const flatStr = JSON.stringify(object);
const formatted = await prettier.format(flatStr, {
parser: "json",
...prettierConfig,
});
fs.writeFileSync(path, formatted);
}
/**
* Strips some sort of English language string down to something that can be compared to be basically equivalent
*/
export function stripEquivalence(value: string): string {
return value.replaceAll(/[.,\s]/g, "").toLowerCase();
}