diff --git a/client/src/mobile/components/MSectionNav.tsx b/client/src/mobile/components/MSectionNav.tsx index 45a5dd7d6..98291a73a 100644 --- a/client/src/mobile/components/MSectionNav.tsx +++ b/client/src/mobile/components/MSectionNav.tsx @@ -6,6 +6,11 @@ export interface MSection { id: string; label: string; icon: LucideIcon; + /** + * The heading the section stands under. Consecutive sections with the same + * group share one heading; a screen whose sections name none shows none. + */ + group?: string; } interface MSectionRowProps { @@ -33,6 +38,50 @@ export function MSectionRow({ section, active, current = false, onPick }: MSecti ); } +/** + * The sections in order, each run of one group under its heading: the same list + * in the dropdown of a phone and in the list beside the page. + */ +export function MSectionList({ + sections, + activeId, + markCurrent = false, + onPick, +}: { + sections: MSection[]; + activeId: string; + /** Marks the active row as the page on show (the list beside the page). */ + markCurrent?: boolean; + onPick: (id: string) => void; +}) { + return ( + <> + {sections.map((section, i) => { + const heading = section.group && section.group !== sections[i - 1]?.group ? section.group : null; + return ( +
+ {heading && ( +
+ {heading} +
+ )} + onPick(section.id)} + /> +
+ ); + })} + + ); +} + /** * The frame of a settings-like screen (settings, administration), one rule for * both: the page column on a phone, a centred column on a wide window, and from @@ -88,15 +137,7 @@ export default function MSectionNav({ {title} {action &&
{action}
} - {sections.map((section) => ( - onPick(section.id)} - /> - ))} + ); } diff --git a/client/src/mobile/screens/settings/MSettings.tsx b/client/src/mobile/screens/settings/MSettings.tsx index 4463fb3e4..b99b1374c 100644 --- a/client/src/mobile/screens/settings/MSettings.tsx +++ b/client/src/mobile/screens/settings/MSettings.tsx @@ -12,6 +12,7 @@ import { Puzzle, Settings2, SlidersHorizontal, + Smartphone, User, } from 'lucide-react' import { useTranslation } from '../../../i18n' @@ -26,8 +27,11 @@ import MSettingsIntegrations from './MSettingsIntegrations' import MSettingsAccount from './MSettingsAccount' import MSettingsAbout from './MSettingsAbout' import MSettingsServer from './MSettingsServer' +import MAppLockCard from './MAppLockCard' +import MClientCertCard from './MClientCertCard' +import { isNativeApp } from '../../../native/platform' import { useIsSplit } from '../../../hooks/sizeClass' -import MSectionNav, { MSectionRow, SECTION_SCREEN, type MSection } from '../../components/MSectionNav' +import MSectionNav, { MSectionList, SECTION_SCREEN, type MSection } from '../../components/MSectionNav' /** * Mobile settings screen: back + section-switcher pill on top, the section @@ -44,21 +48,33 @@ export default function MSettings() { // the page, the way Settings on an iPad does, instead of behind a dropdown. const split = useIsSplit() + // Two groups, so it is clear what a setting reaches: the account's settings + // follow the user to every device; this device's stay on it (the server the + // app opens, the app lock, a client certificate, the offline copy). + const accountGroup = t('settings.groups.account') + const deviceGroup = t('settings.groups.device') + const app = isNativeApp() const tabs: MSection[] = [ - { id: 'display', label: t('settings.tabs.display'), icon: SlidersHorizontal }, - { id: 'appearance', label: t('settings.tabs.appearance'), 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 }] : []), - ...(hasPlugins && !managed ? [{ id: 'plugins', label: t('settings.tabs.plugins'), icon: Puzzle }] : []), - { id: 'offline', label: t('settings.tabs.offline'), icon: CloudOff }, - { id: 'account', label: t('settings.tabs.account'), icon: User }, + { id: 'display', label: t('settings.tabs.display'), icon: SlidersHorizontal, group: accountGroup }, + { id: 'appearance', label: t('settings.tabs.appearance'), icon: Palette, group: accountGroup }, + { id: 'map', label: t('settings.tabs.map'), icon: Map, group: accountGroup }, + { id: 'notifications', label: t('settings.tabs.notifications'), icon: Bell, group: accountGroup }, + ...(hasIntegrations + ? [{ id: 'integrations', label: t('settings.tabs.integrations'), icon: Plug, group: accountGroup }] + : []), + ...(hasPlugins && !managed + ? [{ id: 'plugins', label: t('settings.tabs.plugins'), icon: Puzzle, group: accountGroup }] + : []), + { id: 'account', label: t('settings.tabs.account'), icon: User, group: accountGroup }, + ...(app ? [{ id: 'device', label: t('settings.tabs.device'), icon: Smartphone, group: deviceGroup }] : []), + { id: 'offline', label: t('settings.tabs.offline'), icon: CloudOff, group: deviceGroup }, // Same call as the desktop page: About is about the project — what TREK is, // where to file a bug, where to support it — and a customer of a hosted // instance is the audience for none of it. The version and the source link // move to the footer below, because AGPL §13 wants those offered wherever - // people reach the software over a network. - ...(appVersion && !managed ? [{ id: 'about', label: t('settings.tabs.about'), icon: Info }] : []), + // people reach the software over a network. Under the product's name, not + // under either group. + ...(appVersion && !managed ? [{ id: 'about', label: t('settings.tabs.about'), icon: Info, group: 'TREK' }] : []), ] const active = tabs.find((tab) => tab.id === activeTab) || tabs[0] @@ -105,17 +121,14 @@ export default function MSettings() { {/* Section dropdown, in flow (pushes the content down like the demo) */} {dropOpen && !split && (
- {tabs.map((tab) => ( - { - setActiveTab(tab.id) - setDropOpen(false) - }} - /> - ))} + { + setActiveTab(id) + setDropOpen(false) + }} + />
)} @@ -126,8 +139,15 @@ export default function MSettings() { {active.id === 'integrations' && hasIntegrations && } {/* Per-plugin settings still reuse the existing responsive tab. */} {active.id === 'plugins' && hasPlugins && !managed && } + {/* Each card hides itself where it has nothing to offer; their own margins give way to one gap. */} + {active.id === 'device' && app && ( +
+ + + +
+ )} {active.id === 'offline' && } - {active.id === 'account' && } {active.id === 'account' && } {active.id === 'about' && appVersion && } diff --git a/client/src/mobile/screens/settings/MSettingsAccount.tsx b/client/src/mobile/screens/settings/MSettingsAccount.tsx index 356dea5a6..754d67b88 100644 --- a/client/src/mobile/screens/settings/MSettingsAccount.tsx +++ b/client/src/mobile/screens/settings/MSettingsAccount.tsx @@ -19,8 +19,6 @@ import { import { useTranslation } from '../../../i18n' import { MSetCard, MSetEyebrow, MSetInput, MSetButton, MSetHint } from './MSettingsUi' import MConfirmSheet from './MConfirmSheet' -import MAppLockCard from './MAppLockCard' -import MClientCertCard from './MClientCertCard' import PasswordChecklist from '../../../components/shared/PasswordChecklist' import { fmtDate, usePasskeys } from '../../../components/Settings/usePasskeys' import { stripTrailingSlashes, useAccountSettings } from '../../../components/Settings/useAccountSettings' @@ -181,11 +179,7 @@ export default function MSettingsAccount() { )} {/* ── Two-factor authentication ───────────────────────────── */} - {/* The app lock, inside the app only (Face ID, Touch ID or the passcode). */} - - {/* The client certificate for a server behind mutual TLS, once one is stored. */} - - + {/* The app lock and the client certificate belong to this device: MSettings, "This device". */} {mfaRequiredByPolicy && (
diff --git a/client/tests/unit/mobile/settings/MSettings.test.tsx b/client/tests/unit/mobile/settings/MSettings.test.tsx index 4f6bfc107..3bfe7a46d 100644 --- a/client/tests/unit/mobile/settings/MSettings.test.tsx +++ b/client/tests/unit/mobile/settings/MSettings.test.tsx @@ -1,5 +1,5 @@ // FE-MOB-SET-NAV-001 onwards -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; import userEvent from '@testing-library/user-event'; import { render, screen } from '../../../helpers/render'; import { resetAllStores, seedStore } from '../../../helpers/store'; @@ -7,6 +7,7 @@ import { buildSettings } from '../../../helpers/factories'; import { useSettingsStore } from '../../../../src/store/settingsStore'; import { usePluginStore } from '../../../../src/store/pluginStore'; import MSettings from '../../../../src/mobile/screens/settings/MSettings'; +import * as platform from '../../../../src/native/platform'; describe('MSettings', () => { beforeEach(() => { @@ -35,4 +36,36 @@ describe('MSettings', () => { expect(screen.getByRole('button', { name: /Notifications/ })).toBeInTheDocument(); expect(screen.queryByText('Language & region')).not.toBeInTheDocument(); }); + + it('FE-MOB-SET-NAV-003: the sections stand under "Your account" and "This device", with Offline on the device', async () => { + const user = userEvent.setup(); + render(, { initialEntries: ['/settings'] }); + + await user.click(screen.getByRole('button', { name: /General/ })); + const account = screen.getByText('Your account'); + const device = screen.getByText('This device'); + const offline = screen.getByRole('button', { name: 'Offline' }); + const accountRow = screen.getByRole('button', { name: 'Account' }); + // Account settings first, then what stays on this device. + expect(account.compareDocumentPosition(accountRow) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + expect(accountRow.compareDocumentPosition(device) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + expect(device.compareDocumentPosition(offline) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + // The app's own section exists only in the app. + expect(screen.queryByRole('button', { name: 'App' })).not.toBeInTheDocument(); + }); + + it('FE-MOB-SET-NAV-004: in the app, "App" joins This device', async () => { + const native = vi.spyOn(platform, 'isNativeApp').mockReturnValue(true); + try { + const user = userEvent.setup(); + render(, { initialEntries: ['/settings'] }); + + await user.click(screen.getByRole('button', { name: /General/ })); + const device = screen.getByText('This device'); + const app = screen.getByRole('button', { name: 'App' }); + expect(device.compareDocumentPosition(app) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + } finally { + native.mockRestore(); + } + }); }); diff --git a/shared/src/i18n/ar/settings.ts b/shared/src/i18n/ar/settings.ts index ab4e2ed52..30ff11611 100644 --- a/shared/src/i18n/ar/settings.ts +++ b/shared/src/i18n/ar/settings.ts @@ -322,6 +322,9 @@ const settings: TranslationStrings = { 'settings.mfa.toastEnabled': 'تم تفعيل المصادقة الثنائية', 'settings.mfa.toastDisabled': 'تم تعطيل المصادقة الثنائية', 'settings.tabs.offline': 'دون اتصال', + 'settings.tabs.device': 'التطبيق', + 'settings.groups.account': 'حسابك', + 'settings.groups.device': 'هذا الجهاز', 'settings.notificationPreferences.email': 'البريد الإلكتروني', 'settings.notificationPreferences.webhook': 'Webhook', 'settings.notificationPreferences.inapp': 'داخل التطبيق', diff --git a/shared/src/i18n/az/settings.ts b/shared/src/i18n/az/settings.ts index ceda56b20..dac202db8 100644 --- a/shared/src/i18n/az/settings.ts +++ b/shared/src/i18n/az/settings.ts @@ -31,6 +31,9 @@ const settings: TranslationStrings = { 'settings.tabs.account': 'Hesab', 'settings.tabs.offline': 'Oflayn', + 'settings.tabs.device': 'Tətbiq', + 'settings.groups.account': 'Hesabınız', + 'settings.groups.device': 'Bu cihaz', 'settings.tabs.about': 'Haqqında', 'settings.map': 'Xəritə', diff --git a/shared/src/i18n/br/settings.ts b/shared/src/i18n/br/settings.ts index 0fd635625..a2b196658 100644 --- a/shared/src/i18n/br/settings.ts +++ b/shared/src/i18n/br/settings.ts @@ -20,6 +20,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" é obrigatório', 'settings.tabs.account': 'Conta', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'Sua conta', + 'settings.groups.device': 'Este dispositivo', 'settings.tabs.about': 'Sobre', 'settings.map': 'Mapa', 'settings.mapTemplate': 'Modelo de mapa', diff --git a/shared/src/i18n/ca/settings.ts b/shared/src/i18n/ca/settings.ts index e13ffa9a8..e2688d371 100644 --- a/shared/src/i18n/ca/settings.ts +++ b/shared/src/i18n/ca/settings.ts @@ -9,6 +9,9 @@ const settings: TranslationStrings = { 'settings.tabs.integrations': 'Integracions', 'settings.tabs.account': 'Compte', 'settings.tabs.offline': 'Fora de línia', + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'El teu compte', + 'settings.groups.device': 'Aquest dispositiu', 'settings.tabs.about': 'Quant a', 'settings.map': 'Mapa', 'settings.mapTemplate': 'Plantilla del mapa', diff --git a/shared/src/i18n/cs/settings.ts b/shared/src/i18n/cs/settings.ts index 09c9f69d5..19fdcfafa 100644 --- a/shared/src/i18n/cs/settings.ts +++ b/shared/src/i18n/cs/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '„{field}" je povinné', 'settings.tabs.account': 'Účet', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'Aplikace', + 'settings.groups.account': 'Tvůj účet', + 'settings.groups.device': 'Toto zařízení', 'settings.tabs.about': 'O aplikaci', 'settings.map': 'Mapy', 'settings.mapTemplate': 'Šablona mapy', diff --git a/shared/src/i18n/de/settings.ts b/shared/src/i18n/de/settings.ts index ba6e69654..d8b4ccc35 100644 --- a/shared/src/i18n/de/settings.ts +++ b/shared/src/i18n/de/settings.ts @@ -20,6 +20,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" ist erforderlich', 'settings.tabs.account': 'Konto', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'Dein Konto', + 'settings.groups.device': 'Dieses Gerät', 'settings.tabs.about': 'Über', 'settings.map': 'Karte', 'settings.mapTemplate': 'Karten-Vorlage', diff --git a/shared/src/i18n/en/settings.ts b/shared/src/i18n/en/settings.ts index a28fa726d..2461678c2 100644 --- a/shared/src/i18n/en/settings.ts +++ b/shared/src/i18n/en/settings.ts @@ -28,6 +28,9 @@ const settings: TranslationStrings = { 'settings.pluginActivity.columns.status': 'Result', 'settings.tabs.account': 'Account', 'settings.tabs.offline': 'Offline', + 'settings.tabs.device': 'App', + 'settings.groups.account': 'Your account', + 'settings.groups.device': 'This device', 'settings.tabs.about': 'About', 'settings.map': 'Map', 'settings.mapTemplate': 'Map Template', diff --git a/shared/src/i18n/es/settings.ts b/shared/src/i18n/es/settings.ts index 548afc3d8..b82010266 100644 --- a/shared/src/i18n/es/settings.ts +++ b/shared/src/i18n/es/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" es obligatorio', 'settings.tabs.account': 'Cuenta', 'settings.tabs.offline': 'Sin conexión', + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'Tu cuenta', + 'settings.groups.device': 'Este dispositivo', 'settings.tabs.about': 'Acerca de', 'settings.map': 'Mapa', 'settings.mapTemplate': 'Plantilla del mapa', diff --git a/shared/src/i18n/et/settings.ts b/shared/src/i18n/et/settings.ts index e89e8d55d..46ad6a19c 100644 --- a/shared/src/i18n/et/settings.ts +++ b/shared/src/i18n/et/settings.ts @@ -28,6 +28,9 @@ const settings: TranslationStrings = { 'settings.pluginActivity.columns.status': 'Tulemus', 'settings.tabs.account': 'Konto', 'settings.tabs.offline': 'Võrguühenduseta', + 'settings.tabs.device': 'Rakendus', + 'settings.groups.account': 'Sinu konto', + 'settings.groups.device': 'See seade', 'settings.tabs.about': 'Teave', 'settings.map': 'Kaart', 'settings.mapTemplate': 'Kaardimall', diff --git a/shared/src/i18n/fr/settings.ts b/shared/src/i18n/fr/settings.ts index 191d3fddb..69ce6909c 100644 --- a/shared/src/i18n/fr/settings.ts +++ b/shared/src/i18n/fr/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '« {field} » est obligatoire', 'settings.tabs.account': 'Compte', 'settings.tabs.offline': 'Hors ligne', + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'Ton compte', + 'settings.groups.device': 'Cet appareil', 'settings.tabs.about': 'À propos', 'settings.map': 'Carte', 'settings.mapTemplate': 'Modèle de carte', diff --git a/shared/src/i18n/gr/settings.ts b/shared/src/i18n/gr/settings.ts index 68f36ed64..d23addcfe 100644 --- a/shared/src/i18n/gr/settings.ts +++ b/shared/src/i18n/gr/settings.ts @@ -20,6 +20,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" είναι υποχρεωτικό', 'settings.tabs.account': 'Λογαριασμός', 'settings.tabs.offline': 'Εκτός σύνδεσης', + 'settings.tabs.device': 'Εφαρμογή', + 'settings.groups.account': 'Ο λογαριασμός σου', + 'settings.groups.device': 'Αυτή η συσκευή', 'settings.tabs.about': 'Σχετικά', 'settings.map': 'Χάρτης', 'settings.mapTemplate': 'Πρότυπο Χάρτη', diff --git a/shared/src/i18n/he/settings.ts b/shared/src/i18n/he/settings.ts index 7c6218138..f98bada3e 100644 --- a/shared/src/i18n/he/settings.ts +++ b/shared/src/i18n/he/settings.ts @@ -28,6 +28,9 @@ const settings: TranslationStrings = { 'settings.pluginActivity.columns.status': 'תוצאה', 'settings.tabs.account': 'חשבון', 'settings.tabs.offline': 'אופליין', + 'settings.tabs.device': 'אפליקציה', + 'settings.groups.account': 'החשבון שלך', + 'settings.groups.device': 'המכשיר הזה', 'settings.tabs.about': 'אודות', 'settings.map': 'מפה', 'settings.mapTemplate': 'תבנית מפה', diff --git a/shared/src/i18n/hu/settings.ts b/shared/src/i18n/hu/settings.ts index da3481da7..480a4eb4c 100644 --- a/shared/src/i18n/hu/settings.ts +++ b/shared/src/i18n/hu/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" kötelező', 'settings.tabs.account': 'Fiók', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'Alkalmazás', + 'settings.groups.account': 'Fiókod', + 'settings.groups.device': 'Ez az eszköz', 'settings.tabs.about': 'Névjegy', 'settings.map': 'Térkép', 'settings.mapTemplate': 'Térkép sablon', diff --git a/shared/src/i18n/id/settings.ts b/shared/src/i18n/id/settings.ts index 4c5ee6f8b..4b78664fc 100644 --- a/shared/src/i18n/id/settings.ts +++ b/shared/src/i18n/id/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" wajib diisi', 'settings.tabs.account': 'Akun', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'Aplikasi', + 'settings.groups.account': 'Akun Anda', + 'settings.groups.device': 'Perangkat ini', 'settings.tabs.about': 'Tentang', 'settings.map': 'Peta', 'settings.mapTemplate': 'Template Peta', diff --git a/shared/src/i18n/it/settings.ts b/shared/src/i18n/it/settings.ts index 968d3e272..4f1fee311 100644 --- a/shared/src/i18n/it/settings.ts +++ b/shared/src/i18n/it/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" è obbligatorio', 'settings.tabs.account': 'Account', // same-as-en 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'Il tuo account', + 'settings.groups.device': 'Questo dispositivo', 'settings.tabs.about': 'Informazioni', 'settings.map': 'Mappa', 'settings.mapTemplate': 'Modello Mappa', diff --git a/shared/src/i18n/ja/settings.ts b/shared/src/i18n/ja/settings.ts index 07f40ccac..bcde13a15 100644 --- a/shared/src/i18n/ja/settings.ts +++ b/shared/src/i18n/ja/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '「{field}」は必須です', 'settings.tabs.account': 'アカウント', 'settings.tabs.offline': 'オフライン', + 'settings.tabs.device': 'アプリ', + 'settings.groups.account': 'あなたのアカウント', + 'settings.groups.device': 'このデバイス', 'settings.tabs.about': '情報', 'settings.map': '地図', 'settings.mapTemplate': '地図テンプレート', diff --git a/shared/src/i18n/ko/settings.ts b/shared/src/i18n/ko/settings.ts index bf41114ef..f490c3cce 100644 --- a/shared/src/i18n/ko/settings.ts +++ b/shared/src/i18n/ko/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}"은(는) 필수입니다', 'settings.tabs.account': '계정', 'settings.tabs.offline': '오프라인', + 'settings.tabs.device': '앱', + 'settings.groups.account': '내 계정', + 'settings.groups.device': '이 기기', 'settings.tabs.about': '정보', 'settings.map': '지도', 'settings.mapTemplate': '지도 템플릿', diff --git a/shared/src/i18n/nl/settings.ts b/shared/src/i18n/nl/settings.ts index 1ab31febb..c62f46c9e 100644 --- a/shared/src/i18n/nl/settings.ts +++ b/shared/src/i18n/nl/settings.ts @@ -20,6 +20,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" is vereist', 'settings.tabs.account': 'Account', // same-as-en 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'Je account', + 'settings.groups.device': 'Dit apparaat', 'settings.tabs.about': 'Over', 'settings.map': 'Kaart', 'settings.mapTemplate': 'Kaartsjabloon', diff --git a/shared/src/i18n/pl/settings.ts b/shared/src/i18n/pl/settings.ts index 5a3afd4e3..d5d443477 100644 --- a/shared/src/i18n/pl/settings.ts +++ b/shared/src/i18n/pl/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" jest wymagane', 'settings.tabs.account': 'Konto', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'Aplikacja', + 'settings.groups.account': 'Twoje konto', + 'settings.groups.device': 'To urządzenie', 'settings.tabs.about': 'O aplikacji', 'settings.map': 'Mapa', 'settings.mapTemplate': 'Szablon mapy', diff --git a/shared/src/i18n/ru/settings.ts b/shared/src/i18n/ru/settings.ts index 604c5f24b..da71b7828 100644 --- a/shared/src/i18n/ru/settings.ts +++ b/shared/src/i18n/ru/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '«{field}» обязательно', 'settings.tabs.account': 'Аккаунт', 'settings.tabs.offline': 'Офлайн-режим', + 'settings.tabs.device': 'Приложение', + 'settings.groups.account': 'Ваш аккаунт', + 'settings.groups.device': 'Это устройство', 'settings.tabs.about': 'О приложении', 'settings.map': 'Карта', 'settings.mapTemplate': 'Шаблон карты', diff --git a/shared/src/i18n/sk/settings.ts b/shared/src/i18n/sk/settings.ts index 05f1c1cb6..8d6071e8a 100644 --- a/shared/src/i18n/sk/settings.ts +++ b/shared/src/i18n/sk/settings.ts @@ -28,6 +28,9 @@ const settings: TranslationStrings = { 'settings.pluginActivity.columns.status': 'Výsledok', 'settings.tabs.account': 'Účet', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'Aplikácia', + 'settings.groups.account': 'Tvoj účet', + 'settings.groups.device': 'Toto zariadenie', 'settings.tabs.about': 'O aplikácii', 'settings.map': 'Mapa', 'settings.mapTemplate': 'Šablóna mapy', diff --git a/shared/src/i18n/sv/settings.ts b/shared/src/i18n/sv/settings.ts index ac1cceca1..5a2a85009 100644 --- a/shared/src/i18n/sv/settings.ts +++ b/shared/src/i18n/sv/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" krävs', 'settings.tabs.account': 'Konto', 'settings.tabs.offline': 'Offline', // same-as-en + 'settings.tabs.device': 'App', // same-as-en + 'settings.groups.account': 'Ditt konto', + 'settings.groups.device': 'Den här enheten', 'settings.tabs.about': 'Om', 'settings.map': 'Karta', 'settings.mapTemplate': 'Kartmall', diff --git a/shared/src/i18n/th/settings.ts b/shared/src/i18n/th/settings.ts index 995172967..97a01b6f6 100644 --- a/shared/src/i18n/th/settings.ts +++ b/shared/src/i18n/th/settings.ts @@ -28,6 +28,9 @@ const settings: TranslationStrings = { 'settings.pluginActivity.columns.status': 'ผลลัพธ์', 'settings.tabs.account': 'บัญชี', 'settings.tabs.offline': 'ออฟไลน์', + 'settings.tabs.device': 'แอป', + 'settings.groups.account': 'บัญชีของคุณ', + 'settings.groups.device': 'อุปกรณ์นี้', 'settings.tabs.about': 'เกี่ยวกับ', 'settings.map': 'แผนที่', 'settings.mapTemplate': 'แม่แบบแผนที่', diff --git a/shared/src/i18n/tr/settings.ts b/shared/src/i18n/tr/settings.ts index 4eaef29bb..b2375df27 100644 --- a/shared/src/i18n/tr/settings.ts +++ b/shared/src/i18n/tr/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" gerekli', 'settings.tabs.account': 'Hesap', 'settings.tabs.offline': 'Çevrimdışı', + 'settings.tabs.device': 'Uygulama', + 'settings.groups.account': 'Hesabın', + 'settings.groups.device': 'Bu cihaz', 'settings.tabs.about': 'Hakkında', 'settings.map': 'Harita', 'settings.mapTemplate': 'Harita Şablonu', diff --git a/shared/src/i18n/uk/settings.ts b/shared/src/i18n/uk/settings.ts index d6ded955d..0a4ac7635 100644 --- a/shared/src/i18n/uk/settings.ts +++ b/shared/src/i18n/uk/settings.ts @@ -20,6 +20,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '«{field}» обов’язкове', 'settings.tabs.account': 'Обліковий запис', 'settings.tabs.offline': 'Офлайн', + 'settings.tabs.device': 'Застосунок', + 'settings.groups.account': 'Ваш обліковий запис', + 'settings.groups.device': 'Цей пристрій', 'settings.tabs.about': 'Про застосунок', 'settings.map': 'Карта', 'settings.mapTemplate': 'Шаблон карти', diff --git a/shared/src/i18n/vi/settings.ts b/shared/src/i18n/vi/settings.ts index 9cf6c62be..72c81c9d0 100644 --- a/shared/src/i18n/vi/settings.ts +++ b/shared/src/i18n/vi/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '"{field}" là bắt buộc', 'settings.tabs.account': 'Tài khoản', 'settings.tabs.offline': 'Ngoại tuyến', + 'settings.tabs.device': 'Ứng dụng', + 'settings.groups.account': 'Tài khoản của bạn', + 'settings.groups.device': 'Thiết bị này', 'settings.tabs.about': 'Về', 'settings.map': 'Bản đồ', 'settings.mapTemplate': 'Mẫu bản đồ', diff --git a/shared/src/i18n/zh-TW/settings.ts b/shared/src/i18n/zh-TW/settings.ts index c37747090..3843b16fb 100644 --- a/shared/src/i18n/zh-TW/settings.ts +++ b/shared/src/i18n/zh-TW/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '「{field}」為必填項', 'settings.tabs.account': '帳戶', 'settings.tabs.offline': '離線', + 'settings.tabs.device': '應用程式', + 'settings.groups.account': '你的帳戶', + 'settings.groups.device': '此裝置', 'settings.tabs.about': '關於', 'settings.map': '地圖', 'settings.mapTemplate': '地圖模板', diff --git a/shared/src/i18n/zh/settings.ts b/shared/src/i18n/zh/settings.ts index 5d8aff183..308a1460b 100644 --- a/shared/src/i18n/zh/settings.ts +++ b/shared/src/i18n/zh/settings.ts @@ -19,6 +19,9 @@ const settings: TranslationStrings = { 'settings.plugins.requiredMissing': '「{field}」为必填项', 'settings.tabs.account': '账户', 'settings.tabs.offline': '离线', + 'settings.tabs.device': '应用', + 'settings.groups.account': '你的账户', + 'settings.groups.device': '此设备', 'settings.tabs.about': '关于', 'settings.map': '地图', 'settings.mapTemplate': '地图模板',