// We HAVE to mark this while importing import {Utils} from "../Utils"; Utils.runningFromConsole = true; import LayoutConfig from "../Customizations/JSON/LayoutConfig"; import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; import {existsSync, mkdirSync, readFileSync, writeFile, writeFileSync} from "fs"; import Locale from "../UI/i18n/Locale"; import * as sharp from "sharp" import Translations from "../UI/i18n/Translations"; import {Translation} from "../UI/i18n/Translation"; function enc(str: string): string { return encodeURIComponent(str.toLowerCase()); } function validate(layout: LayoutConfig) { const translations: Translation[] = []; const queue: any[] = [layout] while (queue.length > 0) { const item = queue.pop(); for (const key in item) { const v = item[key]; if (v === undefined) { continue; } if (v instanceof Translation || v?.translations !== undefined) { translations.push(v); } else if ( ["string", "function", "boolean", "number"].indexOf(typeof (v)) < 0) { queue.push(v) } } } const missing = {} const present = {} for (const ln of layout.language) { missing[ln] = 0; present[ln] = 0; for (const translation of translations) { if (translation.translations["*"] !== undefined) { continue; } const txt = translation.translations[ln]; const isMissing = txt === undefined || txt === "" || txt.toLowerCase().indexOf("todo") >= 0; if (isMissing) { console.log(` ${layout.id}: No translation for`, ln, "in", translation.translations, "got:", txt) missing[ln]++ } else { present[ln]++; } } } let message = `Translation completenes for theme ${layout.id}` let isComplete = true; for (const ln of layout.language) { const amiss = missing[ln]; const ok = present[ln]; const total = amiss + ok; message += `\n${ln}: ${ok}/${total}` if (ok !== total) { isComplete = false; } } if (isComplete) { console.log(`${layout.id} is fully translated!`) } else { console.log(message) } } function generateWikiEntry(layout: LayoutConfig) { if (layout.hideFromOverview) { return ""; } const languages = layout.language.map(ln => `{{#language:${ln}|en}}`).join(", ") let auth = "Yes"; if (layout.maintainer !== "" && layout.maintainer !== "MapComplete") { auth = `Yes, by ${layout.maintainer};` } return `{{service_item |name= [https://mapcomplete.osm.be/${layout.id} ${layout.id}] |region= Worldwide |lang= ${languages} |descr= A MapComplete theme: ${Translations.W(layout.description) .InnerRender() .replace(".*<\/a>/, "]]") } |material= {{yes|[https://mapcomplete.osm.be/ ${auth}]}} |image= MapComplete_Screenshot.png |genre= POI, editor, ${layout.id} }}` } const alreadyWritten = [] async function createIcon(iconPath: string, size: number, layout: LayoutConfig) { let name = iconPath.split(".").slice(0, -1).join("."); if (name.startsWith("./")) { name = name.substr(2) } const newname = `${name}${size}.png` .replace(/\//g, "_") .replace("assets_", "assets/generated/"); if (alreadyWritten.indexOf(newname) >= 0) { return newname; } alreadyWritten.push(newname); try { readFileSync(newname); return newname; // File already exists - nothing to do } catch (e) { // Errors are normal here if this file exists } try { console.log("Creating icon ", name, newname) // We already read to file, in order to crash here if the file is not found readFileSync(iconPath); let img = await sharp(iconPath) let resized = await img.resize(size) await resized.toFile(newname) /* svg2img(iconPath, // @ts-ignore {width: size, height: size, preserveAspectRatio: true}) .then((buffer) => { console.log("Writing icon", newname) writeFileSync(newname, buffer); }).catch((error) => { console.log("ERROR while writing" + iconPath, error) }); //*/ } catch (e) { console.error("Could not read icon", iconPath, "due to", e) } return newname; } async function createManifest(layout: LayoutConfig, relativePath: string) { const name = layout.id; const icons = []; let icon = layout.icon; if (icon.endsWith(".svg") || icon.startsWith(""; } catch (e) { customCss = `` } } const og = ` ` let icon = layout.icon; if (icon.startsWith("", og) .replace(/.+?<\/title>/, `<title>${ogTitle}`) .replace("Loading MapComplete, hang on...", `Loading MapComplete theme ${ogTitle}...`) .replace("", customCss) .replace(``, ``); try { output = output .replace(/.*/s, ``) .replace(/.*/s, ``); } catch (e) { console.warn("Error while applying logo: ", e) } return output; } const generatedDir = "./assets/generated"; if (!existsSync(generatedDir)) { mkdirSync(generatedDir) } const blacklist = ["", "test", ".", "..", "manifest", "index", "land", "preferences", "account", "openstreetmap", "custom"] const all = AllKnownLayouts.allSets; let wikiPage = "{|class=\"wikitable sortable\"\n" + "! Name, link !! Genre !! Covered region !! Language !! Description !! Free materials !! Image\n" + "|-"; for (const layoutName in all) { if (blacklist.indexOf(layoutName.toLowerCase()) >= 0) { console.log(`Skipping a layout with name${layoutName}, it is on the blacklist`); continue; } const err = err => { if (err !== null) { console.log("Could not write manifest for ", layoutName, " because ", err) } }; const layout = all[layoutName]; validate(layout) createManifest(layout, "").then(manifObj => { const manif = JSON.stringify(manifObj); const manifestLocation = encodeURIComponent(layout.id.toLowerCase()) + ".webmanifest"; writeFile(manifestLocation, manif, err); }) // Create a landing page for the given theme createLandingPage(layout).then(landing => { writeFile(enc(layout.id) + ".html", landing, err) wikiPage += "\n" + generateWikiEntry(layout); }); } wikiPage += "\n|}" writeFile(generatedDir + "/wikiIndex", wikiPage, (err) => { if (err !== null) { console.log("Could not save wikiindex", err); } }); console.log("Counting all translations") Translations.CountTranslations(); console.log("All done!");