From 4f3368502adc92824b1f67c4877310582a3fe42e Mon Sep 17 00:00:00 2001 From: Maurice Date: Sun, 19 Apr 2026 21:35:31 +0200 Subject: [PATCH 1/2] feat(ui): introduce shared PageSidebar for Settings and Admin Replaces the inline tab bar on SettingsPage and AdminPage with a responsive sidebar layout (left nav on desktop, hamburger drawer on mobile). Each tab gets a lucide-react icon for quick scanning. Both pages drop max-w-6xl so the panel fills the viewport. --- client/src/components/Layout/PageSidebar.tsx | 210 +++++++++++++++++++ client/src/pages/AdminPage.tsx | 57 +++-- client/src/pages/SettingsPage.tsx | 68 +++--- 3 files changed, 267 insertions(+), 68 deletions(-) create mode 100644 client/src/components/Layout/PageSidebar.tsx diff --git a/client/src/components/Layout/PageSidebar.tsx b/client/src/components/Layout/PageSidebar.tsx new file mode 100644 index 00000000..9c293b9f --- /dev/null +++ b/client/src/components/Layout/PageSidebar.tsx @@ -0,0 +1,210 @@ +import React, { useState, useEffect, useRef } from 'react' +import { Menu, X, type LucideIcon } from 'lucide-react' + +export interface PageSidebarTab { + id: string + label: string + icon: LucideIcon +} + +interface PageSidebarProps { + /** Uppercase label shown above the tab list, e.g. "SETTINGS". */ + sidebarLabel: string + tabs: PageSidebarTab[] + activeTab: string + onTabChange: (id: string) => void + children: React.ReactNode + /** Small text at the very bottom of the sidebar (e.g. "v3.0 · self-hosted"). */ + footer?: React.ReactNode +} + +/** + * Left-sidebar + right-panel layout used by the Settings and Admin pages. + * + * Desktop (>=1024px): sidebar is always visible at 260px; panel fills rest. + * Mobile: sidebar collapses behind a hamburger at the top of the panel; tap + * the hamburger to slide the sidebar in as an overlay, tap a tab to close. + */ +export default function PageSidebar({ + sidebarLabel, + tabs, + activeTab, + onTabChange, + children, + footer, +}: PageSidebarProps): React.ReactElement { + const [mobileOpen, setMobileOpen] = useState(false) + const activeLabel = tabs.find(t => t.id === activeTab)?.label ?? '' + + // Close the mobile drawer on Escape or on outside click. + const drawerRef = useRef(null) + useEffect(() => { + if (!mobileOpen) return + const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setMobileOpen(false) } + window.addEventListener('keydown', onKey) + return () => window.removeEventListener('keydown', onKey) + }, [mobileOpen]) + + return ( +
+ {/* Mobile top bar with hamburger */} +
+ +
+ {activeLabel} +
+
+
+ + {/* Desktop sidebar (always visible on lg) */} + + + {/* Mobile drawer */} + {mobileOpen && ( + <> +
setMobileOpen(false)} + /> + + + )} + + {/* Panel */} +
+ {children} +
+
+ ) +} + +function SidebarInner({ + sidebarLabel, + tabs, + activeTab, + onTabChange, + footer, +}: { + sidebarLabel: string | null + tabs: PageSidebarTab[] + activeTab: string + onTabChange: (id: string) => void + footer?: React.ReactNode +}): React.ReactElement { + return ( + <> + {sidebarLabel && ( +
+ {sidebarLabel} +
+ )} + + {footer && ( +
+ {footer} +
+ )} + + ) +} diff --git a/client/src/pages/AdminPage.tsx b/client/src/pages/AdminPage.tsx index 7d099111..e787511b 100644 --- a/client/src/pages/AdminPage.tsx +++ b/client/src/pages/AdminPage.tsx @@ -20,8 +20,9 @@ import PackingTemplateManager from '../components/Admin/PackingTemplateManager' import AuditLogPanel from '../components/Admin/AuditLogPanel' import AdminMcpTokensPanel from '../components/Admin/AdminMcpTokensPanel' import PermissionsPanel from '../components/Admin/PermissionsPanel' -import { Users, Map, Briefcase, Shield, Trash2, Edit2, FileText, Eye, EyeOff, Save, CheckCircle, XCircle, Loader2, UserPlus, ArrowUpCircle, ExternalLink, Download, Sun, Link2, Copy, Plus, RefreshCw, AlertTriangle } from 'lucide-react' +import { Users, Map, Briefcase, Shield, Trash2, Edit2, FileText, Eye, EyeOff, Save, CheckCircle, XCircle, Loader2, UserPlus, ArrowUpCircle, ExternalLink, Download, Sun, Link2, Copy, Plus, RefreshCw, AlertTriangle, SlidersHorizontal, UserCog, Puzzle, Settings as SettingsIcon, Bell, Database, ScrollText, KeyRound, GitBranch, Bug } from 'lucide-react' import CustomSelect from '../components/shared/CustomSelect' +import PageSidebar, { type PageSidebarTab } from '../components/Layout/PageSidebar' interface AdminUser { id: number @@ -183,18 +184,18 @@ export default function AdminPage(): React.ReactElement { const hour12 = useSettingsStore(s => s.settings.time_format) === '12h' const mcpEnabled = useAddonStore(s => s.isEnabled('mcp')) const devMode = useAuthStore(s => s.devMode) - const TABS = [ - { id: 'users', label: t('admin.tabs.users') }, - { id: 'config', label: t('admin.tabs.config') }, - { id: 'defaults', label: t('admin.tabs.defaults') }, - { id: 'addons', label: t('admin.tabs.addons') }, - { id: 'settings', label: t('admin.tabs.settings') }, - { id: 'notifications', label: t('admin.tabs.notifications') }, - { id: 'backup', label: t('admin.tabs.backup') }, - { id: 'audit', label: t('admin.tabs.audit') }, - ...(mcpEnabled ? [{ id: 'mcp-tokens', label: t('admin.tabs.mcpTokens') }] : []), - { id: 'github', label: t('admin.tabs.github') }, - ...(devMode ? [{ id: 'dev-notifications', label: 'Dev: Notifications' }] : []), + const TABS: PageSidebarTab[] = [ + { id: 'users', label: t('admin.tabs.users'), icon: Users }, + { id: 'config', label: t('admin.tabs.config'), icon: SlidersHorizontal }, + { id: 'defaults', label: t('admin.tabs.defaults'), icon: UserCog }, + { id: 'addons', label: t('admin.tabs.addons'), icon: Puzzle }, + { id: 'settings', label: t('admin.tabs.settings'), icon: SettingsIcon }, + { id: 'notifications', label: t('admin.tabs.notifications'), icon: Bell }, + { id: 'backup', label: t('admin.tabs.backup'), icon: Database }, + { id: 'audit', label: t('admin.tabs.audit'), icon: ScrollText }, + ...(mcpEnabled ? [{ id: 'mcp-tokens', label: t('admin.tabs.mcpTokens'), icon: KeyRound }] : []), + { id: 'github', label: t('admin.tabs.github'), icon: GitBranch }, + ...(devMode ? [{ id: 'dev-notifications', label: 'Dev: Notifications', icon: Bug }] : []), ] const [activeTab, setActiveTab] = useState('users') @@ -500,7 +501,7 @@ export default function AdminPage(): React.ReactElement {
-
+
{/* Header */}
@@ -586,24 +587,15 @@ export default function AdminPage(): React.ReactElement {
)} - {/* Tabs */} -
- {TABS.map(tab => ( - - ))} -
- - {/* Tab content */} + {/* Sidebar layout — nav on the left, active panel on the right */} + + {/* Tab content */} {activeTab === 'users' && (
@@ -1618,6 +1610,7 @@ export default function AdminPage(): React.ReactElement { {activeTab === 'defaults' && } {activeTab === 'dev-notifications' && } +
diff --git a/client/src/pages/SettingsPage.tsx b/client/src/pages/SettingsPage.tsx index fedcdb31..c3ead1c3 100644 --- a/client/src/pages/SettingsPage.tsx +++ b/client/src/pages/SettingsPage.tsx @@ -1,10 +1,11 @@ import React, { useState, useEffect } from 'react' import { useSearchParams } from 'react-router-dom' -import { Settings } from 'lucide-react' +import { Settings, Palette, Map, Bell, Plug, CloudOff, User, Info } from 'lucide-react' import { useTranslation } from '../i18n' import { authApi } from '../api/client' import { useAddonStore } from '../store/addonStore' import Navbar from '../components/Layout/Navbar' +import PageSidebar, { type PageSidebarTab } from '../components/Layout/PageSidebar' import DisplaySettingsTab from '../components/Settings/DisplaySettingsTab' import MapSettingsTab from '../components/Settings/MapSettingsTab' import NotificationsTab from '../components/Settings/NotificationsTab' @@ -37,14 +38,18 @@ export default function SettingsPage(): React.ReactElement { } }, [searchParams]) - const TABS = [ - { id: 'display', label: t('settings.tabs.display') }, - { id: 'map', label: t('settings.tabs.map') }, - { id: 'notifications', label: t('settings.tabs.notifications') }, - ...(hasIntegrations ? [{ id: 'integrations', label: t('settings.tabs.integrations') }] : []), - { id: 'offline', label: t('settings.tabs.offline') }, - { id: 'account', label: t('settings.tabs.account') }, - ...(appVersion ? [{ id: 'about', label: t('settings.tabs.about') }] : []), + const tabs: PageSidebarTab[] = [ + { id: 'display', label: t('settings.tabs.display'), icon: Palette }, + { id: 'map', label: t('settings.tabs.map'), icon: Map }, + { id: 'notifications', label: t('settings.tabs.notifications'), icon: Bell }, + ...(hasIntegrations + ? [{ id: 'integrations', label: t('settings.tabs.integrations'), icon: Plug }] + : []), + { id: 'offline', label: t('settings.tabs.offline'), icon: CloudOff }, + { id: 'account', label: t('settings.tabs.account'), icon: User }, + ...(appVersion + ? [{ id: 'about', label: t('settings.tabs.about'), icon: Info }] + : []), ] return ( @@ -52,7 +57,7 @@ export default function SettingsPage(): React.ReactElement {
-
+
{/* Header */}
@@ -64,33 +69,24 @@ export default function SettingsPage(): React.ReactElement {
- {/* Tab bar */} -
- {TABS.map(tab => ( - - ))} -
- - {/* Tab content */} - {activeTab === 'display' && } - {activeTab === 'map' && } - {activeTab === 'notifications' && } - {activeTab === 'integrations' && hasIntegrations && } - {activeTab === 'offline' && } - {activeTab === 'account' && } - {activeTab === 'about' && appVersion && } + {/* Sidebar layout */} + + {activeTab === 'display' && } + {activeTab === 'map' && } + {activeTab === 'notifications' && } + {activeTab === 'integrations' && hasIntegrations && } + {activeTab === 'offline' && } + {activeTab === 'account' && } + {activeTab === 'about' && appVersion && } +
) -} \ No newline at end of file +} From 82bb08e685278b61252d9519f2aaed4df5b595fa Mon Sep 17 00:00:00 2001 From: Maurice Date: Sun, 19 Apr 2026 21:48:26 +0200 Subject: [PATCH 2/2] feat(map-settings): i18n for Mapbox GL, mobile polish Wraps every hardcoded Mapbox/Leaflet string in MapSettingsTab with t() and adds 18 new settings.map* keys across all 15 language files. On mobile the provider-card subtitles are hidden, and the High Quality Mode Experimental badge stacks above the title instead of wrapping awkwardly next to it. --- .../components/Settings/MapSettingsTab.tsx | 41 ++++++++++--------- client/src/i18n/translations/ar.ts | 18 ++++++++ client/src/i18n/translations/br.ts | 18 ++++++++ client/src/i18n/translations/cs.ts | 18 ++++++++ client/src/i18n/translations/de.ts | 18 ++++++++ client/src/i18n/translations/en.ts | 18 ++++++++ client/src/i18n/translations/es.ts | 18 ++++++++ client/src/i18n/translations/fr.ts | 18 ++++++++ client/src/i18n/translations/hu.ts | 18 ++++++++ client/src/i18n/translations/id.ts | 18 ++++++++ client/src/i18n/translations/it.ts | 18 ++++++++ client/src/i18n/translations/nl.ts | 18 ++++++++ client/src/i18n/translations/pl.ts | 18 ++++++++ client/src/i18n/translations/ru.ts | 18 ++++++++ client/src/i18n/translations/zh.ts | 18 ++++++++ client/src/i18n/translations/zhTw.ts | 18 ++++++++ 16 files changed, 291 insertions(+), 20 deletions(-) diff --git a/client/src/components/Settings/MapSettingsTab.tsx b/client/src/components/Settings/MapSettingsTab.tsx index 67fb6608..579973ae 100644 --- a/client/src/components/Settings/MapSettingsTab.tsx +++ b/client/src/components/Settings/MapSettingsTab.tsx @@ -71,6 +71,7 @@ function TagChip({ tag }: { tag: string }) { } function StyleDropdown({ value, onChange }: { value: string; onChange: (v: string) => void }) { + const { t } = useTranslation() const [open, setOpen] = useState(false) const ref = useRef(null) @@ -94,7 +95,7 @@ function StyleDropdown({ value, onChange }: { value: string; onChange: (v: strin > - {selected ? selected.name : 'Select a Mapbox style'} + {selected ? selected.name : t('settings.mapStylePlaceholder')} {selected && ( @@ -213,7 +214,7 @@ export default function MapSettingsTab(): React.ReactElement {
{/* Provider picker — big cards so the choice is obvious */}
- +

- Affects Trip Planner and Journey maps. Atlas always uses Leaflet. + {t('settings.mapProviderHint')}

@@ -281,7 +282,7 @@ export default function MapSettingsTab(): React.ReactElement { {provider === 'mapbox-gl' && (
- +

- Public token (pk.*) from{' '} + {t('settings.mapMapboxTokenHint')}{' '} - mapbox.com → Access tokens + {t('settings.mapMapboxTokenLink')}

- +
@@ -310,7 +311,7 @@ export default function MapSettingsTab(): React.ReactElement { className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm font-mono focus:ring-2 focus:ring-slate-400 focus:border-transparent" />

- Preset or your own mapbox://styles/USER/ID URL + {t('settings.mapStyleHint')}

@@ -320,9 +321,9 @@ export default function MapSettingsTab(): React.ReactElement { : 'border-slate-200 opacity-60 dark:border-slate-700' }`}>
-
3D Buildings & Terrain
+
{t('settings.map3dBuildings')}
- Pitch + real 3D building extrusions — works on every style, including satellite. + {t('settings.map3dHint')}
-
- High Quality Mode - - Experimental +
+ {t('settings.mapHighQuality')} + + {t('settings.mapExperimental')}
- Antialiasing + globe projection for sharper edges and a realistic world view.{' '} - May impact performance on lower-end devices. + {t('settings.mapHighQualityHint')}{' '} + {t('settings.mapHighQualityWarning')}
setMapboxQuality(!mapboxQuality)} />
- Tip: right-click and drag to rotate/pitch the map. Middle-click to add a place (right-click is reserved for rotation). + {t('settings.mapTipLabel')} {t('settings.mapTip')}
)} diff --git a/client/src/i18n/translations/ar.ts b/client/src/i18n/translations/ar.ts index 9f4ec7be..2cafd31e 100644 --- a/client/src/i18n/translations/ar.ts +++ b/client/src/i18n/translations/ar.ts @@ -161,6 +161,24 @@ const ar: Record = { 'settings.mapDefaultHint': 'اتركه فارغًا لاستخدام OpenStreetMap افتراضيًا', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'قالب URL لبلاطات الخريطة', + 'settings.mapProvider': 'مزود الخريطة', + 'settings.mapProviderHint': 'يؤثر على خرائط Trip Planner و Journey. يستخدم Atlas دائمًا Leaflet.', + 'settings.mapLeafletSubtitle': '2D كلاسيكي، أي بلاطات نقطية', + 'settings.mapMapboxSubtitle': 'بلاطات متجهية ومبانٍ ثلاثية الأبعاد وتضاريس', + 'settings.mapExperimental': 'تجريبي', + 'settings.mapMapboxToken': 'رمز وصول Mapbox', + 'settings.mapMapboxTokenHint': 'الرمز العام (pk.*) من', + 'settings.mapMapboxTokenLink': 'mapbox.com ← رموز الوصول', + 'settings.mapStyle': 'نمط الخريطة', + 'settings.mapStylePlaceholder': 'اختر نمط Mapbox', + 'settings.mapStyleHint': 'إعداد مسبق أو عنوان URL mapbox://styles/USER/ID خاص بك', + 'settings.map3dBuildings': 'مبانٍ ثلاثية الأبعاد وتضاريس', + 'settings.map3dHint': 'إمالة + مبانٍ ثلاثية الأبعاد حقيقية — يعمل مع كل نمط بما في ذلك الأقمار الصناعية.', + 'settings.mapHighQuality': 'وضع الجودة العالية', + 'settings.mapHighQualityHint': 'تحسين الحواف + إسقاط كروي لحواف أكثر حدة وعرض واقعي للعالم.', + 'settings.mapHighQualityWarning': 'قد يؤثر على الأداء في الأجهزة الأقل قدرة.', + 'settings.mapTipLabel': 'نصيحة:', + 'settings.mapTip': 'انقر بزر الماوس الأيمن واسحب لتدوير/إمالة الخريطة. النقر الأوسط لإضافة مكان (النقر الأيمن مخصص للتدوير).', 'settings.latitude': 'خط العرض', 'settings.longitude': 'خط الطول', 'settings.saveMap': 'حفظ الخريطة', diff --git a/client/src/i18n/translations/br.ts b/client/src/i18n/translations/br.ts index 401ca860..0375068c 100644 --- a/client/src/i18n/translations/br.ts +++ b/client/src/i18n/translations/br.ts @@ -156,6 +156,24 @@ const br: Record = { 'settings.mapDefaultHint': 'Deixe vazio para OpenStreetMap (padrão)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'URL do modelo de blocos do mapa', + 'settings.mapProvider': 'Provedor de mapa', + 'settings.mapProviderHint': 'Afeta os mapas do Planejador de Viagem e Diário. Atlas sempre usa Leaflet.', + 'settings.mapLeafletSubtitle': 'Clássico 2D, quaisquer blocos raster', + 'settings.mapMapboxSubtitle': 'Blocos vetoriais, prédios 3D & terreno', + 'settings.mapExperimental': 'Experimental', + 'settings.mapMapboxToken': 'Token de acesso Mapbox', + 'settings.mapMapboxTokenHint': 'Token público (pk.*) de', + 'settings.mapMapboxTokenLink': 'mapbox.com → Tokens de acesso', + 'settings.mapStyle': 'Estilo do mapa', + 'settings.mapStylePlaceholder': 'Selecionar um estilo Mapbox', + 'settings.mapStyleHint': 'Preset ou sua própria URL mapbox://styles/USER/ID', + 'settings.map3dBuildings': 'Prédios 3D & terreno', + 'settings.map3dHint': 'Inclinação + extrusões 3D reais de prédios — funciona em todo estilo, incluindo satélite.', + 'settings.mapHighQuality': 'Modo alta qualidade', + 'settings.mapHighQualityHint': 'Antialiasing + projeção global para bordas mais nítidas e uma visão realista do mundo.', + 'settings.mapHighQualityWarning': 'Pode afetar o desempenho em dispositivos menos potentes.', + 'settings.mapTipLabel': 'Dica:', + 'settings.mapTip': 'Clique direito e arraste para girar/inclinar o mapa. Clique do meio para adicionar um local (o clique direito é reservado para rotação).', 'settings.latitude': 'Latitude', 'settings.longitude': 'Longitude', 'settings.saveMap': 'Salvar mapa', diff --git a/client/src/i18n/translations/cs.ts b/client/src/i18n/translations/cs.ts index 0b9887c5..2e65e451 100644 --- a/client/src/i18n/translations/cs.ts +++ b/client/src/i18n/translations/cs.ts @@ -157,6 +157,24 @@ const cs: Record = { 'settings.mapDefaultHint': 'Ponechte prázdné pro OpenStreetMap (výchozí)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'URL šablony pro mapové dlaždice', + 'settings.mapProvider': 'Poskytovatel mapy', + 'settings.mapProviderHint': 'Ovlivňuje mapy v Trip Planneru a Journey. Atlas vždy používá Leaflet.', + 'settings.mapLeafletSubtitle': 'Klasické 2D, libovolné rastrové dlaždice', + 'settings.mapMapboxSubtitle': 'Vektorové dlaždice, 3D budovy a terén', + 'settings.mapExperimental': 'Experimentální', + 'settings.mapMapboxToken': 'Mapbox přístupový token', + 'settings.mapMapboxTokenHint': 'Veřejný token (pk.*) z', + 'settings.mapMapboxTokenLink': 'mapbox.com → Přístupové tokeny', + 'settings.mapStyle': 'Styl mapy', + 'settings.mapStylePlaceholder': 'Vyberte styl Mapbox', + 'settings.mapStyleHint': 'Preset nebo vaše vlastní URL mapbox://styles/USER/ID', + 'settings.map3dBuildings': '3D budovy a terén', + 'settings.map3dHint': 'Náklon + skutečné 3D vyvýšení budov — funguje s každým stylem, včetně satelitu.', + 'settings.mapHighQuality': 'Režim vysoké kvality', + 'settings.mapHighQualityHint': 'Antialiasing + zobrazení glóbu pro ostřejší hrany a realistický pohled na svět.', + 'settings.mapHighQualityWarning': 'Může ovlivnit výkon na slabších zařízeních.', + 'settings.mapTipLabel': 'Tip:', + 'settings.mapTip': 'Pravé tlačítko myši a táhněte pro rotaci/náklon mapy. Prostřední tlačítko pro přidání místa (pravé tlačítko je vyhrazeno pro rotaci).', 'settings.latitude': 'Zeměpisná šířka', 'settings.longitude': 'Zeměpisná délka', 'settings.saveMap': 'Uložit nastavení mapy', diff --git a/client/src/i18n/translations/de.ts b/client/src/i18n/translations/de.ts index d2fe26d7..7319c2b4 100644 --- a/client/src/i18n/translations/de.ts +++ b/client/src/i18n/translations/de.ts @@ -159,6 +159,24 @@ const de: Record = { 'settings.mapDefaultHint': 'Leer lassen für OpenStreetMap (Standard)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'URL-Template für die Kartenkacheln', + 'settings.mapProvider': 'Kartenanbieter', + 'settings.mapProviderHint': 'Gilt für Trip Planner und Journey. Atlas nutzt immer Leaflet.', + 'settings.mapLeafletSubtitle': 'Klassisch 2D, beliebige Raster-Kacheln', + 'settings.mapMapboxSubtitle': 'Vektor-Kacheln, 3D-Gebäude & Terrain', + 'settings.mapExperimental': 'Experimentell', + 'settings.mapMapboxToken': 'Mapbox Access Token', + 'settings.mapMapboxTokenHint': 'Öffentliches Token (pk.*) von', + 'settings.mapMapboxTokenLink': 'mapbox.com → Access Tokens', + 'settings.mapStyle': 'Kartenstil', + 'settings.mapStylePlaceholder': 'Mapbox-Stil wählen', + 'settings.mapStyleHint': 'Preset oder eigene mapbox://styles/USER/ID URL', + 'settings.map3dBuildings': '3D-Gebäude & Terrain', + 'settings.map3dHint': 'Neigung + echte 3D-Gebäude-Extrusionen — funktioniert mit jedem Stil, auch Satellit.', + 'settings.mapHighQuality': 'Hochqualitäts-Modus', + 'settings.mapHighQualityHint': 'Antialiasing + Globus-Projektion für schärfere Kanten und eine realistische Weltsicht.', + 'settings.mapHighQualityWarning': 'Kann die Performance auf schwächeren Geräten beeinträchtigen.', + 'settings.mapTipLabel': 'Tipp:', + 'settings.mapTip': 'Rechtsklick und ziehen, um die Karte zu drehen/neigen. Mittelklick, um einen Ort hinzuzufügen (Rechtsklick ist für die Rotation reserviert).', 'settings.latitude': 'Breitengrad', 'settings.longitude': 'Längengrad', 'settings.saveMap': 'Karte speichern', diff --git a/client/src/i18n/translations/en.ts b/client/src/i18n/translations/en.ts index e1311a17..106f8b06 100644 --- a/client/src/i18n/translations/en.ts +++ b/client/src/i18n/translations/en.ts @@ -159,6 +159,24 @@ const en: Record = { 'settings.mapDefaultHint': 'Leave empty for OpenStreetMap (default)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'URL template for map tiles', + 'settings.mapProvider': 'Map Provider', + 'settings.mapProviderHint': 'Affects Trip Planner and Journey maps. Atlas always uses Leaflet.', + 'settings.mapLeafletSubtitle': 'Classic 2D, any raster tiles', + 'settings.mapMapboxSubtitle': 'Vector tiles, 3D buildings & terrain', + 'settings.mapExperimental': 'Experimental', + 'settings.mapMapboxToken': 'Mapbox Access Token', + 'settings.mapMapboxTokenHint': 'Public token (pk.*) from', + 'settings.mapMapboxTokenLink': 'mapbox.com → Access tokens', + 'settings.mapStyle': 'Map Style', + 'settings.mapStylePlaceholder': 'Select a Mapbox style', + 'settings.mapStyleHint': 'Preset or your own mapbox://styles/USER/ID URL', + 'settings.map3dBuildings': '3D Buildings & Terrain', + 'settings.map3dHint': 'Pitch + real 3D building extrusions — works on every style, including satellite.', + 'settings.mapHighQuality': 'High Quality Mode', + 'settings.mapHighQualityHint': 'Antialiasing + globe projection for sharper edges and a realistic world view.', + 'settings.mapHighQualityWarning': 'May impact performance on lower-end devices.', + 'settings.mapTipLabel': 'Tip:', + 'settings.mapTip': 'right-click and drag to rotate/pitch the map. Middle-click to add a place (right-click is reserved for rotation).', 'settings.latitude': 'Latitude', 'settings.longitude': 'Longitude', 'settings.saveMap': 'Save Map', diff --git a/client/src/i18n/translations/es.ts b/client/src/i18n/translations/es.ts index 718de6ef..c615d44c 100644 --- a/client/src/i18n/translations/es.ts +++ b/client/src/i18n/translations/es.ts @@ -157,6 +157,24 @@ const es: Record = { 'settings.mapDefaultHint': 'Déjalo vacío para OpenStreetMap (por defecto)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'Plantilla de URL para los mosaicos del mapa', + 'settings.mapProvider': 'Proveedor de mapa', + 'settings.mapProviderHint': 'Afecta a los mapas de Trip Planner y Journey. Atlas siempre usa Leaflet.', + 'settings.mapLeafletSubtitle': 'Clásico 2D, cualquier mosaico raster', + 'settings.mapMapboxSubtitle': 'Mosaicos vectoriales, edificios 3D y terreno', + 'settings.mapExperimental': 'Experimental', + 'settings.mapMapboxToken': 'Token de acceso de Mapbox', + 'settings.mapMapboxTokenHint': 'Token público (pk.*) de', + 'settings.mapMapboxTokenLink': 'mapbox.com → Tokens de acceso', + 'settings.mapStyle': 'Estilo de mapa', + 'settings.mapStylePlaceholder': 'Seleccionar un estilo de Mapbox', + 'settings.mapStyleHint': 'Preset o tu propia URL mapbox://styles/USER/ID', + 'settings.map3dBuildings': 'Edificios 3D y terreno', + 'settings.map3dHint': 'Inclinación + extrusiones 3D reales de edificios — funciona con todos los estilos, incluyendo satélite.', + 'settings.mapHighQuality': 'Modo de alta calidad', + 'settings.mapHighQualityHint': 'Antialiasing + proyección global para bordes más nítidos y una vista realista del mundo.', + 'settings.mapHighQualityWarning': 'Puede afectar el rendimiento en dispositivos menos potentes.', + 'settings.mapTipLabel': 'Consejo:', + 'settings.mapTip': 'Clic derecho y arrastrar para rotar/inclinar el mapa. Clic central para añadir un lugar (el clic derecho está reservado para la rotación).', 'settings.latitude': 'Latitud', 'settings.longitude': 'Longitud', 'settings.saveMap': 'Guardar mapa', diff --git a/client/src/i18n/translations/fr.ts b/client/src/i18n/translations/fr.ts index ea018c8a..230a7512 100644 --- a/client/src/i18n/translations/fr.ts +++ b/client/src/i18n/translations/fr.ts @@ -156,6 +156,24 @@ const fr: Record = { 'settings.mapDefaultHint': 'Laissez vide pour OpenStreetMap (par défaut)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'Modèle d\'URL pour les tuiles de carte', + 'settings.mapProvider': 'Fournisseur de carte', + 'settings.mapProviderHint': 'Affecte les cartes Trip Planner et Journey. Atlas utilise toujours Leaflet.', + 'settings.mapLeafletSubtitle': 'Classique 2D, toutes tuiles raster', + 'settings.mapMapboxSubtitle': 'Tuiles vectorielles, bâtiments 3D & terrain', + 'settings.mapExperimental': 'Expérimental', + 'settings.mapMapboxToken': 'Jeton d\'accès Mapbox', + 'settings.mapMapboxTokenHint': 'Jeton public (pk.*) depuis', + 'settings.mapMapboxTokenLink': 'mapbox.com → Jetons d\'accès', + 'settings.mapStyle': 'Style de carte', + 'settings.mapStylePlaceholder': 'Sélectionner un style Mapbox', + 'settings.mapStyleHint': 'Preset ou votre propre URL mapbox://styles/USER/ID', + 'settings.map3dBuildings': 'Bâtiments 3D & terrain', + 'settings.map3dHint': 'Inclinaison + extrusions 3D réelles des bâtiments — fonctionne avec tous les styles, y compris satellite.', + 'settings.mapHighQuality': 'Mode haute qualité', + 'settings.mapHighQualityHint': 'Anticrénelage + projection globe pour des bords plus nets et une vue réaliste du monde.', + 'settings.mapHighQualityWarning': 'Peut affecter les performances sur les appareils moins puissants.', + 'settings.mapTipLabel': 'Astuce :', + 'settings.mapTip': 'Clic droit et glisser pour pivoter/incliner la carte. Clic milieu pour ajouter un lieu (le clic droit est réservé à la rotation).', 'settings.latitude': 'Latitude', 'settings.longitude': 'Longitude', 'settings.saveMap': 'Enregistrer la carte', diff --git a/client/src/i18n/translations/hu.ts b/client/src/i18n/translations/hu.ts index 49d600b3..6500783b 100644 --- a/client/src/i18n/translations/hu.ts +++ b/client/src/i18n/translations/hu.ts @@ -156,6 +156,24 @@ const hu: Record = { 'settings.mapDefaultHint': 'Hagyd üresen az OpenStreetMap használatához (alapértelmezett)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'URL sablon a térképcsempékhez', + 'settings.mapProvider': 'Térkép szolgáltató', + 'settings.mapProviderHint': 'A Trip Planner és Journey térképekre érvényes. Az Atlas mindig Leafletet használ.', + 'settings.mapLeafletSubtitle': 'Klasszikus 2D, bármilyen raszter csempe', + 'settings.mapMapboxSubtitle': 'Vektoros csempék, 3D épületek és terep', + 'settings.mapExperimental': 'Kísérleti', + 'settings.mapMapboxToken': 'Mapbox hozzáférési token', + 'settings.mapMapboxTokenHint': 'Publikus token (pk.*) innen:', + 'settings.mapMapboxTokenLink': 'mapbox.com → Hozzáférési tokenek', + 'settings.mapStyle': 'Térkép stílus', + 'settings.mapStylePlaceholder': 'Válassz Mapbox stílust', + 'settings.mapStyleHint': 'Preset vagy saját mapbox://styles/USER/ID URL', + 'settings.map3dBuildings': '3D épületek és terep', + 'settings.map3dHint': 'Dőlés + valódi 3D épület-kiemelés — minden stílussal működik, beleértve a műholdast.', + 'settings.mapHighQuality': 'Magas minőség mód', + 'settings.mapHighQualityHint': 'Antialiasing + földgömb-vetítés az élesebb kontúrokért és egy valósághű világnézethez.', + 'settings.mapHighQualityWarning': 'Gyengébb eszközökön befolyásolhatja a teljesítményt.', + 'settings.mapTipLabel': 'Tipp:', + 'settings.mapTip': 'Jobb klikk és húzás a térkép forgatásához/döntéséhez. Középső kattintás hely hozzáadásához (a jobb klikk a forgatáshoz van fenntartva).', 'settings.latitude': 'Szélességi fok', 'settings.longitude': 'Hosszúsági fok', 'settings.saveMap': 'Térkép mentése', diff --git a/client/src/i18n/translations/id.ts b/client/src/i18n/translations/id.ts index f1ecd8ff..20417ac6 100644 --- a/client/src/i18n/translations/id.ts +++ b/client/src/i18n/translations/id.ts @@ -159,6 +159,24 @@ const id: Record = { 'settings.mapDefaultHint': 'Kosongkan untuk OpenStreetMap (default)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'Template URL untuk tile peta', + 'settings.mapProvider': 'Penyedia peta', + 'settings.mapProviderHint': 'Berlaku untuk peta Trip Planner dan Journey. Atlas selalu menggunakan Leaflet.', + 'settings.mapLeafletSubtitle': 'Klasik 2D, tile raster apa pun', + 'settings.mapMapboxSubtitle': 'Tile vektor, bangunan 3D & medan', + 'settings.mapExperimental': 'Eksperimental', + 'settings.mapMapboxToken': 'Token akses Mapbox', + 'settings.mapMapboxTokenHint': 'Token publik (pk.*) dari', + 'settings.mapMapboxTokenLink': 'mapbox.com → Token akses', + 'settings.mapStyle': 'Gaya peta', + 'settings.mapStylePlaceholder': 'Pilih gaya Mapbox', + 'settings.mapStyleHint': 'Preset atau URL mapbox://styles/USER/ID milikmu', + 'settings.map3dBuildings': 'Bangunan 3D & medan', + 'settings.map3dHint': 'Kemiringan + ekstrusi bangunan 3D nyata — bekerja di semua gaya, termasuk satelit.', + 'settings.mapHighQuality': 'Mode kualitas tinggi', + 'settings.mapHighQualityHint': 'Antialiasing + proyeksi globe untuk tepi yang lebih tajam dan tampilan dunia realistis.', + 'settings.mapHighQualityWarning': 'Dapat memengaruhi performa pada perangkat kelas bawah.', + 'settings.mapTipLabel': 'Tip:', + 'settings.mapTip': 'Klik kanan dan seret untuk memutar/memiringkan peta. Klik tengah untuk menambah tempat (klik kanan untuk rotasi).', 'settings.latitude': 'Lintang', 'settings.longitude': 'Bujur', 'settings.saveMap': 'Simpan Peta', diff --git a/client/src/i18n/translations/it.ts b/client/src/i18n/translations/it.ts index 4e24e6c0..dd3480a8 100644 --- a/client/src/i18n/translations/it.ts +++ b/client/src/i18n/translations/it.ts @@ -156,6 +156,24 @@ const it: Record = { 'settings.mapDefaultHint': 'Lascia vuoto per OpenStreetMap (predefinito)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'Modello URL per i tile della mappa', + 'settings.mapProvider': 'Provider mappa', + 'settings.mapProviderHint': 'Influisce sulle mappe Trip Planner e Journey. Atlas usa sempre Leaflet.', + 'settings.mapLeafletSubtitle': 'Classica 2D, qualsiasi tile raster', + 'settings.mapMapboxSubtitle': 'Tile vettoriali, edifici 3D e terreno', + 'settings.mapExperimental': 'Sperimentale', + 'settings.mapMapboxToken': 'Token di accesso Mapbox', + 'settings.mapMapboxTokenHint': 'Token pubblico (pk.*) da', + 'settings.mapMapboxTokenLink': 'mapbox.com → Token di accesso', + 'settings.mapStyle': 'Stile mappa', + 'settings.mapStylePlaceholder': 'Seleziona uno stile Mapbox', + 'settings.mapStyleHint': 'Preset o il tuo URL mapbox://styles/USER/ID', + 'settings.map3dBuildings': 'Edifici 3D e terreno', + 'settings.map3dHint': 'Inclinazione + estrusioni 3D reali degli edifici — funziona con ogni stile, incluso satellite.', + 'settings.mapHighQuality': 'Modalità alta qualità', + 'settings.mapHighQualityHint': 'Antialiasing + proiezione globo per bordi più nitidi e una vista realistica del mondo.', + 'settings.mapHighQualityWarning': 'Può influire sulle prestazioni su dispositivi meno potenti.', + 'settings.mapTipLabel': 'Suggerimento:', + 'settings.mapTip': 'Click destro e trascina per ruotare/inclinare la mappa. Click centrale per aggiungere un luogo (il click destro è riservato alla rotazione).', 'settings.latitude': 'Latitudine', 'settings.longitude': 'Longitudine', 'settings.saveMap': 'Salva Mappa', diff --git a/client/src/i18n/translations/nl.ts b/client/src/i18n/translations/nl.ts index 4215a15b..8c116b00 100644 --- a/client/src/i18n/translations/nl.ts +++ b/client/src/i18n/translations/nl.ts @@ -156,6 +156,24 @@ const nl: Record = { 'settings.mapDefaultHint': 'Laat leeg voor OpenStreetMap (standaard)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'URL-sjabloon voor kaarttegels', + 'settings.mapProvider': 'Kaartprovider', + 'settings.mapProviderHint': 'Geldt voor Trip Planner en Journey kaarten. Atlas gebruikt altijd Leaflet.', + 'settings.mapLeafletSubtitle': 'Klassiek 2D, elke raster-tile', + 'settings.mapMapboxSubtitle': 'Vector tiles, 3D-gebouwen & terrein', + 'settings.mapExperimental': 'Experimenteel', + 'settings.mapMapboxToken': 'Mapbox Access Token', + 'settings.mapMapboxTokenHint': 'Openbaar token (pk.*) van', + 'settings.mapMapboxTokenLink': 'mapbox.com → Access tokens', + 'settings.mapStyle': 'Kaartstijl', + 'settings.mapStylePlaceholder': 'Kies een Mapbox-stijl', + 'settings.mapStyleHint': 'Preset of eigen mapbox://styles/USER/ID URL', + 'settings.map3dBuildings': '3D-gebouwen & terrein', + 'settings.map3dHint': 'Kanteling + echte 3D-gebouwenextrusies — werkt op elke stijl, inclusief satelliet.', + 'settings.mapHighQuality': 'Hoge kwaliteit modus', + 'settings.mapHighQualityHint': 'Antialiasing + globeprojectie voor scherpere randen en een realistische wereldweergave.', + 'settings.mapHighQualityWarning': 'Kan de prestaties op minder krachtige apparaten beïnvloeden.', + 'settings.mapTipLabel': 'Tip:', + 'settings.mapTip': 'Rechts-klik en sleep om de kaart te roteren/kantelen. Middenklik om een locatie toe te voegen (rechts-klik is voor rotatie).', 'settings.latitude': 'Breedtegraad', 'settings.longitude': 'Lengtegraad', 'settings.saveMap': 'Kaart opslaan', diff --git a/client/src/i18n/translations/pl.ts b/client/src/i18n/translations/pl.ts index f4118357..de065544 100644 --- a/client/src/i18n/translations/pl.ts +++ b/client/src/i18n/translations/pl.ts @@ -139,6 +139,24 @@ const pl: Record = { 'settings.mapDefaultHint': 'Pozostaw puste dla OpenStreetMap (domyślnie)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'Szablon URL dla kafelków mapy', + 'settings.mapProvider': 'Dostawca mapy', + 'settings.mapProviderHint': 'Dotyczy map Trip Planner i Journey. Atlas zawsze używa Leaflet.', + 'settings.mapLeafletSubtitle': 'Klasyczne 2D, dowolne kafelki rastrowe', + 'settings.mapMapboxSubtitle': 'Kafelki wektorowe, budynki 3D i teren', + 'settings.mapExperimental': 'Eksperymentalne', + 'settings.mapMapboxToken': 'Token dostępu Mapbox', + 'settings.mapMapboxTokenHint': 'Token publiczny (pk.*) z', + 'settings.mapMapboxTokenLink': 'mapbox.com → Tokeny dostępu', + 'settings.mapStyle': 'Styl mapy', + 'settings.mapStylePlaceholder': 'Wybierz styl Mapbox', + 'settings.mapStyleHint': 'Preset lub własny URL mapbox://styles/USER/ID', + 'settings.map3dBuildings': 'Budynki 3D i teren', + 'settings.map3dHint': 'Nachylenie + prawdziwe wytłaczanie budynków 3D — działa w każdym stylu, także satelitarnym.', + 'settings.mapHighQuality': 'Tryb wysokiej jakości', + 'settings.mapHighQualityHint': 'Antialiasing + projekcja globusa dla ostrzejszych krawędzi i realistycznego widoku świata.', + 'settings.mapHighQualityWarning': 'Może wpływać na wydajność na słabszych urządzeniach.', + 'settings.mapTipLabel': 'Wskazówka:', + 'settings.mapTip': 'Kliknij prawym przyciskiem i przeciągnij, aby obrócić/pochylić mapę. Środkowy przycisk dodaje miejsce (prawy jest zarezerwowany dla obrotu).', 'settings.latitude': 'Szerokość', 'settings.longitude': 'Długość', 'settings.saveMap': 'Zapisz mapę', diff --git a/client/src/i18n/translations/ru.ts b/client/src/i18n/translations/ru.ts index c87fc593..d5c16fae 100644 --- a/client/src/i18n/translations/ru.ts +++ b/client/src/i18n/translations/ru.ts @@ -156,6 +156,24 @@ const ru: Record = { 'settings.mapDefaultHint': 'Оставьте пустым для OpenStreetMap (по умолчанию)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': 'URL-шаблон для тайлов карты', + 'settings.mapProvider': 'Провайдер карты', + 'settings.mapProviderHint': 'Применяется к Trip Planner и Journey. Atlas всегда использует Leaflet.', + 'settings.mapLeafletSubtitle': 'Классические 2D, любые растровые тайлы', + 'settings.mapMapboxSubtitle': 'Векторные тайлы, 3D-здания и рельеф', + 'settings.mapExperimental': 'Экспериментально', + 'settings.mapMapboxToken': 'Токен доступа Mapbox', + 'settings.mapMapboxTokenHint': 'Публичный токен (pk.*) с', + 'settings.mapMapboxTokenLink': 'mapbox.com → Токены доступа', + 'settings.mapStyle': 'Стиль карты', + 'settings.mapStylePlaceholder': 'Выберите стиль Mapbox', + 'settings.mapStyleHint': 'Preset или собственный URL mapbox://styles/USER/ID', + 'settings.map3dBuildings': '3D-здания и рельеф', + 'settings.map3dHint': 'Наклон + настоящие 3D-здания — работает со всеми стилями, включая спутник.', + 'settings.mapHighQuality': 'Режим высокого качества', + 'settings.mapHighQualityHint': 'Сглаживание + проекция глобуса для более чётких краёв и реалистичного вида мира.', + 'settings.mapHighQualityWarning': 'Может повлиять на производительность на слабых устройствах.', + 'settings.mapTipLabel': 'Совет:', + 'settings.mapTip': 'Зажмите правую кнопку мыши и перетащите, чтобы повернуть/наклонить карту. Клик средней кнопкой — добавить место (правая кнопка зарезервирована для вращения).', 'settings.latitude': 'Широта', 'settings.longitude': 'Долгота', 'settings.saveMap': 'Сохранить карту', diff --git a/client/src/i18n/translations/zh.ts b/client/src/i18n/translations/zh.ts index 45069d6d..7d8b6ba6 100644 --- a/client/src/i18n/translations/zh.ts +++ b/client/src/i18n/translations/zh.ts @@ -156,6 +156,24 @@ const zh: Record = { 'settings.mapDefaultHint': '留空则使用 OpenStreetMap(默认)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': '地图瓦片 URL 模板', + 'settings.mapProvider': '地图提供商', + 'settings.mapProviderHint': '影响行程规划和旅程地图。Atlas 始终使用 Leaflet。', + 'settings.mapLeafletSubtitle': '经典 2D,任何栅格瓦片', + 'settings.mapMapboxSubtitle': '矢量瓦片、3D 建筑和地形', + 'settings.mapExperimental': '实验性', + 'settings.mapMapboxToken': 'Mapbox 访问令牌', + 'settings.mapMapboxTokenHint': '公共令牌 (pk.*) 来自', + 'settings.mapMapboxTokenLink': 'mapbox.com → 访问令牌', + 'settings.mapStyle': '地图样式', + 'settings.mapStylePlaceholder': '选择 Mapbox 样式', + 'settings.mapStyleHint': '预设或您自己的 mapbox://styles/USER/ID URL', + 'settings.map3dBuildings': '3D 建筑和地形', + 'settings.map3dHint': '倾斜 + 真实 3D 建筑拉伸 — 适用于所有样式,包括卫星。', + 'settings.mapHighQuality': '高画质模式', + 'settings.mapHighQualityHint': '抗锯齿 + 地球投影,带来更清晰的边缘和更真实的世界视图。', + 'settings.mapHighQualityWarning': '可能影响低端设备的性能。', + 'settings.mapTipLabel': '提示:', + 'settings.mapTip': '右键点击并拖动以旋转/倾斜地图。中键点击添加地点(右键用于旋转)。', 'settings.latitude': '纬度', 'settings.longitude': '经度', 'settings.saveMap': '保存地图', diff --git a/client/src/i18n/translations/zhTw.ts b/client/src/i18n/translations/zhTw.ts index 8dc2177b..27b4036f 100644 --- a/client/src/i18n/translations/zhTw.ts +++ b/client/src/i18n/translations/zhTw.ts @@ -156,6 +156,24 @@ const zhTw: Record = { 'settings.mapDefaultHint': '留空則使用 OpenStreetMap(預設)', 'settings.mapTemplatePlaceholder': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'settings.mapHint': '地圖瓦片 URL 模板', + 'settings.mapProvider': '地圖提供商', + 'settings.mapProviderHint': '影響行程規劃和旅程地圖。Atlas 始終使用 Leaflet。', + 'settings.mapLeafletSubtitle': '經典 2D,任何柵格瓦片', + 'settings.mapMapboxSubtitle': '向量瓦片、3D 建築和地形', + 'settings.mapExperimental': '實驗性', + 'settings.mapMapboxToken': 'Mapbox 存取權杖', + 'settings.mapMapboxTokenHint': '公開權杖 (pk.*) 來自', + 'settings.mapMapboxTokenLink': 'mapbox.com → 存取權杖', + 'settings.mapStyle': '地圖樣式', + 'settings.mapStylePlaceholder': '選擇 Mapbox 樣式', + 'settings.mapStyleHint': '預設或您自己的 mapbox://styles/USER/ID URL', + 'settings.map3dBuildings': '3D 建築和地形', + 'settings.map3dHint': '傾斜 + 真實 3D 建築拉伸 — 適用於所有樣式,包括衛星。', + 'settings.mapHighQuality': '高畫質模式', + 'settings.mapHighQualityHint': '抗鋸齒 + 地球投影,帶來更清晰的邊緣和更真實的世界視圖。', + 'settings.mapHighQualityWarning': '可能影響低階裝置的效能。', + 'settings.mapTipLabel': '提示:', + 'settings.mapTip': '右鍵點擊並拖曳以旋轉/傾斜地圖。中鍵點擊新增地點(右鍵用於旋轉)。', 'settings.latitude': '緯度', 'settings.longitude': '經度', 'settings.saveMap': '儲存地圖',