From 2fe487154f204061af24d050f6a2a379598ecffa Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 6 Nov 2026 19:08:51 +0100 Subject: [PATCH] feat(native): the widget gallery in the app's languages, previews with a cover - Names and descriptions of the four widgets in TREKWidgets/.lproj for the 28 languages the app speaks, linted with the other property lists - The preview renderer draws a cover photo state too, and the lock-screen accessories as the system does, without a box of their own --- .github/workflows/native.yml | 2 +- client/ios/App/App.xcodeproj/project.pbxproj | 93 +++++++++++++++++++ .../ios/App/TREKWidgets/NextTripWidget.swift | 19 ++-- .../TREKWidgets/ar.lproj/Localizable.strings | 9 ++ .../TREKWidgets/az.lproj/Localizable.strings | 9 ++ .../TREKWidgets/ca.lproj/Localizable.strings | 9 ++ .../TREKWidgets/cs.lproj/Localizable.strings | 9 ++ .../TREKWidgets/de.lproj/Localizable.strings | 9 ++ .../TREKWidgets/el.lproj/Localizable.strings | 9 ++ .../TREKWidgets/en.lproj/Localizable.strings | 9 ++ .../TREKWidgets/es.lproj/Localizable.strings | 9 ++ .../TREKWidgets/et.lproj/Localizable.strings | 9 ++ .../TREKWidgets/fr.lproj/Localizable.strings | 9 ++ .../TREKWidgets/he.lproj/Localizable.strings | 9 ++ .../TREKWidgets/hu.lproj/Localizable.strings | 9 ++ .../TREKWidgets/id.lproj/Localizable.strings | 9 ++ .../TREKWidgets/it.lproj/Localizable.strings | 9 ++ .../TREKWidgets/ja.lproj/Localizable.strings | 9 ++ .../TREKWidgets/ko.lproj/Localizable.strings | 9 ++ .../TREKWidgets/nl.lproj/Localizable.strings | 9 ++ .../TREKWidgets/pl.lproj/Localizable.strings | 9 ++ .../pt-BR.lproj/Localizable.strings | 9 ++ .../TREKWidgets/ru.lproj/Localizable.strings | 9 ++ .../TREKWidgets/sk.lproj/Localizable.strings | 9 ++ .../TREKWidgets/sv.lproj/Localizable.strings | 9 ++ .../TREKWidgets/th.lproj/Localizable.strings | 9 ++ .../TREKWidgets/tr.lproj/Localizable.strings | 9 ++ .../TREKWidgets/uk.lproj/Localizable.strings | 9 ++ .../TREKWidgets/vi.lproj/Localizable.strings | 9 ++ .../zh-Hans.lproj/Localizable.strings | 9 ++ .../zh-Hant.lproj/Localizable.strings | 9 ++ .../Sources/WidgetPreviews/Render.swift | 58 ++++++++++-- 32 files changed, 408 insertions(+), 16 deletions(-) create mode 100644 client/ios/App/TREKWidgets/ar.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/az.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/ca.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/cs.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/de.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/el.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/en.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/es.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/et.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/fr.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/he.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/hu.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/id.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/it.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/ja.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/ko.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/nl.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/pl.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/pt-BR.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/ru.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/sk.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/sv.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/th.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/tr.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/uk.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/vi.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/zh-Hans.lproj/Localizable.strings create mode 100644 client/ios/App/TREKWidgets/zh-Hant.lproj/Localizable.strings diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index 09f91df91..041d4bd0b 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -103,7 +103,7 @@ jobs: xcodebuild -version - name: Check the property lists - run: plutil -lint client/ios/App/App/Info.plist client/ios/ExportOptions.plist client/ios/App/NotificationService/Info.plist client/ios/App/NotificationService/NotificationService.entitlements client/ios/App/App/App.entitlements client/ios/App/TREKWidgets/Info.plist client/ios/App/TREKWidgets/TREKWidgets.entitlements + run: plutil -lint client/ios/App/App/Info.plist client/ios/ExportOptions.plist client/ios/App/NotificationService/Info.plist client/ios/App/NotificationService/NotificationService.entitlements client/ios/App/App/App.entitlements client/ios/App/TREKWidgets/Info.plist client/ios/App/TREKWidgets/TREKWidgets.entitlements client/ios/App/TREKWidgets/*.lproj/Localizable.strings - uses: actions/setup-node@v6 with: diff --git a/client/ios/App/App.xcodeproj/project.pbxproj b/client/ios/App/App.xcodeproj/project.pbxproj index 2f4b1d126..4df12abc5 100644 --- a/client/ios/App/App.xcodeproj/project.pbxproj +++ b/client/ios/App/App.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ B4C0FC386F71D849E141C91E /* NextTripWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7064A9DA92FCC8ACAA4F22A /* NextTripWidget.swift */; }; B65B7CFC62EEFF443491EA64 /* ActivityKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 192E69C7E66646BC39566925 /* ActivityKit.framework */; }; DF633789F2F667C4CF264C9E /* TripDayLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3259F6C40216CECEBAC6F307 /* TripDayLiveActivity.swift */; }; + A0E8821CA7CF51AF2D6D8422 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 248A72D1C511B4969D89333A /* Localizable.strings */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -110,6 +111,34 @@ E6D2F4930E5F152C8090C4F8 /* BookingsContent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BookingsContent.swift; sourceTree = ""; }; AEF7DCAA1AEE67F1291668D1 /* TodosContent.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TodosContent.swift; sourceTree = ""; }; FFA8802204810ED580DE46F3 /* NotificationService.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 84CB5C3B6BD4E1DE1A8BD2B4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + EE30A6DF943052C6ADE92D63 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + ECC80E1314B31ED2D2347411 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; + 7328217E23761C22814FEC25 /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/Localizable.strings; sourceTree = ""; }; + CC077989456FB9DB5F4FF676 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt-BR; path = pt-BR.lproj/Localizable.strings; sourceTree = ""; }; + 405B04DFF3A978D1046147C6 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; + DD341749E47B7DF89BA9A46D /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + 9A3FAA9DBC67D12389EEF0CF /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 8D361F7B14AE9F5ACE3BFE78 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = ""; }; + 60EB00F3670B41B898765DF9 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + B680CBF1268D2EE1982B165C /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; + 12DE24E0B7E2E5F3564D952B /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = ""; }; + 15884CA899CAD3FFD98BA927 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; + A2F3670F49C3D74C3D98E457 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = ""; }; + 1947CC62E5A6FF3483BEB578 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + 8A0289CB321AE008D8815A19 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + B7F33B7A5C27C126DF29DCA4 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; + 683725459244601B71D92E2D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + 32A62EE30F8AD514D7DB2CAA /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + 037DA05706AE051CEFCA7D75 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 090D74588DA8436DEC275567 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = ""; }; + 9F756414EFBB3542F65C14CE /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; + 5C0109EA628FA897B7BFDD3E /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; + E5800E4ECECED54A47CDABAA /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + A06BBDA4CF70465F2E3F21D3 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; + 3CB565FC61AB5AEA87A3F615 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; + 608F00CBD96957EEA888E50A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh-Hans; path = zh-Hans.lproj/Localizable.strings; sourceTree = ""; }; + 1ED347C91E03E51FDF8ABC1A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh-Hant; path = zh-Hant.lproj/Localizable.strings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -218,6 +247,7 @@ isa = PBXGroup; children = ( BE41D7212DCB0C78E00A8462 /* Info.plist */, + 248A72D1C511B4969D89333A /* Localizable.strings */, C7064A9DA92FCC8ACAA4F22A /* NextTripWidget.swift */, BBB2E19CD540AB30B44BECCA /* TREKWidgets.entitlements */, 8C2E47D0B005674C2B4ADA56 /* TREKWidgetsBundle.swift */, @@ -323,6 +353,33 @@ knownRegions = ( en, Base, + de, + ar, + az, + "pt-BR", + ca, + cs, + es, + et, + fr, + el, + he, + hu, + id, + it, + ja, + ko, + nl, + pl, + ru, + sk, + sv, + th, + tr, + uk, + vi, + "zh-Hans", + "zh-Hant", ); mainGroup = 504EC2FB1FED79650016851F; packageReferences = ( @@ -344,6 +401,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + A0E8821CA7CF51AF2D6D8422 /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -427,6 +485,41 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 248A72D1C511B4969D89333A /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 84CB5C3B6BD4E1DE1A8BD2B4 /* en */, + EE30A6DF943052C6ADE92D63 /* de */, + ECC80E1314B31ED2D2347411 /* ar */, + 7328217E23761C22814FEC25 /* az */, + CC077989456FB9DB5F4FF676 /* pt-BR */, + 405B04DFF3A978D1046147C6 /* ca */, + DD341749E47B7DF89BA9A46D /* cs */, + 9A3FAA9DBC67D12389EEF0CF /* es */, + 8D361F7B14AE9F5ACE3BFE78 /* et */, + 60EB00F3670B41B898765DF9 /* fr */, + B680CBF1268D2EE1982B165C /* el */, + 12DE24E0B7E2E5F3564D952B /* he */, + 15884CA899CAD3FFD98BA927 /* hu */, + A2F3670F49C3D74C3D98E457 /* id */, + 1947CC62E5A6FF3483BEB578 /* it */, + 8A0289CB321AE008D8815A19 /* ja */, + B7F33B7A5C27C126DF29DCA4 /* ko */, + 683725459244601B71D92E2D /* nl */, + 32A62EE30F8AD514D7DB2CAA /* pl */, + 037DA05706AE051CEFCA7D75 /* ru */, + 090D74588DA8436DEC275567 /* sk */, + 9F756414EFBB3542F65C14CE /* sv */, + 5C0109EA628FA897B7BFDD3E /* th */, + E5800E4ECECED54A47CDABAA /* tr */, + A06BBDA4CF70465F2E3F21D3 /* uk */, + 3CB565FC61AB5AEA87A3F615 /* vi */, + 608F00CBD96957EEA888E50A /* zh-Hans */, + 1ED347C91E03E51FDF8ABC1A /* zh-Hant */, + ); + name = Localizable.strings; + sourceTree = ""; + }; 504EC30B1FED79650016851F /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( diff --git a/client/ios/App/TREKWidgets/NextTripWidget.swift b/client/ios/App/TREKWidgets/NextTripWidget.swift index 3512f5105..5c5b3de2c 100644 --- a/client/ios/App/TREKWidgets/NextTripWidget.swift +++ b/client/ios/App/TREKWidgets/NextTripWidget.swift @@ -1,7 +1,8 @@ import SwiftUI import WidgetKit -/// The home-screen and lock-screen widgets. Each reads the snapshot the app +/// The home-screen and lock-screen widgets; their gallery names and +/// descriptions are in .lproj/Localizable.strings. Each reads the snapshot the app /// last wrote (WidgetSnapshot) and draws it with the views in *Content.swift, /// which know nothing of WidgetKit so the preview renderer can draw them too. struct TrekEntry: TimelineEntry { @@ -85,8 +86,8 @@ struct NextTripWidget: Widget { StaticConfiguration(kind: "NextTrip", provider: TrekProvider()) { entry in TripWidgetView(entry: entry) } - .configurationDisplayName("Trip") - .description("Counts down to your next trip, then shows the day and what comes next.") + .configurationDisplayName("widget.trip.name") + .description("widget.trip.description") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge, .accessoryCircular, .accessoryRectangular, .accessoryInline]) } } @@ -116,8 +117,8 @@ struct TodayWidget: Widget { TrekPaper() } } - .configurationDisplayName("Today") - .description("Today's plan as a timeline, with the weather and what is next.") + .configurationDisplayName("widget.today.name") + .description("widget.today.description") .supportedFamilies([.systemMedium, .systemLarge]) } } @@ -139,8 +140,8 @@ struct BookingsWidget: Widget { TrekPaper() } } - .configurationDisplayName("Bookings") - .description("Your next flights, trains, hotels and tables, and when they are due.") + .configurationDisplayName("widget.bookings.name") + .description("widget.bookings.description") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge, .accessoryRectangular, .accessoryInline]) } } @@ -162,8 +163,8 @@ struct TodosWidget: Widget { TrekPaper() } } - .configurationDisplayName("To-dos") - .description("What is still open for the trip, and how far the packing has come.") + .configurationDisplayName("widget.todos.name") + .description("widget.todos.description") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge, .accessoryCircular, .accessoryRectangular]) } } diff --git a/client/ios/App/TREKWidgets/ar.lproj/Localizable.strings b/client/ios/App/TREKWidgets/ar.lproj/Localizable.strings new file mode 100644 index 000000000..be7402335 --- /dev/null +++ b/client/ios/App/TREKWidgets/ar.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "رحلة"; +"widget.trip.description" = "عدّ تنازلي لرحلتك القادمة، ثم اليوم وما يليه أثناء السفر."; +"widget.today.name" = "اليوم"; +"widget.today.description" = "خطة اليوم كخط زمني، مع الطقس والمحطة التالية."; +"widget.bookings.name" = "الحجوزات"; +"widget.bookings.description" = "رحلاتك وقطاراتك وفنادقك وحجوزات المطاعم القادمة ومواعيدها."; +"widget.todos.name" = "المهام"; +"widget.todos.description" = "ما تبقى للرحلة، ومدى تقدم تجهيز الأمتعة."; diff --git a/client/ios/App/TREKWidgets/az.lproj/Localizable.strings b/client/ios/App/TREKWidgets/az.lproj/Localizable.strings new file mode 100644 index 000000000..118e6c36f --- /dev/null +++ b/client/ios/App/TREKWidgets/az.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Səyahət"; +"widget.trip.description" = "Növbəti səyahətə qədər günləri sayır, yolda isə günü və sonrakını göstərir."; +"widget.today.name" = "Bu gün"; +"widget.today.description" = "Günün planı zaman xətti kimi, hava və növbəti dayanacaqla."; +"widget.bookings.name" = "Bronlar"; +"widget.bookings.description" = "Növbəti uçuşlar, qatarlar, otellər və restoranlar, nə vaxt olduqları ilə."; +"widget.todos.name" = "Tapşırıqlar"; +"widget.todos.description" = "Səyahət üçün hələ nəyin qaldığı və yığışmağın nə yerdə olduğu."; diff --git a/client/ios/App/TREKWidgets/ca.lproj/Localizable.strings b/client/ios/App/TREKWidgets/ca.lproj/Localizable.strings new file mode 100644 index 000000000..1d8ff52c9 --- /dev/null +++ b/client/ios/App/TREKWidgets/ca.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Viatge"; +"widget.trip.description" = "Compta els dies fins al proper viatge i, durant el viatge, mostra el dia i el que ve després."; +"widget.today.name" = "Avui"; +"widget.today.description" = "El pla del dia com a línia de temps, amb el temps i la propera parada."; +"widget.bookings.name" = "Reserves"; +"widget.bookings.description" = "Els teus propers vols, trens, hotels i taules, i quan toquen."; +"widget.todos.name" = "Tasques"; +"widget.todos.description" = "El que encara queda per al viatge i com va l'equipatge."; diff --git a/client/ios/App/TREKWidgets/cs.lproj/Localizable.strings b/client/ios/App/TREKWidgets/cs.lproj/Localizable.strings new file mode 100644 index 000000000..f63e71a1e --- /dev/null +++ b/client/ios/App/TREKWidgets/cs.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Cesta"; +"widget.trip.description" = "Odpočítává dny do další cesty a během ní ukazuje den a co přijde dál."; +"widget.today.name" = "Dnes"; +"widget.today.description" = "Plán dne jako časová osa, s počasím a další zastávkou."; +"widget.bookings.name" = "Rezervace"; +"widget.bookings.description" = "Tvoje další lety, vlaky, hotely a stoly, a kdy tě čekají."; +"widget.todos.name" = "Úkoly"; +"widget.todos.description" = "Co ještě zbývá k cestě a jak daleko je balení."; diff --git a/client/ios/App/TREKWidgets/de.lproj/Localizable.strings b/client/ios/App/TREKWidgets/de.lproj/Localizable.strings new file mode 100644 index 000000000..7c807c798 --- /dev/null +++ b/client/ios/App/TREKWidgets/de.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Reise"; +"widget.trip.description" = "Zählt die Tage bis zur nächsten Reise und zeigt unterwegs den Tag und was als Nächstes kommt."; +"widget.today.name" = "Heute"; +"widget.today.description" = "Der Tagesplan als Zeitleiste, mit dem Wetter und dem nächsten Stopp."; +"widget.bookings.name" = "Buchungen"; +"widget.bookings.description" = "Deine nächsten Flüge, Züge, Hotels und Tische, und wann sie anstehen."; +"widget.todos.name" = "To-dos"; +"widget.todos.description" = "Was für die Reise noch offen ist und wie weit das Packen ist."; diff --git a/client/ios/App/TREKWidgets/el.lproj/Localizable.strings b/client/ios/App/TREKWidgets/el.lproj/Localizable.strings new file mode 100644 index 000000000..96e2c399b --- /dev/null +++ b/client/ios/App/TREKWidgets/el.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Ταξίδι"; +"widget.trip.description" = "Μετρά αντίστροφα ως το επόμενο ταξίδι και στη διάρκειά του δείχνει τη μέρα και τι ακολουθεί."; +"widget.today.name" = "Σήμερα"; +"widget.today.description" = "Το πρόγραμμα της ημέρας ως χρονολόγιο, με τον καιρό και την επόμενη στάση."; +"widget.bookings.name" = "Κρατήσεις"; +"widget.bookings.description" = "Οι επόμενες πτήσεις, τα τρένα, τα ξενοδοχεία και τα τραπέζια σου, και πότε είναι."; +"widget.todos.name" = "Εκκρεμότητες"; +"widget.todos.description" = "Τι μένει ακόμα για το ταξίδι και πόσο έχει προχωρήσει το πακετάρισμα."; diff --git a/client/ios/App/TREKWidgets/en.lproj/Localizable.strings b/client/ios/App/TREKWidgets/en.lproj/Localizable.strings new file mode 100644 index 000000000..e498b91f1 --- /dev/null +++ b/client/ios/App/TREKWidgets/en.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Trip"; +"widget.trip.description" = "Counts down to your next trip, then shows the day and what comes next."; +"widget.today.name" = "Today"; +"widget.today.description" = "Today's plan as a timeline, with the weather and what is next."; +"widget.bookings.name" = "Bookings"; +"widget.bookings.description" = "Your next flights, trains, hotels and tables, and when they are due."; +"widget.todos.name" = "To-dos"; +"widget.todos.description" = "What is still open for the trip, and how far the packing has come."; diff --git a/client/ios/App/TREKWidgets/es.lproj/Localizable.strings b/client/ios/App/TREKWidgets/es.lproj/Localizable.strings new file mode 100644 index 000000000..a40e68b64 --- /dev/null +++ b/client/ios/App/TREKWidgets/es.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Viaje"; +"widget.trip.description" = "Cuenta los días hasta tu próximo viaje y, durante él, muestra el día y lo que sigue."; +"widget.today.name" = "Hoy"; +"widget.today.description" = "El plan del día como línea de tiempo, con el tiempo y la próxima parada."; +"widget.bookings.name" = "Reservas"; +"widget.bookings.description" = "Tus próximos vuelos, trenes, hoteles y mesas, y cuándo tocan."; +"widget.todos.name" = "Tareas"; +"widget.todos.description" = "Lo que aún queda para el viaje y cómo va la maleta."; diff --git a/client/ios/App/TREKWidgets/et.lproj/Localizable.strings b/client/ios/App/TREKWidgets/et.lproj/Localizable.strings new file mode 100644 index 000000000..ea095c3fa --- /dev/null +++ b/client/ios/App/TREKWidgets/et.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Reis"; +"widget.trip.description" = "Loeb päevi järgmise reisini ja näitab reisi ajal päeva ning seda, mis järgmisena tuleb."; +"widget.today.name" = "Täna"; +"widget.today.description" = "Päeva plaan ajajoonena, koos ilma ja järgmise peatusega."; +"widget.bookings.name" = "Broneeringud"; +"widget.bookings.description" = "Sinu järgmised lennud, rongid, hotellid ja lauad ning millal need on."; +"widget.todos.name" = "Ülesanded"; +"widget.todos.description" = "Mis reisi jaoks veel tegemata on ja kui kaugel pakkimine on."; diff --git a/client/ios/App/TREKWidgets/fr.lproj/Localizable.strings b/client/ios/App/TREKWidgets/fr.lproj/Localizable.strings new file mode 100644 index 000000000..bdd92e80a --- /dev/null +++ b/client/ios/App/TREKWidgets/fr.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Voyage"; +"widget.trip.description" = "Compte les jours jusqu'au prochain voyage, puis montre la journée et la suite pendant le voyage."; +"widget.today.name" = "Aujourd'hui"; +"widget.today.description" = "Le programme du jour en frise, avec la météo et la prochaine étape."; +"widget.bookings.name" = "Réservations"; +"widget.bookings.description" = "Vos prochains vols, trains, hôtels et tables, et quand ils arrivent."; +"widget.todos.name" = "À faire"; +"widget.todos.description" = "Ce qui reste à faire pour le voyage et où en sont les bagages."; diff --git a/client/ios/App/TREKWidgets/he.lproj/Localizable.strings b/client/ios/App/TREKWidgets/he.lproj/Localizable.strings new file mode 100644 index 000000000..3bee192b7 --- /dev/null +++ b/client/ios/App/TREKWidgets/he.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "טיול"; +"widget.trip.description" = "סופר את הימים עד הטיול הבא, ובמהלכו מציג את היום ואת מה שבא אחריו."; +"widget.today.name" = "היום"; +"widget.today.description" = "תוכנית היום כציר זמן, עם מזג האוויר והעצירה הבאה."; +"widget.bookings.name" = "הזמנות"; +"widget.bookings.description" = "הטיסות, הרכבות, המלונות והשולחנות הבאים שלך, ומתי הם."; +"widget.todos.name" = "משימות"; +"widget.todos.description" = "מה עוד פתוח לטיול וכמה התקדמה האריזה."; diff --git a/client/ios/App/TREKWidgets/hu.lproj/Localizable.strings b/client/ios/App/TREKWidgets/hu.lproj/Localizable.strings new file mode 100644 index 000000000..f570acb5a --- /dev/null +++ b/client/ios/App/TREKWidgets/hu.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Utazás"; +"widget.trip.description" = "Visszaszámol a következő utazásig, útközben pedig a napot és a következő programot mutatja."; +"widget.today.name" = "Ma"; +"widget.today.description" = "A nap terve idővonalon, az időjárással és a következő megállóval."; +"widget.bookings.name" = "Foglalások"; +"widget.bookings.description" = "A következő járataid, vonataid, szállásaid és asztalfoglalásaid, és hogy mikor esedékesek."; +"widget.todos.name" = "Teendők"; +"widget.todos.description" = "Ami még hátravan az utazáshoz, és hol tart a csomagolás."; diff --git a/client/ios/App/TREKWidgets/id.lproj/Localizable.strings b/client/ios/App/TREKWidgets/id.lproj/Localizable.strings new file mode 100644 index 000000000..cabe99845 --- /dev/null +++ b/client/ios/App/TREKWidgets/id.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Perjalanan"; +"widget.trip.description" = "Menghitung mundur ke perjalanan berikutnya, lalu menampilkan hari dan apa yang berikutnya."; +"widget.today.name" = "Hari ini"; +"widget.today.description" = "Rencana hari ini sebagai linimasa, dengan cuaca dan perhentian berikutnya."; +"widget.bookings.name" = "Pemesanan"; +"widget.bookings.description" = "Penerbangan, kereta, hotel, dan meja berikutnya, beserta waktunya."; +"widget.todos.name" = "Tugas"; +"widget.todos.description" = "Yang masih tersisa untuk perjalanan dan sejauh mana persiapan barang."; diff --git a/client/ios/App/TREKWidgets/it.lproj/Localizable.strings b/client/ios/App/TREKWidgets/it.lproj/Localizable.strings new file mode 100644 index 000000000..e09eb65bf --- /dev/null +++ b/client/ios/App/TREKWidgets/it.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Viaggio"; +"widget.trip.description" = "Conta i giorni al prossimo viaggio e durante il viaggio mostra la giornata e cosa viene dopo."; +"widget.today.name" = "Oggi"; +"widget.today.description" = "Il programma del giorno come linea del tempo, con il meteo e la prossima tappa."; +"widget.bookings.name" = "Prenotazioni"; +"widget.bookings.description" = "I tuoi prossimi voli, treni, hotel e tavoli, e quando sono."; +"widget.todos.name" = "Da fare"; +"widget.todos.description" = "Cosa manca ancora per il viaggio e a che punto è la valigia."; diff --git a/client/ios/App/TREKWidgets/ja.lproj/Localizable.strings b/client/ios/App/TREKWidgets/ja.lproj/Localizable.strings new file mode 100644 index 000000000..ea9857346 --- /dev/null +++ b/client/ios/App/TREKWidgets/ja.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "旅行"; +"widget.trip.description" = "次の旅行までをカウントダウンし、旅行中はその日の予定と次の行き先を表示します。"; +"widget.today.name" = "今日"; +"widget.today.description" = "今日の予定をタイムラインで、天気と次の予定とともに表示します。"; +"widget.bookings.name" = "予約"; +"widget.bookings.description" = "次のフライト、列車、ホテル、レストランとその日時。"; +"widget.todos.name" = "やること"; +"widget.todos.description" = "旅行のために残っていることと、荷造りの進み具合。"; diff --git a/client/ios/App/TREKWidgets/ko.lproj/Localizable.strings b/client/ios/App/TREKWidgets/ko.lproj/Localizable.strings new file mode 100644 index 000000000..1ea35cba8 --- /dev/null +++ b/client/ios/App/TREKWidgets/ko.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "여행"; +"widget.trip.description" = "다음 여행까지 남은 날을 세고, 여행 중에는 오늘과 다음 일정을 보여 줍니다."; +"widget.today.name" = "오늘"; +"widget.today.description" = "오늘 일정을 타임라인으로, 날씨와 다음 장소와 함께 보여 줍니다."; +"widget.bookings.name" = "예약"; +"widget.bookings.description" = "다가오는 항공편, 기차, 호텔, 식당과 그 시간."; +"widget.todos.name" = "할 일"; +"widget.todos.description" = "여행을 위해 남은 일과 짐 싸기 진행 상황."; diff --git a/client/ios/App/TREKWidgets/nl.lproj/Localizable.strings b/client/ios/App/TREKWidgets/nl.lproj/Localizable.strings new file mode 100644 index 000000000..c38b4e304 --- /dev/null +++ b/client/ios/App/TREKWidgets/nl.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Reis"; +"widget.trip.description" = "Telt af naar je volgende reis en toont onderweg de dag en wat er hierna komt."; +"widget.today.name" = "Vandaag"; +"widget.today.description" = "Het dagplan als tijdlijn, met het weer en de volgende stop."; +"widget.bookings.name" = "Boekingen"; +"widget.bookings.description" = "Je volgende vluchten, treinen, hotels en tafels, en wanneer ze zijn."; +"widget.todos.name" = "Taken"; +"widget.todos.description" = "Wat er nog openstaat voor de reis en hoe ver het inpakken is."; diff --git a/client/ios/App/TREKWidgets/pl.lproj/Localizable.strings b/client/ios/App/TREKWidgets/pl.lproj/Localizable.strings new file mode 100644 index 000000000..5b2b40417 --- /dev/null +++ b/client/ios/App/TREKWidgets/pl.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Podróż"; +"widget.trip.description" = "Odlicza dni do następnej podróży, a w trakcie pokazuje dzień i to, co dalej."; +"widget.today.name" = "Dziś"; +"widget.today.description" = "Plan dnia jako oś czasu, z pogodą i kolejnym przystankiem."; +"widget.bookings.name" = "Rezerwacje"; +"widget.bookings.description" = "Twoje najbliższe loty, pociągi, hotele i stoliki oraz ich terminy."; +"widget.todos.name" = "Zadania"; +"widget.todos.description" = "Co jeszcze zostało do zrobienia przed podróżą i jak idzie pakowanie."; diff --git a/client/ios/App/TREKWidgets/pt-BR.lproj/Localizable.strings b/client/ios/App/TREKWidgets/pt-BR.lproj/Localizable.strings new file mode 100644 index 000000000..a007aebb6 --- /dev/null +++ b/client/ios/App/TREKWidgets/pt-BR.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Viagem"; +"widget.trip.description" = "Conta os dias até a próxima viagem e, durante ela, mostra o dia e o que vem a seguir."; +"widget.today.name" = "Hoje"; +"widget.today.description" = "O plano do dia como linha do tempo, com o clima e a próxima parada."; +"widget.bookings.name" = "Reservas"; +"widget.bookings.description" = "Seus próximos voos, trens, hotéis e mesas, e quando acontecem."; +"widget.todos.name" = "Tarefas"; +"widget.todos.description" = "O que ainda falta para a viagem e quanto da mala já está pronto."; diff --git a/client/ios/App/TREKWidgets/ru.lproj/Localizable.strings b/client/ios/App/TREKWidgets/ru.lproj/Localizable.strings new file mode 100644 index 000000000..26daed23e --- /dev/null +++ b/client/ios/App/TREKWidgets/ru.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Поездка"; +"widget.trip.description" = "Отсчитывает дни до следующей поездки, а в пути показывает день и что дальше."; +"widget.today.name" = "Сегодня"; +"widget.today.description" = "План дня в виде ленты времени, с погодой и следующей остановкой."; +"widget.bookings.name" = "Бронирования"; +"widget.bookings.description" = "Ближайшие рейсы, поезда, отели и столики и когда они."; +"widget.todos.name" = "Задачи"; +"widget.todos.description" = "Что ещё осталось сделать к поездке и как идут сборы."; diff --git a/client/ios/App/TREKWidgets/sk.lproj/Localizable.strings b/client/ios/App/TREKWidgets/sk.lproj/Localizable.strings new file mode 100644 index 000000000..48659e7f6 --- /dev/null +++ b/client/ios/App/TREKWidgets/sk.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Cesta"; +"widget.trip.description" = "Odpočítava dni do ďalšej cesty a počas nej ukazuje deň a čo príde ďalej."; +"widget.today.name" = "Dnes"; +"widget.today.description" = "Plán dňa ako časová os, s počasím a ďalšou zastávkou."; +"widget.bookings.name" = "Rezervácie"; +"widget.bookings.description" = "Tvoje ďalšie lety, vlaky, hotely a stoly a kedy ťa čakajú."; +"widget.todos.name" = "Úlohy"; +"widget.todos.description" = "Čo ešte zostáva na cestu a ako ďaleko je balenie."; diff --git a/client/ios/App/TREKWidgets/sv.lproj/Localizable.strings b/client/ios/App/TREKWidgets/sv.lproj/Localizable.strings new file mode 100644 index 000000000..e7df67faf --- /dev/null +++ b/client/ios/App/TREKWidgets/sv.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Resa"; +"widget.trip.description" = "Räknar ner till nästa resa och visar under resan dagen och vad som kommer härnäst."; +"widget.today.name" = "Idag"; +"widget.today.description" = "Dagens plan som tidslinje, med vädret och nästa stopp."; +"widget.bookings.name" = "Bokningar"; +"widget.bookings.description" = "Dina nästa flyg, tåg, hotell och bord, och när de är."; +"widget.todos.name" = "Att göra"; +"widget.todos.description" = "Vad som återstår inför resan och hur långt packningen har kommit."; diff --git a/client/ios/App/TREKWidgets/th.lproj/Localizable.strings b/client/ios/App/TREKWidgets/th.lproj/Localizable.strings new file mode 100644 index 000000000..19cb2b2c8 --- /dev/null +++ b/client/ios/App/TREKWidgets/th.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "ทริป"; +"widget.trip.description" = "นับถอยหลังสู่ทริปถัดไป และระหว่างทริปแสดงวันนี้และสิ่งที่จะตามมา"; +"widget.today.name" = "วันนี้"; +"widget.today.description" = "แผนของวันนี้เป็นไทม์ไลน์ พร้อมสภาพอากาศและจุดหมายถัดไป"; +"widget.bookings.name" = "การจอง"; +"widget.bookings.description" = "เที่ยวบิน รถไฟ โรงแรม และร้านอาหารถัดไปของคุณ และเวลาของแต่ละรายการ"; +"widget.todos.name" = "สิ่งที่ต้องทำ"; +"widget.todos.description" = "สิ่งที่ยังค้างอยู่สำหรับทริป และความคืบหน้าการจัดกระเป๋า"; diff --git a/client/ios/App/TREKWidgets/tr.lproj/Localizable.strings b/client/ios/App/TREKWidgets/tr.lproj/Localizable.strings new file mode 100644 index 000000000..43c52e6f2 --- /dev/null +++ b/client/ios/App/TREKWidgets/tr.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Seyahat"; +"widget.trip.description" = "Sonraki seyahate kalan günleri sayar, yolculukta günü ve sırada ne olduğunu gösterir."; +"widget.today.name" = "Bugün"; +"widget.today.description" = "Günün planı zaman çizelgesi olarak, hava durumu ve sonraki durakla."; +"widget.bookings.name" = "Rezervasyonlar"; +"widget.bookings.description" = "Sıradaki uçuşların, trenlerin, otellerin ve masaların, ne zaman oldukları ile."; +"widget.todos.name" = "Yapılacaklar"; +"widget.todos.description" = "Seyahat için hâlâ açık olanlar ve bavulun ne durumda olduğu."; diff --git a/client/ios/App/TREKWidgets/uk.lproj/Localizable.strings b/client/ios/App/TREKWidgets/uk.lproj/Localizable.strings new file mode 100644 index 000000000..5c6dfee55 --- /dev/null +++ b/client/ios/App/TREKWidgets/uk.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Подорож"; +"widget.trip.description" = "Відлічує дні до наступної подорожі, а в дорозі показує день і що далі."; +"widget.today.name" = "Сьогодні"; +"widget.today.description" = "План дня у вигляді стрічки часу, з погодою і наступною зупинкою."; +"widget.bookings.name" = "Бронювання"; +"widget.bookings.description" = "Найближчі рейси, поїзди, готелі й столики та коли вони."; +"widget.todos.name" = "Завдання"; +"widget.todos.description" = "Що ще лишилося зробити до подорожі і як просувається пакування."; diff --git a/client/ios/App/TREKWidgets/vi.lproj/Localizable.strings b/client/ios/App/TREKWidgets/vi.lproj/Localizable.strings new file mode 100644 index 000000000..a59f3060c --- /dev/null +++ b/client/ios/App/TREKWidgets/vi.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "Chuyến đi"; +"widget.trip.description" = "Đếm ngược đến chuyến đi tiếp theo, trong chuyến đi thì hiện ngày và điểm tiếp theo."; +"widget.today.name" = "Hôm nay"; +"widget.today.description" = "Kế hoạch hôm nay dạng dòng thời gian, kèm thời tiết và điểm dừng tiếp theo."; +"widget.bookings.name" = "Đặt chỗ"; +"widget.bookings.description" = "Các chuyến bay, tàu, khách sạn và bàn sắp tới của bạn, cùng thời gian."; +"widget.todos.name" = "Việc cần làm"; +"widget.todos.description" = "Những gì còn lại cho chuyến đi và việc soạn hành lý đã tới đâu."; diff --git a/client/ios/App/TREKWidgets/zh-Hans.lproj/Localizable.strings b/client/ios/App/TREKWidgets/zh-Hans.lproj/Localizable.strings new file mode 100644 index 000000000..3f41e6e20 --- /dev/null +++ b/client/ios/App/TREKWidgets/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "旅行"; +"widget.trip.description" = "倒数到下一次旅行,旅途中显示当天行程和接下来的安排。"; +"widget.today.name" = "今天"; +"widget.today.description" = "以时间线显示今天的计划,附带天气和下一站。"; +"widget.bookings.name" = "预订"; +"widget.bookings.description" = "你接下来的航班、火车、酒店和餐厅,以及时间。"; +"widget.todos.name" = "待办"; +"widget.todos.description" = "旅行还剩哪些事没做,以及行李收拾到哪一步。"; diff --git a/client/ios/App/TREKWidgets/zh-Hant.lproj/Localizable.strings b/client/ios/App/TREKWidgets/zh-Hant.lproj/Localizable.strings new file mode 100644 index 000000000..3dcab3f15 --- /dev/null +++ b/client/ios/App/TREKWidgets/zh-Hant.lproj/Localizable.strings @@ -0,0 +1,9 @@ +/* The widget gallery: names and descriptions (TREKWidgets/NextTripWidget.swift). */ +"widget.trip.name" = "旅行"; +"widget.trip.description" = "倒數到下一次旅行,旅途中顯示當天行程與接下來的安排。"; +"widget.today.name" = "今天"; +"widget.today.description" = "以時間軸顯示今天的計畫,附上天氣與下一站。"; +"widget.bookings.name" = "預訂"; +"widget.bookings.description" = "你接下來的航班、火車、飯店與餐廳,以及時間。"; +"widget.todos.name" = "待辦"; +"widget.todos.description" = "旅行還剩哪些事沒做,以及行李整理到哪一步。"; diff --git a/client/ios/WidgetPreviews/Sources/WidgetPreviews/Render.swift b/client/ios/WidgetPreviews/Sources/WidgetPreviews/Render.swift index 1026d3fdc..d15714287 100644 --- a/client/ios/WidgetPreviews/Sources/WidgetPreviews/Render.swift +++ b/client/ios/WidgetPreviews/Sources/WidgetPreviews/Render.swift @@ -16,10 +16,16 @@ struct Render { components.minute = 10 let now = Calendar.current.date(from: components) ?? Date() + var withCover = WidgetSnapshot.sample(running: false, now: now) + withCover.trip?.cover = sampleCover() + var runningCover = WidgetSnapshot.sample(running: true, now: now) + runningCover.trip?.cover = withCover.trip?.cover let states: [(String, WidgetSnapshot)] = [ ("upcoming", .sample(running: false, now: now)), ("running", .sample(running: true, now: now)), ("empty", .sampleEmpty), + ("cover", withCover), + ("coverrunning", runningCover), ] for scheme in [ColorScheme.light, .dark] { let schemeName = scheme == .dark ? "dark" : "light" @@ -33,18 +39,37 @@ struct Render { } } } - for (stateName, snapshot) in states.prefix(2) { + for (stateName, snapshot) in states where stateName != "empty" { let sheet = HomeScreen(snapshot: snapshot, now: now) .environment(\.colorScheme, scheme) .environment(\.trekStaticPreview, true) try write(sheet, to: out.appendingPathComponent("home-\(stateName)-\(schemeName).png")) } - let lock = LockScreen(snapshots: states.map(\.1), now: now).environment(\.colorScheme, scheme) + let lock = LockScreen(snapshots: states.prefix(3).map(\.1), now: now).environment(\.colorScheme, scheme) try write(lock, to: out.appendingPathComponent("lock-\(schemeName).png")) } print("wrote \(try FileManager.default.contentsOfDirectory(atPath: out.path).count) images to \(out.path)") } + /// A stand-in for a trip's cover photo: dusk over layered mountains, as the + /// small JPEG the app would send. + @MainActor + static func sampleCover() -> String? { + let scene = ZStack(alignment: .bottom) { + LinearGradient(colors: [Color(hex: 0x2B3A67), Color(hex: 0xC0617A), Color(hex: 0xF3A65A)], startPoint: .top, endPoint: .bottom) + Circle().fill(Color(hex: 0xFFE2A8)).frame(width: 120, height: 120).offset(x: 140, y: -170).blur(radius: 2) + Ridge(height: 0.55, seed: 3).fill(Color(hex: 0x6B4A6E)) + Ridge(height: 0.42, seed: 7).fill(Color(hex: 0x3E2E4F)) + Ridge(height: 0.26, seed: 11).fill(Color(hex: 0x1C1726)) + } + .frame(width: 720, height: 480) + let renderer = ImageRenderer(content: scene) + renderer.scale = 1 + guard let image = renderer.nsImage, let tiff = image.tiffRepresentation, let bitmap = NSBitmapImageRep(data: tiff), + let jpeg = bitmap.representation(using: .jpeg, properties: [.compressionFactor: 0.74]) else { return nil } + return jpeg.base64EncodedString() + } + @MainActor static func write(_ view: V, to url: URL) throws { let renderer = ImageRenderer(content: view) @@ -83,13 +108,12 @@ struct WidgetFrame: View { var body: some View { let size = family.previewSize if family.isAccessory { + // As the lock screen draws them: white on the wallpaper, no box of their own + // (the circular one gets the system's soft disc). content .foregroundStyle(.white) .frame(width: size.width, height: size.height) - .background( - RoundedRectangle(cornerRadius: family == .circular ? size.width / 2 : 12, style: .continuous) - .fill(Color.white.opacity(family == .inline ? 0 : 0.14)) - ) + .background(Circle().fill(Color.white.opacity(family == .circular ? 0.14 : 0))) .padding(6) .background(Color(hex: 0x1E2A3A)) } else { @@ -178,3 +202,25 @@ struct LockScreen: View { .background(Color(hex: 0x1E2A3A)) } } + +/// A mountain ridge across the bottom of its frame. +struct Ridge: Shape { + let height: CGFloat + let seed: Int + + func path(in rect: CGRect) -> Path { + var path = Path() + path.move(to: CGPoint(x: rect.minX, y: rect.maxY)) + let steps = 48 + for step in 0...steps { + let x: CGFloat = rect.minX + rect.width * CGFloat(step) / CGFloat(steps) + let phase: CGFloat = CGFloat(step) * 0.35 + CGFloat(seed) + let wave: CGFloat = sin(phase) * 0.06 + sin(phase * 2.7) * 0.035 + let y: CGFloat = rect.maxY - rect.height * (height + wave) + path.addLine(to: CGPoint(x: x, y: y)) + } + path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) + path.closeSubpath() + return path + } +}