forked from MapComplete/MapComplete
		
	Fix: fix validation of question input; remove some obsolete logging; stabilize output of generate:translations wrt newlines
This commit is contained in:
		
							parent
							
								
									755f905c36
								
							
						
					
					
						commit
						1f39ba9ab5
					
				
					 26 changed files with 7328 additions and 71 deletions
				
			
		|  | @ -15,7 +15,6 @@ export default class BBoxFeatureSource extends StaticFeatureSource { | |||
|                     if (mustTouch.data === undefined) { | ||||
|                         return features | ||||
|                     } | ||||
|                     console.log("UPdating touching bbox") | ||||
|                     const box = mustTouch.data | ||||
|                     return features.filter((feature) => { | ||||
|                         if (feature.geometry.type === "Point") { | ||||
|  |  | |||
|  | @ -1,22 +1,20 @@ | |||
| import { Translation, TypedTranslation } from "../../UI/i18n/Translation" | ||||
| import { TagsFilter } from "../../Logic/Tags/TagsFilter" | ||||
| import {Translation, TypedTranslation} from "../../UI/i18n/Translation" | ||||
| import {TagsFilter} from "../../Logic/Tags/TagsFilter" | ||||
| import Translations from "../../UI/i18n/Translations" | ||||
| import { TagUtils, UploadableTag } from "../../Logic/Tags/TagUtils" | ||||
| import { And } from "../../Logic/Tags/And" | ||||
| import { Utils } from "../../Utils" | ||||
| import { Tag } from "../../Logic/Tags/Tag" | ||||
| import {TagUtils, UploadableTag} from "../../Logic/Tags/TagUtils" | ||||
| import {And} from "../../Logic/Tags/And" | ||||
| import {Utils} from "../../Utils" | ||||
| import {Tag} from "../../Logic/Tags/Tag" | ||||
| import BaseUIElement from "../../UI/BaseUIElement" | ||||
| import Combine from "../../UI/Base/Combine" | ||||
| import Title from "../../UI/Base/Title" | ||||
| import Link from "../../UI/Base/Link" | ||||
| import List from "../../UI/Base/List" | ||||
| import { | ||||
|     MappingConfigJson, | ||||
|     QuestionableTagRenderingConfigJson, | ||||
| } from "./Json/QuestionableTagRenderingConfigJson" | ||||
| import { FixedUiElement } from "../../UI/Base/FixedUiElement" | ||||
| import { Paragraph } from "../../UI/Base/Paragraph" | ||||
| import {MappingConfigJson, QuestionableTagRenderingConfigJson,} from "./Json/QuestionableTagRenderingConfigJson" | ||||
| import {FixedUiElement} from "../../UI/Base/FixedUiElement" | ||||
| import {Paragraph} from "../../UI/Base/Paragraph" | ||||
| import Svg from "../../Svg" | ||||
| import Validators, {ValidatorType} from "../../UI/InputElement/Validators"; | ||||
| 
 | ||||
| export interface Mapping { | ||||
|     readonly if: UploadableTag | ||||
|  | @ -623,13 +621,19 @@ export default class TagRenderingConfig { | |||
|      * | ||||
|      * @param singleSelectedMapping (Only used if multiAnswer == false): the single mapping to apply. Use (mappings.length) for the freeform | ||||
|      * @param multiSelectedMapping (Only used if multiAnswer == true): all the mappings that must be applied. Set multiSelectedMapping[mappings.length] to use the freeform as well | ||||
|      * @param currentProperties: The current properties of the object for which the question should be answered | ||||
|      */ | ||||
|     public constructChangeSpecification( | ||||
|         freeformValue: string | undefined, | ||||
|         singleSelectedMapping: number, | ||||
|         multiSelectedMapping: boolean[] | undefined | ||||
|         multiSelectedMapping: boolean[] | undefined, | ||||
|         currentProperties: Record<string, string> | ||||
|     ): UploadableTag { | ||||
|         freeformValue = freeformValue?.trim() | ||||
|         const validator = Validators.get(<ValidatorType> this.freeform?.type) | ||||
|         if(validator && freeformValue){ | ||||
|             freeformValue = validator.reformat(freeformValue,() => currentProperties["_country"]) | ||||
|         } | ||||
|         if (freeformValue === "") { | ||||
|             freeformValue = undefined | ||||
|         } | ||||
|  | @ -666,7 +670,7 @@ export default class TagRenderingConfig { | |||
|                 .filter((_, i) => !multiSelectedMapping[i]) | ||||
|                 .map((m) => m.ifnot) | ||||
| 
 | ||||
|             if (multiSelectedMapping.at(-1)) { | ||||
|             if (multiSelectedMapping.at(-1) && this.freeform) { | ||||
|                 // The freeform value was selected as well
 | ||||
|                 selectedMappings.push( | ||||
|                     new And([ | ||||
|  | @ -677,22 +681,29 @@ export default class TagRenderingConfig { | |||
|             } | ||||
|             return TagUtils.FlattenMultiAnswer([...selectedMappings, ...unselectedMappings]) | ||||
|         } else { | ||||
|             if (singleSelectedMapping === undefined) { | ||||
|                 return undefined | ||||
|             } | ||||
|             if (singleSelectedMapping === this.mappings.length) { | ||||
|                 if (freeformValue === undefined) { | ||||
|                     return undefined | ||||
|             // Is at least one mapping shown in the answer?
 | ||||
|             const someMappingIsShown = this.mappings.some(m => { | ||||
|                 if(typeof m.hideInAnswer === "boolean"){ | ||||
|                     return !m.hideInAnswer | ||||
|                 } | ||||
|                 const isHidden = m.hideInAnswer.matchesProperties(currentProperties) | ||||
|                 return !isHidden | ||||
|             }  ) | ||||
|             // If all mappings are hidden for the current tags, we can safely assume that we should use the freeform key
 | ||||
|             const useFreeform = freeformValue !== undefined && (singleSelectedMapping === this.mappings.length || !someMappingIsShown) | ||||
|             if (useFreeform) { | ||||
|                 return new And([ | ||||
|                     new Tag(this.freeform.key, freeformValue), | ||||
|                     ...(this.freeform.addExtraTags ?? []), | ||||
|                 ]) | ||||
|             } else { | ||||
|             } else if(singleSelectedMapping) { | ||||
|                 return new And([ | ||||
|                     this.mappings[singleSelectedMapping].if, | ||||
|                     ...(this.mappings[singleSelectedMapping].addExtraTags ?? []), | ||||
|                 ]) | ||||
|             }else{ | ||||
|                 console.log("TagRenderingConfig.ConstructSpecification has a weird fallback for", {freeformValue, singleSelectedMapping, multiSelectedMapping, currentProperties}) | ||||
|                 return undefined | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -296,7 +296,8 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|         ) | ||||
| 
 | ||||
|         this.initActors() | ||||
|         this.drawSpecialLayers(lastClick) | ||||
|         this.addLastClick(lastClick) | ||||
|         this.drawSpecialLayers() | ||||
|         this.initHotkeys() | ||||
|         this.miscSetup() | ||||
|         console.log("State setup completed", this) | ||||
|  | @ -424,10 +425,9 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|     /** | ||||
|      * Add the special layers to the map | ||||
|      */ | ||||
|     private drawSpecialLayers(last_click: LastClickFeatureSource) { | ||||
|     private drawSpecialLayers() { | ||||
|         type AddedByDefaultTypes = typeof Constants.added_by_default[number] | ||||
|         const empty = [] | ||||
|         this.addLastClick(last_click) | ||||
|         /** | ||||
|          * A listing which maps the layerId onto the featureSource | ||||
|          */ | ||||
|  |  | |||
|  | @ -6,29 +6,43 @@ | |||
|   import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import { Translation } from "../i18n/Translation"; | ||||
|   import { createEventDispatcher, onDestroy } from "svelte"; | ||||
|   import {Validator} from "./Validator"; | ||||
| 
 | ||||
|   export let value: UIEventSource<string>; | ||||
|   // Internal state, only copied to 'value' so that no invalid values leak outside | ||||
|   let _value = new UIEventSource(value.data ?? ""); | ||||
|   onDestroy(value.addCallbackAndRunD(v => _value.setData(v ?? ""))); | ||||
|   export let type: ValidatorType; | ||||
|   let validator = Validators.get(type); | ||||
|   export let feedback: UIEventSource<Translation> | undefined = undefined; | ||||
|   export let getCountry: () => string | undefined | ||||
|   let validator : Validator = Validators.get(type) | ||||
|   $: { | ||||
|     // The type changed -> reset some values | ||||
|     validator = Validators.get(type) | ||||
|     _value.setData("") | ||||
|     feedback =  feedback?.setData(validator?.getFeedback(_value.data, getCountry)); | ||||
|   } | ||||
|    | ||||
|   onDestroy(value.addCallbackAndRun(v => { | ||||
|     if(v === undefined || v === ""){ | ||||
|       _value.setData("") | ||||
|     } | ||||
|   })) | ||||
|   onDestroy(_value.addCallbackAndRun(v => { | ||||
|     if (validator.isValid(v)) { | ||||
|     if (validator.isValid(v, getCountry)) { | ||||
|       feedback?.setData(undefined); | ||||
|       value.setData(v); | ||||
|       return; | ||||
|     } | ||||
|     value.setData(undefined); | ||||
|     feedback?.setData(validator.getFeedback(v)); | ||||
|     feedback?.setData(validator.getFeedback(v, getCountry)); | ||||
|   })) | ||||
| 
 | ||||
|   if (validator === undefined) { | ||||
|     throw "Not a valid type for a validator:" + type; | ||||
|   } | ||||
| 
 | ||||
|   const isValid = _value.map(v => validator.isValid(v)); | ||||
|   const isValid = _value.map(v => validator.isValid(v, getCountry)); | ||||
| 
 | ||||
|   let htmlElem: HTMLInputElement; | ||||
| 
 | ||||
|  |  | |||
|  | @ -44,9 +44,8 @@ export abstract class Validator { | |||
|     /** | ||||
|      * Gets a piece of feedback. By default, validation.<type> will be used, resulting in a generic 'not a valid <type>'. | ||||
|      * However, inheritors might overwrite this to give more specific feedback | ||||
|      * @param s | ||||
|      */ | ||||
|     public getFeedback(s: string): Translation { | ||||
|     public getFeedback(s: string, requestCountry?: () => string): Translation { | ||||
|         const tr = Translations.t.validation[this.name] | ||||
|         if (tr !== undefined) { | ||||
|             return tr["feedback"] | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| import { Translation } from "../../i18n/Translation.js" | ||||
| import {Translation} from "../../i18n/Translation.js" | ||||
| import Translations from "../../i18n/Translations.js" | ||||
| import * as emailValidatorLibrary from "email-validator" | ||||
| import { Validator } from "../Validator" | ||||
| import {Validator} from "../Validator" | ||||
| 
 | ||||
| export default class EmailValidator extends Validator { | ||||
|     constructor() { | ||||
|         super("email", "An email adress", "email") | ||||
|  |  | |||
|  | @ -1,12 +1,28 @@ | |||
| import { parsePhoneNumberFromString } from "libphonenumber-js" | ||||
| import { Validator } from "../Validator" | ||||
| import {parsePhoneNumberFromString} from "libphonenumber-js" | ||||
| import {Validator} from "../Validator" | ||||
| import {Translation} from "../../i18n/Translation"; | ||||
| import Translations from "../../i18n/Translations"; | ||||
| 
 | ||||
| export default class PhoneValidator extends Validator { | ||||
|     constructor() { | ||||
|         super("phone", "A phone number", "tel") | ||||
|     } | ||||
| 
 | ||||
|     isValid(str, country: () => string): boolean { | ||||
| 
 | ||||
|     getFeedback(s: string, requestCountry?: () => string): Translation { | ||||
|         const tr = Translations.t.validation.phone | ||||
|         const generic = tr.feedback | ||||
|         if(requestCountry){ | ||||
|         const country = requestCountry() | ||||
|             if(country){ | ||||
|                 return  tr.feedbackCountry.Subs({country}) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return generic | ||||
|     } | ||||
| 
 | ||||
|     public isValid(str, country: () => string): boolean { | ||||
|         if (str === undefined) { | ||||
|             return false | ||||
|         } | ||||
|  | @ -20,13 +36,17 @@ export default class PhoneValidator extends Validator { | |||
|         return parsePhoneNumberFromString(str, countryCode)?.isValid() ?? false | ||||
|     } | ||||
| 
 | ||||
|     reformat = (str, country: () => string) => { | ||||
|     public reformat(str, country: () => string) { | ||||
|         if (str.startsWith("tel:")) { | ||||
|             str = str.substring("tel:".length) | ||||
|         } | ||||
|         let countryCode = undefined | ||||
|         if(country){ | ||||
|             countryCode = country() | ||||
|         } | ||||
|         return parsePhoneNumberFromString( | ||||
|             str, | ||||
|             country()?.toUpperCase() as any | ||||
|             countryCode?.toUpperCase() as any | ||||
|         )?.formatInternational() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -498,7 +498,7 @@ export class OH { | |||
|                 lat: tags._lat, | ||||
|                 lon: tags._lon, | ||||
|                 address: { | ||||
|                     country_code: tags._country.toLowerCase(), | ||||
|                     country_code: tags._country?.toLowerCase(), | ||||
|                     state: undefined, | ||||
|                 }, | ||||
|             }, | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import { VariableUiElement } from "../Base/VariableUIElement" | |||
| import Table from "../Base/Table" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import Loading from "../Base/Loading"; | ||||
| 
 | ||||
| export default class OpeningHoursVisualization extends Toggle { | ||||
|     private static readonly weekdays: Translation[] = [ | ||||
|  | @ -29,6 +30,7 @@ export default class OpeningHoursVisualization extends Toggle { | |||
|         prefix = "", | ||||
|         postfix = "" | ||||
|     ) { | ||||
|         const country = tags.map(tags => tags._country) | ||||
|         const ohTable = new VariableUiElement( | ||||
|             tags | ||||
|                 .map((tags) => { | ||||
|  | @ -66,12 +68,12 @@ export default class OpeningHoursVisualization extends Toggle { | |||
|                             ), | ||||
|                         ]) | ||||
|                     } | ||||
|                 }) | ||||
|                 }, [country]) | ||||
|         ) | ||||
| 
 | ||||
|         super( | ||||
|             ohTable, | ||||
|             Translations.t.general.opening_hours.loadingCountry.Clone(), | ||||
|             new Loading(Translations.t.general.opening_hours.loadingCountry), | ||||
|             tags.map((tgs) => tgs._country !== undefined) | ||||
|         ) | ||||
|     } | ||||
|  | @ -160,7 +162,7 @@ export default class OpeningHoursVisualization extends Toggle { | |||
|         const weekdayStyles = [] | ||||
|         for (let i = 0; i < 7; i++) { | ||||
|             const day = OpeningHoursVisualization.weekdays[i].Clone() | ||||
|             day.SetClass("w-full h-full block") | ||||
|             day.SetClass("w-full h-full flex") | ||||
| 
 | ||||
|             const rangesForDay = ranges[i].map((range) => | ||||
|                 OpeningHoursVisualization.CreateRangeElem( | ||||
|  |  | |||
|  | @ -177,6 +177,7 @@ export default class MoveWizard extends Toggle { | |||
| 
 | ||||
|                 state.featureProperties.getStore(id).ping() | ||||
|                 currentStep.setData("moved") | ||||
|                 state.mapProperties.location.setData(loc) | ||||
|             }) | ||||
|             const zoomInFurhter = t.zoomInFurther.SetClass("alert block m-6") | ||||
|             return new Combine([ | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ import Lazy from "../Base/Lazy" | |||
| import { OsmServiceState } from "../../Logic/Osm/OsmConnection" | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated | ||||
|  * This element is getting stripped and is not used anymore | ||||
|  * Generates all the questions, one by one | ||||
|  */ | ||||
| export default class QuestionBox extends VariableUiElement { | ||||
|  |  | |||
|  | @ -19,17 +19,20 @@ | |||
| 
 | ||||
|   let dispatch = createEventDispatcher<{ "selected" }>(); | ||||
|   onDestroy(value.addCallbackD(() => {dispatch("selected")})) | ||||
|   function getCountry() { | ||||
|     return tags.data["_country"] | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <div class="inline-flex flex-col"> | ||||
| 
 | ||||
|   {#if config.freeform.inline} | ||||
|     <Inline key={config.freeform.key} {tags} template={config.render}> | ||||
|       <ValidatedInput {feedback} on:selected={() => dispatch("selected")} | ||||
|       <ValidatedInput {feedback} {getCountry} on:selected={() => dispatch("selected")} | ||||
|                       type={config.freeform.type} {value}></ValidatedInput> | ||||
|     </Inline> | ||||
|   {:else} | ||||
|     <ValidatedInput {feedback} on:selected={() => dispatch("selected")} | ||||
|     <ValidatedInput {feedback} {getCountry} on:selected={() => dispatch("selected")} | ||||
|                     type={config.freeform.type} {value}></ValidatedInput> | ||||
| 
 | ||||
|   {/if} | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
|   import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import SpecialTranslation from "./SpecialTranslation.svelte"; | ||||
|   import TagHint from "../TagHint.svelte"; | ||||
|   import Validators from "../../InputElement/Validators"; | ||||
| 
 | ||||
|   export let config: TagRenderingConfig; | ||||
|   export let tags: UIEventSource<Record<string, string>>; | ||||
|  | @ -34,9 +35,12 @@ | |||
|   let selectedMapping: number = undefined; | ||||
|   let checkedMappings: boolean[]; | ||||
|   $: { | ||||
|     // We received a new config -> reinit | ||||
|     console.log("Initing checkedMappings for", config) | ||||
|     if (config.mappings?.length > 0 && (checkedMappings === undefined || checkedMappings?.length < config.mappings.length)) { | ||||
|       checkedMappings = [...config.mappings.map(_ => false), false /*One element extra in case a freeform value is added*/]; | ||||
|     } | ||||
|     freeformInput.setData(undefined) | ||||
|   } | ||||
|   let selectedTags: TagsFilter = undefined; | ||||
| 
 | ||||
|  | @ -54,9 +58,10 @@ | |||
|   $: { | ||||
|     mappings = config.mappings?.filter(m => !mappingIsHidden(m)); | ||||
|     try { | ||||
|       selectedTags = config?.constructChangeSpecification($freeformInput, selectedMapping, checkedMappings); | ||||
|       let freeformInputValue = $freeformInput | ||||
|       selectedTags = config?.constructChangeSpecification(freeformInputValue, selectedMapping, checkedMappings, tags.data); | ||||
|     } catch (e) { | ||||
|       console.debug("Could not calculate changeSpecification:", e); | ||||
|       console.error("Could not calculate changeSpecification:", e); | ||||
|       selectedTags = undefined; | ||||
|     } | ||||
|   } | ||||
|  | @ -99,7 +104,7 @@ | |||
|       } | ||||
|     ); | ||||
|     freeformInput.setData(undefined); | ||||
|     selectedMapping = 0; | ||||
|     selectedMapping = undefined; | ||||
|     selectedTags = undefined; | ||||
| 
 | ||||
|     change.CreateChangeDescriptions().then(changes => | ||||
|  | @ -139,14 +144,14 @@ | |||
|         {#each config.mappings as mapping, i (mapping.then)} | ||||
|           <!-- Even though we have a list of 'mappings' already, we still iterate over the list as to keep the original indices--> | ||||
|           {#if !mappingIsHidden(mapping)  } | ||||
|             <label> | ||||
|             <label class="flex"> | ||||
|               <input type="radio" bind:group={selectedMapping} name={"mappings-radio-"+config.id} value={i}> | ||||
|               <TagRenderingMapping {mapping} {tags} {state} {selectedElement} {layer}></TagRenderingMapping> | ||||
|             </label> | ||||
|           {/if} | ||||
|         {/each} | ||||
|         {#if config.freeform?.key} | ||||
|           <label> | ||||
|           <label class="flex"> | ||||
|             <input type="radio" bind:group={selectedMapping} name={"mappings-radio-"+config.id} | ||||
|                    value={config.mappings.length}> | ||||
|             <FreeformInput {config} {tags} feature={selectedElement} value={freeformInput} | ||||
|  | @ -159,13 +164,14 @@ | |||
|       <div class="flex flex-col"> | ||||
|         {#each config.mappings as mapping, i (mapping.then)} | ||||
|           {#if !mappingIsHidden(mapping)} | ||||
|             <label> | ||||
|             <label class="flex"> | ||||
|               <input type="checkbox" name={"mappings-checkbox-"+config.id+"-"+i} bind:checked={checkedMappings[i]}> | ||||
|               <TagRenderingMapping {mapping} {tags} {state} {selectedElement}></TagRenderingMapping> | ||||
|             </label>{/if} | ||||
|             </label> | ||||
|           {/if} | ||||
|         {/each} | ||||
|         {#if config.freeform?.key} | ||||
|           <label> | ||||
|           <label class="flex"> | ||||
|             <input type="checkbox" name={"mappings-checkbox-"+config.id+"-"+config.mappings.length} | ||||
|                    bind:checked={checkedMappings[config.mappings.length]}> | ||||
|             <FreeformInput {config} {tags} feature={selectedElement} value={freeformInput} | ||||
|  | @ -184,7 +190,7 @@ | |||
|           <Tr t={Translations.t.general.save}></Tr> | ||||
|         </button> | ||||
|       {:else } | ||||
|         <div class="w-6 h-6"> | ||||
|         <div class="inline-flex w-6 h-6"> | ||||
|           <!-- Invalid value; show an inactive button or something like that--> | ||||
|           <ExclamationIcon/> | ||||
|         </div> | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ import { SearchablePillsSelector } from "../Input/SearchableMappingsSelector" | |||
| import { OsmTags } from "../../Models/OsmFeature" | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated: getting stripped and getting ported | ||||
|  * Shows the question element. | ||||
|  * Note that the value _migh_ already be known, e.g. when selected or when changing the value | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										7
									
								
								assets/fonts/Ubuntu-L-normal.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								assets/fonts/Ubuntu-L-normal.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								assets/fonts/Ubuntu-M-normal.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								assets/fonts/Ubuntu-M-normal.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								assets/fonts/UbuntuMono-B-bold.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								assets/fonts/UbuntuMono-B-bold.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -1006,7 +1006,8 @@ | |||
|         }, | ||||
|         "phone": { | ||||
|             "description": "a phone number", | ||||
|             "feedback": "This is not a valid phone number" | ||||
|             "feedback": "This is not a valid phone number", | ||||
|             "feedbackCountry": "This is not a valid phone number (for country {country})" | ||||
|         }, | ||||
|         "pnat": { | ||||
|             "description": "a positive, whole number", | ||||
|  |  | |||
							
								
								
									
										908
									
								
								public/assets/templates/MapComplete-flyer.back.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										908
									
								
								public/assets/templates/MapComplete-flyer.back.svg
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 56 KiB | 
							
								
								
									
										1492
									
								
								public/assets/templates/MapComplete-flyer.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1492
									
								
								public/assets/templates/MapComplete-flyer.svg
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 1.6 MiB | 
							
								
								
									
										2353
									
								
								public/assets/templates/MapComplete-poster-a2.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2353
									
								
								public/assets/templates/MapComplete-poster-a2.svg
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 1.5 MiB | 
							
								
								
									
										2398
									
								
								public/assets/templates/MapComplete-poster-a3.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2398
									
								
								public/assets/templates/MapComplete-poster-a3.svg
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 1.5 MiB | 
							
								
								
									
										7
									
								
								public/assets/templates/Ubuntu-L-normal.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								public/assets/templates/Ubuntu-L-normal.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								public/assets/templates/Ubuntu-M-normal.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								public/assets/templates/Ubuntu-M-normal.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								public/assets/templates/UbuntuMono-B-bold.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								public/assets/templates/UbuntuMono-B-bold.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -1,6 +1,6 @@ | |||
| import * as fs from "fs" | ||||
| import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs" | ||||
| import { Utils } from "../Utils" | ||||
| import {existsSync, mkdirSync, readFileSync, writeFileSync} from "fs" | ||||
| import {Utils} from "../Utils" | ||||
| import ScriptUtils from "./ScriptUtils" | ||||
| 
 | ||||
| const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"] | ||||
|  | @ -12,7 +12,7 @@ class TranslationPart { | |||
|         const files = ScriptUtils.readDirRecSync(path, 1).filter((file) => file.endsWith(".json")) | ||||
|         const rootTranslation = new TranslationPart() | ||||
|         for (const file of files) { | ||||
|             const content = JSON.parse(readFileSync(file, { encoding: "utf8" })) | ||||
|             const content = JSON.parse(readFileSync(file, {encoding: "utf8"})) | ||||
|             rootTranslation.addTranslation(file.substr(0, file.length - ".json".length), content) | ||||
|         } | ||||
|         return rootTranslation | ||||
|  | @ -52,10 +52,10 @@ class TranslationPart { | |||
|             if (typeof v != "string") { | ||||
|                 console.error( | ||||
|                     `Non-string object at ${context} in translation while trying to add the translation ` + | ||||
|                         JSON.stringify(v) + | ||||
|                         ` to '` + | ||||
|                         translationsKey + | ||||
|                         "'. The offending object which _should_ be a translation is: ", | ||||
|                     JSON.stringify(v) + | ||||
|                     ` to '` + | ||||
|                     translationsKey + | ||||
|                     "'. The offending object which _should_ be a translation is: ", | ||||
|                     v, | ||||
|                     "\n\nThe current object is (only showing en):", | ||||
|                     this.toJson(), | ||||
|  | @ -94,9 +94,9 @@ class TranslationPart { | |||
|             if (noTranslate !== undefined) { | ||||
|                 console.log( | ||||
|                     "Ignoring some translations for " + | ||||
|                         context + | ||||
|                         ": " + | ||||
|                         dontTranslateKeys.join(", ") | ||||
|                     context + | ||||
|                     ": " + | ||||
|                     dontTranslateKeys.join(", ") | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | @ -243,14 +243,14 @@ class TranslationPart { | |||
|                 } | ||||
|                 subparts = subparts.map((p) => p.split(/\(.*\)/)[0]) | ||||
|                 for (const subpart of subparts) { | ||||
|                     neededSubparts.add({ part: subpart, usedByLanguage: lang }) | ||||
|                     neededSubparts.add({part: subpart, usedByLanguage: lang}) | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         // Actually check for the needed sub-parts, e.g. that {key} isn't translated into {sleutel}
 | ||||
|         this.contents.forEach((value, key) => { | ||||
|             neededSubparts.forEach(({ part, usedByLanguage }) => { | ||||
|             neededSubparts.forEach(({part, usedByLanguage}) => { | ||||
|                 if (typeof value !== "string") { | ||||
|                     return | ||||
|                 } | ||||
|  | @ -444,6 +444,7 @@ function removeEmptyString(object: object) { | |||
|     } | ||||
|     return object | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Formats the specified file, helps to prevent merge conflicts | ||||
|  * */ | ||||
|  | @ -659,7 +660,8 @@ function mergeLayerTranslations() { | |||
|     const layerFiles = ScriptUtils.getLayerFiles() | ||||
|     for (const layerFile of layerFiles) { | ||||
|         mergeLayerTranslation(layerFile.parsed, layerFile.path, loadTranslationFilesFrom("layers")) | ||||
|         writeFileSync(layerFile.path, JSON.stringify(layerFile.parsed, null, "  ")) // layers use 2 spaces
 | ||||
|         const endsWithNewline = readFileSync(layerFile.path, {encoding: "utf8"})?.endsWith("\n") ?? true | ||||
|         writeFileSync(layerFile.path, JSON.stringify(layerFile.parsed, null, "  ") + (endsWithNewline ? "\n" : "")) // layers use 2 spaces
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -674,7 +676,8 @@ function mergeThemeTranslations() { | |||
| 
 | ||||
|         const allTranslations = new TranslationPart() | ||||
|         allTranslations.recursiveAdd(config, themeFile.path) | ||||
|         writeFileSync(themeFile.path, JSON.stringify(config, null, "  ")) // Themefiles use 2 spaces
 | ||||
|         const endsWithNewline = readFileSync(themeFile.path, {encoding: "utf8"})?.endsWith("\n") ?? true | ||||
|         writeFileSync(themeFile.path, JSON.stringify(config, null, "  ") + (endsWithNewline ? "\n" : "")) // Themefiles use 2 spaces
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -693,7 +696,8 @@ if (!themeOverwritesWeblate) { | |||
|         questionsPath, | ||||
|         loadTranslationFilesFrom("shared-questions") | ||||
|     ) | ||||
|     writeFileSync(questionsPath, JSON.stringify(questionsParsed, null, "  ")) | ||||
|     const endsWithNewline = readFileSync(questionsPath, {encoding: "utf8"}).endsWith("\n") | ||||
|     writeFileSync(questionsPath, JSON.stringify(questionsParsed, null, "  ") + (endsWithNewline ? "\n" : "")) | ||||
| } else { | ||||
|     console.log("Ignore weblate") | ||||
| } | ||||
|  | @ -704,13 +708,13 @@ const l2 = generateTranslationsObjectFrom( | |||
|     "themes" | ||||
| ) | ||||
| const l3 = generateTranslationsObjectFrom( | ||||
|     [{ path: questionsPath, parsed: questionsParsed }], | ||||
|     [{path: questionsPath, parsed: questionsParsed}], | ||||
|     "shared-questions" | ||||
| ) | ||||
| 
 | ||||
| const usedLanguages: string[] = Utils.Dedup(l1.concat(l2).concat(l3)).filter((v) => v !== "*") | ||||
| usedLanguages.sort() | ||||
| fs.writeFileSync("./assets/used_languages.json", JSON.stringify({ languages: usedLanguages })) | ||||
| fs.writeFileSync("./assets/used_languages.json", JSON.stringify({languages: usedLanguages})) | ||||
| 
 | ||||
| if (!themeOverwritesWeblate) { | ||||
|     // Generates the core translations
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue