diff --git a/Models/ThemeConfig/Conversion/PrepareTheme.ts b/Models/ThemeConfig/Conversion/PrepareTheme.ts index b563a232a..be7af4abd 100644 --- a/Models/ThemeConfig/Conversion/PrepareTheme.ts +++ b/Models/ThemeConfig/Conversion/PrepareTheme.ts @@ -9,6 +9,7 @@ import LayerConfig from "../LayerConfig"; import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson"; import {SubstitutedTranslation} from "../../../UI/SubstitutedTranslation"; import DependencyCalculator from "../DependencyCalculator"; +import Translations from "../../../UI/i18n/Translations"; class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfigJson[]> { private readonly _state: DesugaringContext; @@ -279,6 +280,72 @@ export class AddMiniMap extends DesugaringStep { } } +class AddContextToTransltionsInLayout extends DesugaringStep { + + constructor() { + super("Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too",["_context"], "AddContextToTranlationsInLayout"); + } + + convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { + const conversion = new AddContextToTranslations("themes:") + return conversion.convert(json, json.id); + } + +} + +class AddContextToTranslations extends DesugaringStep { + private readonly _prefix: string; + + constructor(prefix = "") { + super("Adds a '_context' to every object that is probably a translation", ["_context"], "AddContextToTranslation"); + this._prefix = prefix; + } + + /** + * const theme = { + * layers: [ + * { + * builtin: ["abc"], + * override: { + * title:{ + * en: "Some title" + * } + * } + * } + * ] + * } + * const rewritten = new AddContextToTranslations("prefix:").convert(theme, "context").result + * const expected = { + * layers: [ + * { + * builtin: ["abc"], + * override: { + * title:{ + * _context: "prefix:context.layers.0.override.title" + * en: "Some title" + * } + * } + * } + * ] + * } + * rewritten // => expected + */ + convert(json: T, context: string): { result: T; errors?: string[]; warnings?: string[]; information?: string[] } { + + const result = Utils.WalkJson(json, (leaf, path) => { + if(typeof leaf === "object"){ + return {...leaf, _context: this._prefix + context+"."+ path.join(".")} + }else{ + return leaf + } + }, obj => obj !== undefined && obj !== null && Translations.isProbablyATranslation(obj)) + + return { + result + }; + } + +} class ApplyOverrideAll extends DesugaringStep { @@ -327,8 +394,13 @@ class AddDependencyLayersToTheme extends DesugaringStep { const dependencies: { neededLayer: string, reason: string, context?: string, neededBy: string }[] = [] for (const layerConfig of alreadyLoaded) { - const layerDeps = DependencyCalculator.getLayerDependencies(new LayerConfig(layerConfig)) - dependencies.push(...layerDeps) + try{ + const layerDeps = DependencyCalculator.getLayerDependencies(new LayerConfig(layerConfig)) + dependencies.push(...layerDeps) + }catch(e){ + console.error(e) + throw "Detecting layer dependencies for "+layerConfig.id+" failed due to "+e + } } for (const dependency of dependencies) { @@ -454,6 +526,7 @@ export class PrepareTheme extends Fuse { constructor(state: DesugaringContext) { super( "Fully prepares and expands a theme", + new AddContextToTransltionsInLayout(), new PreparePersonalTheme(state), new WarnForUnsubstitutedLayersInTheme(), new On("layers", new Concat(new SubstituteLayer(state))), diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 6608ecfe7..ce2e56f05 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -27,6 +27,7 @@ import FilterConfigJson from "./Json/FilterConfigJson"; import {And} from "../../Logic/Tags/And"; import {Overpass} from "../../Logic/Osm/Overpass"; import Constants from "../Constants"; +import undefinedError = Mocha.utils.undefinedError; export default class LayerConfig extends WithContextLoader { diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 8e3af95f0..f0504ada6 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -15,11 +15,15 @@ export class Translation extends BaseUIElement { constructor(translations: object, context?: string) { super() - this.context = context; if (translations === undefined) { console.error("Translation without content at "+context) throw `Translation without content (${context})` } + this.context = translations["_context"] ?? context; + if(translations["_context"] !== undefined){ + translations = {...translations} + delete translations["_context"] + } if (typeof translations === "string") { translations = {"*": translations}; } @@ -28,6 +32,9 @@ export class Translation extends BaseUIElement { if (!translations.hasOwnProperty(translationsKey)) { continue } + if(translationsKey === "_context"){ + continue + } count++; if (typeof (translations[translationsKey]) != "string") { console.error("Non-string object in translation: ", translations[translationsKey]) diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index 4f23c8908..482cc7eeb 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -107,7 +107,7 @@ export default class Translations { return false } // is a weird key found? - if(Object.keys(transl).some(key => !this.knownLanguages.has(key))){ + if(Object.keys(transl).some(key => key !== '_context' && !this.knownLanguages.has(key))){ return false } diff --git a/Utils.ts b/Utils.ts index 9770e3b3f..7ee06e174 100644 --- a/Utils.ts +++ b/Utils.ts @@ -515,41 +515,46 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } /** - * Apply a function on every leaf of the JSON; used to rewrite parts of the JSON + * Apply a function on every leaf of the JSON; used to rewrite parts of the JSON. + * Returns a modified copy of the original object. + * + * Hangs if the object contains a loop */ - static WalkJson(json: any, f: (v: number | string | boolean | undefined) => any, isLeaf: (object) => boolean = undefined) { + static WalkJson(json: any, f: (v: object | number | string | boolean | undefined, path: string[]) => any, isLeaf: (object) => boolean = undefined, path: string[] = []) { if (json === undefined) { - return f(undefined) + return f(undefined, path) } const jtp = typeof json if (isLeaf !== undefined) { if (jtp === "object") { if (isLeaf(json)) { - return f(json) + return f(json, path) } } else { return json } } else if (jtp === "boolean" || jtp === "string" || jtp === "number") { - return f(json) + return f(json, path) } if (Array.isArray(json)) { - return json.map(sub => { - return Utils.WalkJson(sub, f, isLeaf); + return json.map((sub,i) => { + return Utils.WalkJson(sub, f, isLeaf, [...path,""+i]); }) } const cp = {...json} for (const key in json) { - cp[key] = Utils.WalkJson(json[key], f, isLeaf) + cp[key] = Utils.WalkJson(json[key], f, isLeaf, [...path, key]) } return cp } /** - * Walks an object recursively. Will hang on objects with loops + * Walks an object recursively, will execute the 'collect'-callback on every leaf. + * + * Will hang on objects with loops */ - static WalkObject(json: any, collect: (v: number | string | boolean | undefined, path: string[]) => any, isLeaf: (object) => boolean = undefined, path = []) { + static WalkObject(json: any, collect: (v: number | string | boolean | undefined, path: string[]) => any, isLeaf: (object) => boolean = undefined, path = []): void { if (json === undefined) { return; } @@ -563,12 +568,14 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be return collect(json, path) } } else if (jtp === "boolean" || jtp === "string" || jtp === "number") { - return collect(json, path) + collect(json, path) + return } if (Array.isArray(json)) { - return json.map((sub, i) => { + json.map((sub, i) => { return Utils.WalkObject(sub, collect, isLeaf, [...path, i]); }) + return } for (const key in json) { diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index b03b39e7a..da73732b3 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -231,10 +231,6 @@ "if": "theme=openwindpowermap", "then": "./assets/themes/openwindpowermap/logo.svg" }, - { - "if": "theme=parking-lanes", - "then": "./assets/svg/bug.svg" - }, { "if": "theme=parkings", "then": "./assets/themes/parkings/parkings.svg" diff --git a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json index 169d81435..fa0f7383f 100644 --- a/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json +++ b/assets/themes/toerisme_vlaanderen/toerisme_vlaanderen.json @@ -50,7 +50,7 @@ ] } }, - "=filter": null, + "filter": null, "=mapRendering": [ { "location": [