Merge branch 'master' into refactoring/new-ui
This commit is contained in:
commit
06cbf65eb1
18 changed files with 702 additions and 28 deletions
185
Docs/Architecture.md
Normal file
185
Docs/Architecture.md
Normal file
|
@ -0,0 +1,185 @@
|
|||
Architecture
|
||||
============
|
||||
|
||||
This document aims to give an architectural overview of how MapCompelte is built. It should give some feeling on how everything fits together.
|
||||
|
||||
Servers?
|
||||
--------
|
||||
|
||||
There are no servers for MapComplete, all services are configured by third parties.
|
||||
|
||||
Minimal HTML - Minimal CSS
|
||||
--------------------------
|
||||
|
||||
There is quasi no HTML. Most of the components are generated by TypeScript and attached dynamically. The html is a barebones skeleton which serves every theme.
|
||||
|
||||
|
||||
The UIEventSource
|
||||
-----------------
|
||||
|
||||
Most (but not all) objects in MapComplete get all the state they need as a parameter in the constructor. However, as is the case with most graphical applications, there are quite some dynamical values.
|
||||
|
||||
All values which change regularly are wrapped into a [UIEventSource](https://github.com/pietervdvn/MapComplete/blob/master/Logic/UIEventSource.ts).
|
||||
An UiEventSource is a wrapper containing a value and offers the possibility to add a callback function which is called every time the value is changed (with setData)
|
||||
|
||||
Furthermore, there are various helper functions, the most widely used one being `map` - generating a new event source with the new value applied.
|
||||
Note that 'map' will also absorb some changes, e.g. `const someEventSource : UIEventSource<string[]> = ... ; someEventSource.map(list = list.length)` will only trigger when the length of the list has changed.
|
||||
|
||||
An object which receives an UIEventSource is responsible of responding onto changes of this object. This is especially true for UI-components
|
||||
|
||||
UI
|
||||
--
|
||||
|
||||
The Graphical User Interface is composed of various UI-elements. For every UI-element, there is a BaseUIElement which creates the actual HTMLElement when needed.
|
||||
|
||||
There are some basic elements, such as:
|
||||
|
||||
- FixedUIElement which shows a fixed, unchangeble element
|
||||
- Img to show an image
|
||||
- Combine which wrap everything given (strings and other elements) in a div
|
||||
- List
|
||||
|
||||
There is one special component: the VariableUIElement
|
||||
The variableUIElement takes a `UIEventSource<string|BaseUIElement>` and will dynamicaly show whatever the UIEventSource contains at the moment.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
|
||||
const src : UIEventSource<string> = ... // E.g. user input, data that will be updated...
|
||||
new VariableUIElement(src)
|
||||
.AttachTo('some-id') // attach it to the html
|
||||
```
|
||||
|
||||
Note that every component offers support for `onClick( someCallBack)`
|
||||
|
||||
### Translations
|
||||
|
||||
To add a translation:
|
||||
|
||||
1. Open `langs/en.json`
|
||||
2. Find a correct spot for your translation in the tree
|
||||
3. run `npm run generate:translations`
|
||||
4. `import Translations`
|
||||
5. Translations.t.<your-translation>.Clone() is the UIElement offering your translation
|
||||
|
||||
### Input elements`
|
||||
|
||||
Input elements are a special kind of BaseElement and which offer a piece of a form to the user, e.g. a TextField, a Radio button, a dropdown, ...
|
||||
|
||||
The constructor will ask all the parameters to configure them. The actual value can be obtained via `inputElement.GetValue()`, which is a UIEVentSource that will be triggered every time the user changes the input.
|
||||
|
||||
### Advanced elements
|
||||
|
||||
There are some components which offer useful functionality:
|
||||
|
||||
|
||||
- The `subtleButton` which is a friendly, big button
|
||||
- The Toggle: `const t = new Toggle( componentA, componentB, source)` is a UIEventSource which shows `componentA` as long as `source` contains `true` and will show `componentB` otherwise.
|
||||
|
||||
|
||||
### Styling
|
||||
|
||||
Styling is done as much as possible with [TailwindCSS](https://tailwindcss.com/). It contains a ton of utility classes, each of them containing a few rules.
|
||||
|
||||
For exmaple: ` someBaseUIElement.SetClass("flex flex-col border border-black rounded-full")` will set the component to be a flex object, as column, with a black border and pill-shaped.
|
||||
|
||||
If tailwind is not enough, `baseUiElement.SetStyle("background: red; someOtherCssRule: abc;")`
|
||||
|
||||
### An example
|
||||
|
||||
For example: the user should input wether or not a shop is closed during public holidays. There are three options:
|
||||
|
||||
1. closed
|
||||
2. opened as usual
|
||||
3. opened with different hours as usual
|
||||
|
||||
In the case of different hours, input hours should be too.
|
||||
|
||||
This can be constructed as following:
|
||||
|
||||
|
||||
```
|
||||
// We construct the dropdown element with values and labelshttps://tailwindcss.com/
|
||||
const isOpened = new Dropdown<string>(Translations.t.is_this_shop_opened_during_holidays,
|
||||
[
|
||||
{ value: "closed", Translation.t.shop_closed_during_holidays.Clone()},
|
||||
{ value: "open", Translations.t.shop_opened_as_usual.Clone()},
|
||||
{ value: "hours", Translations.t.shop_opened_with_other_hours.Clone()}
|
||||
] )
|
||||
|
||||
const startHour = new DateInput(...)drop
|
||||
const endHour = new DateInput( ... )
|
||||
// We construct a toggle which'll only show the extra questions if needed
|
||||
const extraQuestion = new Toggle(
|
||||
new Combine([Translations.t.openFrom, startHour, Translations.t.openTill, endHour]),
|
||||
undefined,
|
||||
isOpened.GetValue().map(isopened => isopened === "hours")
|
||||
)
|
||||
|
||||
return new Combine([isOpened, extraQuestion])
|
||||
```
|
||||
|
||||
### Constructing a special class
|
||||
|
||||
If you make a specialized class to offer a certain functionality, you can organize it as following:
|
||||
|
||||
1. Create a new class:
|
||||
|
||||
```
|
||||
|
||||
export default class MyComponent {
|
||||
|
||||
constructor(neededParameters, neededUIEventSources) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
2. Construct the needed UI in the constructor
|
||||
|
||||
|
||||
```
|
||||
|
||||
export default class MyComponent {
|
||||
|
||||
constructor(neededParameters, neededUIEventSources) {
|
||||
|
||||
|
||||
const component = ...
|
||||
const toggle = ...
|
||||
... other components ...
|
||||
|
||||
toggle.GetValue.AddCallbackAndRun(isSelected => { .. some actions ... }
|
||||
|
||||
new Combine([everything, ...] )
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
3. You'll notice that you'll end up with one certain component (in this example the combine) to wrap it all together. Change the class to extend this type of component and use super to wrap it all up:
|
||||
|
||||
|
||||
```
|
||||
|
||||
export default class MyComponent extends Combine {
|
||||
|
||||
constructor(...) {
|
||||
|
||||
...
|
||||
super([everything, ...])
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
Logic
|
||||
-----
|
||||
|
||||
With the
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
"addPicture": "Add picture",
|
||||
"uploadingPicture": "Uploading your picture…",
|
||||
"uploadingMultiple": "Uploading {count} pictures…",
|
||||
"pleaseLogin": "Please login to add a picture",
|
||||
"pleaseLogin": "Please log in to add a picture",
|
||||
"willBePublished": "Your picture will be published: ",
|
||||
"cco": "in the public domain",
|
||||
"ccbs": "under the CC-BY-SA-license",
|
||||
"ccb": "under the CC-BY-license",
|
||||
"uploadFailed": "Could not upload your picture. Do you have internet and are third party API's allowed? Brave browser or UMatrix might block them.",
|
||||
"uploadFailed": "Could not upload your picture. Are you connected to the Internet, and allow third party API's? The Brave browser or the uMatrix plugin might block them.",
|
||||
"respectPrivacy": "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.",
|
||||
"uploadDone": "<span class='thanks'>Your picture has been added. Thanks for helping out!</span>",
|
||||
"dontDelete": "Cancel",
|
||||
|
|
1
langs/layers/nan.json
Normal file
1
langs/layers/nan.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -229,6 +229,9 @@
|
|||
},
|
||||
"5": {
|
||||
"render": "{access}"
|
||||
},
|
||||
"1": {
|
||||
"question": "К какому типу относится эта велопарковка?"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -754,4 +757,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,28 @@
|
|||
"question": "這個長椅有幾個位子?"
|
||||
},
|
||||
"3": {
|
||||
"render": "材質:{material}"
|
||||
"render": "材質:{material}",
|
||||
"question": "這個長椅 (座位) 是什麼做的?",
|
||||
"mappings": {
|
||||
"5": {
|
||||
"then": "材質:鋼鐵"
|
||||
},
|
||||
"4": {
|
||||
"then": "材質:塑膠"
|
||||
},
|
||||
"3": {
|
||||
"then": "材質:水泥"
|
||||
},
|
||||
"2": {
|
||||
"then": "材質:石頭"
|
||||
},
|
||||
"1": {
|
||||
"then": "材質:金屬"
|
||||
},
|
||||
"0": {
|
||||
"then": "材質:木頭"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"render": "顏色:{colour}",
|
||||
|
@ -55,8 +76,315 @@
|
|||
}
|
||||
},
|
||||
"6": {
|
||||
"question": "上一次探察長椅是什麼時候?"
|
||||
"question": "上一次探察長椅是什麼時候?",
|
||||
"render": "這個長椅最後是在 {survey:date} 探查的"
|
||||
},
|
||||
"4": {
|
||||
"render": "當坐在長椅時,那個人朝向 {direction}°。",
|
||||
"question": "坐在長椅時是面對那個方向?"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"0": {
|
||||
"description": "新增長椅",
|
||||
"title": "長椅"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bike_parking": {
|
||||
"tagRenderings": {
|
||||
"2": {
|
||||
"mappings": {
|
||||
"4": {
|
||||
"then": "屋頂停車場"
|
||||
},
|
||||
"3": {
|
||||
"then": "地面層停車場"
|
||||
},
|
||||
"2": {
|
||||
"then": "地面停車場"
|
||||
},
|
||||
"1": {
|
||||
"then": "地下停車場"
|
||||
},
|
||||
"0": {
|
||||
"then": "地下停車場"
|
||||
}
|
||||
},
|
||||
"question": "這個單車停車場的相對位置是?"
|
||||
},
|
||||
"1": {
|
||||
"mappings": {
|
||||
"7": {
|
||||
"then": "樓層當中標示為單車停車場的區域"
|
||||
},
|
||||
"6": {
|
||||
"then": "柱子 <img style='width: 25%'' src='./assets/layers/bike_parking/bollard.svg'>"
|
||||
},
|
||||
"5": {
|
||||
"then": "車棚 <img style='width: 25%'' src='./assets/layers/bike_parking/shed.svg'>"
|
||||
},
|
||||
"4": {
|
||||
"then": "兩層<img style='width: 25%'' src='./assets/layers/bike_parking/two_tier.svg'>"
|
||||
},
|
||||
"3": {
|
||||
"then": "車架<img style='width: 25%'' src='./assets/layers/bike_parking/rack.svg'>"
|
||||
},
|
||||
"2": {
|
||||
"then": "車把架 <img style='width: 25%'' src='./assets/layers/bike_parking/handlebar_holder.svg'>"
|
||||
},
|
||||
"1": {
|
||||
"then": "車輪架/圓圈 <img style='width: 25%'' src='./assets/layers/bike_parking/wall_loops.svg'>"
|
||||
},
|
||||
"0": {
|
||||
"then": "單車架 <img style='width: 25%' src='./assets/layers/bike_parking/staple.svg'>"
|
||||
}
|
||||
},
|
||||
"render": "這個單車停車場的類型是:{bicycle_parking}",
|
||||
"question": "這是那種類型的單車停車場?"
|
||||
},
|
||||
"6": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "這停車場有設計 (官方) 空間給裝箱的單車。"
|
||||
},
|
||||
"0": {
|
||||
"then": "這個停車場有地方可以放裝箱單車"
|
||||
}
|
||||
},
|
||||
"question": "這個單車停車場有地方放裝箱的單車嗎?"
|
||||
},
|
||||
"5": {
|
||||
"mappings": {
|
||||
"2": {
|
||||
"then": "通行性僅限學校、公司或組織的成員"
|
||||
},
|
||||
"1": {
|
||||
"then": "通行性主要是為了企業的顧客"
|
||||
},
|
||||
"0": {
|
||||
"then": "公開可用"
|
||||
}
|
||||
},
|
||||
"render": "{access}",
|
||||
"question": "誰可以使用這個單車停車場?"
|
||||
},
|
||||
"4": {
|
||||
"render": "{capacity} 單車的地方",
|
||||
"question": "這個單車停車場能放幾台單車 (包括裝箱單車)?"
|
||||
},
|
||||
"3": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "這個停車場沒有遮蔽"
|
||||
},
|
||||
"0": {
|
||||
"then": "這個停車場有遮蔽 (有屋頂)"
|
||||
}
|
||||
},
|
||||
"question": "這個停車場是否有車棚?如果是室內停車場也請選擇\"遮蔽\"。"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "單車停車場"
|
||||
},
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "單車停車場"
|
||||
}
|
||||
},
|
||||
"name": "單車停車場"
|
||||
},
|
||||
"bike_monitoring_station": {
|
||||
"title": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "單車計數站 {ref}"
|
||||
},
|
||||
"0": {
|
||||
"then": "單車計數站 {name}"
|
||||
}
|
||||
},
|
||||
"render": "單車計數站"
|
||||
},
|
||||
"name": "監視站"
|
||||
},
|
||||
"bike_cleaning": {
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "單車清理服務"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "單車清理服務 <i>{name}</i>"
|
||||
}
|
||||
},
|
||||
"render": "單車清理服務"
|
||||
},
|
||||
"name": "單車清理服務"
|
||||
},
|
||||
"bike_cafe": {
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "單車咖啡廳"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"8": {
|
||||
"question": "何時這個單車咖啡廳營運?"
|
||||
},
|
||||
"7": {
|
||||
"question": "{name} 的電子郵件地址是?"
|
||||
},
|
||||
"6": {
|
||||
"question": "{name} 的電話號碼是?"
|
||||
},
|
||||
"5": {
|
||||
"question": "{name} 的網站是?"
|
||||
},
|
||||
"4": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "這個單車咖啡廳並不修理單車"
|
||||
},
|
||||
"0": {
|
||||
"then": "這個單車咖啡廳修理單車"
|
||||
}
|
||||
},
|
||||
"question": "這個單車咖啡廳是否能修理單車?"
|
||||
},
|
||||
"3": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "這個單車咖啡廳並沒有提供工具讓你修理"
|
||||
},
|
||||
"0": {
|
||||
"then": "這個單車咖啡廳提供工具讓你修理"
|
||||
}
|
||||
},
|
||||
"question": "這裡是否有工具修理你的單車嗎?"
|
||||
},
|
||||
"2": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "這個單車咖啡廳並沒有為所有人提供單車打氣甬"
|
||||
},
|
||||
"0": {
|
||||
"then": "這個單車咖啡廳有提供給任何人都能使用的單車打氣甬"
|
||||
}
|
||||
},
|
||||
"question": "這個單車咖啡廳有提供給任何人都能使用的單車打氣甬嗎?"
|
||||
},
|
||||
"1": {
|
||||
"render": "這個單車咖啡廳叫做 {name}",
|
||||
"question": "這個單車咖啡廳的名稱是?"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "單車咖啡廳<i>{name}</i>"
|
||||
}
|
||||
},
|
||||
"render": "單車咖啡廳"
|
||||
},
|
||||
"name": "單車咖啡廳"
|
||||
},
|
||||
"bicycle_tube_vending_machine": {
|
||||
"tagRenderings": {
|
||||
"1": {
|
||||
"mappings": {
|
||||
"2": {
|
||||
"then": "這個自動販賣機已經關閉了"
|
||||
},
|
||||
"1": {
|
||||
"then": "這個自動販賣機沒有運作了"
|
||||
},
|
||||
"0": {
|
||||
"then": "這個自動販賣機仍運作"
|
||||
}
|
||||
},
|
||||
"render": "運作狀態是 <i>{operational_status</i>",
|
||||
"question": "這個自動販賣機仍有運作嗎?"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "自行車內胎自動售貨機"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "自行車內胎自動售貨機"
|
||||
},
|
||||
"name": "自行車內胎自動售貨機"
|
||||
},
|
||||
"bicycle_library": {
|
||||
"presets": {
|
||||
"0": {
|
||||
"description": "單車圖書館有一大批單車供人租借",
|
||||
"title": "自行車圖書館 ( Fietsbibliotheek)"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"7": {
|
||||
"mappings": {
|
||||
"2": {
|
||||
"then": "有提供行動不便人士的單車"
|
||||
},
|
||||
"1": {
|
||||
"then": "有提供成人單車"
|
||||
},
|
||||
"0": {
|
||||
"then": "提供兒童單車"
|
||||
}
|
||||
},
|
||||
"question": "誰可以在這裡租單車?"
|
||||
},
|
||||
"6": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "租借單車價錢 €20/year 與 €20 保證金"
|
||||
},
|
||||
"0": {
|
||||
"then": "租借單車免費"
|
||||
}
|
||||
},
|
||||
"render": "租借單車需要 {charge}",
|
||||
"question": "租用單車的費用多少?"
|
||||
},
|
||||
"1": {
|
||||
"render": "這個單車圖書館叫做 {name}",
|
||||
"question": "這個單車圖書館的名稱是?"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "單車圖書館"
|
||||
},
|
||||
"name": "單車圖書館",
|
||||
"description": "能夠長期租用單車的設施"
|
||||
},
|
||||
"bench_at_pt": {
|
||||
"tagRenderings": {
|
||||
"2": {
|
||||
"render": "站立長椅"
|
||||
},
|
||||
"1": {
|
||||
"render": "{name}"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "涼亭內的長椅"
|
||||
},
|
||||
"0": {
|
||||
"then": "大眾運輸站點的長椅"
|
||||
}
|
||||
},
|
||||
"render": "長椅"
|
||||
},
|
||||
"name": "大眾運輸站點的長椅"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1
langs/nan.json
Normal file
1
langs/nan.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -23,6 +23,7 @@
|
|||
"dontDelete": "Avbryt",
|
||||
"uploadingMultiple": "Laster opp {count} bilder …",
|
||||
"uploadingPicture": "Laster opp bildet ditt …",
|
||||
"addPicture": "Legg til bilde"
|
||||
"addPicture": "Legg til bilde",
|
||||
"pleaseLogin": "Logg inn for å legge til et bilde"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@
|
|||
"ccbs": "под лицензией CC-BY-SA",
|
||||
"cco": "в открытом доступе",
|
||||
"willBePublished": "Ваше изображение будет опубликоавано: ",
|
||||
"pleaseLogin": "Войдите чтобы добавить изображение",
|
||||
"pleaseLogin": "Пожалуйста, войдите в систему, чтобы добавить изображение",
|
||||
"uploadingMultiple": "Загружаем {count} изображений…",
|
||||
"uploadingPicture": "Загружаем изображение…",
|
||||
"addPicture": "Добавить изображение"
|
||||
|
|
|
@ -1 +1,10 @@
|
|||
{}
|
||||
{
|
||||
"undefined": {
|
||||
"website": {
|
||||
"question": "Apa situs web dari {name}?"
|
||||
},
|
||||
"email": {
|
||||
"question": "Apa alamat surel dari {name}?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1
langs/shared-questions/nan.json
Normal file
1
langs/shared-questions/nan.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -1 +1,7 @@
|
|||
{}
|
||||
{
|
||||
"undefined": {
|
||||
"phone": {
|
||||
"question": "Vad är telefonnumret till {name}?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,20 @@
|
|||
{}
|
||||
{
|
||||
"undefined": {
|
||||
"opening_hours": {
|
||||
"render": "<h3>開放時間</h3>{opening_hours_table(opening_hours)}",
|
||||
"question": "{name} 的開放時間是什麼?"
|
||||
},
|
||||
"description": {
|
||||
"question": "有什麼相關的資訊你無法在先前的問題回應的嗎?請加在這邊吧。<br/><span style='font-size: small'>不要重覆答覆已經知道的事情</span>"
|
||||
},
|
||||
"website": {
|
||||
"question": "{name} 網址是什麼?"
|
||||
},
|
||||
"email": {
|
||||
"question": "{name} 的電子郵件地址是什麼?"
|
||||
},
|
||||
"phone": {
|
||||
"question": "{name} 的電話號碼是什麼?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
"centerMessage": {
|
||||
"ready": "Klar!",
|
||||
"zoomIn": "Zooma in för att visa eller redigera data",
|
||||
"loadingData": "Laddar data…"
|
||||
"loadingData": "Laddar data…",
|
||||
"retrying": "Det gick inte att ladda in data. Försöker igen om {count} sekunder…"
|
||||
},
|
||||
"image": {
|
||||
"isDeleted": "Borttagen",
|
||||
|
|
1
langs/themes/nan.json
Normal file
1
langs/themes/nan.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -1 +1,9 @@
|
|||
{}
|
||||
{
|
||||
"artworks": {
|
||||
"title": "Öppen konstverkskarta"
|
||||
},
|
||||
"aed": {
|
||||
"description": "På denna karta kan man hitta och markera närliggande defibrillatorer",
|
||||
"title": "Öppna AED-karta"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,8 @@
|
|||
},
|
||||
"benches": {
|
||||
"title": "長椅",
|
||||
"shortDescription": "長椅的地圖"
|
||||
"shortDescription": "長椅的地圖",
|
||||
"description": "這份地圖顯示開放街圖上所有記錄的長椅:單獨的長椅,屬於大眾運輸站點或涼亭的長椅。只要有開放街圖帳號,你可以新增長椅或是編輯既有長椅的詳細內容。"
|
||||
},
|
||||
"bicyclelib": {
|
||||
"title": "單車圖書館",
|
||||
|
@ -109,8 +110,104 @@
|
|||
"then": "這個地方並沒有廁所"
|
||||
}
|
||||
}
|
||||
},
|
||||
"11": {
|
||||
"question": "你想要為這個地方加一般的敘述嗎?(不要重覆加先前問過或提供的資訊,請保持敘述性-請將意見留在評價)",
|
||||
"render": "這個地方更詳細的資訊: {description}"
|
||||
},
|
||||
"10": {
|
||||
"mappings": {
|
||||
"2": {
|
||||
"then": "如果有長期租用合約才有可能待下來(如果你選擇這個地方則會在這份地圖消失)"
|
||||
},
|
||||
"1": {
|
||||
"then": "沒有,這裡沒有永久的客戶"
|
||||
},
|
||||
"0": {
|
||||
"then": "有,這個地方有提供長期租用,但你也可以用天計算費用"
|
||||
}
|
||||
},
|
||||
"question": "這個地方有提供長期租用嗎?"
|
||||
},
|
||||
"9": {
|
||||
"question": "這個地方有網站嗎?",
|
||||
"render": "官方網站:<a href='{website}'>{website}</a>"
|
||||
},
|
||||
"7": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "你不需要額外付費來使用網路連線"
|
||||
},
|
||||
"0": {
|
||||
"then": "你需要額外付費來使用網路連線"
|
||||
}
|
||||
},
|
||||
"question": "你需要為網路連線付費嗎?"
|
||||
},
|
||||
"6": {
|
||||
"mappings": {
|
||||
"2": {
|
||||
"then": "這裡沒有網路連線"
|
||||
},
|
||||
"1": {
|
||||
"then": "這裡有網路連線"
|
||||
},
|
||||
"0": {
|
||||
"then": "這裡有網路連線"
|
||||
}
|
||||
},
|
||||
"question": "這個地方有提網路連線嗎?"
|
||||
},
|
||||
"5": {
|
||||
"question": "多少露營者能夠待在這裡?(如果沒有明顯的空間數字或是允許車輛則可以跳過)",
|
||||
"render": "{capacity} 露營者能夠同時使用這個地方"
|
||||
},
|
||||
"4": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "這個地方沒有衛生設施"
|
||||
},
|
||||
"0": {
|
||||
"then": "這個地方有衛生設施"
|
||||
}
|
||||
},
|
||||
"question": "這個地方有衛生設施嗎?"
|
||||
},
|
||||
"3": {
|
||||
"question": "這個地方收多少費用?",
|
||||
"render": "這個地方收費 {charge}"
|
||||
},
|
||||
"2": {
|
||||
"mappings": {
|
||||
"1": {
|
||||
"then": "可以免費使用"
|
||||
},
|
||||
"0": {
|
||||
"then": "你要付費才能使用"
|
||||
}
|
||||
},
|
||||
"question": "這個地方收費嗎?"
|
||||
},
|
||||
"1": {
|
||||
"question": "這個地方叫做什麼?",
|
||||
"render": "這個地方叫做 {name}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "露營地"
|
||||
}
|
||||
},
|
||||
"description": "露營地",
|
||||
"title": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "沒有名稱的露營地"
|
||||
}
|
||||
},
|
||||
"render": "露營地 {name}"
|
||||
},
|
||||
"name": "露營地"
|
||||
},
|
||||
"1": {
|
||||
"tagRenderings": {
|
||||
|
@ -127,7 +224,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "這個網站收集所有官方露營地點,以及那邊能排放廢水。你可以加上詳細的服務項目與價格,加上圖片以及評價。這是網站與網路 app,資料則是存在開放街圖,因此會永遠免費,而且可以被所有 app 再利用。",
|
||||
"shortDescription": "露營者尋找渡過夜晚的場地"
|
||||
},
|
||||
"charging_stations": {
|
||||
"title": "充電站",
|
||||
|
@ -262,7 +361,9 @@
|
|||
"description": "運動場地是進行運動的地方"
|
||||
},
|
||||
"surveillance": {
|
||||
"title": "被監視的監視器"
|
||||
"title": "被監視的監視器",
|
||||
"description": "在這份開放地圖,你可以找到監視鏡頭。",
|
||||
"shortDescription": "監視鏡頭與其他型式的監視"
|
||||
},
|
||||
"toilets": {
|
||||
"title": "開放廁所地圖",
|
||||
|
@ -272,5 +373,14 @@
|
|||
"title": "樹木",
|
||||
"shortDescription": "所有樹木的地圖",
|
||||
"description": "繪製所有樹木!"
|
||||
},
|
||||
"bike_monitoring_stations": {
|
||||
"description": "這個主題顯示單車監視站的即時資料",
|
||||
"shortDescription": "布魯塞爾車行資料的即時單車監視站資料",
|
||||
"title": "自行車監視站"
|
||||
},
|
||||
"ghostbikes": {
|
||||
"description": "<b>幽靈單車</b>是用來紀念死於交通事故的單車騎士,在事發地點附近放置白色單車。<br/><br/>在這份地圖上面,你可以看到所有在開放街圖已知的幽靈單車。有缺漏的幽靈單車嗎?所有人都可以在這邊新增或是更新資訊-只有你有(免費)開放街圖帳號。",
|
||||
"title": "幽靈單車"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"saved": "<span class=\"thanks\">已儲存審核,謝謝你的分享!</span>",
|
||||
"saving_review": "儲存中…",
|
||||
"affiliated_reviewer_warning": "(關係者審核)",
|
||||
"i_am_affiliated": "<span>我是這物件的相關關係者</span><br><span class=\"subtle\">確認你是否是擁有者、創造者、員工、...</span>",
|
||||
"i_am_affiliated": "<span>我是這物件的相關關係者</span><br><span class=\"subtle\">確認你是否是擁有者、創造者、員工等等</span>",
|
||||
"posting_as": "以貼文",
|
||||
"no_rating": "還沒有評分",
|
||||
"write_a_comment": "留下審核…",
|
||||
|
@ -57,7 +57,7 @@
|
|||
"zoomInToSeeThisLayer": "放大來看這個圖層"
|
||||
},
|
||||
"backgroundMap": "背景地圖",
|
||||
"aboutMapcomplete": "<h3>關於 MapComplete</h3><p>使用 MapComplete 你可以藉由<b>單一主題</b>豐富開放街圖的圖資。回答幾個問題,然後幾分鐘之內你的貢獻立刻就傳遍全球!<b>主題維護者</b>定議主題的元素、問題與語言。</p><h3>發現更多</h3><p>MapComplete 總是提供學習更多開放街圖<b>下一步的知識</b>。</p><ul><li>當你內嵌網站,網頁內嵌會連結到全螢幕的 MapComplete</li><li>全螢幕的版本提供關於開放街圖的資訊</li><li>不登入檢視成果,但是要編輯則需登入 OSM。</li><li>如果你沒有登入,你會被要求先登入</li><li>當你回答單一問題時,你可以在地圖新增新的節點</li><li>過了一陣子,實際的 OSM-標籤會顯示,之後會連接到 wiki</li></ul><p></p><br><p>你有注意到<b>問題</b>嗎?你想請求<b>功能</b>嗎?想要<b>幫忙翻譯</b>嗎?來到<a href=\"https://github.com/pietervdvn/MapComplete\" target=\"_blank\">原始碼</a>或是<a href=\"https://github.com/pietervdvn/MapComplete/issues\" target=\"_blank\">問題追蹤器。</a></p><p>想要看到<b>你的進度</b>嗎?到<a href=\"https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%222021-01-01%22%2C%22value%22%3A%222021-01-01%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D\" target=\"_blank\">OsmCha</a>追蹤編輯數。</p>",
|
||||
"aboutMapcomplete": "<h3>關於 MapComplete</h3><p>使用 MapComplete 你可以藉由<b>單一主題</b>豐富開放街圖的圖資。回答幾個問題,然後幾分鐘之內你的貢獻立刻就傳遍全球!<b>主題維護者</b>定議主題的元素、問題與語言。</p><h3>發現更多</h3><p>MapComplete 總是提供學習更多開放街圖<b>下一步的知識</b>。</p><ul><li>當你內嵌網站,網頁內嵌會連結到全螢幕的 MapComplete</li><li>全螢幕的版本提供關於開放街圖的資訊</li><li>不登入檢視成果,但是要編輯則需登入 OSM。</li><li>如果你沒有登入,你會被要求先登入</li><li>當你回答單一問題時,你可以在地圖新增新的節點</li><li>過了一陣子,實際的 OSM-標籤會顯示,之後會連結到 wiki</li></ul><p></p><br><p>你有注意到<b>問題</b>嗎?你想請求<b>功能</b>嗎?想要<b>幫忙翻譯</b>嗎?來到<a href=\"https://github.com/pietervdvn/MapComplete\" target=\"_blank\">原始碼</a>或是<a href=\"https://github.com/pietervdvn/MapComplete/issues\" target=\"_blank\">問題追蹤器。</a></p><p>想要看到<b>你的進度</b>嗎?到<a href=\"https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%222021-01-01%22%2C%22value%22%3A%222021-01-01%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D\" target=\"_blank\">OsmCha</a>追蹤編輯數。</p>",
|
||||
"customThemeIntro": "<h3>客製化主題</h3>觀看這些先前使用者創造的主題。",
|
||||
"noTagsSelected": "沒有選取標籤",
|
||||
"getStartedNewAccount": " 或是 <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">註冊新帳號</a>",
|
||||
|
@ -69,7 +69,7 @@
|
|||
"createYourOwnTheme": "從零開始建立你的 MapComplete 主題",
|
||||
"streetcomplete": "行動裝置另有類似的應用程式 <a class=\"underline hover:text-blue-800\" href=\"https://play.google.com/store/apps/details?id=de.westnordost.streetcomplete\" target=\"_blank\">StreetComplete</a>。",
|
||||
"requestATheme": "如果你有客製化要求,請到問題追踪器那邊提出要求",
|
||||
"intro": "<h3>看更多主題地圖?</h3>你享受收集地理資料嗎?<br>還有更多主題。"
|
||||
"intro": "<h3>看更多主題地圖?</h3>您喜歡蒐集地理資料嗎?<br>還有更多主題。"
|
||||
},
|
||||
"sharescreen": {
|
||||
"fsIncludeCurrentLocation": "包含目前位置",
|
||||
|
@ -86,7 +86,7 @@
|
|||
"editThisTheme": "編輯這個主題",
|
||||
"thanksForSharing": "感謝分享!",
|
||||
"copiedToClipboard": "複製連結到簡貼簿",
|
||||
"embedIntro": "<h3>嵌入到你的網站</h3>請考慮將這份地圖嵌入你的網站。<br>地圖毋須額外許可,非常歡迎你多加利用。<br>一切都是免費的,而且之後也是免費的,越有更多人使用,則越顯得它的價值。",
|
||||
"embedIntro": "<h3>嵌入到你的網站</h3>請考慮將這份地圖嵌入您的網站。<br>地圖毋須額外授權,非常歡迎您多加利用。<br>一切都是免費的,而且之後也是免費的,越有更多人使用,則越顯得它的價值。",
|
||||
"addToHomeScreen": "<h3>新增到你主頁畫面</h3>你可以輕易將這網站加到你智慧型手機的主頁畫面,在網址列點選 '新增到主頁按鈕'來做這件事情。",
|
||||
"intro": "<h3>分享這地圖</h3>複製下面的連結來向朋友與家人分享這份地圖:"
|
||||
},
|
||||
|
@ -107,7 +107,7 @@
|
|||
"emailOf": "{category} 的電子郵件地址是?",
|
||||
"websiteIs": "網站:<a href=\"{website}\" target=\"_blank\">{website}</a>",
|
||||
"websiteOf": "{category} 的網站網址是?",
|
||||
"phoneNumberIs": "這 {category} 的電話號碼是 <a target=\"_blank\">{phone}</a>",
|
||||
"phoneNumberIs": "此 {category} 的電話號碼為 <a target=\"_blank\">{phone}</a>",
|
||||
"phoneNumberOf": "{category} 的電話號碼是?"
|
||||
},
|
||||
"noNameCategory": "{category} 沒有名稱",
|
||||
|
@ -117,12 +117,12 @@
|
|||
"add": {
|
||||
"layerNotEnabled": "圖層 {layer} 目前無法使用,請先啟用這圖層再加新的節點",
|
||||
"openLayerControl": "開啟圖層控制框",
|
||||
"confirmButton": "在這裡新增 {category} 。<br><div class=\"alert\">你新增的物件大家都會看到</div>",
|
||||
"confirmButton": "在此新增 {category}。<br><div class=\"alert\">大家都可以看到您新增的內容</div>",
|
||||
"confirmIntro": "<h3>在這裡新增 {title} ?</h3>你在這裡新增的節點<b>所有人都看得到</b>。請只有在確定有物件存在的情形下才新增上去,許多應用程式都使用這份資料。",
|
||||
"stillLoading": "目前仍在載入資料,請稍後再來新增節點。",
|
||||
"zoomInFurther": "放大來新增新的節點。",
|
||||
"pleaseLogin": "<a class=\"activate-osm-authentication\">請先登入來新增節點</a>",
|
||||
"intro": "你點的地方目前還沒有任何已知的資料。<br>",
|
||||
"intro": "您點擊處目前未有已知的資料。<br>",
|
||||
"title": "新增新的節點?",
|
||||
"addNew": "在這裡新增新的 {category}"
|
||||
},
|
||||
|
@ -162,12 +162,12 @@
|
|||
"dontDelete": "取消",
|
||||
"uploadDone": "<span class=\"thanks\">已經新增你的照片,謝謝你的協助!</span>",
|
||||
"respectPrivacy": "請別照人像或是車牌,不要上傳 Google 地圖、Google 街景或其他受版權保護的資料來源。",
|
||||
"uploadFailed": "無法上傳你的圖片,你確定有網路連線以及允許第三方 API 介接?Brave 或是 UMatrix 可能會阻擋連線。",
|
||||
"uploadFailed": "無法上傳您的圖片。您是否已連線至網際網路,並允許第三方 API?Brave 瀏覽器或 uMatrix 外掛程式都可能會封鎖它們。",
|
||||
"ccb": "以 CC-BY 授權條款",
|
||||
"ccbs": "以 CC-BY-SA 授權條款",
|
||||
"cco": "公有領域",
|
||||
"willBePublished": "你的圖片將依以下授權釋出: ",
|
||||
"pleaseLogin": "請先登入再來新增圖片",
|
||||
"pleaseLogin": "請登入以新增圖片",
|
||||
"uploadingMultiple": "正在上傳 {count} 張圖片…",
|
||||
"uploadingPicture": "正在上傳你的圖片…",
|
||||
"addPicture": "新增圖片"
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
"email-validator": "^2.0.4",
|
||||
"escape-html": "^1.0.3",
|
||||
"i18next-client": "^1.11.4",
|
||||
"jquery": "^3.5.1",
|
||||
"jquery": "^3.6.0",
|
||||
"latlon2country": "^1.1.3",
|
||||
"leaflet": "^1.7.1",
|
||||
"leaflet-providers": "^1.10.2",
|
||||
|
|
Loading…
Reference in a new issue