forked from MapComplete/MapComplete
Add typings to translations, move Subs into 'TypedTranslations', cleanup of duplicate parts in translation files, fix #752
This commit is contained in:
parent
f5d5f304ae
commit
e391c1ce20
12 changed files with 64 additions and 318 deletions
|
@ -1,4 +1,4 @@
|
|||
import {Translation} from "../../UI/i18n/Translation";
|
||||
import {Translation, TypedTranslation} from "../../UI/i18n/Translation";
|
||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||
|
@ -22,8 +22,8 @@ export default class TagRenderingConfig {
|
|||
|
||||
public readonly id: string;
|
||||
public readonly group: string;
|
||||
public readonly render?: Translation;
|
||||
public readonly question?: Translation;
|
||||
public readonly render?: TypedTranslation<object>;
|
||||
public readonly question?: TypedTranslation<object>;
|
||||
public readonly condition?: TagsFilter;
|
||||
|
||||
public readonly configuration_warnings: string[] = []
|
||||
|
@ -43,7 +43,7 @@ export default class TagRenderingConfig {
|
|||
public readonly mappings?: {
|
||||
readonly if: TagsFilter,
|
||||
readonly ifnot?: TagsFilter,
|
||||
readonly then: Translation,
|
||||
readonly then: TypedTranslation<object>,
|
||||
readonly icon: string,
|
||||
readonly iconClass: string
|
||||
readonly hideInAnswer: boolean | TagsFilter
|
||||
|
@ -110,12 +110,13 @@ export default class TagRenderingConfig {
|
|||
}
|
||||
const type = json.freeform.type ?? "string"
|
||||
|
||||
let placeholder = Translations.T(json.freeform.placeholder)
|
||||
let placeholder: Translation = Translations.T(json.freeform.placeholder)
|
||||
if (placeholder === undefined) {
|
||||
const typeDescription = Translations.t.validation[type]?.description
|
||||
placeholder = Translations.T(json.freeform.key+" ("+type+")")
|
||||
if(typeDescription !== undefined){
|
||||
placeholder = placeholder.Subs({[type]: typeDescription})
|
||||
placeholder = Translations.T(json.freeform.key+" ("+type+")").Subs({[type]: typeDescription})
|
||||
}else{
|
||||
placeholder = Translations.T(json.freeform.key+" ("+type+")")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,7 +384,7 @@ export default class TagRenderingConfig {
|
|||
let freeformKeyDefined = this.freeform?.key !== undefined;
|
||||
let usedFreeformValues = new Set<string>()
|
||||
// We run over all the mappings first, to check if the mapping matches
|
||||
const applicableMappings: { then: Translation, img?: string }[] = Utils.NoNull((this.mappings ?? [])?.map(mapping => {
|
||||
const applicableMappings: { then: TypedTranslation<any>, img?: string }[] = Utils.NoNull((this.mappings ?? [])?.map(mapping => {
|
||||
if (mapping.if === undefined) {
|
||||
return mapping;
|
||||
}
|
||||
|
@ -404,7 +405,7 @@ export default class TagRenderingConfig {
|
|||
const leftovers = freeformValues.filter(v => !usedFreeformValues.has(v))
|
||||
for (const leftover of leftovers) {
|
||||
applicableMappings.push({then:
|
||||
this.render.replace("{"+this.freeform.key+"}", leftover)
|
||||
new TypedTranslation<object>(this.render.replace("{"+this.freeform.key+"}", leftover).translations)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -412,7 +413,7 @@ export default class TagRenderingConfig {
|
|||
return applicableMappings
|
||||
}
|
||||
|
||||
public GetRenderValue(tags: any, defltValue: any = undefined): Translation {
|
||||
public GetRenderValue(tags: any, defltValue: any = undefined): TypedTranslation<any> {
|
||||
return this.GetRenderValueWithImage(tags, defltValue).then
|
||||
}
|
||||
|
||||
|
@ -421,7 +422,7 @@ export default class TagRenderingConfig {
|
|||
* Not compatible with multiAnswer - use GetRenderValueS instead in that case
|
||||
* @constructor
|
||||
*/
|
||||
public GetRenderValueWithImage(tags: any, defltValue: any = undefined): { then: Translation, icon?: string } {
|
||||
public GetRenderValueWithImage(tags: any, defltValue: any = undefined): { then: TypedTranslation<any>, icon?: string } {
|
||||
if (this.mappings !== undefined && !this.multiAnswer) {
|
||||
for (const mapping of this.mappings) {
|
||||
if (mapping.if === undefined) {
|
||||
|
|
|
@ -22,7 +22,7 @@ import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
|||
import Constants from "../../Models/Constants";
|
||||
import ContributorCount from "../../Logic/ContributorCount";
|
||||
import Img from "../Base/Img";
|
||||
import {Translation} from "../i18n/Translation";
|
||||
import {TypedTranslation} from "../i18n/Translation";
|
||||
import TranslatorsPanel from "./TranslatorsPanel";
|
||||
|
||||
export class OpenIdEditor extends VariableUiElement {
|
||||
|
@ -198,7 +198,7 @@ export default class CopyrightPanel extends Combine {
|
|||
this.SetStyle("max-width:100%; width: 40rem; margin-left: 0.75rem; margin-right: 0.5rem")
|
||||
}
|
||||
|
||||
private static CodeContributors(contributors, translation: Translation): BaseUIElement {
|
||||
private static CodeContributors(contributors, translation: TypedTranslation<{contributors, hiddenCount}>): BaseUIElement {
|
||||
|
||||
const total = contributors.contributors.length;
|
||||
let filtered = [...contributors.contributors]
|
||||
|
|
|
@ -14,7 +14,7 @@ import Title from "../Base/Title";
|
|||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import Svg from "../../Svg";
|
||||
|
||||
import * as native_languages from "../../assets/language_native.json"
|
||||
|
||||
class TranslatorsPanelContent extends Combine {
|
||||
constructor(layout: LayoutConfig, isTranslator: UIEventSource<boolean>) {
|
||||
|
@ -48,7 +48,8 @@ class TranslatorsPanelContent extends Combine {
|
|||
// "translationCompleteness": "Translations for {theme} in {language} are at {percentage}: {translated} out of {total}",
|
||||
const translated = seed.Subs({total, theme: layout.title,
|
||||
percentage: new Translation(completenessPercentage),
|
||||
translated: new Translation(completenessTr)
|
||||
translated: new Translation(completenessTr),
|
||||
language: seed.OnEveryLanguage((_, lng) => native_languages[lng])
|
||||
})
|
||||
|
||||
super([
|
||||
|
|
|
@ -25,7 +25,7 @@ export default class ReviewElement extends VariableUiElement {
|
|||
SingleReview.GenStars(avg),
|
||||
new Link(
|
||||
revs.length === 1 ? Translations.t.reviews.title_singular.Clone() :
|
||||
Translations.t.reviews.title.Clone()
|
||||
Translations.t.reviews.title
|
||||
.Subs({count: "" + revs.length}),
|
||||
`https://mangrove.reviews/search?sub=${encodeURIComponent(subject)}`,
|
||||
true
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Wikidata, {WikidataResponse} from "../../Logic/Web/Wikidata";
|
||||
import {Translation} from "../i18n/Translation";
|
||||
import {Translation, TypedTranslation} from "../i18n/Translation";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import Loading from "../Base/Loading";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
@ -22,7 +22,7 @@ export default class WikidataPreviewBox extends VariableUiElement {
|
|||
private static extraProperties: {
|
||||
requires?: { p: number, q?: number }[],
|
||||
property: string,
|
||||
display: Translation | Map<string, string | (() => BaseUIElement) /*If translation: Subs({value: * }) */>
|
||||
display: TypedTranslation<{value}> | Map<string, string | (() => BaseUIElement) /*If translation: Subs({value: * }) */>
|
||||
}[] = [
|
||||
{
|
||||
requires: WikidataPreviewBox.isHuman,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import Locale from "./Locale";
|
||||
import {Utils} from "../../Utils";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import Link from "../Base/Link";
|
||||
import Svg from "../../Svg";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import LinkToWeblate from "../Base/LinkToWeblate";
|
||||
|
||||
export class Translation extends BaseUIElement {
|
||||
|
@ -165,24 +162,6 @@ export class Translation extends BaseUIElement {
|
|||
return this.SupportedLanguages().map(lng => this.translations[lng]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes text in a translation.
|
||||
* If a translation is passed, it'll be fused
|
||||
*
|
||||
* // Should replace simple keys
|
||||
* new Translation({"en": "Some text {key}"}).Subs({key: "xyz"}).textFor("en") // => "Some text xyz"
|
||||
*
|
||||
* // Should fuse translations
|
||||
* const subpart = new Translation({"en": "subpart","nl":"onderdeel"})
|
||||
* const tr = new Translation({"en": "Full sentence with {part}", nl: "Volledige zin met {part}"})
|
||||
* const subbed = tr.Subs({part: subpart})
|
||||
* subbed.textFor("en") // => "Full sentence with subpart"
|
||||
* subbed.textFor("nl") // => "Volledige zin met onderdeel"
|
||||
*/
|
||||
public Subs(text: any, context?: string): Translation {
|
||||
return this.OnEveryLanguage((template, lang) => Utils.SubstituteKeys(template, text, lang), context)
|
||||
}
|
||||
|
||||
public OnEveryLanguage(f: (s: string, language: string) => string, context?: string): Translation {
|
||||
const newTranslations = {};
|
||||
for (const lang in this.translations) {
|
||||
|
@ -278,5 +257,28 @@ export class Translation extends BaseUIElement {
|
|||
return this.txt
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class TypedTranslation<T> extends Translation {
|
||||
constructor(translations: object, context?: string) {
|
||||
super(translations, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes text in a translation.
|
||||
* If a translation is passed, it'll be fused
|
||||
*
|
||||
* // Should replace simple keys
|
||||
* new TypedTranslation<object>({"en": "Some text {key}"}).Subs({key: "xyz"}).textFor("en") // => "Some text xyz"
|
||||
*
|
||||
* // Should fuse translations
|
||||
* const subpart = new Translation({"en": "subpart","nl":"onderdeel"})
|
||||
* const tr = new TypedTranslation<object>({"en": "Full sentence with {part}", nl: "Volledige zin met {part}"})
|
||||
* const subbed = tr.Subs({part: subpart})
|
||||
* subbed.textFor("en") // => "Full sentence with subpart"
|
||||
* subbed.textFor("nl") // => "Volledige zin met onderdeel"
|
||||
*/
|
||||
Subs(text: T, context?: string): Translation {
|
||||
return this.OnEveryLanguage((template, lang) => Utils.SubstituteKeys(template, text, lang), context)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import {Translation} from "./Translation";
|
||||
import {Translation, TypedTranslation} from "./Translation";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import * as known_languages from "../../assets/generated/used_languages.json"
|
||||
import CompiledTranslations from "../../assets/generated/CompiledTranslations";
|
||||
|
@ -22,7 +22,7 @@ export default class Translations {
|
|||
return s;
|
||||
}
|
||||
|
||||
static T(t: string | any, context = undefined): Translation {
|
||||
static T(t: string | any, context = undefined): TypedTranslation<object> {
|
||||
if (t === undefined || t === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -30,17 +30,17 @@ export default class Translations {
|
|||
t = "" + t
|
||||
}
|
||||
if (typeof t === "string") {
|
||||
return new Translation({"*": t}, context);
|
||||
return new TypedTranslation({"*": t}, context);
|
||||
}
|
||||
if (t.render !== undefined) {
|
||||
const msg = "Creating a translation, but this object contains a 'render'-field. Use the translation directly"
|
||||
console.error(msg, t);
|
||||
throw msg
|
||||
}
|
||||
if (t instanceof Translation) {
|
||||
if (t instanceof TypedTranslation) {
|
||||
return t;
|
||||
}
|
||||
return new Translation(t, context);
|
||||
return new TypedTranslation(t, context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,11 +42,6 @@
|
|||
"getStartedLogin": "Entra no OpenStreetMap para comezar",
|
||||
"getStartedNewAccount": " ou <a href='https://www.openstreetmap.org/user/new' target='_blank'>crea unha nova conta</a>",
|
||||
"goToInbox": "Abrir mensaxes",
|
||||
"index": {
|
||||
"intro": "O MapComplete é un visor e editor do OpenStreetMap, que te amosa información sobre un tema específico.",
|
||||
"pickTheme": "Escolle un tema para comezar.",
|
||||
"title": "Benvido ao MapComplete"
|
||||
},
|
||||
"layerSelection": {
|
||||
"title": "Seleccionar capas",
|
||||
"zoomInToSeeThisLayer": "Achégate para ver esta capa"
|
||||
|
|
|
@ -97,12 +97,6 @@
|
|||
"backToMapcomplete": "Terug naar het themaoverzicht",
|
||||
"backgroundMap": "Achtergrondkaart",
|
||||
"cancel": "Annuleren",
|
||||
"centerMessage": {
|
||||
"loadingData": "Data wordt geladen…",
|
||||
"ready": "Klaar!",
|
||||
"retrying": "Data inladen mislukt. Opnieuw proberen over {count} seconden…",
|
||||
"zoomIn": "Zoom in om de data te zien en te bewerken"
|
||||
},
|
||||
"confirm": "Bevestigen",
|
||||
"customThemeIntro": "<h3>Onofficiële thema's</h3>De onderstaande thema's heb je eerder bezocht en zijn gemaakt door andere OpenStreetMappers.",
|
||||
"download": {
|
||||
|
@ -132,12 +126,6 @@
|
|||
"histogram": {
|
||||
"error_loading": "Kan het histogram niet laden"
|
||||
},
|
||||
"index": {
|
||||
"#": "Deze teksten worden getoond boven de themaknoppen als er geen thema is geladen",
|
||||
"intro": "MapComplete is een OpenStreetMap applicatie waar informatie over een specifiek thema bekeken en aangepast kan worden.",
|
||||
"pickTheme": "Kies hieronder een thema om te beginnen.",
|
||||
"title": "Welkom bij MapComplete"
|
||||
},
|
||||
"layerSelection": {
|
||||
"title": "Selecteer lagen",
|
||||
"zoomInToSeeThisLayer": "Vergroot de kaart om deze laag te zien"
|
||||
|
@ -199,22 +187,6 @@
|
|||
"readYourMessages": "Gelieve eerst je berichten op OpenStreetMap te lezen alvorens nieuwe punten toe te voegen.",
|
||||
"removeLocationHistory": "Verwijder de geschiedenis aan locaties",
|
||||
"returnToTheMap": "Ga terug naar de kaart",
|
||||
"reviews": {
|
||||
"affiliated_reviewer_warning": "(Review door betrokkene)",
|
||||
"attribution": "De beoordelingen worden voorzien door <a href=\"https://mangrove.reviews/\" target=\"_blank\">Mangrove Reviews</a> en zijn beschikbaar onder de<a href=\"https://mangrove.reviews/terms#8-licensing-of-content\" target=\"_blank\">CC-BY 4.0-licentie</a>.",
|
||||
"i_am_affiliated": "<span>Ik ben persoonlijk betrokken</span><br/><span class='subtle'>Vink aan indien je de oprichter, maker, werknemer, ... of dergelijke bent</span>",
|
||||
"name_required": "De naam van dit object moet gekend zijn om een review te kunnen maken",
|
||||
"no_rating": "Geen score bekend",
|
||||
"no_reviews_yet": "Er zijn nog geen beoordelingen. Wees de eerste om een beoordeling te schrijven en help open data en het bedrijf!",
|
||||
"plz_login": "Meld je aan om een beoordeling te geven",
|
||||
"posting_as": "Ingelogd als",
|
||||
"saved": "<span class='thanks'>Bedankt om je beoordeling te delen!</span>",
|
||||
"saving_review": "Opslaan…",
|
||||
"title": "{count} beoordelingen",
|
||||
"title_singular": "Eén beoordeling",
|
||||
"tos": "Als je je review publiceert, ga je akkoord met de <a href='https://mangrove.reviews/terms' target='_blank'>de gebruiksvoorwaarden en privacy policy van Mangrove.reviews</a>",
|
||||
"write_a_comment": "Schrijf een beoordeling…"
|
||||
},
|
||||
"save": "Opslaan",
|
||||
"search": {
|
||||
"error": "Niet gelukt...",
|
||||
|
|
112
langs/pl.json
112
langs/pl.json
|
@ -35,25 +35,6 @@
|
|||
"cancel": "Anuluj",
|
||||
"customThemeIntro": "<h3>Motywy własne</h3>Są to wcześniej odwiedzone motywy stworzone przez użytkowników.",
|
||||
"fewChangesBefore": "Proszę odpowiedzieć na kilka pytań dotyczących istniejących punktów przed dodaniem nowego punktu.",
|
||||
"general": {
|
||||
"about": "Łatwo edytuj i dodaj OpenStreetMap dla określonego motywu",
|
||||
"aboutMapcomplete": "<h3>O MapComplete</h3><p>Dzięki MapComplete możesz wzbogacić OpenStreetMap o informacje na <b>pojedynczy temat.</b> Odpowiedz na kilka pytań, a w ciągu kilku minut Twój wkład będzie dostępny na całym świecie! Opiekun <b>tematu</b> definiuje elementy, pytania i języki dla tematu.</p><h3>Dowiedz się więcej</h3><p>MapComplete zawsze <b>oferuje następny krok</b>, by dowiedzieć się więcej o OpenStreetMap.</p><ul><li>Po osadzeniu na stronie internetowej, element iframe łączy się z pełnoekranowym MapComplete</li><li>Wersja pełnoekranowa oferuje informacje o OpenStreetMap</li><li>Przeglądanie działa bez logowania, ale edycja wymaga loginu OSM.</li><li>Jeżeli nie jesteś zalogowany, zostaniesz poproszony o zalogowanie się</li><li>Po udzieleniu odpowiedzi na jedno pytanie, możesz dodać nowe punkty do mapy</li><li>Po chwili wyświetlane są rzeczywiste tagi OSM, które później linkują do wiki</li></ul><p></p><br><p>Zauważyłeś <b>problem</b>? Czy masz <b>prośbę o dodanie jakiejś funkcji</b>? Chcesz <b>pomóc w tłumaczeniu</b>? Udaj się do <a href=\"https://github.com/pietervdvn/MapComplete\" target=\"_blank\">kodu źródłowego</a> lub <a href=\"https://github.com/pietervdvn/MapComplete/issues\" target=\"_blank\">issue trackera.</a> </p><p> Chcesz zobaczyć <b>swoje postępy</b>? Śledź liczbę edycji na <a href=\"{osmcha_link}\" target=\"_blank\">OsmCha</a>.</p>",
|
||||
"add": {
|
||||
"addNew": "Dodaj nową {category} tutaj",
|
||||
"confirmButton": "Dodaj tutaj {category}.<br><div class=\"alert\">Twój dodatek jest widoczny dla wszystkich</div>",
|
||||
"confirmIntro": "<h3>Czy dodać tutaj {title}?</h3> Punkt, który tutaj utworzysz, będzie <b>widoczny dla wszystkich</b>. Proszę, dodawaj rzeczy do mapy tylko wtedy, gdy naprawdę istnieją. Wiele aplikacji korzysta z tych danych.",
|
||||
"intro": "Kliknąłeś gdzieś, gdzie nie są jeszcze znane żadne dane.<br>",
|
||||
"layerNotEnabled": "Warstwa {layer} nie jest włączona. Włącz tę warstwę, aby dodać punkt",
|
||||
"openLayerControl": "Otwórz okno sterowania warstwą",
|
||||
"pleaseLogin": "<a class=\"activate-osm-authentication\">Zaloguj się, aby dodać nowy punkt</a>",
|
||||
"stillLoading": "Dane wciąż się ładują. Poczekaj chwilę, zanim dodasz nowy punkt.",
|
||||
"title": "Czy dodać nowy punkt?",
|
||||
"zoomInFurther": "Powiększ jeszcze bardziej, aby dodać punkt."
|
||||
},
|
||||
"backgroundMap": "Tło mapy",
|
||||
"cancel": "Anuluj",
|
||||
"customThemeIntro": "<h3>Motywy własne</h3>Są to wcześniej odwiedzone motywy stworzone przez użytkowników.",
|
||||
"fewChangesBefore": "Proszę odpowiedzieć na kilka pytań dotyczących istniejących punktów przed dodaniem nowego punktu.",
|
||||
"getStartedLogin": "Zaloguj się za pomocą OpenStreetMap, aby rozpocząć",
|
||||
"getStartedNewAccount": " lub <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">utwórz nowe konto</a>",
|
||||
"goToInbox": "Otwórz skrzynkę odbiorczą",
|
||||
|
@ -141,99 +122,6 @@
|
|||
},
|
||||
"welcomeBack": "Jesteś zalogowany, witaj z powrotem!"
|
||||
},
|
||||
"getStartedLogin": "Zaloguj się za pomocą OpenStreetMap, aby rozpocząć",
|
||||
"getStartedNewAccount": " lub <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">utwórz nowe konto</a>",
|
||||
"goToInbox": "Otwórz skrzynkę odbiorczą",
|
||||
"index": {
|
||||
"#": "Te teksty są wyświetlane nad przyciskami motywu, gdy nie jest załadowany żaden motyw",
|
||||
"intro": "MapComplete to przeglądarka i edytor OpenStreetMap, który pokazuje informacje podzielone według tematu.",
|
||||
"pickTheme": "Wybierz temat poniżej, aby rozpocząć.",
|
||||
"title": "Witaj w MapComplete"
|
||||
},
|
||||
"layerSelection": {
|
||||
"title": "Wybierz warstwy",
|
||||
"zoomInToSeeThisLayer": "Powiększ, aby zobaczyć tę warstwę"
|
||||
},
|
||||
"loginToStart": "Zaloguj się, aby odpowiedzieć na to pytanie",
|
||||
"loginWithOpenStreetMap": "Zaloguj z OpenStreetMap",
|
||||
"nameInlineQuestion": "Nazwa tej {category} to $$$",
|
||||
"noNameCategory": "{category} bez nazwy",
|
||||
"noTagsSelected": "Nie wybrano tagów",
|
||||
"number": "numer",
|
||||
"oneSkippedQuestion": "Jedno pytanie zostało pominięte",
|
||||
"opening_hours": {
|
||||
"closed_permanently": "Zamknięte na nieokreślony czas",
|
||||
"closed_until": "Zamknięte do {date}",
|
||||
"error_loading": "Błąd: nie można zwizualizować tych godzin otwarcia.",
|
||||
"not_all_rules_parsed": "Godziny otwarcia tego sklepu są skomplikowane. Następujące reguły są ignorowane w elemencie wejściowym:",
|
||||
"openTill": "do",
|
||||
"open_24_7": "Otwarte przez całą dobę",
|
||||
"open_during_ph": "W czasie świąt państwowych udogodnienie to jest",
|
||||
"opensAt": "z",
|
||||
"ph_closed": "zamknięte",
|
||||
"ph_not_known": " ",
|
||||
"ph_open": "otwarte"
|
||||
},
|
||||
"osmLinkTooltip": "Zobacz ten obiekt na OpenStreetMap, aby uzyskać historię i więcej opcji edycji",
|
||||
"pickLanguage": "Wybierz język: ",
|
||||
"questions": {
|
||||
"emailIs": "Adres e-mail {category} to <a href=\"mailto:{email}\" target=\"_blank\">{email}</a>",
|
||||
"emailOf": "Jaki jest adres e-mail {category}?",
|
||||
"phoneNumberIs": "Numer telefonu {category} to <a target=\"_blank\">{phone}</a>",
|
||||
"phoneNumberOf": "Jaki jest numer telefonu do {category}?",
|
||||
"websiteIs": "Strona internetowa: <a href=\"{website}\" target=\"_blank\">{website}</a>",
|
||||
"websiteOf": "Jaka jest strona internetowa {category}?"
|
||||
},
|
||||
"readYourMessages": "Przeczytaj wszystkie wiadomości OpenStreetMap przed dodaniem nowego punktu.",
|
||||
"returnToTheMap": "Wróć do mapy",
|
||||
"save": "Zapisz",
|
||||
"search": {
|
||||
"error": "Coś poszło nie tak…",
|
||||
"nothing": "Nic nie znaleziono…",
|
||||
"search": "Wyszukaj lokalizację",
|
||||
"searching": "Szukanie…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3> Dodaj do ekranu głównego</h3>Możesz łatwo dodać tę stronę do ekranu głównego smartfona, aby poczuć się jak w domu. Kliknij przycisk \"Dodaj do ekranu głównego\" na pasku adresu URL, aby to zrobić.",
|
||||
"copiedToClipboard": "Link został skopiowany do schowka",
|
||||
"editThemeDescription": "Dodaj lub zmień pytania do tego motywu mapy",
|
||||
"editThisTheme": "Edytuj ten motyw",
|
||||
"embedIntro": "<h3>Umieść na swojej stronie internetowej</h3>Proszę, umieść tę mapę na swojej stronie internetowej. <br>Zachęcamy cię do tego - nie musisz nawet pytać o zgodę. <br>Jest ona darmowa i zawsze będzie. Im więcej osób jej używa, tym bardziej staje się wartościowa.",
|
||||
"fsAddNew": "Włącz przycisk \"Dodaj nowe POI\"",
|
||||
"fsGeolocation": "Włącz przycisk „Zlokalizuj mnie” (tylko na urządzeniach mobilnych)",
|
||||
"fsIncludeCurrentBackgroundMap": "Dołącz bieżący wybór tła <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Uwzględnij wybór bieżącej warstwy",
|
||||
"fsIncludeCurrentLocation": "Uwzględnij bieżącą lokalizację",
|
||||
"fsLayerControlToggle": "Zacznij od rozwiniętej kontroli warstw",
|
||||
"fsLayers": "Włącz kontrolę warstw",
|
||||
"fsSearch": "Włącz pasek wyszukiwania",
|
||||
"fsUserbadge": "Włącz przycisk logowania",
|
||||
"fsWelcomeMessage": "Pokaż wyskakujące okienko wiadomości powitalnej i powiązane zakładki",
|
||||
"intro": "<h3> Udostępnij tę mapę</h3> Udostępnij tę mapę, kopiując poniższy link i wysyłając ją do przyjaciół i rodziny:",
|
||||
"thanksForSharing": "Dzięki za udostępnienie!"
|
||||
},
|
||||
"skip": "Pomiń to pytanie",
|
||||
"skippedQuestions": "Niektóre pytania są pominięte",
|
||||
"weekdays": {
|
||||
"abbreviations": {
|
||||
"friday": "Pt",
|
||||
"monday": "Pn",
|
||||
"saturday": "Sob",
|
||||
"sunday": "Niedz",
|
||||
"thursday": "Czw",
|
||||
"tuesday": "Wt",
|
||||
"wednesday": "Śr"
|
||||
},
|
||||
"friday": "Piątek",
|
||||
"monday": "Poniedziałek",
|
||||
"saturday": "Sobota",
|
||||
"sunday": "Niedziela",
|
||||
"thursday": "Czwartek",
|
||||
"tuesday": "Wtorek",
|
||||
"wednesday": "Środa"
|
||||
},
|
||||
"welcomeBack": "Jesteś zalogowany, witaj z powrotem!"
|
||||
},
|
||||
"image": {
|
||||
"addPicture": "Dodaj zdjęcie",
|
||||
"ccb": "na licencji CC-BY",
|
||||
|
|
|
@ -90,130 +90,6 @@
|
|||
"title": "下載可視的資料"
|
||||
},
|
||||
"fewChangesBefore": "請先回答有關既有節點的問題再來新增新節點。",
|
||||
"general": {
|
||||
"about": "相當容易編輯,而且能為開放街圖新增特定主題",
|
||||
"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=\"{osmcha_link}\" target=\"_blank\">OsmCha</a>追蹤編輯數。</p>",
|
||||
"add": {
|
||||
"addNew": "在這裡新增新的 {category}",
|
||||
"confirmButton": "在此新增 {category}。<br><div class=\"alert\">大家都可以看到您新增的內容</div>",
|
||||
"confirmIntro": "<h3>在這裡新增 {title} ?</h3>你在這裡新增的節點<b>所有人都看得到</b>。請只有在確定有物件存在的情形下才新增上去,許多應用程式都使用這份資料。",
|
||||
"intro": "您點擊處目前未有已知的資料。<br>",
|
||||
"layerNotEnabled": "圖層 {layer} 目前無法使用,請先啟用這圖層再加新的節點",
|
||||
"openLayerControl": "開啟圖層控制框",
|
||||
"pleaseLogin": "<a class=\"activate-osm-authentication\">請先登入來新增節點</a>",
|
||||
"stillLoading": "目前仍在載入資料,請稍後再來新增節點。",
|
||||
"title": "新增新的節點?",
|
||||
"zoomInFurther": "放大來新增新的節點。"
|
||||
},
|
||||
"attribution": {
|
||||
"attributionContent": "<p>所有資料由<a href=\"https://osm.org\" target=\"_blank\">開放街圖</a>提供,在<a href=\"https://osm.org/copyright\" target=\"_blank\">開放資料庫授權條款</a>之下自由再利用。</p>",
|
||||
"attributionTitle": "署名通知",
|
||||
"codeContributionsBy": "MapComplete 是由 {contributors} 和其他 <a href=\"https://github.com/pietervdvn/MapComplete/graphs/contributors\" target=\"_blank\">{hiddenCount} 位貢獻者</a>構建而成",
|
||||
"iconAttribution": {
|
||||
"title": "使用的圖示"
|
||||
},
|
||||
"mapContributionsBy": "目前檢視的資料由 {contributors} 貢獻編輯",
|
||||
"mapContributionsByAndHidden": "目前顯到的資料是由 {contributors} 和其他 {hiddenCount} 位貢獻者編輯貢獻",
|
||||
"themeBy": "由 {author} 維護主題"
|
||||
},
|
||||
"backgroundMap": "背景地圖",
|
||||
"cancel": "取消",
|
||||
"customThemeIntro": "<h3>客製化主題</h3>觀看這些先前使用者創造的主題。",
|
||||
"fewChangesBefore": "請先回答有關既有節點的問題再來新增新節點。",
|
||||
"getStartedLogin": "登入開放街圖帳號來開始",
|
||||
"getStartedNewAccount": " 或是 <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">註冊新帳號</a>",
|
||||
"goToInbox": "開啟訊息框",
|
||||
"layerSelection": {
|
||||
"title": "選擇圖層",
|
||||
"zoomInToSeeThisLayer": "放大來看這個圖層"
|
||||
},
|
||||
"loginToStart": "登入之後來回答這問題",
|
||||
"loginWithOpenStreetMap": "用開放街圖帳號登入",
|
||||
"morescreen": {
|
||||
"createYourOwnTheme": "從零開始建立你的 MapComplete 主題",
|
||||
"intro": "<h3>看更多主題地圖?</h3>您喜歡蒐集地理資料嗎?<br>還有更多主題。",
|
||||
"requestATheme": "如果你有客製化要求,請到問題追踪器那邊提出要求",
|
||||
"streetcomplete": "行動裝置另有類似的應用程式 <a class=\"underline hover:text-blue-800\" href=\"https://play.google.com/store/apps/details?id=de.westnordost.streetcomplete\" target=\"_blank\">StreetComplete</a>。"
|
||||
},
|
||||
"nameInlineQuestion": "這個 {category} 的名稱是 $$$",
|
||||
"noNameCategory": "{category} 沒有名稱",
|
||||
"noTagsSelected": "沒有選取標籤",
|
||||
"number": "號碼",
|
||||
"oneSkippedQuestion": "跳過一個問題",
|
||||
"openStreetMapIntro": "<h3>開放的地圖</h3><p>如果有一份地圖,任何人都能自由使用與編輯,單一的地圖能夠儲存所有地理相關資訊?這樣不就很酷嗎?接著,所有的網站使用不同的、範圍小的,不相容的地圖 (通常也都過時了),也就不再需要了。</p><p><b><a href=\"https://OpenStreetMap.org\" target=\"_blank\">開放街圖</a></b>就是這樣的地圖,人人都能免費這些圖資 (只要<a href=\"https://osm.org/copyright\" target=\"_blank\">署名與公開變動這資料</a>)。只要遵循這些,任何人都能自由新增新資料與修正錯誤,這些網站也都使用開放街圖,資料也都來自開放街圖,你的答案與修正也會加到開放街圖上面。</p><p>許多人與應用程式已經採用開放街圖了:<a href=\"https://organicmaps.app//\" target=\"_blank\">Organic Maps</a>、<a href=\"https://osmAnd.net\" target=\"_blank\">OsmAnd</a>,還有 Facebook、Instagram,蘋果地圖、Bing 地圖(部分)採用開放街圖。如果你在開放街圖上變動資料,也會同時影響這些應用 - 在他們下次更新資料之後!</p>",
|
||||
"opening_hours": {
|
||||
"closed_permanently": "不清楚關閉多久了",
|
||||
"closed_until": "{date} 起關閉",
|
||||
"error_loading": "錯誤:無法視覺化開放時間。",
|
||||
"not_all_rules_parsed": "這間店的開放時間相當複雜,在輸入元素時忽略接下來的規則:",
|
||||
"openTill": "結束時間",
|
||||
"open_24_7": "24小時營業",
|
||||
"open_during_ph": "國定假日的時候,這個場所是",
|
||||
"opensAt": "開始時間",
|
||||
"ph_closed": "無營業",
|
||||
"ph_not_known": " ",
|
||||
"ph_open": "有營業"
|
||||
},
|
||||
"osmLinkTooltip": "在開放街圖歷史和更多編輯選項下面來檢視這物件",
|
||||
"pickLanguage": "選擇語言: ",
|
||||
"questions": {
|
||||
"emailIs": "{category} 的電子郵件地址是<a href=\"mailto:{email}\" target=\"_blank\">{email}</a>",
|
||||
"emailOf": "{category} 的電子郵件地址是?",
|
||||
"phoneNumberIs": "此 {category} 的電話號碼為 <a target=\"_blank\">{phone}</a>",
|
||||
"phoneNumberOf": "{category} 的電話號碼是?",
|
||||
"websiteIs": "網站:<a href=\"{website}\" target=\"_blank\">{website}</a>",
|
||||
"websiteOf": "{category} 的網站網址是?"
|
||||
},
|
||||
"readYourMessages": "請先閱讀開放街圖訊息之前再來新增新節點。",
|
||||
"returnToTheMap": "回到地圖",
|
||||
"save": "儲存",
|
||||
"search": {
|
||||
"error": "有狀況發生了…",
|
||||
"nothing": "沒有找到…",
|
||||
"search": "搜尋地點",
|
||||
"searching": "搜尋中…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>新增到您的主畫面</h3>您可以輕易將這網站新增到您智慧型手機的主畫面,在網址列點選「新增到主畫面按鈕」來做這件事情。",
|
||||
"copiedToClipboard": "複製連結到簡貼簿",
|
||||
"editThemeDescription": "新增或改變這個地圖的問題",
|
||||
"editThisTheme": "編輯這個主題",
|
||||
"embedIntro": "<h3>嵌入到你的網站</h3>請考慮將這份地圖嵌入您的網站。<br>地圖毋須額外授權,非常歡迎您多加利用。<br>一切都是免費的,而且之後也是免費的,越有更多人使用,則越顯得它的價值。",
|
||||
"fsAddNew": "啟用'新增新的興趣點'按鈕",
|
||||
"fsGeolocation": "啟用'地理定位自身'按鈕 (只有行動版本)",
|
||||
"fsIncludeCurrentBackgroundMap": "包含目前背景選擇<b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "包含目前選擇圖層",
|
||||
"fsIncludeCurrentLocation": "包含目前位置",
|
||||
"fsLayerControlToggle": "開始時擴展圖層控制",
|
||||
"fsLayers": "啟用圖層控制",
|
||||
"fsSearch": "啟用搜尋列",
|
||||
"fsUserbadge": "啟用登入按鈕",
|
||||
"fsWelcomeMessage": "顯示歡迎訊息以及相關頁籤",
|
||||
"intro": "<h3>分享這地圖</h3>複製下面的連結來向朋友與家人分享這份地圖:",
|
||||
"thanksForSharing": "感謝分享!"
|
||||
},
|
||||
"skip": "跳過這問題",
|
||||
"skippedQuestions": "有些問題已經跳過了",
|
||||
"weekdays": {
|
||||
"abbreviations": {
|
||||
"friday": "星期五",
|
||||
"monday": "星期一",
|
||||
"saturday": "星期六",
|
||||
"sunday": "星期日",
|
||||
"thursday": "星期四",
|
||||
"tuesday": "星期二",
|
||||
"wednesday": "星期三"
|
||||
},
|
||||
"friday": "星期五",
|
||||
"monday": "星期一",
|
||||
"saturday": "星期六",
|
||||
"sunday": "星期日",
|
||||
"thursday": "星期四",
|
||||
"tuesday": "星期二",
|
||||
"wednesday": "星期三"
|
||||
},
|
||||
"welcomeBack": "你已經登入了,歡迎回來!"
|
||||
},
|
||||
"getStartedLogin": "登入開放街圖帳號來開始",
|
||||
"getStartedNewAccount": " 或是 <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">註冊新帳號</a>",
|
||||
"goToInbox": "開啟訊息框",
|
||||
|
|
|
@ -2,8 +2,6 @@ import * as fs from "fs";
|
|||
import {readFileSync, writeFileSync} from "fs";
|
||||
import {Utils} from "../Utils";
|
||||
import ScriptUtils from "./ScriptUtils";
|
||||
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||
import TranslatorsPanel from "../UI/BigComponents/TranslatorsPanel";
|
||||
|
||||
const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"];
|
||||
|
||||
|
@ -275,7 +273,20 @@ function transformTranslation(obj: any, path: string[] = [], languageWhitelist :
|
|||
}
|
||||
value = nv;
|
||||
}
|
||||
values += `${Utils.Times((_) => " ", path.length + 1)}get ${key}() { return new Translation(${JSON.stringify(value)}, "core:${path.join(".")}.${key}") },
|
||||
|
||||
|
||||
if(value["en"] === undefined){
|
||||
throw `At ${path.join(".")}: Missing 'en' translation for ${JSON.stringify(value)}`
|
||||
}
|
||||
const subParts : string[] = value["en"].match(/{[^}]*}/g)
|
||||
let expr = `return new Translation(${JSON.stringify(value)}, "core:${path.join(".")}.${key}")`
|
||||
if(subParts !== null){
|
||||
// convert '{to_substitute}' into 'to_substitute'
|
||||
const types = Utils.Dedup( subParts.map(tp => tp.substring(1, tp.length - 1)))
|
||||
expr = `return new TypedTranslation<{ ${types.join(", ")} }>(${JSON.stringify(value)}, "core:${path.join(".")}.${key}")`
|
||||
}
|
||||
|
||||
values += `${Utils.Times((_) => " ", path.length + 1)}get ${key}() { ${expr} },
|
||||
`
|
||||
} else {
|
||||
values += (Utils.Times((_) => " ", path.length + 1)) + key + ": " + transformTranslation(value, [...path, key], languageWhitelist) + ",\n"
|
||||
|
@ -318,7 +329,7 @@ function genTranslations() {
|
|||
const translations = JSON.parse(fs.readFileSync("./assets/generated/translations.json", "utf-8"))
|
||||
const transformed = transformTranslation(translations);
|
||||
|
||||
let module = `import {Translation} from "../../UI/i18n/Translation"\n\nexport default class CompiledTranslations {\n\n`;
|
||||
let module = `import {Translation, TypedTranslation} from "../../UI/i18n/Translation"\n\nexport default class CompiledTranslations {\n\n`;
|
||||
module += " public static t = " + transformed;
|
||||
module += "\n }"
|
||||
|
||||
|
|
Loading…
Reference in a new issue