mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
05ee3d50e4
The release modal takes the 4.3.0 design: the release and its headline cards on the left, the note and the support links on the right, at the design's own size. The cards carry small drawings of the features instead of an icon. The 4.0.0 entry is retired. In its place sits one entry, release-notes, that every big release rewrites and keeps. It recurs per version with no upper bound, so every user sees it once on each update, whatever they did with it before, and a reload of the same version keeps it closed. An integration test holds that promise through the real entry and the real dismiss route. All 23 locales carry the new copy and drop the 4.0.0 strings.
91 lines
3.6 KiB
JavaScript
91 lines
3.6 KiB
JavaScript
/**
|
|
* Reads the canonical locale and reports every line that calls TREK self-hosted.
|
|
*
|
|
* Plain .mjs and outside `src/` for the same reason as i18n-parity.mjs next to
|
|
* it: shared's typecheck runs with only shared's own dependencies installed
|
|
* (`npm ci --workspace shared`), so there is no @types/node and a `node:fs`
|
|
* import inside `src/` fails to compile. The spec imports this the same way the
|
|
* parity spec imports its script.
|
|
*
|
|
* Runnable on its own — `node scripts/i18n-selfhost-wording.mjs` prints the hits
|
|
* — which is what you want when a CI failure names a line you have to find.
|
|
*/
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
|
const EN_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'src', 'i18n', 'en');
|
|
|
|
/** Case-insensitive, and covers the hyphenated, spaced and joined spellings. */
|
|
export const FORBIDDEN = /self[-\s]?hosted/i;
|
|
|
|
/**
|
|
* The strings that keep the phrase, each with the reason it is right where it is.
|
|
*
|
|
* Keyed on the translation key rather than the file, so an exemption covers one
|
|
* sentence instead of everything that happens to live beside it. Keep the list
|
|
* short: it is the only way this rule can be worn down.
|
|
*/
|
|
export const ALLOWED = [
|
|
{
|
|
key: 'settings.about.description',
|
|
because:
|
|
'the self-description shown to somebody who set TREK up themselves, and it is ' +
|
|
'accurate for them. A centrally administered install renders ' +
|
|
'settings.about.descriptionManaged instead, so neither reader is told the ' +
|
|
'wrong thing and self-hosters lose nothing.',
|
|
},
|
|
{
|
|
key: 'system_notice.release_notes.feature_dawarich_body',
|
|
because:
|
|
'it calls Dawarich self-hosted, not TREK, which is what Dawarich is, and the ' +
|
|
'release notice carrying it is shown only where the managed condition is false',
|
|
},
|
|
{
|
|
key: 'system_notice.v3_thankyou.body',
|
|
because:
|
|
'a dated message from the author about the project, pinned to one release, ' +
|
|
'and shown only where the managed condition is false',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Walks the canonical locale and reports every line carrying the phrase,
|
|
* attributed to the key it belongs to.
|
|
*
|
|
* A value can span several lines, so the key is the last one seen at or above
|
|
* the hit rather than something parsed out of the hit itself.
|
|
*/
|
|
export function collectSelfhostHits(dir = EN_DIR) {
|
|
const hits = [];
|
|
for (const name of fs.readdirSync(dir)) {
|
|
if (!name.endsWith('.ts')) continue;
|
|
const lines = fs.readFileSync(path.join(dir, name), 'utf8').split('\n');
|
|
let key = '';
|
|
lines.forEach((text, i) => {
|
|
const declared = /^\s*'([^']+)'\s*:/.exec(text);
|
|
if (declared) key = declared[1];
|
|
if (FORBIDDEN.test(text)) hits.push({ file: name, line: i + 1, key, text: text.trim() });
|
|
});
|
|
}
|
|
return hits;
|
|
}
|
|
|
|
/** Hits that no exemption covers — the ones that fail the build. */
|
|
export function findOffenders(dir = EN_DIR) {
|
|
const allowed = new Set(ALLOWED.map((a) => a.key));
|
|
return collectSelfhostHits(dir).filter((h) => !allowed.has(h.key));
|
|
}
|
|
|
|
// pathToFileURL rather than a `file://` template: on Windows argv[1] arrives with
|
|
// backslashes and a drive letter, so the plain comparison never matches and the
|
|
// script silently does nothing when run by hand.
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
const offenders = findOffenders();
|
|
for (const h of offenders) {
|
|
console.log(`${h.file}:${h.line} [${h.key}] ${h.text.slice(0, 80)}`);
|
|
}
|
|
console.log(offenders.length === 0 ? 'Wording: OK' : `Wording: ${offenders.length} offender(s)`);
|
|
process.exit(offenders.length === 0 ? 0 : 1);
|
|
}
|