import { createContext, useContext } from 'react'; import type { Lang, Msg } from './types'; const pl = { 'app.name': 'Kraków bez barier', 'app.tagline': 'Sprawdź, czy miejsce i trasa pasują do Twoich potrzeb — z informacją, skąd to wiemy.', 'banner.proto': 'Prototyp na hackathon.', 'banner.data': 'Dane: zapis OpenStreetMap z {date}. Miejsca oznaczone DEMO są fikcyjne.', 'nav.label': 'Główna nawigacja', 'nav.places': 'Miejsca', 'nav.route': 'Trasa', 'nav.about': 'O danych', 'nav.profile': 'Profil', 'lang.label': 'Język', 'skip': 'Przejdź do treści', 'profile.title': 'Twoje potrzeby', 'profile.intro': 'Wybierz, kim podróżujesz. Zapytamy tylko o preferencje dotyczące barier — nie o stan zdrowia. Zapisujemy je wyłącznie na tym urządzeniu.', 'profile.kind': 'Podróżuję', 'profile.wheelchair': 'Na wózku inwalidzkim', 'profile.wheelchair.hint': 'Wózek ręczny lub elektryczny', 'profile.stroller': 'Z wózkiem dziecięcym', 'profile.stroller.hint': 'Rodzic lub opiekun z wózkiem', 'profile.limits': 'Granice, których nie chcesz przekraczać', 'profile.kerb': 'Najwyższy krawężnik', 'profile.width': 'Najwęższe przejście', 'profile.incline': 'Największe nachylenie', 'profile.prefs': 'Na co zwracać uwagę', 'profile.cobbles': 'Unikaj kostki brukowej i nierównej nawierzchni', 'profile.toilet': 'Potrzebuję dostępnej toalety w pobliżu', 'profile.rest': 'Potrzebuję miejsc do odpoczynku (ławki)', 'profile.changing': 'Potrzebuję przewijaka', 'profile.save': 'Zapisz i szukaj', 'profile.saved': 'Profil zapisany na tym urządzeniu.', 'profile.privacy': 'Bez konta, bez analityki. Profil nigdy nie opuszcza Twojego urządzenia.', 'unit.cm': 'cm', 'unit.pct': '%', 'places.title': 'Szukaj miejsca', 'places.search': 'Nazwa lub adres', 'places.category': 'Rodzaj miejsca', 'places.onlyFit': 'Pokaż tylko pasujące lub częściowo pasujące', 'places.count': 'Wyniki: {n}', 'places.more': 'Pokaż więcej', 'places.empty': 'Brak miejsc dla tych kryteriów.', 'places.hint': 'Oceny liczone są dla Twojego profilu: {profile}.', 'view.list': 'Lista', 'view.map': 'Mapa', 'view.label': 'Widok wyników', 'cat.all': 'Wszystkie', 'cat.food': 'Jedzenie i picie', 'cat.culture': 'Kultura i zabytki', 'cat.shop': 'Sklepy', 'cat.stay': 'Noclegi', 'cat.health': 'Zdrowie', 'cat.services': 'Usługi', 'cat.toilets': 'Toalety', 'cat.elevator': 'Windy', 'cat.transit': 'Transport', 'verdict.match': 'Pasuje wg danych', 'verdict.partial': 'Częściowo pasuje', 'verdict.conflict': 'Sprzeczne dane', 'verdict.incomplete': 'Za mało danych', 'verdict.fail': 'Nie pasuje', 'verdict.match.long': 'Zmapowane dane są zgodne z Twoim profilem. To nie jest gwarancja — sprawdź datę i źródło poniżej.', 'verdict.partial.long': 'Dane są zgodne z profilem, ale są uwagi, które warto sprawdzić przed wyjazdem.', 'verdict.conflict.long': 'Źródła się nie zgadzają. Nie traktuj tego miejsca jako dostępnego, dopóki ktoś tego nie potwierdzi.', 'verdict.incomplete.long': 'Brakuje danych albo są nieaktualne. Brak informacji nie oznacza, że miejsce jest dostępne.', 'verdict.fail.long': 'Zmapowane dane wskazują barierę niezgodną z Twoimi granicami.', 'status.ok': 'W porządku', 'status.warn': 'Uwaga', 'status.fail': 'Bariera', 'status.unknown': 'Brak danych', 'status.conflict': 'Sprzeczne', 'conf.verified': 'Potwierdzone', 'conf.mapped': 'Zmapowane', 'conf.stale': 'Mogło się zmienić', 'conf.inferred': 'Wywnioskowane', 'conf.user': 'Niezweryfikowane zgłoszenie', 'conf.none': 'Brak', 'conf.verified.help': 'Ma datę kontroli (check_date lub survey:date) z ostatnich 24 miesięcy.', 'conf.mapped.help': 'Wpis w OSM edytowany w ciągu 24 miesięcy, bez daty kontroli.', 'conf.stale.help': 'Wpis w OSM nie był zmieniany od ponad 24 miesięcy.', 'conf.inferred.help': 'Wyliczone z położenia pobliskich obiektów, nie opisane dla tego miejsca.', 'conf.user.help': 'Zgłoszone w tej aplikacji, jeszcze nikt tego nie potwierdził.', 'conf.none.help': 'Nic nie wiadomo.', 'src.label': 'Źródło', 'src.osm-tag': 'OpenStreetMap (opis miejsca)', 'src.osm-geometry': 'OpenStreetMap (obiekty w pobliżu)', 'src.user-report': 'Zgłoszenie użytkownika (to urządzenie)', 'src.demo': 'Dane przykładowe DEMO', 'src.none': 'Brak źródła', 'fact.updated': 'Dane z', 'fact.dateUnknown': 'data nieznana', 'fact.confidence': 'Wiarygodność', 'fact.otherSource': 'Inne źródło mówi', 'fact.report': 'Popraw lub uzupełnij', 'fact.required': 'ważne dla Twojego profilu', 'fact.title.entrance': 'Wejście', 'fact.title.steps': 'Schody przy wejściu', 'fact.title.ramp': 'Podjazd lub winda', 'fact.title.width': 'Szerokość wejścia', 'fact.title.surface': 'Nawierzchnia przed wejściem', 'fact.title.toilet': 'Toaleta', 'fact.title.rest': 'Miejsca odpoczynku', 'fact.title.changing': 'Przewijak', 'f.entrance.yes': 'Wejście oznaczone jako dostępne dla wózka.', 'f.entrance.limited': 'Dostępne z ograniczeniami (np. część obiektu lub z pomocą).', 'f.entrance.no': 'Wejście oznaczone jako niedostępne dla wózka.', 'f.entrance.none': 'Nikt nie opisał, czy wejście jest dostępne.', 'f.steps.warn': 'Zmapowane schody ok. {d} m od wejścia (liczba stopni: {n}).', 'f.steps.conflict': 'Wejście oznaczone jako dostępne, ale zmapowane są schody ok. {d} m dalej (liczba stopni: {n}) i brak podjazdu lub windy.', 'f.steps.hint': 'Wywnioskowane z położenia schodów — mogą prowadzić do innego wejścia.', 'f.steps.none': 'Brak zmapowanych schodów w pobliżu.', 'f.steps.noneHint': 'To nie dowodzi, że schodów nie ma — mogą być niezmapowane.', 'f.ramp.yes': 'Zmapowany podjazd ok. {d} m od wejścia.', 'f.ramp.lift': 'Zmapowana winda ok. {d} m od wejścia.', 'f.ramp.none': 'Brak danych o podjeździe lub windzie.', 'f.width.ok': 'Zgłoszona szerokość: {cm} cm (Twoje minimum: {min} cm).', 'f.width.fail': 'Zgłoszona szerokość: {cm} cm — mniej niż Twoje minimum ({min} cm).', 'f.width.none': 'Szerokość wejścia nie jest zmapowana.', 'f.width.noneHint': 'W OSM prawie nikt jej nie opisuje. Możesz ją zgłosić.', 'f.surface.good': 'Nawierzchnia: {surface} — równa.', 'f.surface.fair': 'Nawierzchnia: {surface} — raczej równa.', 'f.surface.rough': 'Nawierzchnia: {surface} — nierówna, wolniejszy i bardziej męczący przejazd.', 'f.surface.unknown': 'Brak danych o nawierzchni przed wejściem.', 'f.toilet.own.yes': 'Toaleta w obiekcie oznaczona jako dostępna dla wózka.', 'f.toilet.own.no': 'Toaleta w obiekcie oznaczona jako niedostępna dla wózka.', 'f.toilet.near': 'Dostępna toaleta publiczna ok. {d} m stąd.', 'f.toilet.fee': 'Toaleta jest płatna.', 'f.toilet.nearUnknown': 'Toaleta publiczna ok. {d} m stąd, ale nie wiadomo, czy jest dostępna dla wózka.', 'f.toilet.none': 'Brak danych o dostępnej toalecie.', 'f.rest.ok': 'Zmapowane ławki w pobliżu: {n}, najbliższa ok. {d} m.', 'f.rest.none': 'Brak zmapowanych ławek w promieniu 150 m.', 'f.rest.noneHint': 'Mogą istnieć niezmapowane.', 'f.changing.yes': 'Przewijak oznaczony w obiekcie.', 'f.changing.no': 'Brak przewijaka (oznaczone w OSM).', 'f.changing.near': 'Przewijak w toalecie publicznej ok. {d} m stąd.', 'f.changing.none': 'Brak danych o przewijaku.', 'raw': '{text}', 'surface.sett': 'kostka brukowa', 'surface.cobblestone': 'bruk', 'surface.unhewn_cobblestone': 'nieociosany bruk', 'surface.paving_stones': 'płyty chodnikowe', 'surface.asphalt': 'asfalt', 'surface.concrete': 'beton', 'surface.paved': 'utwardzona', 'surface.concrete:plates': 'płyty betonowe', 'surface.concrete:lanes': 'pasy betonowe', 'surface.compacted': 'ubita', 'surface.fine_gravel': 'drobny żwir', 'surface.gravel': 'żwir', 'surface.wood': 'drewno', 'surface.dirt': 'ziemia', 'surface.ground': 'grunt', 'surface.grass': 'trawa', 'surface.unpaved': 'nieutwardzona', 'surface.stone': 'kamień', 'surface.rock': 'skała', 'surface.metal': 'metal', 'surface.metal_grid': 'kratka metalowa', 'surface.tiles': 'płytki', 'surface.sand': 'piasek', 'surface.pebblestone': 'kamyki', 'detail.back': 'Wróć do listy', 'detail.routeTo': 'Trasa do tego miejsca', 'detail.openOsm': 'Zobacz w OpenStreetMap', 'detail.refresh': 'Odśwież dane z OSM', 'detail.refreshing': 'Pobieranie…', 'detail.refreshed': 'Odświeżono z OpenStreetMap: {time}.', 'detail.refreshFail': 'Nie udało się połączyć z OpenStreetMap. Widzisz zapis z {date}. To, że nie udało się odświeżyć, nie oznacza, że dane są aktualne.', 'detail.refreshDemo': 'To miejsce jest przykładowe — nie ma go w OpenStreetMap.', 'detail.demo': 'DEMO', 'detail.demoNote': 'Fikcyjne miejsce pokazujące stan danych: „{state}”.', 'detail.facts': 'Szczegóły dostępności', 'detail.reports': 'Zgłoszenia użytkowników', 'detail.reports.none': 'Brak zgłoszeń.', 'detail.reports.note': 'Zgłoszenia są niezweryfikowane i oddzielone od danych OSM. W tym prototypie zapisują się tylko na tym urządzeniu.', 'detail.reports.demo': 'przykładowe', 'detail.reports.remove': 'Usuń zgłoszenie', 'rep.title': 'Zgłoś poprawkę lub uzupełnienie', 'rep.attr': 'Czego dotyczy zgłoszenie', 'rep.attr.entrance': 'Dostępność wejścia', 'rep.attr.steps': 'Schody przy wejściu', 'rep.attr.width': 'Szerokość wejścia', 'rep.attr.toilet': 'Toaleta dostępna dla wózka', 'rep.attr.ramp': 'Podjazd lub winda', 'rep.value': 'Co zastałeś(-aś) na miejscu', 'rep.yes': 'Tak', 'rep.limited': 'Częściowo', 'rep.no': 'Nie', 'rep.widthLabel': 'Szerokość w centymetrach', 'rep.note': 'Notatka (opcjonalnie)', 'rep.submit': 'Zapisz zgłoszenie', 'rep.cancel': 'Anuluj', 'rep.thanks': 'Zgłoszenie zapisane. Jest oznaczone jako niezweryfikowane.', 'rep.widthInvalid': 'Podaj szerokość od 30 do 300 cm.', 'rep.disclaimer': 'Nie zbieramy danych osobowych. Zgłoszenie zawiera tylko to, co wpiszesz.', 'route.title': 'Zaplanuj trasę', 'route.intro': 'Trasa omija schody, wysokie krawężniki i strome odcinki według Twojego profilu. Brak danych o odcinku nie jest traktowany jako potwierdzenie dostępności.', 'route.from': 'Skąd', 'route.to': 'Dokąd', 'route.placeholder': 'Zacznij pisać nazwę miejsca', 'route.swap': 'Zamień miejscami', 'route.go': 'Wyznacz trasę', 'route.myLocation': 'Użyj mojej lokalizacji', 'route.locating': 'Ustalanie lokalizacji…', 'route.locOut': 'Jesteś poza obszarem demonstracyjnym (Stare Miasto, Kazimierz, Wawel).', 'route.locFail': 'Nie udało się ustalić lokalizacji. Wpisz punkt startu.', 'route.presets': 'Przykładowe trasy', 'route.unknownPlace': 'Nie znaleziono miejsca „{text}”. Wybierz pozycję z podpowiedzi.', 'route.same': 'Start i cel są takie same.', 'route.ok': 'Trasa bez zmapowanych barier', 'route.blocked': 'Nie znaleziono trasy bez barier', 'route.blocked.long': 'W zmapowanych danych każda droga prowadzi przez barierę niezgodną z Twoimi granicami. Pokazujemy najlepszą próbę z zaznaczonymi barierami — nie traktuj jej jako dostępnej.', 'route.none': 'Brak trasy w dostępnych danych', 'route.none.no-start': 'Punkt startu jest zbyt daleko od zmapowanych chodników.', 'route.none.no-end': 'Cel jest zbyt daleko od zmapowanych chodników.', 'route.none.disconnected': 'Nie ma połączenia między tymi punktami w zmapowanych danych.', 'route.summary': 'Długość {km} km · ok. {min} min (szacunek)', 'route.quality': 'Nawierzchnia znana na {pct}% trasy.', 'route.unknownKerbs': 'Skrzyżowania z nieznaną wysokością krawężnika: {n}.', 'route.barriers': 'Bariery i uwagi na trasie', 'route.barriers.none': 'Brak zmapowanych barier — ale dane mają luki.', 'route.steps': 'Opis trasy tekstem', 'route.focus': 'Pokaż na mapie', 'route.legend': 'Linia ciągła — brak barier, kreskowana — uwaga, kropkowana — bariera.', 'route.start': 'Start', 'route.end': 'Cel', 'route.estimate': 'Czas to szacunek dla spokojnego tempa; nie uwzględnia świateł ani tłumu.', 'flag.steps': 'Schody', 'flag.kerb': 'Zbyt wysoki krawężnik', 'flag.kerb-unknown': 'Przejście — wysokość krawężnika nieznana', 'flag.cobbles': 'Kostka brukowa', 'flag.rough': 'Nierówna nawierzchnia', 'flag.steep': 'Stromy odcinek', 'flag.slope-unknown': 'Pochylenie — wartość nieznana', 'flag.narrow': 'Zbyt wąskie przejście', 'flag.no-wheelchair': 'Oznaczone jako niedostępne dla wózka', 'flag.surface-unknown': 'Nawierzchnia nieznana', 'flag.ramp-steps': 'Schody z podjazdem', 'turn.start': 'Wyruszasz', 'turn.straight': 'Idź prosto', 'turn.left': 'Skręć w lewo', 'turn.right': 'Skręć w prawo', 'turn.sharp-left': 'Skręć ostro w lewo', 'turn.sharp-right': 'Skręć ostro w prawo', 'turn.back': 'Zawróć', 'seg.unnamed': 'bez nazwy', 'seg.hw.footway': 'chodnik', 'seg.hw.pedestrian': 'ulica piesza', 'seg.hw.path': 'ścieżka', 'seg.hw.steps': 'schody', 'seg.hw.cycleway': 'droga rowerowa', 'seg.hw.service': 'droga dojazdowa', 'seg.hw.living_street': 'strefa zamieszkania', 'seg.hw.residential': 'ulica osiedlowa', 'seg.hw.tertiary': 'ulica', 'seg.hw.unclassified': 'ulica', 'seg.hw.track': 'droga gruntowa', 'seg.line': '{turn}: {name} ({kind}) — {m} m{surface}', 'seg.surfaceSuffix': ', {surface}', 'map.label': 'Mapa miejsc i trasy', 'map.alt': 'Mapa jest uzupełnieniem. Te same informacje znajdziesz w liście miejsc i w opisie trasy tekstem.', 'map.legend': 'Legenda mapy', 'map.hollow': 'pusty środek = brak danych', 'map.attribution': '© autorzy OpenStreetMap', 'map.zoomIn': 'Przybliż', 'map.zoomOut': 'Oddal', 'map.attrToggle': 'Pokaż lub ukryj źródła mapy', 'map.offline': 'Brak połączenia — kafelki mapy mogą się nie wczytać, dane miejsc działają offline.', 'about.title': 'O danych i projekcie', 'about.intro': 'Narzędzie pokazuje szczegóły dostępności z otwartych źródeł, a nie ogólne „dostępne / niedostępne”. Każda informacja ma źródło, datę i poziom wiarygodności.', 'about.data': 'Źródła danych i ich wiarygodność', 'about.data.body1': 'Mapa, miejsca, schody, krawężniki, nawierzchnie, ławki i toalety pochodzą z OpenStreetMap (licencja ODbL, wymagane oznaczenie źródła). Pobieramy je przez publiczne API Overpass dla obszaru demonstracyjnego i zapisujemy jako migawkę z datą w aplikacji.', 'about.data.body2': 'Data przy informacji to data ostatniej edycji obiektu w OSM, o ile nie ma daty kontroli (check_date, survey:date). Edycja nie zawsze dotyczyła danych o dostępności — dlatego bez daty kontroli nigdy nie nazywamy danych „potwierdzonymi”.', 'about.data.body3': 'Dane miejskie (Otwarte Dane Krakowa, MSIP) nie są jeszcze wpięte w prototyp. Architektura przewiduje je jako kolejny adapter; przed użyciem trzeba sprawdzić licencję każdego zbioru.', 'about.legend': 'Poziomy wiarygodności', 'about.missing': 'Co widzisz, gdy danych brakuje lub są sprzeczne', 'about.missing.body': 'Brak informacji pokazujemy jako „Brak danych” z szarą ikoną znaku zapytania — nigdy jako dostępność. Gdy zgłoszenie użytkownika przeczy OSM, miejsce dostaje status „Sprzeczne dane” i oba stanowiska są widoczne. Gdy nie da się odświeżyć źródła, widzisz datę ostatniego zapisu i ostrzeżenie.', 'about.arch': 'Architektura i dodawanie miast', 'about.arch.body': 'Trzy warstwy: (1) adaptery źródeł (OSM, zgłoszenia, w przyszłości dane miejskie), (2) normalizacja do jednego modelu faktu {wartość, źródło, data, wiarygodność}, (3) prezentacja zależna od profilu. Nowe miasto to konfiguracja: obszar geograficzny i lista adapterów — bez zmian w interfejsie. Nowe kategorie miejsc dodaje się w jednej tabeli.', 'about.refresh': 'Aktualizacja i poprawianie danych', 'about.refresh.body': 'Migawkę odbudowuje skrypt (npm: data:build), a pojedyncze miejsce można odświeżyć na żywo z Overpass. Błędne dane poprawia się w OSM, a w aplikacji — zgłoszeniem, które jest wyraźnie oznaczone jako niezweryfikowane. W wersji produkcyjnej zgłoszenia trafiają do kolejki moderacji, a po potwierdzeniu przez dwie niezależne osoby — do OSM.', 'about.privacy': 'Prywatność i bezpieczeństwo', 'about.privacy.body': 'Nie pytamy o niepełnosprawność — tylko o preferencje dotyczące barier. Profil i zgłoszenia są przechowywane lokalnie na urządzeniu. Brak kont i analityki. Lokalizacja (opcjonalna) jest używana tylko w przeglądarce. Połączenia z mapą i API są szyfrowane (HTTPS).', 'about.a11y': 'Dostępność samej aplikacji', 'about.a11y.body': 'Cel: WCAG 2.2 AA. Zrobione: pełna obsługa klawiaturą, widoczny fokus, kontrast z systemu USWDS, informacje nie tylko kolorem (ikona + tekst), lista i opis trasy tekstem jako odpowiednik mapy, markery barier jako przyciski. Do dopracowania: wybór miejsca bezpośrednio z mapy nie jest dostępny z klawiatury (użyj listy), brak testów z czytnikami na wszystkich platformach, brak trybu wysokiego kontrastu poza ustawieniami systemu.', 'about.biz': 'Model biznesowy i skalowanie', 'about.biz.body': 'Bezpłatna aplikacja dla mieszkańców i turystów. Przychód z usług B2B: (1) widżet i raport „dostępność obiektu” dla hoteli, organizatorów wydarzeń i zarządców nieruchomości, (2) API dostępności dla systemów rezerwacyjnych i aplikacji turystycznych, (3) pakiet wdrożeniowy dla miast. Koszty to hosting statyczny, okresowe pobieranie z Overpass i moderacja zgłoszeń. Za produkt odpowiada podmiot prowadzący usługę, nie UMK.', 'about.limits': 'Znane ograniczenia prototypu', 'about.limits.body': 'Obszar: Stare Miasto, Kazimierz, Wawel. Zgłoszenia tylko lokalnie. Brak danych miejskich. Wysokość krawężników znana tylko dla części przejść. Szerokość wejść prawie nigdy nie jest zmapowana. Czasy przejścia to szacunki. Kafelki mapy z serwera OSM nadają się do demonstracji — produkcja wymaga własnego dostawcy kafelków.', 'about.demo': 'Kontrolki demonstracyjne', 'about.simulate': 'Symuluj niedostępność źródła danych (OSM)', 'about.simulate.help': 'Odświeżenie miejsca zakończy się błędem — pokazuje, co zobaczy użytkownik.', 'about.attr': 'Mapa i dane © autorzy OpenStreetMap, licencja ODbL. Interfejs: U.S. Web Design System (domena publiczna / MIT), czcionka Public Sans (OFL). Silnik mapy: MapLibre GL JS (BSD-3).', 'about.reset': 'Wyczyść dane lokalne (profil i zgłoszenia)', 'about.resetDone': 'Dane lokalne usunięte.', 'common.close': 'Zamknij', 'common.loading': 'Wczytywanie danych…', 'common.loadError': 'Nie udało się wczytać danych. Odśwież stronę.', 'common.noName': 'Miejsce bez nazwy', 'common.km': 'km', 'footer.attr': 'Dane © autorzy OpenStreetMap (ODbL).', } as const; export type Key = keyof typeof pl; export const hasKey = (k: string): k is Key => k in pl; type Dict = Record; const en: Dict = { 'app.name': 'Kraków without barriers', 'app.tagline': 'Check whether a place or route fits your needs — and see how we know.', 'banner.proto': 'Hackathon prototype.', 'banner.data': 'Data: OpenStreetMap snapshot from {date}. Places marked DEMO are fictional.', 'nav.label': 'Main navigation', 'nav.places': 'Places', 'nav.route': 'Route', 'nav.about': 'About the data', 'nav.profile': 'Profile', 'lang.label': 'Language', 'skip': 'Skip to content', 'profile.title': 'Your needs', 'profile.intro': 'Choose how you travel. We only ask about barrier preferences — never about health. They are stored on this device only.', 'profile.kind': 'I travel', 'profile.wheelchair': 'In a wheelchair', 'profile.wheelchair.hint': 'Manual or powered', 'profile.stroller': 'With a stroller', 'profile.stroller.hint': 'Parent or carer with a pushchair', 'profile.limits': 'Limits you do not want to exceed', 'profile.kerb': 'Highest kerb', 'profile.width': 'Narrowest passage', 'profile.incline': 'Steepest slope', 'profile.prefs': 'What to watch for', 'profile.cobbles': 'Avoid cobblestones and rough surfaces', 'profile.toilet': 'I need an accessible toilet nearby', 'profile.rest': 'I need places to rest (benches)', 'profile.changing': 'I need a baby-changing table', 'profile.save': 'Save and search', 'profile.saved': 'Profile saved on this device.', 'profile.privacy': 'No account, no analytics. Your profile never leaves your device.', 'unit.cm': 'cm', 'unit.pct': '%', 'places.title': 'Find a place', 'places.search': 'Name or address', 'places.category': 'Type of place', 'places.onlyFit': 'Show only matching or partly matching', 'places.count': 'Results: {n}', 'places.more': 'Show more', 'places.empty': 'No places for these criteria.', 'places.hint': 'Ratings are computed for your profile: {profile}.', 'view.list': 'List', 'view.map': 'Map', 'view.label': 'Results view', 'cat.all': 'All', 'cat.food': 'Food and drink', 'cat.culture': 'Culture and sights', 'cat.shop': 'Shops', 'cat.stay': 'Stays', 'cat.health': 'Health', 'cat.services': 'Services', 'cat.toilets': 'Toilets', 'cat.elevator': 'Lifts', 'cat.transit': 'Transport', 'verdict.match': 'Fits, per the data', 'verdict.partial': 'Partly fits', 'verdict.conflict': 'Conflicting data', 'verdict.incomplete': 'Not enough data', 'verdict.fail': 'Does not fit', 'verdict.match.long': 'Mapped data matches your profile. It is not a guarantee — check the date and source below.', 'verdict.partial.long': 'Data matches your profile, but there are notes worth checking before you go.', 'verdict.conflict.long': 'Sources disagree. Do not treat this place as accessible until someone confirms it.', 'verdict.incomplete.long': 'Data is missing or outdated. No information does not mean the place is accessible.', 'verdict.fail.long': 'Mapped data shows a barrier beyond your limits.', 'status.ok': 'OK', 'status.warn': 'Caution', 'status.fail': 'Barrier', 'status.unknown': 'No data', 'status.conflict': 'Conflicting', 'conf.verified': 'Confirmed', 'conf.mapped': 'Mapped', 'conf.stale': 'May have changed', 'conf.inferred': 'Inferred', 'conf.user': 'Unverified report', 'conf.none': 'None', 'conf.verified.help': 'Has a survey date (check_date or survey:date) within the last 24 months.', 'conf.mapped.help': 'OSM entry edited within 24 months, with no survey date.', 'conf.stale.help': 'OSM entry has not been changed for over 24 months.', 'conf.inferred.help': 'Worked out from nearby objects, not described for this place.', 'conf.user.help': 'Reported in this app; nobody has confirmed it yet.', 'conf.none.help': 'Nothing is known.', 'src.label': 'Source', 'src.osm-tag': 'OpenStreetMap (place tags)', 'src.osm-geometry': 'OpenStreetMap (nearby objects)', 'src.user-report': 'User report (this device)', 'src.demo': 'DEMO sample data', 'src.none': 'No source', 'fact.updated': 'Data from', 'fact.dateUnknown': 'date unknown', 'fact.confidence': 'Reliability', 'fact.otherSource': 'Another source says', 'fact.report': 'Correct or add', 'fact.required': 'matters for your profile', 'fact.title.entrance': 'Entrance', 'fact.title.steps': 'Steps near the entrance', 'fact.title.ramp': 'Ramp or lift', 'fact.title.width': 'Entrance width', 'fact.title.surface': 'Surface outside', 'fact.title.toilet': 'Toilet', 'fact.title.rest': 'Places to rest', 'fact.title.changing': 'Baby changing', 'f.entrance.yes': 'Entrance is tagged as wheelchair accessible.', 'f.entrance.limited': 'Accessible with limits (for example part of the venue, or with help).', 'f.entrance.no': 'Entrance is tagged as not wheelchair accessible.', 'f.entrance.none': 'Nobody has described whether the entrance is accessible.', 'f.steps.warn': 'Mapped steps about {d} m from the entrance (step count: {n}).', 'f.steps.conflict': 'Entrance is tagged accessible, yet steps are mapped about {d} m away (step count: {n}) with no ramp or lift nearby.', 'f.steps.hint': 'Inferred from where steps are — they may serve a different entrance.', 'f.steps.none': 'No steps mapped nearby.', 'f.steps.noneHint': 'This does not prove there are none — they may be unmapped.', 'f.ramp.yes': 'Mapped ramp about {d} m from the entrance.', 'f.ramp.lift': 'Mapped lift about {d} m from the entrance.', 'f.ramp.none': 'No data about a ramp or lift.', 'f.width.ok': 'Reported width: {cm} cm (your minimum: {min} cm).', 'f.width.fail': 'Reported width: {cm} cm — below your minimum ({min} cm).', 'f.width.none': 'Entrance width is not mapped.', 'f.width.noneHint': 'Almost nobody maps it in OSM. You can report it.', 'f.surface.good': 'Surface: {surface} — smooth.', 'f.surface.fair': 'Surface: {surface} — fairly smooth.', 'f.surface.rough': 'Surface: {surface} — rough; slower and more tiring to travel on.', 'f.surface.unknown': 'No data about the surface outside.', 'f.toilet.own.yes': 'Toilet in the venue is tagged as wheelchair accessible.', 'f.toilet.own.no': 'Toilet in the venue is tagged as not wheelchair accessible.', 'f.toilet.near': 'Accessible public toilet about {d} m away.', 'f.toilet.fee': 'The toilet is paid.', 'f.toilet.nearUnknown': 'Public toilet about {d} m away, but its wheelchair access is unknown.', 'f.toilet.none': 'No data about an accessible toilet.', 'f.rest.ok': 'Mapped benches nearby: {n}, nearest about {d} m.', 'f.rest.none': 'No benches mapped within 150 m.', 'f.rest.noneHint': 'Unmapped ones may exist.', 'f.changing.yes': 'Baby-changing table tagged in the venue.', 'f.changing.no': 'No baby-changing table (tagged in OSM).', 'f.changing.near': 'Baby-changing table in a public toilet about {d} m away.', 'f.changing.none': 'No data about a baby-changing table.', 'raw': '{text}', 'surface.sett': 'setts (cobbles)', 'surface.cobblestone': 'cobblestone', 'surface.unhewn_cobblestone': 'rough cobblestone', 'surface.paving_stones': 'paving slabs', 'surface.asphalt': 'asphalt', 'surface.concrete': 'concrete', 'surface.paved': 'paved', 'surface.concrete:plates': 'concrete plates', 'surface.concrete:lanes': 'concrete lanes', 'surface.compacted': 'compacted', 'surface.fine_gravel': 'fine gravel', 'surface.gravel': 'gravel', 'surface.wood': 'wood', 'surface.dirt': 'dirt', 'surface.ground': 'ground', 'surface.grass': 'grass', 'surface.unpaved': 'unpaved', 'surface.stone': 'stone', 'surface.rock': 'rock', 'surface.metal': 'metal', 'surface.metal_grid': 'metal grating', 'surface.tiles': 'tiles', 'surface.sand': 'sand', 'surface.pebblestone': 'pebbles', 'detail.back': 'Back to the list', 'detail.routeTo': 'Route to this place', 'detail.openOsm': 'View on OpenStreetMap', 'detail.refresh': 'Refresh from OSM', 'detail.refreshing': 'Fetching…', 'detail.refreshed': 'Refreshed from OpenStreetMap: {time}.', 'detail.refreshFail': 'Could not reach OpenStreetMap. You are seeing the snapshot from {date}. A failed refresh does not mean the data is current.', 'detail.refreshDemo': 'This place is a sample — it does not exist in OpenStreetMap.', 'detail.demo': 'DEMO', 'detail.demoNote': 'Fictional place showing the data state: “{state}”.', 'detail.facts': 'Accessibility details', 'detail.reports': 'User reports', 'detail.reports.none': 'No reports.', 'detail.reports.note': 'Reports are unverified and kept apart from OSM data. In this prototype they are stored on this device only.', 'detail.reports.demo': 'sample', 'detail.reports.remove': 'Remove report', 'rep.title': 'Report a correction or addition', 'rep.attr': 'What the report is about', 'rep.attr.entrance': 'Entrance accessibility', 'rep.attr.steps': 'Steps at the entrance', 'rep.attr.width': 'Entrance width', 'rep.attr.toilet': 'Wheelchair-accessible toilet', 'rep.attr.ramp': 'Ramp or lift', 'rep.value': 'What you found on site', 'rep.yes': 'Yes', 'rep.limited': 'Partly', 'rep.no': 'No', 'rep.widthLabel': 'Width in centimetres', 'rep.note': 'Note (optional)', 'rep.submit': 'Save report', 'rep.cancel': 'Cancel', 'rep.thanks': 'Report saved. It is marked as unverified.', 'rep.widthInvalid': 'Enter a width between 30 and 300 cm.', 'rep.disclaimer': 'We collect no personal data. A report contains only what you type.', 'route.title': 'Plan a route', 'route.intro': 'The route avoids steps, high kerbs and steep sections for your profile. Missing data about a section is not treated as confirmation that it is accessible.', 'route.from': 'From', 'route.to': 'To', 'route.placeholder': 'Start typing a place name', 'route.swap': 'Swap start and destination', 'route.go': 'Find route', 'route.myLocation': 'Use my location', 'route.locating': 'Finding your location…', 'route.locOut': 'You are outside the demo area (Old Town, Kazimierz, Wawel).', 'route.locFail': 'Could not get your location. Type a starting point.', 'route.presets': 'Example routes', 'route.unknownPlace': 'No place found for “{text}”. Pick one of the suggestions.', 'route.same': 'Start and destination are the same.', 'route.ok': 'Route with no mapped barriers', 'route.blocked': 'No barrier-free route found', 'route.blocked.long': 'In the mapped data every way goes through a barrier beyond your limits. We show the best attempt with the barriers marked — do not treat it as accessible.', 'route.none': 'No route in the available data', 'route.none.no-start': 'The start is too far from mapped footpaths.', 'route.none.no-end': 'The destination is too far from mapped footpaths.', 'route.none.disconnected': 'The mapped data has no connection between these points.', 'route.summary': '{km} km · about {min} min (estimate)', 'route.quality': 'Surface known for {pct}% of the route.', 'route.unknownKerbs': 'Crossings with unknown kerb height: {n}.', 'route.barriers': 'Barriers and notes on the route', 'route.barriers.none': 'No mapped barriers — but the data has gaps.', 'route.steps': 'Route as text', 'route.focus': 'Show on map', 'route.legend': 'Solid line — no barriers, dashed — caution, dotted — barrier.', 'route.start': 'Start', 'route.end': 'Destination', 'route.estimate': 'Time is an estimate at a relaxed pace; it ignores traffic lights and crowds.', 'flag.steps': 'Steps', 'flag.kerb': 'Kerb too high', 'flag.kerb-unknown': 'Crossing — kerb height unknown', 'flag.cobbles': 'Cobblestones', 'flag.rough': 'Rough surface', 'flag.steep': 'Steep section', 'flag.slope-unknown': 'Slope — value unknown', 'flag.narrow': 'Passage too narrow', 'flag.no-wheelchair': 'Tagged as not wheelchair accessible', 'flag.surface-unknown': 'Surface unknown', 'flag.ramp-steps': 'Steps with a ramp', 'turn.start': 'Set off', 'turn.straight': 'Continue straight', 'turn.left': 'Turn left', 'turn.right': 'Turn right', 'turn.sharp-left': 'Turn sharp left', 'turn.sharp-right': 'Turn sharp right', 'turn.back': 'Turn back', 'seg.unnamed': 'unnamed', 'seg.hw.footway': 'footpath', 'seg.hw.pedestrian': 'pedestrian street', 'seg.hw.path': 'path', 'seg.hw.steps': 'steps', 'seg.hw.cycleway': 'cycle path', 'seg.hw.service': 'service road', 'seg.hw.living_street': 'living street', 'seg.hw.residential': 'residential street', 'seg.hw.tertiary': 'street', 'seg.hw.unclassified': 'street', 'seg.hw.track': 'dirt track', 'seg.line': '{turn}: {name} ({kind}) — {m} m{surface}', 'seg.surfaceSuffix': ', {surface}', 'map.label': 'Map of places and route', 'map.alt': 'The map is a supplement. The same information is in the list of places and in the text route.', 'map.legend': 'Map legend', 'map.hollow': 'hollow centre = no data', 'map.attribution': '© OpenStreetMap contributors', 'map.zoomIn': 'Zoom in', 'map.zoomOut': 'Zoom out', 'map.attrToggle': 'Toggle map credits', 'map.offline': 'You are offline — map tiles may not load; place data works offline.', 'about.title': 'About the data and the project', 'about.intro': 'The tool shows accessibility details from open sources, not a blanket “accessible / not accessible”. Every piece of information has a source, a date and a reliability level.', 'about.data': 'Data sources and their reliability', 'about.data.body1': 'The map, places, steps, kerbs, surfaces, benches and toilets come from OpenStreetMap (ODbL licence, attribution required). We fetch them through the public Overpass API for the demo area and keep a dated snapshot in the app.', 'about.data.body2': 'The date next to a fact is the last edit of the object in OSM unless there is a survey date (check_date, survey:date). An edit may not have touched the accessibility tags — so without a survey date we never call data “confirmed”.', 'about.data.body3': 'City data (Kraków Open Data, MSIP) is not wired into the prototype yet. The architecture treats it as one more adapter; each dataset’s licence must be checked before use.', 'about.legend': 'Reliability levels', 'about.missing': 'What you see when data is missing or conflicting', 'about.missing.body': 'Missing information is shown as “No data” with a grey question-mark icon — never as accessibility. When a user report contradicts OSM, the place gets “Conflicting data” and both positions are visible. When the source cannot be refreshed you see the date of the last snapshot and a warning.', 'about.arch': 'Architecture and adding cities', 'about.arch.body': 'Three layers: (1) source adapters (OSM, user reports, later city data), (2) normalisation into one fact model {value, source, date, reliability}, (3) profile-aware presentation. A new city is configuration: a geographic area and a list of adapters — no interface changes. New place categories go in a single table.', 'about.refresh': 'Updating and correcting data', 'about.refresh.body': 'A script rebuilds the snapshot (npm: data:build), and a single place can be refreshed live from Overpass. Wrong data is fixed in OSM, and in the app by a report that is clearly marked unverified. In production reports go to a moderation queue and, once two independent people confirm, to OSM.', 'about.privacy': 'Privacy and security', 'about.privacy.body': 'We do not ask about disability — only barrier preferences. Profile and reports are stored locally on the device. No accounts, no analytics. Location (optional) is used only in the browser. Connections to the map and APIs are encrypted (HTTPS).', 'about.a11y': 'Accessibility of the app itself', 'about.a11y.body': 'Target: WCAG 2.2 AA. Done: full keyboard operation, visible focus, contrast from the USWDS system, information never by colour alone (icon + text), list and text route as equivalent of the map, barrier markers as buttons. To improve: picking a place directly on the map is not keyboard accessible (use the list), not tested with screen readers on every platform, no dedicated high-contrast mode beyond system settings.', 'about.biz': 'Business model and scaling', 'about.biz.body': 'Free app for residents and tourists. Revenue from B2B: (1) an “venue accessibility” widget and report for hotels, event organisers and property managers, (2) an accessibility API for booking systems and travel apps, (3) a roll-out package for cities. Costs are static hosting, periodic Overpass pulls and report moderation. The operating entity owns the product, not the city office.', 'about.limits': 'Known prototype limitations', 'about.limits.body': 'Area: Old Town, Kazimierz, Wawel. Reports are local only. No city data. Kerb height is known for only some crossings. Entrance width is almost never mapped. Walking times are estimates. OSM tile servers are fine for a demo — production needs its own tile provider.', 'about.demo': 'Demo controls', 'about.simulate': 'Simulate the data source being unavailable (OSM)', 'about.simulate.help': 'Refreshing a place will fail — shows what a user would see.', 'about.attr': 'Map and data © OpenStreetMap contributors, ODbL licence. Interface: U.S. Web Design System (public domain / MIT), Public Sans typeface (OFL). Map engine: MapLibre GL JS (BSD-3).', 'about.reset': 'Clear local data (profile and reports)', 'about.resetDone': 'Local data cleared.', 'common.close': 'Close', 'common.loading': 'Loading data…', 'common.loadError': 'Could not load the data. Reload the page.', 'common.noName': 'Unnamed place', 'common.km': 'km', 'footer.attr': 'Data © OpenStreetMap contributors (ODbL).', }; export const DICT: Record = { pl, en }; export type Translate = (key: Key, params?: Record) => string; export function makeT(lang: Lang): { t: Translate; tm: (m: Msg) => string } { const dict = DICT[lang]; const fmt = (s: string, params?: Record) => s.replace(/\{(\w+)\}/g, (_, k: string) => { const v = params?.[k]; if (v === undefined) return `{${k}}`; if (typeof v === 'string' && v.startsWith('@')) return (dict as Record)[v.slice(1)] ?? v.slice(1); return String(v); }); const t: Translate = (key, params) => fmt(dict[key], params); const tm = (m: Msg) => fmt((dict as Record)[m.k] ?? m.k, m.p); return { t, tm }; } export interface I18n { lang: Lang; setLang: (l: Lang) => void; t: Translate; tm: (m: Msg) => string; date: (iso: string | null) => string; } export const I18nContext = createContext(null); export function useI18n(): I18n { const v = useContext(I18nContext); if (!v) throw new Error('I18nContext missing'); return v; } export function formatDate(iso: string | null, lang: Lang): string | null { if (!iso) return null; const d = new Date(iso); if (Number.isNaN(d.getTime())) return null; return new Intl.DateTimeFormat(lang === 'pl' ? 'pl-PL' : 'en-GB', { year: 'numeric', month: 'long', day: 'numeric' }).format(d); }