mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 05:56:53 +00:00
feat(admin): the phone's API-keys card, rebuilt like the desktop tab
The desktop tab was reshaped around one idea: a key should be the exception rather than the thing you are expected to paste. The phone still had the older shape, where Google wore the "Recommended" chip and the TREK Places API was not on the screen at all. Two pages, opposite advice, same setting. So the phone gets the same three comparable blocks in the same order: the TREK index first, with its own ribbon and a folded panel of what it does and does not carry, then the two keys under theirs. The Google caveat is said before the field rather than after it, because somebody about to paste a key should read it while deciding. The four Google switches moved under the key they spend, folded away: they are set once and never touched again, and open they were longer than the rest of the card together. Open-Meteo left the card entirely, as it did on the desktop, since it takes no key and had the loudest treatment of anything there. The provider choice is TREK's own picker now. A native select is drawn by the operating system and was the one control on the page that did not belong to the app. Its description was also describing a TREK that no longer exists: it said one service answers place search. The index and OpenStreetMap answer every search and the setting only decides who is asked once they come up empty (maps.service.ts, keyedProvider). Both that line and the missing-key notice say so now, in all 23 languages, and the two key hints have short forms for the narrow screen.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import { AlertTriangle, RefreshCw, Sun } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { AlertTriangle, ChevronDown, RefreshCw } from 'lucide-react'
|
||||
import { adminApi, authApi } from '../../../api/client'
|
||||
import { getApiErrorMessage } from '../../../types'
|
||||
import type { TranslationFn } from '../../../types'
|
||||
import type { useAdmin } from '../../../pages/admin/useAdmin'
|
||||
import MToggle from '../../components/MToggle'
|
||||
import { MBlockDisclosure, MProviderBlock, MTrekApiBlock } from './MApiProviderBlocks'
|
||||
import MSetPickerSheet from '../settings/MSetPickerSheet'
|
||||
import {
|
||||
MAdminButton,
|
||||
MAdminCard,
|
||||
@@ -23,6 +26,7 @@ interface MAdminSettingsSectionProps {
|
||||
// types, API keys, OIDC and the danger zone — the full desktop settings tab
|
||||
// relaid as mobile cards. All state and mutations come from useAdmin.
|
||||
export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectionProps) {
|
||||
const [providerPickerOpen, setProviderPickerOpen] = useState(false)
|
||||
const {
|
||||
toast,
|
||||
setPlacesPhotosEnabled, setPlacesAutocompleteEnabled, setPlacesDetailsEnabled, setPlacesEnrichEnabled, setPlaceShadowEnabled,
|
||||
@@ -217,20 +221,22 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
<MAdminCard>
|
||||
<MAdminCardHead title={t('admin.apiKeys')} hint={t('admin.apiKeysHint')} />
|
||||
<div className="space-y-3">
|
||||
{/* Comparable blocks in the order we would have somebody choose them,
|
||||
matching the desktop tab: the free source first, the keys after it.
|
||||
The recommendation used to sit on the Google field here, which told
|
||||
a phone admin the opposite of what the same setting says on a
|
||||
laptop. */}
|
||||
<MTrekApiBlock />
|
||||
|
||||
{/* The keys belong to the operator on a managed install, same as on the
|
||||
desktop tab; the provider choice below stays the admin's. */}
|
||||
{!managed && (<>
|
||||
<MAdminField
|
||||
label={
|
||||
<span className="flex items-center gap-2">
|
||||
{t('admin.mapsKey')}
|
||||
<span className="rounded-full bg-[color:color-mix(in_srgb,var(--m-st-confirmed)_14%,transparent)] px-[7px] py-[2px] font-geist text-[0.5625rem] font-bold text-[color:var(--m-st-confirmed)]">
|
||||
{t('admin.recommended')}
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
hint={t('admin.mapsKeyHintLong')}
|
||||
>
|
||||
<MProviderBlock title={t('admin.mapsKey')} badge={t('admin.googleCaveat.badge')} tone="caution">
|
||||
{/* Said before the field, not after it: somebody about to paste a key
|
||||
should read this while deciding, not once they already have. */}
|
||||
<p className="font-geist text-[0.625rem] leading-relaxed text-m-faint">
|
||||
{t('admin.mapsKeyHintShort')}
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<MAdminSecretInput
|
||||
@@ -251,63 +257,19 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
</MAdminButton>
|
||||
</div>
|
||||
{validation.maps === true && (
|
||||
<p className="mt-1 font-geist text-[0.625rem] font-bold text-[color:var(--m-st-confirmed)]">
|
||||
<p className="font-geist text-[0.625rem] font-bold text-[color:var(--m-st-confirmed)]">
|
||||
{t('admin.keyValid')}
|
||||
</p>
|
||||
)}
|
||||
{validation.maps === false && (
|
||||
<p className="mt-1 font-geist text-[0.625rem] font-bold text-[color:var(--m-st-danger)]">
|
||||
<p className="font-geist text-[0.625rem] font-bold text-[color:var(--m-st-danger)]">
|
||||
{t('admin.keyInvalid')}
|
||||
</p>
|
||||
)}
|
||||
</MAdminField>
|
||||
|
||||
{/* No Test button, same as the desktop tab: /auth/validate-keys only
|
||||
knows how to probe Google Places and OpenWeatherMap. */}
|
||||
<MAdminField label={t('admin.amapKey')} hint={t('admin.amapKeyHint')}>
|
||||
<MAdminSecretInput
|
||||
aria-label={t('admin.amapKey')}
|
||||
value={amapKey}
|
||||
onChange={(e) => setAmapKey(e.target.value)}
|
||||
placeholder={t('settings.keyPlaceholder')}
|
||||
/>
|
||||
</MAdminField>
|
||||
|
||||
<MAdminField label={t('admin.unsplashKey')} hint={t('admin.unsplashKeyHint')}>
|
||||
<MAdminSecretInput
|
||||
aria-label={t('admin.unsplashKey')}
|
||||
value={unsplashKey}
|
||||
onChange={(e) => setUnsplashKey(e.target.value)}
|
||||
placeholder={t('settings.keyPlaceholder')}
|
||||
/>
|
||||
</MAdminField>
|
||||
</>)}
|
||||
|
||||
<MAdminField label={t('admin.placesProvider.title')} hint={t('admin.placesProvider.subtitle')}>
|
||||
<select
|
||||
value={placesProvider}
|
||||
disabled={savingPlacesProvider}
|
||||
onChange={(e) => handleSavePlacesProvider(e.target.value)}
|
||||
className="h-[42px] w-full rounded-xl border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)] px-3 text-[0.84375rem] text-m-ink outline-none disabled:opacity-50"
|
||||
>
|
||||
<option value="auto">{t('admin.placesProvider.auto')}</option>
|
||||
<option value="google">{t('admin.placesProvider.google')}</option>
|
||||
<option value="amap">{t('admin.placesProvider.amap')}</option>
|
||||
<option value="openstreetmap">{t('admin.placesProvider.openstreetmap')}</option>
|
||||
</select>
|
||||
{/* From app-config, like the desktop card: the key fields are empty
|
||||
on a managed install and on an operator key set by environment. */}
|
||||
{((placesProvider === 'google' && !hasMapsKey) || (placesProvider === 'amap' && !hasAmapKey)) && (
|
||||
<p className="mt-1 font-geist text-[0.625rem] font-bold text-[color:var(--m-st-pending)]">
|
||||
{t('admin.placesProvider.missingKey')}
|
||||
</p>
|
||||
)}
|
||||
</MAdminField>
|
||||
|
||||
<div>
|
||||
{/* The four Google switches trade away quota that is the operator's on
|
||||
a managed install; the search log below is the instance's own. */}
|
||||
{!managed && (<>
|
||||
{/* What the key is allowed to be spent on. Under the key rather than
|
||||
loose at the foot of the card: every one of them is a Google
|
||||
request, and none of them mean anything without it. */}
|
||||
<MBlockDisclosure label={t('admin.placesUsageTitle')}>
|
||||
<MAdminRow
|
||||
title={t('admin.placesPhotos.title')}
|
||||
hint={t('admin.placesPhotos.subtitle')}
|
||||
@@ -388,8 +350,71 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>)}
|
||||
</MBlockDisclosure>
|
||||
</MProviderBlock>
|
||||
|
||||
{/* No Test button, same as the desktop tab: /auth/validate-keys only
|
||||
knows how to probe Google Places and OpenWeatherMap. */}
|
||||
<MProviderBlock title={t('admin.amapKey')}>
|
||||
<p className="font-geist text-[0.625rem] leading-relaxed text-m-faint">{t('admin.amapKeyHintShort')}</p>
|
||||
<MAdminSecretInput
|
||||
aria-label={t('admin.amapKey')}
|
||||
value={amapKey}
|
||||
onChange={(e) => setAmapKey(e.target.value)}
|
||||
placeholder={t('settings.keyPlaceholder')}
|
||||
/>
|
||||
</MProviderBlock>
|
||||
|
||||
<MProviderBlock title={t('admin.unsplashKey')}>
|
||||
<p className="font-geist text-[0.625rem] leading-relaxed text-m-faint">{t('admin.unsplashKeyHint')}</p>
|
||||
<MAdminSecretInput
|
||||
aria-label={t('admin.unsplashKey')}
|
||||
value={unsplashKey}
|
||||
onChange={(e) => setUnsplashKey(e.target.value)}
|
||||
placeholder={t('settings.keyPlaceholder')}
|
||||
/>
|
||||
</MProviderBlock>
|
||||
</>)}
|
||||
|
||||
<MAdminField label={t('admin.placesProvider.title')} hint={t('admin.placesProvider.subtitle')}>
|
||||
{/* The app's own picker rather than a bare <select>: a native one is
|
||||
drawn by the operating system and was the one control on this page
|
||||
that did not belong to TREK. Same sheet the settings selects use. */}
|
||||
<button
|
||||
type="button"
|
||||
disabled={savingPlacesProvider}
|
||||
onClick={() => setProviderPickerOpen(true)}
|
||||
className="flex h-[42px] w-full items-center gap-2 rounded-xl border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)] px-3 text-left text-[0.84375rem] text-m-ink disabled:opacity-50"
|
||||
>
|
||||
<span className="min-w-0 flex-1 truncate">{t(`admin.placesProvider.${placesProvider}`)}</span>
|
||||
<ChevronDown size={15} strokeWidth={2.2} className="flex-none text-m-faint" aria-hidden />
|
||||
</button>
|
||||
<MSetPickerSheet
|
||||
open={providerPickerOpen}
|
||||
onClose={() => setProviderPickerOpen(false)}
|
||||
title={t('admin.placesProvider.title')}
|
||||
value={placesProvider}
|
||||
onSelect={(value) => { if (value !== placesProvider) void handleSavePlacesProvider(value) }}
|
||||
options={[
|
||||
{ value: 'auto', label: t('admin.placesProvider.auto') },
|
||||
{ value: 'google', label: t('admin.placesProvider.google') },
|
||||
{ value: 'amap', label: t('admin.placesProvider.amap') },
|
||||
{ value: 'openstreetmap', label: t('admin.placesProvider.openstreetmap') },
|
||||
]}
|
||||
/>
|
||||
{/* From app-config, like the desktop card: the key fields are empty
|
||||
on a managed install and on an operator key set by environment. */}
|
||||
{((placesProvider === 'google' && !hasMapsKey) || (placesProvider === 'amap' && !hasAmapKey)) && (
|
||||
<p className="mt-1 font-geist text-[0.625rem] font-bold text-[color:var(--m-st-pending)]">
|
||||
{t('admin.placesProvider.missingKey')}
|
||||
</p>
|
||||
)}
|
||||
</MAdminField>
|
||||
|
||||
<div>
|
||||
{/* The instance's own search log, which is not Google's to spend. */}
|
||||
<MAdminRow
|
||||
first
|
||||
title={t('admin.placeShadow.title')}
|
||||
hint={t('admin.placeShadow.subtitle')}
|
||||
trailing={
|
||||
@@ -411,39 +436,6 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Open-Meteo weather info */}
|
||||
<div className="rounded-2xl border border-[color:color-mix(in_srgb,var(--m-st-confirmed)_28%,transparent)] bg-[color:color-mix(in_srgb,var(--m-st-confirmed)_10%,transparent)] p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex h-7 w-7 flex-none items-center justify-center rounded-lg bg-[color:var(--m-st-confirmed)] text-white">
|
||||
<Sun size={14} strokeWidth={2.2} />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate text-[0.78125rem] font-extrabold text-m-ink">
|
||||
{t('admin.weather.title')}
|
||||
</span>
|
||||
<span className="flex-none rounded-full bg-[color:color-mix(in_srgb,var(--m-st-confirmed)_14%,transparent)] px-2 py-[2px] font-geist text-[0.5625rem] font-bold text-[color:var(--m-st-confirmed)]">
|
||||
{t('admin.weather.badge')}
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-2 font-geist text-[0.625rem] leading-relaxed text-m-muted">
|
||||
{t('admin.weather.description')}
|
||||
</p>
|
||||
<p className="mt-1 font-geist text-[0.59375rem] leading-relaxed text-m-faint">
|
||||
{t('admin.weather.locationHint')}
|
||||
</p>
|
||||
<div className="mt-2 space-y-[6px]">
|
||||
{[
|
||||
{ title: t('admin.weather.forecast'), desc: t('admin.weather.forecastDesc') },
|
||||
{ title: t('admin.weather.climate'), desc: t('admin.weather.climateDesc') },
|
||||
{ title: t('admin.weather.requests'), desc: t('admin.weather.requestsDesc') },
|
||||
].map((row) => (
|
||||
<div key={row.title} className="rounded-xl bg-[color:var(--m-ic)] px-3 py-2">
|
||||
<p className="text-[0.6875rem] font-bold text-m-ink">{row.title}</p>
|
||||
<p className="mt-[1px] font-geist text-[0.59375rem] text-m-muted">{row.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!managed && (
|
||||
<MAdminButton busy={savingKeys} onClick={handleSaveApiKeys}>
|
||||
{t('common.save')}
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
import { useState } from 'react'
|
||||
import { Check, ChevronRight, Globe2, KeyRound, Library, ShieldOff, WifiOff, X } from 'lucide-react'
|
||||
import TrekMark from '../../../components/shared/TrekMark'
|
||||
import { useTranslation } from '../../../i18n'
|
||||
|
||||
/**
|
||||
* The API-keys card's two building blocks, in the phone's own material.
|
||||
*
|
||||
* The desktop tab was rebuilt around one idea: a key should be the exception
|
||||
* rather than the thing you are expected to paste. The TREK Places API leads,
|
||||
* the paid providers follow as comparable blocks, and the "recommended" mark
|
||||
* belongs to the free option instead of to the Google field. The phone still
|
||||
* had the older shape, where Google wore the recommendation and TREK was not on
|
||||
* the screen at all, which is the opposite advice on the same setting.
|
||||
*
|
||||
* Rebuilt rather than shared with the desktop component: every surface, radius
|
||||
* and type size here comes from the mobile tokens, and the desktop card is a
|
||||
* two-column layout with a hover-revealed disclosure. What is shared is the
|
||||
* wording, key for key, so the two pages cannot drift in what they claim.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Proper nouns, so they are not translated. Naming them is also a licence
|
||||
* obligation rather than decoration: ODbL and CC BY-SA both require attribution
|
||||
* wherever their content is shown.
|
||||
*/
|
||||
const SOURCES = ['Overture Maps Foundation', 'OpenStreetMap', 'Wikivoyage', 'Wikimedia']
|
||||
|
||||
const chip = 'rounded-full border border-[color:var(--m-rowbr)] px-2 py-[2px] font-geist text-[0.625rem]'
|
||||
|
||||
/** One group of chips inside the disclosure: what you get, what you do not, where it comes from. */
|
||||
function ChipGroup({
|
||||
icon,
|
||||
label,
|
||||
items,
|
||||
note,
|
||||
dashed = false,
|
||||
}: {
|
||||
icon: React.ReactNode
|
||||
label: string
|
||||
items: string[]
|
||||
note: string
|
||||
dashed?: boolean
|
||||
}) {
|
||||
return (
|
||||
<div className="rounded-[14px] border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)] p-[11px]">
|
||||
<p className="flex items-center gap-[6px] text-[0.6875rem] font-bold text-m-ink">
|
||||
{icon}
|
||||
{label}
|
||||
</p>
|
||||
<ul className="mt-2 flex flex-wrap gap-[5px]">
|
||||
{items.map(item => (
|
||||
<li key={item} className={`${chip} ${dashed ? 'border-dashed text-m-faint' : 'text-m-muted'}`}>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="mt-2 font-geist text-[0.625rem] leading-relaxed text-m-faint">{note}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** The TREK Places API, above the paid keys because it is the alternative to them. */
|
||||
export function MTrekApiBlock() {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const facts = [
|
||||
{ Icon: Globe2, text: t('admin.trekApi.factPlaces') },
|
||||
{ Icon: KeyRound, text: t('admin.trekApi.factNoKey') },
|
||||
{ Icon: WifiOff, text: t('admin.trekApi.factOffline') },
|
||||
{ Icon: ShieldOff, text: t('admin.trekApi.factPrivacy') },
|
||||
]
|
||||
|
||||
// The same fields the desktop card lists, reusing words TREK already has.
|
||||
const fields = [
|
||||
t('places.formName'),
|
||||
t('collections.coordinates'),
|
||||
t('places.formCategory'),
|
||||
t('places.formAddress'),
|
||||
t('admin.trekApi.fieldPhone'),
|
||||
t('common.email'),
|
||||
t('places.formWebsite'),
|
||||
t('places.formDescription'),
|
||||
t('inspector.openingHours'),
|
||||
t('admin.trekApi.fieldStableId'),
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="relative mt-[18px]">
|
||||
{/* The recommendation sits on the border, as on the desktop card, and small:
|
||||
a wide coloured banner would shout over the settings beside it. */}
|
||||
<span className="pointer-events-none absolute -top-2 left-3 z-10 rounded-md bg-m-act px-2 py-[1px] font-geist text-[0.5625rem] font-bold tracking-[.12em] text-m-actfg uppercase">
|
||||
{t('admin.trekApi.badgeDefault')}
|
||||
</span>
|
||||
|
||||
<div className="overflow-hidden rounded-[16px] border border-[color:var(--m-act)] bg-[color:var(--m-ic)]">
|
||||
<div className="px-[14px] pt-[18px] pb-[13px]">
|
||||
<TrekMark className="h-6 w-auto text-m-ink" aria-label="TREK Places API" />
|
||||
<p className="mt-[9px] text-[0.8125rem] leading-relaxed text-m-muted">{t('admin.trekApi.tagline')}</p>
|
||||
<ul className="mt-3 grid grid-cols-1 gap-[7px]">
|
||||
{facts.map(({ Icon, text }) => (
|
||||
<li key={text} className="flex items-center gap-2 font-geist text-[0.6875rem] text-m-muted">
|
||||
<Icon size={13} strokeWidth={2} className="flex-none text-m-faint" aria-hidden />
|
||||
{text}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* A button rather than <details>: the phone shell styles its own
|
||||
disclosures and Safari draws a marker on the summary that no reset
|
||||
reaches reliably. */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(v => !v)}
|
||||
aria-expanded={open}
|
||||
className="flex w-full items-center gap-2 border-t border-[color:var(--m-rowbr)] px-[14px] py-[10px] text-left"
|
||||
>
|
||||
<ChevronRight
|
||||
size={15}
|
||||
strokeWidth={2.2}
|
||||
className={`flex-none text-m-faint transition-transform ${open ? 'rotate-90' : ''}`}
|
||||
aria-hidden
|
||||
/>
|
||||
<span className="text-[0.8125rem] font-semibold text-m-muted">{t('admin.trekApi.more')}</span>
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div className="space-y-[9px] px-[14px] pt-1 pb-[14px]">
|
||||
<ChipGroup
|
||||
icon={<Check size={13} strokeWidth={2.4} className="text-[color:var(--m-st-confirmed)]" aria-hidden />}
|
||||
label={t('admin.trekApi.included')}
|
||||
items={fields}
|
||||
note={t('admin.trekApi.includedNote')}
|
||||
/>
|
||||
<ChipGroup
|
||||
icon={<X size={13} strokeWidth={2.4} className="text-m-faint" aria-hidden />}
|
||||
label={t('admin.trekApi.notIncluded')}
|
||||
items={[t('admin.trekApi.notRatings'), t('admin.trekApi.notPhotos')]}
|
||||
note={t('admin.trekApi.notIncludedNote')}
|
||||
dashed
|
||||
/>
|
||||
<ChipGroup
|
||||
icon={<Library size={13} strokeWidth={2.2} className="text-m-faint" aria-hidden />}
|
||||
label={t('admin.trekApi.sourcesLabel')}
|
||||
items={SOURCES}
|
||||
note={t('admin.trekApi.sourcesNote')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A folded group of settings that belong to the block above them.
|
||||
*
|
||||
* Closed to begin with, and on purpose: the four Google switches are set once
|
||||
* when a key is pasted and never touched again, and open they were longer than
|
||||
* everything else on the card put together.
|
||||
*/
|
||||
export function MBlockDisclosure({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
return (
|
||||
<div className="-mx-[14px] -mb-[13px] mt-1 border-t border-[color:var(--m-rowbr)]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(v => !v)}
|
||||
aria-expanded={open}
|
||||
className="flex w-full items-center gap-2 px-[14px] py-[10px] text-left"
|
||||
>
|
||||
<ChevronRight
|
||||
size={15}
|
||||
strokeWidth={2.2}
|
||||
className={`flex-none text-m-faint transition-transform ${open ? 'rotate-90' : ''}`}
|
||||
aria-hidden
|
||||
/>
|
||||
<span className="text-[0.8125rem] font-semibold text-m-muted">{label}</span>
|
||||
</button>
|
||||
{open && <div className="px-[14px] pb-[10px]">{children}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* One paid provider, in the same shape as the TREK block above it, so the card
|
||||
* reads as a list of comparable options rather than one highlighted thing with
|
||||
* loose fields underneath.
|
||||
*/
|
||||
export function MProviderBlock({
|
||||
title,
|
||||
badge,
|
||||
tone = 'muted',
|
||||
children,
|
||||
}: {
|
||||
title: string
|
||||
badge?: string
|
||||
/** `caution` marks a provider that costs something other than money. Never `danger`: using a key is a legitimate choice. */
|
||||
tone?: 'muted' | 'caution'
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
// The ribbon rides on the top border, so the block needs room above it or the
|
||||
// capitals touch whatever ends the block before.
|
||||
<div className={`relative ${badge ? 'mt-[18px]' : 'mt-3'}`}>
|
||||
{badge && (
|
||||
<span
|
||||
className={`pointer-events-none absolute -top-2 left-3 z-10 rounded-md border px-2 py-[1px] font-geist text-[0.5625rem] font-bold tracking-[.12em] uppercase ${
|
||||
tone === 'caution'
|
||||
? 'border-[color:var(--m-st-pending)] bg-[color:color-mix(in_srgb,var(--m-st-pending)_16%,var(--m-sheetop))] text-[color:var(--m-st-pending)]'
|
||||
: 'border-[color:var(--m-rowbr)] bg-[color:var(--m-sheetop)] text-m-faint'
|
||||
}`}
|
||||
>
|
||||
{badge}
|
||||
</span>
|
||||
)}
|
||||
<div className="overflow-hidden rounded-[16px] border border-[color:var(--m-rowbr)] bg-[color:var(--m-ic)]">
|
||||
<div className="px-[14px] pt-[18px] pb-[13px]">
|
||||
<p className="text-[0.8125rem] font-semibold text-m-ink">{title}</p>
|
||||
<div className="mt-[10px] space-y-3">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -300,7 +300,7 @@ describe('AdminSettingsTab', () => {
|
||||
// failing, which is quiet enough to be mistaken for the provider working.
|
||||
renderTab({ placesProvider: 'amap', hasAmapKey: false });
|
||||
expect(
|
||||
within(card('API Keys')).getByText(/falls back to OpenStreetMap/i),
|
||||
within(card('API Keys')).getByText(/TREK index and OpenStreetMap alone/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -274,7 +274,8 @@ describe('MAdminSettingsSection', () => {
|
||||
expect(screen.queryByLabelText('Google Maps API Key')).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(/Amap/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('Unsplash API Key')).not.toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue('Automatic')).toBeInTheDocument();
|
||||
// The native select became TREK's own picker: a button carrying the choice.
|
||||
expect(screen.getByRole('button', { name: 'Automatic' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('FE-MOB-ASET-016c: the missing-key notice follows app-config, not the fields', () => {
|
||||
@@ -282,11 +283,11 @@ describe('MAdminSettingsSection', () => {
|
||||
// failing. Which of the two is true is app-config's answer: the key fields
|
||||
// are empty on a managed install and on a key set by the environment.
|
||||
const { unmount } = render(<Harness admin={buildAdminHook({ placesProvider: 'amap', hasAmapKey: false })} />);
|
||||
expect(screen.getByText(/falls back to OpenStreetMap/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/TREK index and OpenStreetMap alone/i)).toBeInTheDocument();
|
||||
unmount();
|
||||
|
||||
renderSettings({ managed: true, placesProvider: 'google', mapsKey: '', hasMapsKey: true });
|
||||
expect(screen.queryByText(/falls back to OpenStreetMap/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/TREK index and OpenStreetMap alone/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('FE-MOB-ASET-017: the Google Places toggles persist optimistically', async () => {
|
||||
@@ -307,6 +308,9 @@ describe('MAdminSettingsSection', () => {
|
||||
}),
|
||||
);
|
||||
const admin = renderSettings();
|
||||
// They live under the Google key now, folded away by default: set once when a
|
||||
// key is pasted, never touched again.
|
||||
await user.click(screen.getByRole('button', { name: 'What the key is used for' }));
|
||||
|
||||
await user.click(toggle('Place Photos'));
|
||||
await user.click(toggle('Place Autocomplete'));
|
||||
@@ -331,6 +335,7 @@ describe('MAdminSettingsSection', () => {
|
||||
placesAutocompleteEnabled: true,
|
||||
placesDetailsEnabled: true,
|
||||
});
|
||||
await user.click(screen.getByRole('button', { name: 'What the key is used for' }));
|
||||
|
||||
await user.click(toggle('Place Photos'));
|
||||
await user.click(toggle('Place Autocomplete'));
|
||||
@@ -342,13 +347,14 @@ describe('MAdminSettingsSection', () => {
|
||||
expect(admin.setPlacesDetailsEnabledState).toHaveBeenLastCalledWith(true);
|
||||
});
|
||||
|
||||
it('FE-MOB-ASET-019: the Open-Meteo card lists the three facts', () => {
|
||||
it('FE-MOB-ASET-019: the API-keys card is about keys, and weather needs none', () => {
|
||||
// The Open-Meteo panel left this card, as it did on the desktop tab: it takes
|
||||
// no key and has nothing to configure, so it had no business in a card about
|
||||
// keys, where it had the loudest treatment of anything on it.
|
||||
renderSettings();
|
||||
|
||||
expect(screen.getByText('Weather Data')).toBeInTheDocument();
|
||||
expect(screen.getByText('Free, no API key required')).toBeInTheDocument();
|
||||
expect(screen.getByText('16-day forecast')).toBeInTheDocument();
|
||||
expect(screen.getByText('10,000 requests / day')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Weather Data')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('10,000 requests / day')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('FE-MOB-ASET-020: the OIDC form renders its values and saves a payload without an empty secret', async () => {
|
||||
|
||||
@@ -131,7 +131,9 @@ describe('MJourneyEntrySheet quick capture', () => {
|
||||
// move arrived nameless and the day read as a column of placeholders
|
||||
// (discussion #2299). The story field is what Add details still unlocks.
|
||||
expect(screen.getByPlaceholderText('Give this moment a name...')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Add details' }));
|
||||
// Shortened to "+ Details": the old label wrapped onto two lines in the
|
||||
// sheet's footer beside Cancel and Save (discussion #2299).
|
||||
fireEvent.click(screen.getByRole('button', { name: '+ Details' }));
|
||||
expect(screen.getByPlaceholderText('Give this moment a name...')).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText('Write your story...')).toBeInTheDocument();
|
||||
await waitFor(() => expect(mapsApi.reverse).toHaveBeenCalled());
|
||||
|
||||
@@ -159,12 +159,12 @@ const admin: TranslationStrings = {
|
||||
'للبحث عن الأماكن في البر الرئيسي للصين، حيث لا يمكن الوصول إلى Google وتكون بيانات OpenStreetMap شحيحة. يتطلب مفتاحًا من نوع «Web 服务» (خدمة ويب)، وليس مفتاح JS API. يمكن الحصول عليه من console.amap.com.',
|
||||
'admin.placesProvider.title': 'مزوّد البحث عن الأماكن',
|
||||
'admin.placesProvider.subtitle':
|
||||
'الخدمة التي تتولى البحث عن الأماكن والإكمال التلقائي والتفاصيل. «تلقائي» يستخدم Google عند توفر مفتاح Google، ثم Amap، ثم OpenStreetMap.',
|
||||
'يجيب فهرس TREK الخاص وOpenStreetMap عن كل بحث. هنا يُختار من يُسأل أيضاً عندما لا يجدان شيئاً: «تلقائي» يفضّل Google عند وجود مفتاح، ثم Amap.',
|
||||
'admin.placesProvider.auto': 'تلقائي',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'المزوّد المحدد لا يحتوي على مفتاح واجهة برمجة تطبيقات، لذا يعود البحث عن الأماكن إلى OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'لا يوجد مفتاح API للمزوّد المختار، لذا يجيب عن البحث عن الأماكن فهرس TREK وOpenStreetMap وحدهما.',
|
||||
'admin.placesProvider.saved': 'تم حفظ مزوّد البحث عن الأماكن',
|
||||
'admin.validateKey': 'اختبار',
|
||||
'admin.keyValid': 'متصل',
|
||||
@@ -778,5 +778,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'بدون رحلة',
|
||||
'admin.invite.tripHint': 'تتم إضافة المستخدم الجديد تلقائيًا إلى هذه الرحلة عند تسجيله عبر الرابط.',
|
||||
'admin.invite.boundTo': 'يُضاف إلى {trip}',
|
||||
'admin.placesUsageTitle': 'فيمَ يُستخدم المفتاح',
|
||||
'admin.mapsKeyHintShort': 'يضيف الصور والتقييمات وساعات العمل. عندها يذهب كل بحث إلى Google.',
|
||||
'admin.amapKeyHintShort': 'للبحث عن الأماكن داخل الصين القارية. يحتاج مفتاح خدمة ويب، لا مفتاح JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -136,13 +136,12 @@ const admin: TranslationStrings = {
|
||||
'admin.amapKeyHint':
|
||||
'Para a busca de lugares na China continental, onde o Google é inacessível e o OpenStreetMap tem poucos dados. Exige uma chave do tipo "Web 服务" (serviço web), não uma chave da API JS. Obtenha em console.amap.com.',
|
||||
'admin.placesProvider.title': 'Provedor de busca de lugares',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Qual serviço responde à busca de lugares, ao autocompletar e aos detalhes. Automático usa o Google quando há uma chave do Google configurada, depois o Amap e depois o OpenStreetMap.',
|
||||
'admin.placesProvider.subtitle': 'O índice do próprio TREK e o OpenStreetMap respondem a cada busca. Aqui se escolhe quem mais é consultado quando eles não acham nada: Automático prefere o Google se houver chave, depois Amap.',
|
||||
'admin.placesProvider.auto': 'Automático',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'O provedor selecionado não tem nenhuma chave de API configurada, então a busca de lugares volta para o OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'O provedor selecionado não tem chave de API configurada, então a busca de lugares é respondida apenas pelo índice do TREK e pelo OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Provedor de busca de lugares salvo',
|
||||
'admin.validateKey': 'Testar',
|
||||
'admin.keyValid': 'Conectado',
|
||||
@@ -798,5 +797,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Nenhuma viagem',
|
||||
'admin.invite.tripHint': 'O novo usuário é adicionado automaticamente a esta viagem ao se registrar pelo link.',
|
||||
'admin.invite.boundTo': 'adiciona a {trip}',
|
||||
'admin.placesUsageTitle': 'Para que a chave é usada',
|
||||
'admin.mapsKeyHintShort': 'Adiciona fotos, avaliações e horários. Cada consulta passa então pelo Google.',
|
||||
'admin.amapKeyHintShort': 'Para busca de lugares na China continental. Precisa de uma chave de serviço web, não de API JS.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -444,13 +444,13 @@ const admin: TranslationStrings = {
|
||||
"Per a la cerca de llocs a la Xina continental, on Google no és accessible i la cobertura d'OpenStreetMap és escassa. Cal una clau «Web 服务» (servei web), no una clau de l'API JS. Se n'obté una a console.amap.com.",
|
||||
'admin.placesProvider.title': 'Proveïdor de la cerca de llocs',
|
||||
'admin.placesProvider.subtitle':
|
||||
"Quin servei respon la cerca de llocs, l'autocompleció i els detalls. Automàtic fa servir Google quan hi ha una clau de Google configurada, després Amap i després OpenStreetMap.",
|
||||
"L'índex propi de TREK i OpenStreetMap responen cada cerca. Aquí es tria a qui més es consulta quan no troben res: Automàtic prefereix Google si hi ha clau, després Amap.",
|
||||
'admin.placesProvider.auto': 'Automàtic',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey':
|
||||
'El proveïdor seleccionat no té cap clau API configurada, així que la cerca de llocs recorre a OpenStreetMap.',
|
||||
"El proveïdor seleccionat no té cap clau d'API configurada, així que la cerca de llocs la responen només l'índex de TREK i OpenStreetMap.",
|
||||
'admin.placesProvider.saved': 'Proveïdor de la cerca de llocs desat',
|
||||
'admin.tabs.plugins': 'Connectors',
|
||||
'admin.tabs.storage': 'Emmagatzematge',
|
||||
@@ -720,5 +720,8 @@ const admin: TranslationStrings = {
|
||||
'admin.plugins.metaRequires': 'Requereix',
|
||||
'admin.plugins.metaReviewed': 'Revisat el',
|
||||
'admin.plugins.downloads': 'Baixades',
|
||||
'admin.placesUsageTitle': "Per a què s'usa la clau",
|
||||
'admin.mapsKeyHintShort': 'Afegeix fotos, valoracions i horaris. Cada consulta passa llavors per Google.',
|
||||
'admin.amapKeyHintShort': 'Per a la cerca de llocs a la Xina continental. Cal una clau de servei web, no una clau API JS.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -137,12 +137,12 @@ const admin: TranslationStrings = {
|
||||
'Pro hledání míst v kontinentální Číně, kde Google není dostupný a OpenStreetMap má málo dat. Vyžaduje klíč typu „Web 服务" (webová služba), nikoli klíč JS API. Získáte na console.amap.com.',
|
||||
'admin.placesProvider.title': 'Poskytovatel hledání míst',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Která služba obsluhuje hledání míst, našeptávač a podrobnosti. Automaticky použije Google, pokud je nastaven klíč Google, poté Amap a poté OpenStreetMap.',
|
||||
'Vlastní index TREKu a OpenStreetMap odpovídají na každé hledání. Zde se volí, koho se zeptat navíc, když nic nenajdou: Automaticky upřednostní Google, pokud je klíč, pak Amap.',
|
||||
'admin.placesProvider.auto': 'Automaticky',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Pro vybraného poskytovatele není nastaven API klíč, hledání míst proto přejde na OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'Zvolený poskytovatel nemá nastavený API klíč, na hledání míst tak odpovídá jen index TREKu a OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Poskytovatel hledání míst uložen',
|
||||
'admin.validateKey': 'Testovat',
|
||||
'admin.keyValid': 'Připojeno',
|
||||
@@ -790,5 +790,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Žádná cesta',
|
||||
'admin.invite.tripHint': 'Nový uživatel bude po registraci přes odkaz automaticky přidán k této cestě.',
|
||||
'admin.invite.boundTo': 'přidá k {trip}',
|
||||
'admin.placesUsageTitle': 'K čemu se klíč používá',
|
||||
'admin.mapsKeyHintShort': 'Přidá fotky, hodnocení a otevírací dobu. Každý dotaz pak jde na Google.',
|
||||
'admin.amapKeyHintShort': 'Pro vyhledávání míst v pevninské Číně. Potřebuje klíč webové služby, ne klíč JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -138,13 +138,12 @@ const admin: TranslationStrings = {
|
||||
'admin.amapKeyHint':
|
||||
'Für die Ortssuche in Festlandchina, wo Google nicht erreichbar ist und OpenStreetMap kaum Daten hat. Benötigt einen Key vom Typ „Web 服务" (Webdienst), nicht einen JS-API-Key. Erhältlich auf console.amap.com.',
|
||||
'admin.placesProvider.title': 'Anbieter für die Ortssuche',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Welcher Dienst Ortssuche, Vorschläge und Details beantwortet. Automatisch nutzt Google, wenn ein Google-Key hinterlegt ist, dann Amap, dann OpenStreetMap.',
|
||||
'admin.placesProvider.subtitle': 'Der TREK-Index und OpenStreetMap beantworten jede Suche. Hier wird gewählt, wer zusätzlich gefragt wird, wenn die beiden nichts finden: Automatisch bevorzugt Google, wenn ein Schlüssel da ist, danach Amap.',
|
||||
'admin.placesProvider.auto': 'Automatisch',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Für den gewählten Anbieter ist kein API-Key hinterlegt, die Ortssuche fällt daher auf OpenStreetMap zurück.',
|
||||
'admin.placesProvider.missingKey': 'Für den gewählten Anbieter ist kein API-Schlüssel hinterlegt, die Ortssuche beantworten daher nur der TREK-Index und OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Anbieter für die Ortssuche gespeichert',
|
||||
'admin.validateKey': 'Test',
|
||||
'admin.keyValid': 'Verbunden',
|
||||
@@ -805,5 +804,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripHint':
|
||||
'Der neue Nutzer wird automatisch zu diesem Trip hinzugefügt, wenn er sich über den Link registriert.',
|
||||
'admin.invite.boundTo': 'fügt zu {trip} hinzu',
|
||||
'admin.placesUsageTitle': 'Wofür der Schlüssel benutzt wird',
|
||||
'admin.mapsKeyHintShort': 'Ergänzt Fotos, Bewertungen und Öffnungszeiten. Jede Abfrage geht dann an Google.',
|
||||
'admin.amapKeyHintShort': 'Für die Ortssuche in Festlandchina. Braucht einen Web-Service-Schlüssel, keinen JS-API-Schlüssel.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -178,13 +178,12 @@ const admin: TranslationStrings = {
|
||||
'admin.amapKeyHint':
|
||||
'For place search inside mainland China, where Google is unreachable and OpenStreetMap coverage is thin. Needs a "Web 服务" (web service) key, not a JS API key. Get one at console.amap.com.',
|
||||
'admin.placesProvider.title': 'Place search provider',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Which service answers place search, autocomplete and details. Automatic uses Google when a Google key is configured, then Amap, then OpenStreetMap.',
|
||||
'admin.placesProvider.subtitle': "TREK's own index and OpenStreetMap answer every search. This picks who else is asked when they find nothing: Automatic prefers Google where a key exists, then Amap.",
|
||||
'admin.placesProvider.auto': 'Automatic',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'The selected provider has no API key configured, so place search falls back to OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'The selected provider has no API key configured, so place search is answered by the TREK index and OpenStreetMap alone.',
|
||||
'admin.placesProvider.saved': 'Place search provider saved',
|
||||
'admin.validateKey': 'Test',
|
||||
'admin.keyValid': 'Connected',
|
||||
@@ -780,5 +779,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'No trip',
|
||||
'admin.invite.tripHint': 'The new user is automatically added to this trip when they register via the link.',
|
||||
'admin.invite.boundTo': 'adds to {trip}',
|
||||
'admin.placesUsageTitle': 'What the key is used for',
|
||||
'admin.mapsKeyHintShort': 'Adds photos, ratings and opening hours. Every lookup then goes to Google.',
|
||||
'admin.amapKeyHintShort': 'For place search inside mainland China. Needs a web service key, not a JS API key.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -138,13 +138,12 @@ const admin: TranslationStrings = {
|
||||
'admin.amapKeyHint':
|
||||
'Para la búsqueda de lugares en China continental, donde Google no es accesible y OpenStreetMap apenas tiene datos. Requiere una clave de tipo «Web 服务» (servicio web), no una clave de la API de JS. Consíguela en console.amap.com.',
|
||||
'admin.placesProvider.title': 'Proveedor de búsqueda de lugares',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Qué servicio responde a la búsqueda de lugares, el autocompletado y los detalles. Automático usa Google si hay una clave de Google configurada, luego Amap y luego OpenStreetMap.',
|
||||
'admin.placesProvider.subtitle': 'El índice propio de TREK y OpenStreetMap responden a cada búsqueda. Aquí se elige a quién más se consulta cuando no encuentran nada: Automático prefiere Google si hay clave, luego Amap.',
|
||||
'admin.placesProvider.auto': 'Automático',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'El proveedor seleccionado no tiene ninguna clave de API configurada, así que la búsqueda de lugares recurre a OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'El proveedor seleccionado no tiene clave de API configurada, así que la búsqueda de lugares la responden solo el índice de TREK y OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Proveedor de búsqueda de lugares guardado',
|
||||
'admin.validateKey': 'Probar',
|
||||
'admin.keyValid': 'Conectado',
|
||||
@@ -815,5 +814,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripHint':
|
||||
'El nuevo usuario se añade automáticamente a este viaje cuando se registra mediante el enlace.',
|
||||
'admin.invite.boundTo': 'se añade a {trip}',
|
||||
'admin.placesUsageTitle': 'Para qué se usa la clave',
|
||||
'admin.mapsKeyHintShort': 'Añade fotos, valoraciones y horarios. Cada consulta pasa entonces por Google.',
|
||||
'admin.amapKeyHintShort': 'Para la búsqueda de lugares en China continental. Necesita una clave de servicio web, no de API JS.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -137,13 +137,12 @@ const admin: TranslationStrings = {
|
||||
'admin.amapKeyHint':
|
||||
'Pour la recherche de lieux en Chine continentale, où Google est inaccessible et où OpenStreetMap contient très peu de données. Nécessite une clé de type « Web 服务 » (service web), pas une clé d’API JS. À obtenir sur console.amap.com.',
|
||||
'admin.placesProvider.title': 'Fournisseur de recherche de lieux',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Quel service répond à la recherche de lieux, à l’autocomplétion et aux détails. Automatique utilise Google si une clé Google est configurée, puis Amap, puis OpenStreetMap.',
|
||||
'admin.placesProvider.subtitle': "L'index propre de TREK et OpenStreetMap répondent à chaque recherche. Ceci choisit qui est interrogé en plus quand ils ne trouvent rien : Automatique préfère Google si une clé existe, puis Amap.",
|
||||
'admin.placesProvider.auto': 'Automatique',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Aucune clé API n’est configurée pour le fournisseur sélectionné : la recherche de lieux se replie donc sur OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': "Le fournisseur sélectionné n'a pas de clé API configurée : la recherche de lieux est donc assurée par le seul index TREK et OpenStreetMap.",
|
||||
'admin.placesProvider.saved': 'Fournisseur de recherche de lieux enregistré',
|
||||
'admin.validateKey': 'Tester',
|
||||
'admin.keyValid': 'Connecté',
|
||||
@@ -813,5 +812,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripHint':
|
||||
"Le nouvel utilisateur est automatiquement ajouté à ce voyage lorsqu'il s'inscrit via le lien.",
|
||||
'admin.invite.boundTo': 'ajoute à {trip}',
|
||||
'admin.placesUsageTitle': 'À quoi sert la clé',
|
||||
'admin.mapsKeyHintShort': 'Ajoute photos, avis et horaires. Chaque recherche passe alors par Google.',
|
||||
'admin.amapKeyHintShort': 'Pour la recherche de lieux en Chine continentale. Nécessite une clé de service web, pas une clé API JS.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -184,12 +184,12 @@ const admin: TranslationStrings = {
|
||||
'Για αναζήτηση τοποθεσιών στην ηπειρωτική Κίνα, όπου το Google δεν είναι προσβάσιμο και το OpenStreetMap έχει ελάχιστα δεδομένα. Χρειάζεται κλειδί τύπου «Web 服务» (υπηρεσία web), όχι κλειδί JS API. Διαθέσιμο στο console.amap.com.',
|
||||
'admin.placesProvider.title': 'Πάροχος αναζήτησης τοποθεσιών',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Ποια υπηρεσία απαντά στην αναζήτηση τοποθεσιών, στις προτάσεις και στις λεπτομέρειες. Το «Αυτόματα» χρησιμοποιεί το Google όταν υπάρχει κλειδί Google, μετά το Amap και μετά το OpenStreetMap.',
|
||||
'Το δικό του ευρετήριο του TREK και το OpenStreetMap απαντούν σε κάθε αναζήτηση. Εδώ επιλέγεται ποιος άλλος ρωτιέται όταν δεν βρίσκουν τίποτα: το Αυτόματο προτιμά τη Google αν υπάρχει κλειδί, μετά το Amap.',
|
||||
'admin.placesProvider.auto': 'Αυτόματα',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Ο επιλεγμένος πάροχος δεν έχει ρυθμισμένο κλειδί API, οπότε η αναζήτηση τοποθεσιών επιστρέφει στο OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'Ο επιλεγμένος πάροχος δεν έχει ρυθμισμένο κλειδί API, οπότε στην αναζήτηση τόπων απαντούν μόνο το ευρετήριο του TREK και το OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Ο πάροχος αναζήτησης τοποθεσιών αποθηκεύτηκε',
|
||||
'admin.validateKey': 'Δοκιμή',
|
||||
'admin.keyValid': 'Συνδέθηκε',
|
||||
@@ -820,5 +820,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Χωρίς ταξίδι',
|
||||
'admin.invite.tripHint': 'Ο νέος χρήστης προστίθεται αυτόματα σε αυτό το ταξίδι όταν εγγραφεί μέσω του συνδέσμου.',
|
||||
'admin.invite.boundTo': 'προσθήκη στο {trip}',
|
||||
'admin.placesUsageTitle': 'Σε τι χρησιμοποιείται το κλειδί',
|
||||
'admin.mapsKeyHintShort': 'Προσθέτει φωτογραφίες, βαθμολογίες και ώρες λειτουργίας. Κάθε αναζήτηση πηγαίνει τότε στη Google.',
|
||||
'admin.amapKeyHintShort': 'Για αναζήτηση τόπων στην ηπειρωτική Κίνα. Χρειάζεται κλειδί web service, όχι JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -138,12 +138,12 @@ const admin: TranslationStrings = {
|
||||
'Helykereséshez Kína szárazföldi részén, ahol a Google nem érhető el, az OpenStreetMap adatai pedig hiányosak. „Web 服务" (webszolgáltatás) típusú kulcs kell hozzá, nem JS API-kulcs. A console.amap.com oldalon kérhető.',
|
||||
'admin.placesProvider.title': 'Helykeresési szolgáltató',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Melyik szolgáltatás válaszol a helykeresésre, a javaslatokra és a részletekre. Az automatikus a Google-t használja, ha van Google-kulcs, utána az Amapot, végül az OpenStreetMapet.',
|
||||
'A TREK saját indexe és az OpenStreetMap minden keresésre válaszol. Itt választod ki, kit kérdezünk még, ha ők nem találnak semmit: az Automatikus a Google-t részesíti előnyben, ha van kulcs, utána az Amapot.',
|
||||
'admin.placesProvider.auto': 'Automatikus',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'A kiválasztott szolgáltatóhoz nincs API-kulcs beállítva, ezért a helykeresés az OpenStreetMapre esik vissza.',
|
||||
'admin.placesProvider.missingKey': 'A kiválasztott szolgáltatóhoz nincs API-kulcs beállítva, ezért a helykeresésre csak a TREK indexe és az OpenStreetMap válaszol.',
|
||||
'admin.placesProvider.saved': 'Helykeresési szolgáltató elmentve',
|
||||
'admin.validateKey': 'Teszt',
|
||||
'admin.keyValid': 'Csatlakozva',
|
||||
@@ -812,5 +812,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripHint':
|
||||
'Az új felhasználó automatikusan hozzáadódik ehhez az utazáshoz, amikor a linken keresztül regisztrál.',
|
||||
'admin.invite.boundTo': 'hozzáadja a következőhöz: {trip}',
|
||||
'admin.placesUsageTitle': 'Mire használjuk a kulcsot',
|
||||
'admin.mapsKeyHintShort': 'Fényképeket, értékeléseket és nyitvatartást ad hozzá. Minden lekérdezés a Google-höz megy.',
|
||||
'admin.amapKeyHintShort': 'Helykereséshez a szárazföldi Kínában. Webszolgáltatás-kulcs kell, nem JS API-kulcs.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -180,12 +180,12 @@ const admin: TranslationStrings = {
|
||||
'Untuk pencarian tempat di Tiongkok daratan, tempat Google tidak dapat diakses dan data OpenStreetMap sangat sedikit. Perlu kunci jenis "Web 服务" (layanan web), bukan kunci JS API. Dapatkan di console.amap.com.',
|
||||
'admin.placesProvider.title': 'Penyedia pencarian tempat',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Layanan mana yang menangani pencarian tempat, saran otomatis, dan detail. Otomatis memakai Google jika kunci Google tersedia, lalu Amap, lalu OpenStreetMap.',
|
||||
'Indeks milik TREK dan OpenStreetMap menjawab setiap pencarian. Di sini dipilih siapa lagi yang ditanya bila keduanya tidak menemukan apa pun: Otomatis memilih Google bila ada kunci, lalu Amap.',
|
||||
'admin.placesProvider.auto': 'Otomatis',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Penyedia yang dipilih belum memiliki kunci API, sehingga pencarian tempat kembali ke OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'Penyedia yang dipilih belum punya kunci API, jadi pencarian tempat hanya dijawab oleh indeks TREK dan OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Penyedia pencarian tempat disimpan',
|
||||
'admin.validateKey': 'Uji',
|
||||
'admin.keyValid': 'Terhubung',
|
||||
@@ -806,5 +806,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Tanpa perjalanan',
|
||||
'admin.invite.tripHint': 'Pengguna baru otomatis ditambahkan ke perjalanan ini saat mereka mendaftar melalui tautan.',
|
||||
'admin.invite.boundTo': 'menambahkan ke {trip}',
|
||||
'admin.placesUsageTitle': 'Untuk apa kunci ini dipakai',
|
||||
'admin.mapsKeyHintShort': 'Menambahkan foto, penilaian, dan jam buka. Setiap pencarian lalu ke Google.',
|
||||
'admin.amapKeyHintShort': 'Untuk pencarian tempat di Tiongkok daratan. Perlu kunci layanan web, bukan kunci JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -137,13 +137,12 @@ const admin: TranslationStrings = {
|
||||
'admin.amapKeyHint':
|
||||
'Per la ricerca di luoghi nella Cina continentale, dove Google non è raggiungibile e OpenStreetMap ha pochi dati. Serve una chiave di tipo «Web 服务» (servizio web), non una chiave API JS. Si ottiene su console.amap.com.',
|
||||
'admin.placesProvider.title': 'Provider per la ricerca di luoghi',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Quale servizio risponde a ricerca di luoghi, completamento automatico e dettagli. Automatico usa Google se è configurata una chiave Google, poi Amap, poi OpenStreetMap.',
|
||||
'admin.placesProvider.subtitle': "L'indice di TREK e OpenStreetMap rispondono a ogni ricerca. Qui si sceglie chi altro viene interrogato quando non trovano nulla: Automatico preferisce Google se esiste una chiave, poi Amap.",
|
||||
'admin.placesProvider.auto': 'Automatico',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Il provider selezionato non ha alcuna chiave API configurata, quindi la ricerca di luoghi ripiega su OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': "Il provider selezionato non ha una chiave API configurata, quindi alla ricerca di luoghi rispondono solo l'indice TREK e OpenStreetMap.",
|
||||
'admin.placesProvider.saved': 'Provider per la ricerca di luoghi salvato',
|
||||
'admin.validateKey': 'Testa',
|
||||
'admin.keyValid': 'Connessa',
|
||||
@@ -808,5 +807,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripHint':
|
||||
'Il nuovo utente viene aggiunto automaticamente a questo viaggio quando si registra tramite il link.',
|
||||
'admin.invite.boundTo': 'aggiunge a {trip}',
|
||||
'admin.placesUsageTitle': 'A cosa serve la chiave',
|
||||
'admin.mapsKeyHintShort': 'Aggiunge foto, valutazioni e orari. Ogni ricerca passa allora da Google.',
|
||||
'admin.amapKeyHintShort': 'Per la ricerca di luoghi nella Cina continentale. Richiede una chiave web service, non una chiave API JS.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -172,12 +172,12 @@ const admin: TranslationStrings = {
|
||||
'Google に接続できず OpenStreetMap のデータも少ない中国本土での地点検索用です。JS API キーではなく「Web 服务」(Web サービス)タイプのキーが必要です。console.amap.com で取得できます。',
|
||||
'admin.placesProvider.title': '地点検索のプロバイダー',
|
||||
'admin.placesProvider.subtitle':
|
||||
'地点検索・入力候補・詳細をどのサービスが担当するか。自動は、Google キーが設定されていれば Google、次に Amap、次に OpenStreetMap を使います。',
|
||||
'TREK 自身の索引と OpenStreetMap がすべての検索に答えます。ここでは、それらが何も見つけられなかったときに誰に尋ねるかを選びます。自動はキーがあれば Google を、次に Amap を選びます。',
|
||||
'admin.placesProvider.auto': '自動',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap(高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': '選択したプロバイダーに API キーが設定されていないため、地点検索は OpenStreetMap にフォールバックします。',
|
||||
'admin.placesProvider.missingKey': '選択したプロバイダに API キーが設定されていないため、地点検索には TREK の索引と OpenStreetMap だけが答えます。',
|
||||
'admin.placesProvider.saved': '地点検索のプロバイダーを保存しました',
|
||||
'admin.validateKey': 'テスト',
|
||||
'admin.keyValid': '接続済み',
|
||||
@@ -769,5 +769,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': '旅行なし',
|
||||
'admin.invite.tripHint': '新しいユーザーがリンク経由で登録すると、自動的にこの旅行に追加されます。',
|
||||
'admin.invite.boundTo': '{trip}に追加',
|
||||
'admin.placesUsageTitle': 'キーの使いみち',
|
||||
'admin.mapsKeyHintShort': '写真・評価・営業時間が加わります。以降の検索はすべて Google に送られます。',
|
||||
'admin.amapKeyHintShort': '中国本土での地点検索用。JS API キーではなく Web サービスキーが必要です。',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -177,12 +177,12 @@ const admin: TranslationStrings = {
|
||||
'Google에 접속할 수 없고 OpenStreetMap 데이터도 부족한 중국 본토에서 장소를 검색할 때 사용합니다. JS API 키가 아니라 "Web 服务"(웹 서비스) 유형의 키가 필요합니다. console.amap.com에서 발급받을 수 있습니다.',
|
||||
'admin.placesProvider.title': '장소 검색 제공자',
|
||||
'admin.placesProvider.subtitle':
|
||||
'장소 검색, 자동 완성, 상세 정보를 어느 서비스가 처리할지 결정합니다. 자동은 Google 키가 있으면 Google을, 그다음 Amap을, 그다음 OpenStreetMap을 사용합니다.',
|
||||
'TREK 자체 색인과 OpenStreetMap이 모든 검색에 답합니다. 여기서는 둘이 아무것도 찾지 못했을 때 누구에게 더 물을지 고릅니다. 자동은 키가 있으면 Google을, 그다음 Amap을 씁니다.',
|
||||
'admin.placesProvider.auto': '자동',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap(高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': '선택한 제공자에 API 키가 설정되어 있지 않아 장소 검색이 OpenStreetMap으로 대체됩니다.',
|
||||
'admin.placesProvider.missingKey': '선택한 공급자에 API 키가 설정되어 있지 않아 장소 검색은 TREK 색인과 OpenStreetMap만 응답합니다.',
|
||||
'admin.placesProvider.saved': '장소 검색 제공자를 저장했습니다',
|
||||
'admin.validateKey': '테스트',
|
||||
'admin.keyValid': '연결됨',
|
||||
@@ -766,5 +766,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': '여행 없음',
|
||||
'admin.invite.tripHint': '새 사용자가 이 링크를 통해 가입하면 해당 여행에 자동으로 추가됩니다.',
|
||||
'admin.invite.boundTo': '{trip}에 추가',
|
||||
'admin.placesUsageTitle': '키를 사용하는 곳',
|
||||
'admin.mapsKeyHintShort': '사진, 평점, 영업시간이 추가됩니다. 이후 모든 조회는 Google로 갑니다.',
|
||||
'admin.amapKeyHintShort': '중국 본토에서의 장소 검색용. JS API 키가 아니라 웹 서비스 키가 필요합니다.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -138,13 +138,12 @@ const admin: TranslationStrings = {
|
||||
'admin.amapKeyHint':
|
||||
'Voor het zoeken naar plaatsen in het Chinese vasteland, waar Google onbereikbaar is en OpenStreetMap weinig gegevens heeft. Vereist een sleutel van het type ‘Web 服务’ (webservice), geen JS API-sleutel. Verkrijgbaar op console.amap.com.',
|
||||
'admin.placesProvider.title': 'Provider voor plaatszoeken',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Welke dienst het zoeken naar plaatsen, de suggesties en de details verzorgt. Automatisch gebruikt Google als er een Google-sleutel is ingesteld, daarna Amap, daarna OpenStreetMap.',
|
||||
'admin.placesProvider.subtitle': 'De eigen index van TREK en OpenStreetMap beantwoorden elke zoekopdracht. Hier kies je wie er nog meer wordt gevraagd als zij niets vinden: Automatisch geeft de voorkeur aan Google als er een sleutel is, daarna Amap.',
|
||||
'admin.placesProvider.auto': 'Automatisch',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Voor de gekozen provider is geen API-sleutel ingesteld, dus het zoeken naar plaatsen valt terug op OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'Voor de gekozen aanbieder is geen API-sleutel ingesteld, dus plaatszoeken wordt alleen door de TREK-index en OpenStreetMap beantwoord.',
|
||||
'admin.placesProvider.saved': 'Provider voor plaatszoeken opgeslagen',
|
||||
'admin.validateKey': 'Testen',
|
||||
'admin.keyValid': 'Verbonden',
|
||||
@@ -804,5 +803,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripHint':
|
||||
'De nieuwe gebruiker wordt automatisch aan deze reis toegevoegd wanneer hij zich via de link registreert.',
|
||||
'admin.invite.boundTo': 'voegt toe aan {trip}',
|
||||
'admin.placesUsageTitle': 'Waar de sleutel voor wordt gebruikt',
|
||||
'admin.mapsKeyHintShort': 'Voegt foto\'s, beoordelingen en openingstijden toe. Elke opzoeking gaat dan naar Google.',
|
||||
'admin.amapKeyHintShort': 'Voor plaatszoeken in het vasteland van China. Vereist een webservicesleutel, geen JS-API-sleutel.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -130,12 +130,12 @@ const admin: TranslationStrings = {
|
||||
'Do wyszukiwania miejsc w Chinach kontynentalnych, gdzie Google jest nieosiągalny, a OpenStreetMap ma niewiele danych. Wymaga klucza typu „Web 服务" (usługa sieciowa), a nie klucza JS API. Można go uzyskać na console.amap.com.',
|
||||
'admin.placesProvider.title': 'Dostawca wyszukiwania miejsc',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Która usługa obsługuje wyszukiwanie miejsc, podpowiedzi i szczegóły. Automatycznie używa Google, jeśli ustawiono klucz Google, następnie Amap, a potem OpenStreetMap.',
|
||||
'Własny indeks TREK i OpenStreetMap odpowiadają na każde wyszukiwanie. Tutaj wybierasz, kogo zapytać dodatkowo, gdy nic nie znajdą: Automatycznie preferuje Google, jeśli jest klucz, potem Amap.',
|
||||
'admin.placesProvider.auto': 'Automatycznie',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Wybrany dostawca nie ma ustawionego klucza API, więc wyszukiwanie miejsc wróci do OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'Wybrany dostawca nie ma skonfigurowanego klucza API, więc na wyszukiwanie miejsc odpowiadają tylko indeks TREK i OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Zapisano dostawcę wyszukiwania miejsc',
|
||||
'admin.validateKey': 'Testuj',
|
||||
'admin.keyValid': 'Połączono',
|
||||
@@ -812,5 +812,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Brak podróży',
|
||||
'admin.invite.tripHint': 'Nowy użytkownik zostanie automatycznie dodany do tej podróży po rejestracji przez link.',
|
||||
'admin.invite.boundTo': 'dodaje do {trip}',
|
||||
'admin.placesUsageTitle': 'Do czego służy klucz',
|
||||
'admin.mapsKeyHintShort': 'Dodaje zdjęcia, oceny i godziny otwarcia. Każde zapytanie trafia wtedy do Google.',
|
||||
'admin.amapKeyHintShort': 'Do wyszukiwania miejsc w Chinach kontynentalnych. Wymaga klucza usługi webowej, nie klucza JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -139,12 +139,12 @@ const admin: TranslationStrings = {
|
||||
'Для поиска мест в континентальном Китае, где Google недоступен, а данных OpenStreetMap мало. Нужен ключ типа «Web 服务» (веб-сервис), а не ключ JS API. Получить можно на console.amap.com.',
|
||||
'admin.placesProvider.title': 'Провайдер поиска мест',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Какой сервис отвечает за поиск мест, подсказки и детали. «Автоматически» использует Google, если задан ключ Google, затем Amap, затем OpenStreetMap.',
|
||||
'Собственный индекс TREK и OpenStreetMap отвечают на каждый поиск. Здесь выбирается, кого спросить дополнительно, если они ничего не нашли: «Автоматически» предпочитает Google при наличии ключа, затем Amap.',
|
||||
'admin.placesProvider.auto': 'Автоматически',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Для выбранного провайдера не задан API-ключ, поэтому поиск мест переключается на OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'Для выбранного поставщика не задан ключ API, поэтому на поиск мест отвечают только индекс TREK и OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Провайдер поиска мест сохранён',
|
||||
'admin.validateKey': 'Проверить',
|
||||
'admin.keyValid': 'Подключено',
|
||||
@@ -805,5 +805,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Без поездки',
|
||||
'admin.invite.tripHint': 'Новый пользователь автоматически добавляется в эту поездку при регистрации по ссылке.',
|
||||
'admin.invite.boundTo': 'добавляет в {trip}',
|
||||
'admin.placesUsageTitle': 'Для чего используется ключ',
|
||||
'admin.mapsKeyHintShort': 'Добавляет фото, оценки и часы работы. Каждый запрос тогда идёт в Google.',
|
||||
'admin.amapKeyHintShort': 'Для поиска мест в материковом Китае. Нужен ключ веб-сервиса, а не ключ JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -181,13 +181,13 @@ const admin: TranslationStrings = {
|
||||
'För platssökning i Fastlandskina, där Google inte går att nå och OpenStreetMap har tunn täckning. Kräver en "Web 服务"-nyckel (webbtjänst), inte en JS API-nyckel. Skaffa en på console.amap.com.',
|
||||
'admin.placesProvider.title': 'Leverantör för platssökning',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Vilken tjänst som svarar på platssökning, autokomplettering och detaljer. Automatisk använder Google när en Google-nyckel är konfigurerad, därefter Amap och sedan OpenStreetMap.',
|
||||
'TREK:s egen indexering och OpenStreetMap svarar på varje sökning. Här väljs vem som frågas därutöver när de inte hittar något: Automatiskt föredrar Google om en nyckel finns, sedan Amap.',
|
||||
'admin.placesProvider.auto': 'Automatisk',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey':
|
||||
'Den valda leverantören har ingen API-nyckel konfigurerad, så platssökningen faller tillbaka på OpenStreetMap.',
|
||||
'Den valda leverantören saknar API-nyckel, så platssökningen besvaras enbart av TREK:s index och OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Leverantör för platssökning sparad',
|
||||
'admin.validateKey': 'Test',
|
||||
'admin.keyValid': 'Ansluten',
|
||||
@@ -806,5 +806,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripHint':
|
||||
'Den nya användaren läggs automatiskt till i den här resan när de registrerar sig via länken.',
|
||||
'admin.invite.boundTo': 'läggs till i {trip}',
|
||||
'admin.placesUsageTitle': 'Vad nyckeln används till',
|
||||
'admin.mapsKeyHintShort': 'Lägger till foton, betyg och öppettider. Varje sökning går då till Google.',
|
||||
'admin.amapKeyHintShort': 'För platssökning i Fastlandskina. Kräver en webbtjänstnyckel, inte en JS API-nyckel.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -181,12 +181,12 @@ const admin: TranslationStrings = {
|
||||
'Google’ın erişilemediği ve OpenStreetMap verisinin çok az olduğu Çin anakarasında yer arama için. «Web 服务» (web servisi) türünde bir anahtar gerekir, JS API anahtarı değil. console.amap.com adresinden alınır.',
|
||||
'admin.placesProvider.title': 'Yer arama sağlayıcısı',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Yer aramasını, otomatik tamamlamayı ve ayrıntıları hangi servisin yanıtlayacağı. Otomatik, Google anahtarı varsa Google’ı, sonra Amap’i, sonra OpenStreetMap’i kullanır.',
|
||||
"TREK'in kendi dizini ve OpenStreetMap her aramayı yanıtlar. Burada, onlar bir şey bulamadığında başka kime sorulacağı seçilir: Otomatik, anahtar varsa Google'ı, sonra Amap'i tercih eder.",
|
||||
'admin.placesProvider.auto': 'Otomatik',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Seçilen sağlayıcı için API anahtarı ayarlanmamış, bu yüzden yer arama OpenStreetMap’e geri döner.',
|
||||
'admin.placesProvider.missingKey': 'Seçilen sağlayıcı için API anahtarı tanımlı değil; yer aramasını yalnızca TREK dizini ve OpenStreetMap yanıtlar.',
|
||||
'admin.placesProvider.saved': 'Yer arama sağlayıcısı kaydedildi',
|
||||
'admin.validateKey': 'Test et',
|
||||
'admin.keyValid': 'Bağlı',
|
||||
@@ -807,5 +807,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Seyahat yok',
|
||||
'admin.invite.tripHint': 'Yeni kullanıcı bağlantı üzerinden kaydolduğunda otomatik olarak bu seyahate eklenir.',
|
||||
'admin.invite.boundTo': '{trip} seyahatine ekler',
|
||||
'admin.placesUsageTitle': 'Anahtar ne için kullanılır',
|
||||
'admin.mapsKeyHintShort': "Fotoğraf, puan ve çalışma saatleri ekler. Her sorgu bundan sonra Google'a gider.",
|
||||
'admin.amapKeyHintShort': 'Çin anakarasında yer araması için. JS API anahtarı değil, web servis anahtarı gerekir.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -139,12 +139,12 @@ const admin: TranslationStrings = {
|
||||
'Для пошуку місць у континентальному Китаї, де Google недоступний, а даних OpenStreetMap обмаль. Потрібен ключ типу «Web 服务» (вебсервіс), а не ключ JS API. Отримати можна на console.amap.com.',
|
||||
'admin.placesProvider.title': 'Постачальник пошуку місць',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Яка служба відповідає за пошук місць, підказки та деталі. «Автоматично» використовує Google, якщо задано ключ Google, потім Amap, потім OpenStreetMap.',
|
||||
'Власний індекс TREK і OpenStreetMap відповідають на кожен пошук. Тут обирається, кого запитати додатково, якщо вони нічого не знайшли: «Автоматично» надає перевагу Google за наявності ключа, потім Amap.',
|
||||
'admin.placesProvider.auto': 'Автоматично',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': 'Для вибраного постачальника не задано API-ключ, тому пошук місць повертається до OpenStreetMap.',
|
||||
'admin.placesProvider.missingKey': 'Для обраного постачальника не задано ключ API, тому на пошук місць відповідають лише індекс TREK і OpenStreetMap.',
|
||||
'admin.placesProvider.saved': 'Постачальника пошуку місць збережено',
|
||||
'admin.validateKey': 'Перевірити',
|
||||
'admin.keyValid': 'Підключено',
|
||||
@@ -802,5 +802,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Без подорожі',
|
||||
'admin.invite.tripHint': 'Новий користувач автоматично додається до цієї подорожі, коли реєструється за посиланням.',
|
||||
'admin.invite.boundTo': 'додає до {trip}',
|
||||
'admin.placesUsageTitle': 'Для чого використовується ключ',
|
||||
'admin.mapsKeyHintShort': 'Додає фото, оцінки та години роботи. Кожен запит тоді йде до Google.',
|
||||
'admin.amapKeyHintShort': 'Для пошуку місць у материковому Китаї. Потрібен ключ вебсервісу, а не ключ JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -180,13 +180,13 @@ const admin: TranslationStrings = {
|
||||
'Dùng cho tìm kiếm địa điểm tại Trung Quốc đại lục, nơi không truy cập được Google và dữ liệu OpenStreetMap còn thưa. Cần khóa loại "Web 服务" (dịch vụ web), không phải khóa JS API. Lấy khóa tại console.amap.com.',
|
||||
'admin.placesProvider.title': 'Nhà cung cấp tìm kiếm địa điểm',
|
||||
'admin.placesProvider.subtitle':
|
||||
'Dịch vụ nào trả lời tìm kiếm địa điểm, tự động hoàn thành và chi tiết. Tự động dùng Google khi đã cấu hình khóa Google, sau đó là Amap, rồi đến OpenStreetMap.',
|
||||
'Chỉ mục riêng của TREK và OpenStreetMap trả lời mọi tìm kiếm. Ở đây chọn ai được hỏi thêm khi chúng không tìm thấy gì: Tự động ưu tiên Google nếu có khóa, rồi đến Amap.',
|
||||
'admin.placesProvider.auto': 'Tự động',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': 'Amap (高德地图)',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey':
|
||||
'Nhà cung cấp đã chọn chưa có khóa API, nên tìm kiếm địa điểm sẽ dùng OpenStreetMap thay thế.',
|
||||
'Nhà cung cấp đã chọn chưa có khóa API, nên tìm kiếm địa điểm chỉ do chỉ mục TREK và OpenStreetMap trả lời.',
|
||||
'admin.placesProvider.saved': 'Đã lưu nhà cung cấp tìm kiếm địa điểm',
|
||||
'admin.validateKey': 'Bài kiểm tra',
|
||||
'admin.keyValid': 'Đã kết nối',
|
||||
@@ -746,5 +746,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': 'Không có chuyến đi',
|
||||
'admin.invite.tripHint': 'Người dùng mới sẽ tự động được thêm vào chuyến đi này khi họ đăng ký qua liên kết.',
|
||||
'admin.invite.boundTo': 'thêm vào {trip}',
|
||||
'admin.placesUsageTitle': 'Khóa được dùng cho việc gì',
|
||||
'admin.mapsKeyHintShort': 'Thêm ảnh, đánh giá và giờ mở cửa. Mọi tra cứu khi đó sẽ đi qua Google.',
|
||||
'admin.amapKeyHintShort': 'Cho tìm kiếm địa điểm tại Trung Quốc đại lục. Cần khóa dịch vụ web, không phải khóa JS API.',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -176,12 +176,12 @@ const admin: TranslationStrings = {
|
||||
'用於中國大陸境內的地點搜尋(Google 無法存取,OpenStreetMap 內容也很少)。需要「Web 服務」類型的 Key,而非 JS API Key。在 console.amap.com 申請。',
|
||||
'admin.placesProvider.title': '地點搜尋來源',
|
||||
'admin.placesProvider.subtitle':
|
||||
'決定由哪個服務處理地點搜尋、輸入提示和詳情。自動:配了 Google Key 就用 Google,否則用高德,都沒有則用 OpenStreetMap。',
|
||||
'TREK 自有索引和 OpenStreetMap 會回應每一次搜尋。這裡選擇的是它們一無所獲時還問誰:自動在有金鑰時優先 Google,其次 Amap。',
|
||||
'admin.placesProvider.auto': '自動',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': '高德地圖',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': '所選來源尚未設定 API Key,地點搜尋會回退到 OpenStreetMap。',
|
||||
'admin.placesProvider.missingKey': '所選服務商未設定 API 金鑰,因此地點搜尋僅由 TREK 索引和 OpenStreetMap 回應。',
|
||||
'admin.placesProvider.saved': '地點搜尋來源已儲存',
|
||||
'admin.validateKey': '測試',
|
||||
'admin.keyValid': '已連線',
|
||||
@@ -744,5 +744,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': '不指定行程',
|
||||
'admin.invite.tripHint': '新使用者透過連結註冊時,會自動加入此行程。',
|
||||
'admin.invite.boundTo': '加入 {trip}',
|
||||
'admin.placesUsageTitle': '金鑰用於什麼',
|
||||
'admin.mapsKeyHintShort': '增加照片、評分和營業時間。之後每次查詢都會送往 Google。',
|
||||
'admin.amapKeyHintShort': '用於中國大陸的地點搜尋。需要 Web 服務金鑰,而非 JS API 金鑰。',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
@@ -137,12 +137,12 @@ const admin: TranslationStrings = {
|
||||
'用于中国大陆境内的地点搜索(Google 无法访问,OpenStreetMap 内容也很少)。需要「Web 服务」类型的 Key,不是 JS API Key。在 console.amap.com 申请。',
|
||||
'admin.placesProvider.title': '地点搜索源',
|
||||
'admin.placesProvider.subtitle':
|
||||
'决定由哪个服务处理地点搜索、输入提示和详情。自动:配了 Google Key 就用 Google,否则用高德,都没有则用 OpenStreetMap。',
|
||||
'TREK 自有索引和 OpenStreetMap 会回应每一次搜索。这里选择的是它们一无所获时还问谁:自动在有密钥时优先 Google,其次 Amap。',
|
||||
'admin.placesProvider.auto': '自动',
|
||||
'admin.placesProvider.google': 'Google Places',
|
||||
'admin.placesProvider.amap': '高德地图',
|
||||
'admin.placesProvider.openstreetmap': 'OpenStreetMap',
|
||||
'admin.placesProvider.missingKey': '所选源还没有配置 API Key,地点搜索会回退到 OpenStreetMap。',
|
||||
'admin.placesProvider.missingKey': '所选服务商未配置 API 密钥,因此地点搜索仅由 TREK 索引和 OpenStreetMap 回应。',
|
||||
'admin.placesProvider.saved': '地点搜索源已保存',
|
||||
'admin.validateKey': '测试',
|
||||
'admin.keyValid': '已连接',
|
||||
@@ -742,5 +742,8 @@ const admin: TranslationStrings = {
|
||||
'admin.invite.tripNone': '不选择行程',
|
||||
'admin.invite.tripHint': '新用户通过链接注册时会自动加入此行程。',
|
||||
'admin.invite.boundTo': '加入 {trip}',
|
||||
'admin.placesUsageTitle': '密钥用于什么',
|
||||
'admin.mapsKeyHintShort': '增加照片、评分和营业时间。之后每次查询都会发往 Google。',
|
||||
'admin.amapKeyHintShort': '用于中国大陆的地点搜索。需要 Web 服务密钥,而非 JS API 密钥。',
|
||||
};
|
||||
export default admin;
|
||||
|
||||
Reference in New Issue
Block a user