diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15b8246c2..c4b62038a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: jobs: build: - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 496269f84..58d603500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## Unreleased +- Access your most recent Webxdc-apps from the Homescreen using our shiny new widget (#2406) - Don't show message-input when forwarding (#2435) +- Long-tap links for copying to clipboard (#2445) ## v1.50.3 diff --git a/DcWebxdcWidget/Assets.xcassets/Contents.json b/DcWebxdcWidget/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/DcWebxdcWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DcWebxdcWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/DcWebxdcWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/DcWebxdcWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DcWebxdcWidget/Assets.xcassets/checklist.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/checklist.imageset/Contents.json new file mode 100644 index 000000000..ccbddaba2 --- /dev/null +++ b/DcWebxdcWidget/Assets.xcassets/checklist.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "checklist.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DcWebxdcWidget/Assets.xcassets/checklist.imageset/checklist.png b/DcWebxdcWidget/Assets.xcassets/checklist.imageset/checklist.png new file mode 100644 index 000000000..e452412e4 Binary files /dev/null and b/DcWebxdcWidget/Assets.xcassets/checklist.imageset/checklist.png differ diff --git a/DcWebxdcWidget/Assets.xcassets/hello.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/hello.imageset/Contents.json new file mode 100644 index 000000000..2834f1755 --- /dev/null +++ b/DcWebxdcWidget/Assets.xcassets/hello.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "hello.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DcWebxdcWidget/Assets.xcassets/hello.imageset/hello.png b/DcWebxdcWidget/Assets.xcassets/hello.imageset/hello.png new file mode 100644 index 000000000..0a5ed8dea Binary files /dev/null and b/DcWebxdcWidget/Assets.xcassets/hello.imageset/hello.png differ diff --git a/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/Contents.json new file mode 100644 index 000000000..720e758f6 --- /dev/null +++ b/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "packabunchas.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png b/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png new file mode 100644 index 000000000..d25bed7b1 Binary files /dev/null and b/DcWebxdcWidget/Assets.xcassets/packabunchas.imageset/packabunchas.png differ diff --git a/DcWebxdcWidget/Assets.xcassets/pixel.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/pixel.imageset/Contents.json new file mode 100644 index 000000000..53f779f84 --- /dev/null +++ b/DcWebxdcWidget/Assets.xcassets/pixel.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "pixel.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DcWebxdcWidget/Assets.xcassets/pixel.imageset/pixel.png b/DcWebxdcWidget/Assets.xcassets/pixel.imageset/pixel.png new file mode 100644 index 000000000..39f2762c5 Binary files /dev/null and b/DcWebxdcWidget/Assets.xcassets/pixel.imageset/pixel.png differ diff --git a/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/Contents.json b/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/Contents.json new file mode 100644 index 000000000..9a83266fe --- /dev/null +++ b/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "webxdc.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/webxdc.png b/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/webxdc.png new file mode 100644 index 000000000..ab6bdf600 Binary files /dev/null and b/DcWebxdcWidget/Assets.xcassets/webxdc.imageset/webxdc.png differ diff --git a/DcWebxdcWidget/DcWebxdcWidget.swift b/DcWebxdcWidget/DcWebxdcWidget.swift new file mode 100644 index 000000000..c97f9c77a --- /dev/null +++ b/DcWebxdcWidget/DcWebxdcWidget.swift @@ -0,0 +1,166 @@ +import WidgetKit +import SwiftUI +import DcCore + +struct Provider: TimelineProvider { + func placeholder(in context: Context) -> UsedWebxdcEntry { + let limit: Int + switch context.family { + case .systemSmall: + limit = 4 + case .systemMedium: + limit = 7 + default: + limit = 7 + } + + let apps = [ + WebxdcApp(accountId: 0, chatId: 0, messageId: 0, image: UIImage(named: "checklist"), title: "checklist"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 1, image: UIImage(named: "hello"), title: "hello"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 6, image: UIImage(named: "packabunchas"), title: "packabunchas"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 3, image: UIImage(named: "webxdc"), title: "webxdc"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 2, image: UIImage(named: "pixel"), title: "pixel"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 4, image: UIImage(named: "checklist"), title: "checklist"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 5, image: UIImage(named: "hello"), title: "hello"), + WebxdcApp(accountId: 0, chatId: 0, messageId: 7, image: UIImage(named: "webxdc"), title: "webxdc"), + ] + + return UsedWebxdcEntry(date: Date(), apps: Array(apps.prefix(limit))) + } + + func getSnapshot(in context: Context, completion: @escaping (UsedWebxdcEntry) -> Void) { + let entry = placeholder(in: context) + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + /// ---------------------------------- + /// **DO NOT** start dcAccounts.startIo() in widget. + /// This works only for one process, and widgets may run concurrently to the main app in their own process. + /// ---------------------------------- + let dcAccounts = DcAccounts.shared + dcAccounts.openDatabase(writeable: false) + let dcContext = dcAccounts.getSelected() + let chatId = 0 + let ignore = Int32(0) + + let limit: Int + switch context.family { + case .systemSmall: limit = 4 + case .systemMedium: limit = 8 + default: limit = 8 + } + + let messageIds: [Int] = Array(dcContext.getChatMedia(chatId: chatId, messageType: DC_MSG_WEBXDC, messageType2: ignore, messageType3: ignore).reversed().prefix(limit)) + + let apps = messageIds.compactMap { + dcContext.getMessage(id: $0) + }.compactMap { msg in + let name = msg.getWebxdcAppName() + let image = msg.getWebxdcPreviewImage() + let accountId = dcContext.id + let chatId = msg.chatId + + return WebxdcApp( + accountId: accountId, + chatId: chatId, + messageId: msg.id, + image: image, + title: name + ) + } + + let currentDate = Date() + let entry = UsedWebxdcEntry(date: currentDate, apps: apps) + let nextDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)! + + let timeline = Timeline(entries: [entry], policy: .after(nextDate)) + completion(timeline) + } +} + +struct UsedWebxdcEntry: TimelineEntry { + + let date: Date + let apps: [WebxdcApp] +} + +struct WebxdcApp: Hashable, Identifiable { + var id: Int { messageId } + + let accountId: Int + let chatId: Int + let messageId: Int + + let image: UIImage? + let title: String + + var url: URL { + var urlComponents = URLComponents() + urlComponents.scheme = "chat.delta.deeplink" + urlComponents.host = "webxdc" + urlComponents.queryItems = [ + URLQueryItem(name: "msgId", value: "\(messageId)"), + URLQueryItem(name: "chatId", value: "\(chatId)"), + URLQueryItem(name: "accountId", value: "\(accountId)"), + ] + + return urlComponents.url! + } +} + +struct DcWebxdcWidgetEntryView: View { + var entry: Provider.Entry + + var body: some View { + if entry.apps.isEmpty { + Text(String.localized("widget_no_apps")) + } else { + let rows = [GridItem(.fixed(56)), GridItem(.fixed(56))] + LazyHGrid(rows: rows) { + ForEach(entry.apps) { app in + WebXDCAppView(app: app).accessibilityLabel(Text(app.title)) + } + } + } + } +} + +struct WebXDCAppView: View { + var app: WebxdcApp + + var body: some View { + Link(destination: app.url) { + if let image = app.image { + Image(uiImage: image) + .resizable() + .frame(width: 56, height: 56) + .cornerRadius(12) + } else { + Color(.systemBackground) + .frame(width: 56, height: 56) + .cornerRadius(12) + } + } + } +} + +struct DcWebxdcWidget: Widget { + let kind: String = "DcWebxdcWidget" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: Provider()) { entry in + if #available(iOS 17.0, *) { + DcWebxdcWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } else { + DcWebxdcWidgetEntryView(entry: entry) + .padding() + .background() + } + } + .supportedFamilies([.systemSmall, .systemMedium]) + .configurationDisplayName(String.localized("widget_most_recent_apps_title")) + .description(String.localized("widget_most_recent_apps_description")) + } +} diff --git a/DcWebxdcWidget/DcWebxdcWidgetBundle.swift b/DcWebxdcWidget/DcWebxdcWidgetBundle.swift new file mode 100644 index 000000000..dc9bf32fb --- /dev/null +++ b/DcWebxdcWidget/DcWebxdcWidgetBundle.swift @@ -0,0 +1,9 @@ +import WidgetKit +import SwiftUI + +@main +struct DcWebxdcWidgetBundle: WidgetBundle { + var body: some Widget { + DcWebxdcWidget() + } +} diff --git a/DcWebxdcWidget/Info.plist b/DcWebxdcWidget/Info.plist new file mode 100644 index 000000000..0f118fb75 --- /dev/null +++ b/DcWebxdcWidget/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/deltachat-ios.xcodeproj/project.pbxproj b/deltachat-ios.xcodeproj/project.pbxproj index 8fba093b3..a80877e36 100644 --- a/deltachat-ios.xcodeproj/project.pbxproj +++ b/deltachat-ios.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -219,10 +219,17 @@ D84AED272B566C0700D753F6 /* ReactionsOverviewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84AED262B566C0700D753F6 /* ReactionsOverviewTableViewCell.swift */; }; D85DF9782C4A96CB00A01408 /* UserDefaults+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF9772C4A96CB00A01408 /* UserDefaults+Extensions.swift */; }; D85DF9802C5250E200A01408 /* ProgressAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF97F2C5250E200A01408 /* ProgressAlertHandler.swift */; }; + D878C4FD2CF72AA0009AF551 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */; }; + D878C4FF2CF72AA0009AF551 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */; }; + D878C50C2CF72AA1009AF551 /* DcWebxdcWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D87C64672D01E4EF004472D6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3060119E22DDE24000C1CE6F /* Localizable.strings */; }; D8A072A02BED0FD8001A4C7C /* InstantOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A0729F2BED0FD8001A4C7C /* InstantOnboardingView.swift */; }; D8C19DCE2C1B456700B32F6D /* SendContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCD2C1B456700B32F6D /* SendContactViewController.swift */; }; D8C19DD02C1C9FFE00B32F6D /* ContactCardPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C19DCF2C1C95A900B32F6D /* ContactCardPreview.swift */; }; D8C1B0DD2CE7421C00C233A7 /* ShareProxyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C1B0DC2CE7421C00C233A7 /* ShareProxyViewController.swift */; }; + D8C7B68A2CF86F91003A6AD1 /* DcCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; }; + D8C7B68B2CF86F91003A6AD1 /* DcCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8C7B6982CF87F5E003A6AD1 /* DcMsg+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304219D82440734A00516852 /* DcMsg+Extension.swift */; }; D8CDEFE02C087CDA00146773 /* ContactCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CDEFDF2C087CDA00146773 /* ContactCardCell.swift */; }; D8CDEFE22C087D1000146773 /* ContactCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CDEFE12C087D1000146773 /* ContactCardView.swift */; }; D8CF2DDC2CDD110F001C2352 /* ProxySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CF2DD92CDD110F001C2352 /* ProxySettingsViewController.swift */; }; @@ -246,6 +253,13 @@ remoteGlobalIDString = B2D0E4912B93A4B200791949; remoteInfo = DcNotificationService; }; + D878C50A2CF72AA1009AF551 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7A9FB1381FB061E2001FEA36 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D878C4FA2CF72AA0009AF551; + remoteInfo = MostRecentWebXDCWidgetExtension; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -267,11 +281,23 @@ dstSubfolderSpec = 13; files = ( B2D0E4992B93A4B200791949 /* DcNotificationService.appex in Embed Foundation Extensions */, + D878C50C2CF72AA1009AF551 /* DcWebxdcWidget.appex in Embed Foundation Extensions */, 30E8F21A2447285600CE2C90 /* Delta Chat.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + D8C7B68C2CF86F91003A6AD1 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D8C7B68B2CF86F91003A6AD1 /* DcCore.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -607,6 +633,9 @@ D84AED262B566C0700D753F6 /* ReactionsOverviewTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsOverviewTableViewCell.swift; sourceTree = ""; }; D85DF9772C4A96CB00A01408 /* UserDefaults+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Extensions.swift"; sourceTree = ""; }; D85DF97F2C5250E200A01408 /* ProgressAlertHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressAlertHandler.swift; sourceTree = ""; }; + D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = DcWebxdcWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; D8A0729F2BED0FD8001A4C7C /* InstantOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstantOnboardingView.swift; sourceTree = ""; }; D8C19DCD2C1B456700B32F6D /* SendContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendContactViewController.swift; sourceTree = ""; }; D8C19DCF2C1C95A900B32F6D /* ContactCardPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactCardPreview.swift; sourceTree = ""; }; @@ -621,6 +650,20 @@ E23C80FA36453692862D4A90 /* Pods_deltachat_iosTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_deltachat_iosTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + D878C50F2CF72AA1009AF551 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + D878C5002CF72AA0009AF551 /* DcWebxdcWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (D878C50F2CF72AA1009AF551 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = DcWebxdcWidget; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 30E8F20D2447285600CE2C90 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -647,6 +690,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D878C4F82CF72AA0009AF551 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8C7B68A2CF86F91003A6AD1 /* DcCore.framework in Frameworks */, + D878C4FF2CF72AA0009AF551 /* SwiftUI.framework in Frameworks */, + D878C4FD2CF72AA0009AF551 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -864,6 +917,7 @@ 7A9FB1421FB061E2001FEA36 /* deltachat-ios */, 30E8F2112447285600CE2C90 /* DcShare */, B2D0E4932B93A4B200791949 /* DcNotificationService */, + D878C5002CF72AA0009AF551 /* DcWebxdcWidget */, 7A9FB1411FB061E2001FEA36 /* Products */, 7A9FB4F81FB084E6001FEA36 /* Frameworks */, AFE4D4B4B038293E63BC1537 /* Pods */, @@ -879,6 +933,7 @@ 7A9FB1401FB061E2001FEA36 /* deltachat-ios.app */, 30E8F2102447285600CE2C90 /* Delta Chat.appex */, B2D0E4922B93A4B200791949 /* DcNotificationService.appex */, + D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */, ); name = Products; sourceTree = ""; @@ -923,6 +978,8 @@ A6A81736FBF0A4844C6D619F /* Pods_DcShare.framework */, 12E18F2336264C265BB756C2 /* Pods_deltachat_ios.framework */, E23C80FA36453692862D4A90 /* Pods_deltachat_iosTests.framework */, + D878C4FC2CF72AA0009AF551 /* WidgetKit.framework */, + D878C4FE2CF72AA0009AF551 /* SwiftUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -1231,6 +1288,7 @@ dependencies = ( 30E8F2192447285600CE2C90 /* PBXTargetDependency */, B2D0E4982B93A4B200791949 /* PBXTargetDependency */, + D878C50B2CF72AA1009AF551 /* PBXTargetDependency */, ); name = "deltachat-ios"; productName = "deltachat-ios"; @@ -1254,6 +1312,29 @@ productReference = B2D0E4922B93A4B200791949 /* DcNotificationService.appex */; productType = "com.apple.product-type.app-extension"; }; + D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */ = { + isa = PBXNativeTarget; + buildConfigurationList = D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "DcWebxdcWidget" */; + buildPhases = ( + D878C4F72CF72AA0009AF551 /* Sources */, + D878C4F82CF72AA0009AF551 /* Frameworks */, + D878C4F92CF72AA0009AF551 /* Resources */, + D8C7B68C2CF86F91003A6AD1 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + D878C5002CF72AA0009AF551 /* DcWebxdcWidget */, + ); + name = DcWebxdcWidget; + packageProductDependencies = ( + ); + productName = MostRecentWebXDCWidgetExtension; + productReference = D878C4FB2CF72AA0009AF551 /* DcWebxdcWidget.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1261,7 +1342,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1520; + LastSwiftUpdateCheck = 1610; LastUpgradeCheck = 1500; ORGANIZATIONNAME = "merlinux GmbH"; TargetAttributes = { @@ -1292,6 +1373,9 @@ B2D0E4912B93A4B200791949 = { CreatedOnToolsVersion = 15.2; }; + D878C4FA2CF72AA0009AF551 = { + CreatedOnToolsVersion = 16.1; + }; }; }; buildConfigurationList = 7A9FB13B1FB061E2001FEA36 /* Build configuration list for PBXProject "deltachat-ios" */; @@ -1354,6 +1438,7 @@ 7A9FB13F1FB061E2001FEA36 /* deltachat-ios */, 30E8F20F2447285600CE2C90 /* DcShare */, B2D0E4912B93A4B200791949 /* DcNotificationService */, + D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */, ); }; /* End PBXProject section */ @@ -1397,6 +1482,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D878C4F92CF72AA0009AF551 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D87C64672D01E4EF004472D6 /* Localizable.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -1739,6 +1832,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D878C4F72CF72AA0009AF551 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8C7B6982CF87F5E003A6AD1 /* DcMsg+Extension.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1752,6 +1853,11 @@ target = B2D0E4912B93A4B200791949 /* DcNotificationService */; targetProxy = B2D0E4972B93A4B200791949 /* PBXContainerItemProxy */; }; + D878C50B2CF72AA1009AF551 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D878C4FA2CF72AA0009AF551 /* DcWebxdcWidget */; + targetProxy = D878C50A2CF72AA1009AF551 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -2349,6 +2455,82 @@ }; name = Release; }; + D878C50D2CF72AA1009AF551 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = "deltachat-ios/deltachat-ios.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8Y86453UA8; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = DcWebxdcWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = DcWebxdcWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 merlinux GmbH. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcWebxdcWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D878C50E2CF72AA1009AF551 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = "deltachat-ios/deltachat-ios.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8Y86453UA8; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = DcWebxdcWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = DcWebxdcWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 merlinux GmbH. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcWebxdcWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2388,6 +2570,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D878C5102CF72AA1009AF551 /* Build configuration list for PBXNativeTarget "DcWebxdcWidget" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D878C50D2CF72AA1009AF551 /* Debug */, + D878C50E2CF72AA1009AF551 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 7A9FB1381FB061E2001FEA36 /* Project object */; diff --git a/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme new file mode 100644 index 000000000..de6f24785 --- /dev/null +++ b/deltachat-ios.xcodeproj/xcshareddata/xcschemes/DcWebxdcWidgetExtension.xcscheme @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deltachat-ios/Chat/ChatViewController.swift b/deltachat-ios/Chat/ChatViewController.swift index bd412610e..dc160c170 100644 --- a/deltachat-ios/Chat/ChatViewController.swift +++ b/deltachat-ios/Chat/ChatViewController.swift @@ -2021,6 +2021,15 @@ extension ChatViewController { menuElements.append(action) } + private func isLinkTapped(indexPath: IndexPath, point: CGPoint) -> String? { + if let cell = tableView.cellForRow(at: indexPath) as? BaseMessageCell { + let label = cell.messageLabel.label + let localTouchLocation = tableView.convert(point, to: label) + return label.getCopyableLinkText(localTouchLocation) + } + return nil + } + // context menu for iOS 13+ override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { let messageId = messageIds[indexPath.row] @@ -2067,7 +2076,13 @@ extension ChatViewController { UIAction.menuAction(localizationKey: "forward", image: image, indexPath: indexPath, action: { self.forward(at: $0 ) }) ) - if let text = message.text, !text.isEmpty { + if let link = isLinkTapped(indexPath: indexPath, point: point) { + children.append( + UIAction.menuAction(localizationKey: "menu_copy_link_to_clipboard", systemImageName: "link", indexPath: indexPath, action: { _ in + UIPasteboard.general.string = link + }) + ) + } else if let text = message.text, !text.isEmpty { let copyTitle = message.file == nil ? "global_menu_edit_copy_desktop" : "menu_copy_text_to_clipboard" children.append( UIAction.menuAction(localizationKey: copyTitle, systemImageName: "doc.on.doc", indexPath: indexPath, action: { self.copyToClipboard(at: $0 ) }) diff --git a/deltachat-ios/Chat/Views/MessageLabel.swift b/deltachat-ios/Chat/Views/MessageLabel.swift index f23956e40..181ab1826 100644 --- a/deltachat-ios/Chat/Views/MessageLabel.swift +++ b/deltachat-ios/Chat/Views/MessageLabel.swift @@ -452,49 +452,65 @@ open class MessageLabel: UILabel { } - open func handleGesture(_ touchLocation: CGPoint) -> Bool { - - guard let index = stringIndex(at: touchLocation) else { return false } + internal func detectLink(_ touchLocation: CGPoint) -> (DetectorType, NewMessageTextCheckingType)? { + guard let index = stringIndex(at: touchLocation) else { return nil } for (detectorType, ranges) in rangesForDetectors { for (range, value) in ranges { if range.contains(index) { - handleGesture(for: detectorType, value: value) - return true + return (detectorType, value) } } } - return false + + return nil } - /// swiftlint:disable cyclomatic_complexity - private func handleGesture(for detectorType: DetectorType, value: NewMessageTextCheckingType) { + internal func getCopyableLinkText(_ touchLocation: CGPoint) -> String? { + guard let (_, value) = detectLink(touchLocation) else { return nil } + + switch value { + case let .link(url): + guard let url else { return nil } + return url.absoluteString + case let .phoneNumber(phoneNumber): + return phoneNumber + case let .custom(_, match): + return match + case .addressComponents, .date, .transitInfoComponents: + return nil + } + } + + internal func handleGesture(_ touchLocation: CGPoint) -> Bool { + guard let (detectorType, value) = detectLink(touchLocation) else { return false } + switch value { case let .addressComponents(addressComponents): var transformedAddressComponents = [String: String]() - guard let addressComponents = addressComponents else { return } + guard let addressComponents else { return false } addressComponents.forEach { (key, value) in transformedAddressComponents[key.rawValue] = value } handleAddress(transformedAddressComponents) case let .phoneNumber(phoneNumber): - guard let phoneNumber = phoneNumber else { return } + guard let phoneNumber else { return false } handlePhoneNumber(phoneNumber) case let .date(date): - guard let date = date else { return } + guard let date else { return false } handleDate(date) case let .link(url): - guard let url = url else { return } + guard let url else { return false } handleURL(url) case let .transitInfoComponents(transitInformation): var transformedTransitInformation = [String: String]() - guard let transitInformation = transitInformation else { return } + guard let transitInformation else { return false } transitInformation.forEach { (key, value) in transformedTransitInformation[key.rawValue] = value } handleTransitInformation(transformedTransitInformation) case let .custom(pattern, match): - guard let match = match else { return } + guard let match else { return false } switch detectorType { case .hashtag: handleHashtag(match) @@ -506,6 +522,8 @@ open class MessageLabel: UILabel { handleCustom(pattern, match: match) } } + + return true } private func handleAddress(_ addressComponents: [String: String]) { diff --git a/deltachat-ios/bqi.lproj/Localizable.strings b/deltachat-ios/bqi.lproj/Localizable.strings index b529a6138..0e2e27905 100644 --- a/deltachat-ios/bqi.lproj/Localizable.strings +++ b/deltachat-ios/bqi.lproj/Localizable.strings @@ -14,317 +14,317 @@ "default_value" = "پؽش فرز (%1$@)"; "default_value_as_above" = "پؽش فرز (جۊر روء)"; "custom" = "سفارشی"; -"none" = "hičkoyêki"; -"automatic" = "xotkār"; -"strict" = "saxtger"; -"open" = "gušiďên"; -"download" = "dānlod"; -"downloading" = "honêy dānlod êbu..."; -"open_attachment" = "gušiďên zamima"; -"join" = "avoďên"; -"rejoin" = "zênu avoďên"; -"delete" = "pāk kerdên"; -"info" = "zêbār"; -"update" = "vê ru kerdên"; -"emoji" = "êmuji"; -"attachment" = "zamima"; -"back" = "vorgaštên"; -"close" = "bastên"; +"none" = "هیچکویکی"; +"automatic" = "خوتکار"; +"strict" = "سختگر"; +"open" = "گۊشیڌن"; +"download" = "دانلود"; +"downloading" = "هونی دانلود ابۊ..."; +"open_attachment" = "گۊشیڌن زمینه"; +"join" = "ٱووڌن"; +"rejoin" = "ز نۊ ٱووڌن"; +"delete" = "پاک کردن"; +"info" = "زبار"; +"update" = "ورۊ کردن"; +"emoji" = "ایموجی"; +"attachment" = "زمینه"; +"back" = "وورگشتن"; +"close" = "بستن"; // "Resend" means "Sending the selected message(s) again to the same chat". The string is used in a menu and should be as short as possible. Resending may be needed after failures or to repost old messages to new members. -"resend" = "zênu bêfêšn"; +"resend" = "ز نۊ بفشن"; // Verb "to archive", as in "put a chat in the archive", not a noun "The Archive". -"archive" = "bāyêguvi"; +"archive" = "بایگۊوی"; // Verb "to unarchive", as in "remove a chat from the archive", opposite of the previous string -"unarchive" = "bêdar zêyďên zê bāyêguvi"; -"mute" = "bî dong"; -"save" = "zaft"; -"chat" = "goftoloft"; -"media" = "vārasgar"; -"profile" = "porofāyl"; -"main_menu" = "balgê asli"; -"start_chat" = "nāhāďên pā goftoloft"; -"show_full_message" = "nêšow dāďên payomê kāmêl"; +"unarchive" = "و در زیڌن ز بایگۊوی"; +"mute" = "بؽ دونگ"; +"save" = "زفت کردن"; +"chat" = "گوفت ۉ لوفت"; +"media" = "وارسگر"; +"profile" = "پوروفایل"; +"main_menu" = "نومگه ٱسلی"; +"start_chat" = "ناڌن پا گوفت ۉ لوفت"; +"show_full_message" = "نشۉ داڌن پیوم کامل..."; // Stay short here, say ~16 characters. The source string could also be "All Read", maybe that hint can make translations easier :) -"mark_all_as_read" = "poy nê xonde biďe bêza"; +"mark_all_as_read" = "پوی ن خونده بیڌه بزݩ"; // Shortest text for "Mark as being read". In english, this could be "Read" (past tense of "to read"), in german, this could be "Gelesen". -"mark_as_read_short" = "xonde vâbiďa"; +"mark_as_read_short" = "علامت و عونوان خونده بیڌه"; // Placeholder text when something is loading -"loading" = "honêy bār êvane..."; -"hide" = "bîďār kerdên"; -"activate" = "faāl kerdên"; -"load_remote_content" = "bār vani šîvātā zê dar"; -"always" = "hamiša"; -"once" = "him ya kêrat"; -"show_warning" = "nêšow dāďên hošdār"; -"show_password" = "nêšow dāďên razm"; -"hide_password" = "bîďār kerdên razm"; -"not_now" = "sako na"; -"never" = "hič"; -"one_moment" = "yadamow..."; -"done" = "anjom vâbi"; -"offline" = "āflāyn"; +"loading" = "هونی بار اونه..."; +"hide" = "بؽڌار کردن"; +"activate" = "فعال کردن"; +"load_remote_content" = "بار ونی شؽواتا ز در"; +"always" = "همیشه"; +"once" = "هیم ی کرت"; +"show_warning" = "نشۉ داڌن هوشدار"; +"show_password" = "نشۉ داڌن رزم"; +"hide_password" = "بؽڌار کردن رزم"; +"not_now" = "سکو ن"; +"never" = "هیچ"; +"one_moment" = "ی دمۉ..."; +"done" = "ٱنجوم وابی"; +"offline" = "آفلاین"; // For the next view or as "continue". Should be as short as possible. -"next" = "niyâyi"; -"error" = "xatā"; -"error_x" = "xatā: %1$@"; -"file_not_found" = "%1$@ najost"; -"copied_to_clipboard" = "mênê virga lefgiri vâbi."; -"contacts_headline" = "homdangal"; -"email_address" = "nêšuvi imêyl"; -"password" = "razm"; -"existing_password" = "razmê mowjud"; -"now" = "sako"; +"next" = "نیایی"; +"error" = "ختا"; +"error_x" = "ختا: %1$@"; +"file_not_found" = "%1$@ ن نجوست."; +"copied_to_clipboard" = "من ویرگه لف گیری وابی."; +"contacts_headline" = "هومدنگۉݩ"; +"email_address" = "نشۊوی ایمیل"; +"password" = "رزم"; +"existing_password" = "رزم مۉجۊڌ"; +"now" = "سکو"; // Headline for destructive actions with no undo. Could also be "Caution" or "Warning". -"danger" = "bêpā"; -"today" = "amru"; -"yesterday" = "duš"; -"this_week" = "i hafta"; -"this_month" = "i mā"; -"last_week" = "haftê zitari"; -"last_month" = "mā zitari"; +"danger" = "بپا"; +"today" = "ٱمرۊ"; +"yesterday" = "دۊش"; +"this_week" = "ای هفته"; +"this_month" = "ای ما"; +"last_week" = "هفته زیتری"; +"last_month" = "ما زیتری"; // Refers to the time a contact was last seen. Shown below contact name in the profile. The placeholder will be replaced by date or time, resulting in "Last seen at 12:13 AM" or "Last seen Nov 12" -"last_seen_at" = "ritorê dindâyi mênê %1$@"; +"last_seen_at" = "ریتور دیندایی من %1$@"; // Refers to the time a contact was last seen. Shown below contact name in the profile. The placeholder will be replaced by a relative point in time as "3 minutes ago" (see https://momentjs.com for more examples and languages) -"last_seen_relative" = "ritorê dindâyi %1$@"; -"last_seen_unknown" = "ritorê dindâyi: diyār nî"; +"last_seen_relative" = "ریتور دیندایی %1$@"; +"last_seen_unknown" = "ریتور دیندایی: دیار نؽ"; // Shown beside messages that are "N minutes old". Prefer short strings, or well-known abbreviations. // Shown beside messages that are "N hours old". Prefer short strings, or well-known abbreviations. // Short form for "N Items Selected" -"self" = "mo"; -"draft" = "pîš hîl"; -"image" = "šîvāt"; +"self" = "مو"; +"draft" = "پؽش هؽل"; +"image" = "شؽوات"; // Used in summaries as "Draft: Reply", similar as "Draft: Image". Use a noun here, not a verb (not: "to reply") -"reply_noun" = "vêlom"; -"gif" = "gif"; +"reply_noun" = "ولوم"; +"gif" = "گیف"; // "Stickers" as known from other messengers; in some languages, the English "Sticker" is fine. -"sticker" = "êstikêr"; -"images" = "šîvātā"; -"audio" = "dong"; -"voice_message" = "payomê dong"; -"video" = "film"; -"documents" = "sanadā"; -"contact" = "homdang"; -"camera" = "šîvātgėr"; +"sticker" = "استیکر"; +"images" = "شؽواتا"; +"audio" = "دونگ"; +"voice_message" = "پیوم دونگ"; +"video" = "فیلم"; +"documents" = "سندا"; +"contact" = "هومدنگ"; +"camera" = "شؽواتگر"; // As in "start a video recording" or "take a photo"; eg. the description of the "shutter button" in cameras -"capture" = "gêrîďên"; -"switch_camera" = "ālêštê šîvātgêr"; -"toggle_fullscreen" = "ālêštê vaziyat poy balga"; -"location" = "jāga"; -"locations" = "jāgayal"; -"gallery" = "šîvātmāl"; -"images_and_videos" = "šîvātā vo filmā"; -"file" = "fāyl"; -"files" = "fāylā"; +"capture" = "گرؽڌن"; +"switch_camera" = "آلشت شؽواتگر"; +"toggle_fullscreen" = "آلشت وزیت پوی بلگه"; +"location" = "جاگه"; +"locations" = "جاگه یل"; +"gallery" = "شؽوات مال"; +"images_and_videos" = "شؽواتا وو فیلما"; +"file" = "فایل"; +"files" = "فایلا"; // "App" is used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -"webxdc_app" = "barnoma"; +"webxdc_app" = "برنومه"; // plural of "App"; used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -"webxdc_apps" = "barnomêyal"; -"unknown" = "nāšênās"; -"green" = "sawz"; -"red" = "so\'r"; -"blue" = "kêvu"; -"purple" = "bênawš"; -"white" = "êsbîď"; -"zoom" = "gap kerdên"; -"extra_small" = "qalêvê kučir"; -"small" = "kučir"; -"normal" = "âdi"; -"large" = "gap"; -"extra_large" = "qalêvê gap"; -"fast" = "zêl"; -"slow" = "hovār"; +"webxdc_apps" = "برنومه یل"; +"unknown" = "ناشناس"; +"green" = "سوز"; +"red" = "سوئر"; +"blue" = "کوۊ"; +"purple" = "بنوش"; +"white" = "اسبؽڌ"; +"zoom" = "گپ کردن"; +"extra_small" = "قلوه کۊچیر"; +"small" = "کۊچیر"; +"normal" = "عادی"; +"large" = "گپ"; +"extra_large" = "قلوه گپ"; +"fast" = "زل"; +"slow" = "هووار"; // menu labels (or icon, buttons...) -"menu_new_contact" = "homdangê nu"; -"menu_new_chat" = "goftoloftê nu"; -"menu_new_group" = "jarga nu"; +"menu_new_contact" = "هومدنگ نۊ"; +"menu_new_chat" = "گوفت ۉ لوفت نۊ"; +"menu_new_group" = "جرگه نۊ"; // consider keeping the term "broadcast" as in WhatsApp or Telegram -"broadcast_list" = "nomga Broadcast"; -"broadcast_lists" = "nomgayal Broadcast"; -"new_broadcast_list" = "nomga Broadcast nu"; -"menu_send" = "fêšnāďên"; -"menu_edit_group" = "ālêštê jarga"; -"menu_group_name_and_image" = "nom vo šîvātê jarga"; -"menu_archive_chat" = "bāyêguvi kerdên goftoloft"; -"menu_unarchive_chat" = "bêdar zêyďên goftoloft zê bāyêguvi"; -"menu_add_attachment" = "êzāf kerdên zamima"; -"menu_leave_group" = "ra\'ďên zê jarga"; -"menu_delete_chat" = "pāk kerdên poy goftoloft"; +"broadcast_list" = "نومگه Broadcast"; +"broadcast_lists" = "نومگه یل Broadcast"; +"new_broadcast_list" = "نومگه Broadcast نۊ"; +"menu_send" = "فشناڌن"; +"menu_edit_group" = "آلشت جرگه"; +"menu_group_name_and_image" = "نوم وو شؽوات جرگه"; +"menu_archive_chat" = "بایگۊوی گوفت ۉ لوفت"; +"menu_unarchive_chat" = "و در زیڌن گوفت ۉ لوفت ز بایگۊوی"; +"menu_add_attachment" = "ازاف کردن زمینه"; +"menu_leave_group" = "رئڌن ز جرگه"; +"menu_delete_chat" = "پاک کردن گوفت ۉ لوفت"; // Command to delete all messages in a chat. The chat itself will not be deleted but will be empty afterwards, so make sure to be different from "Delete Chat" here. "Clear" is a verb here, "Empty Chat" would also be fine (eg. in German "Chat leeren") -"clear_chat" = "roftênê virgārê goftoloft"; -"menu_delete_messages" = "pāk kerdên payomā"; -"delete_contact" = "pāk kerdên homdang"; -"menu_delete_location" = "i jāga pāk bu?"; -"menu_copy_to_clipboard" = "lefgiri mênê virga"; -"menu_copy_link_to_clipboard" = "lefgiri ling"; -"menu_copy_text_to_clipboard" = "lefgiri hîl"; -"menu_copy_image_to_clipboard" = "lefgiri šîvāt"; -"menu_copy_email_to_clipboard" = "lefgiri imêyl"; -"paste_from_clipboard" = "jā vandên zê virga"; -"menu_reply" = "vêlom bê payom"; -"menu_mute" = "bî dong kerdên vārasuviyā"; -"menu_unmute" = "vā dong kerdên"; -"menu_all_media" = "poy vārasgarā"; +"clear_chat" = "روفتن ویرگار گوفت ۉ لوفت"; +"menu_delete_messages" = "پاک کردن پیوما"; +"delete_contact" = "پاک کردن هومدنگ"; +"menu_delete_location" = "ای جاگه پاک بۊ؟"; +"menu_copy_to_clipboard" = "لف گیری من ویرگه"; +"menu_copy_link_to_clipboard" = "لف گیری لینک"; +"menu_copy_text_to_clipboard" = "لف گیری هؽل"; +"menu_copy_image_to_clipboard" = "لف گیری شؽوات"; +"menu_copy_email_to_clipboard" = "لف گیری ایمیل"; +"paste_from_clipboard" = "جا وندن ز ویرگه"; +"menu_reply" = "ولوم ب پیوم"; +"menu_mute" = "بؽ دونگ کردن وارسۊویا"; +"menu_unmute" = "وا دونگ کردن"; +"menu_all_media" = "پوی وارسگرا"; // Command to jump to the original message corresponding to a gallery image or document -"show_in_chat" = "nêšow dāďên mênê goftoloft"; -"menu_share" = "yakrasuvi"; -"accept" = "qêvul"; -"menu_play" = "pêšk"; -"menu_pause" = "vāstāďên"; -"menu_scroll_to_bottom" = "ra\'ďên bê lam"; -"menu_scroll_to_top" = "ra\'ďên bê ro\'"; -"menu_help" = "hayâri"; -"menu_select_all" = "pêsandê poy"; -"select_chat" = "pêsandê goftoloft"; -"select_more" = "pêsandê bištar"; -"menu_edit_name" = "ālêštê nom"; -"menu_settings" = "sāmovā"; -"menu_advanced" = "pîš ra\'ďa"; -"menu_view_profile" = "niyaštên porofāyl"; -"menu_zoom_in" = "gap kerdên"; -"menu_zoom_out" = "kučir kerdên"; -"menu_save_log" = "zaft kerdên gozārêš"; -"menu_more_options" = "guzinêyalê bištar"; -"jump_to_message" = "ra\'ďên bê payom"; -"device_talk" = "payomā dasgā"; -"device_talk_subtitle" = "payomā mêni"; -"edit_contact" = "ālêštê homdang"; +"show_in_chat" = "نشۉ داڌن من گوفت ۉ لوفت"; +"menu_share" = "یک رسۊوی"; +"accept" = "قوۊل"; +"menu_play" = "پشک"; +"menu_pause" = "واڌاشتن"; +"menu_scroll_to_bottom" = "رئڌن ب لم"; +"menu_scroll_to_top" = "رئڌن ب روء"; +"menu_help" = "هیاری"; +"menu_select_all" = "پسند پوی"; +"select_chat" = "پسند گوفت ۉ لوفت"; +"select_more" = "پسند بیشتر"; +"menu_edit_name" = "آلشت نوم"; +"menu_settings" = "سامووا"; +"menu_advanced" = "پؽش رئڌه"; +"menu_view_profile" = "نیشتن پوروفایل"; +"menu_zoom_in" = "گپ کردن"; +"menu_zoom_out" = "کۊچیر کردن"; +"menu_save_log" = "زفت کردن گوزارش"; +"menu_more_options" = "گۊزینیل بیشتر"; +"jump_to_message" = "رئڌن ب پیوم"; +"device_talk" = "پیوما دسگا"; +"device_talk_subtitle" = "پیوما منی"; +"edit_contact" = "آلشت هومدنگ"; // Verb "to pin", making something sticky, not a noun or abbreviation for "pin number". -"pin_chat" = "disniďên goftoloft"; +"pin_chat" = "دیسنیڌن گوفت ۉ لوفت"; // Verb "to pin", making something sticky, not a noun or abbreviation for "pin number". -"pin" = "disniďên"; -"source_code" = "kodê bončak"; -"mute_for_one_hour" = "bî dong kerdên si 1 sāat"; -"mute_for_two_hours" = "bî dong kerdên si 2 sāat"; -"mute_for_one_day" = "bî dong kerdên si 1 ru"; -"mute_for_seven_days" = "bî dong kerdên si 7 ru"; -"mute_forever" = "bî dong kerdên si hamiša"; -"share_location_for_5_minutes" = "si 5 dîqa"; -"share_location_for_30_minutes" = "si 30 dîqa"; -"share_location_for_one_hour" = "si 1 sāat"; -"share_location_for_two_hours" = "si 2 sāat"; -"share_location_for_six_hours" = "si 6 sāat"; -"file_saved_to" = "fāyl mênê \"%1$@\" zaft vâbi."; -"videochat" = "goftoloftê vidiyoyi"; -"videochat_invitation" = "mokêš bê goftoloft vidiyoyi"; -"ask_start_chat_with" = "goftoloft vā %1$@?"; -"ask_delete_value" = "pāk kerdên %1$@?"; +"pin" = "دیسنیڌن"; +"source_code" = "کود بونچک"; +"mute_for_one_hour" = "بؽ دونگ کردن سی 1 ساعت"; +"mute_for_two_hours" = "بؽ دونگ کردن سی 2 ساعت"; +"mute_for_one_day" = "بؽ دونگ کردن سی 1 رۊ"; +"mute_for_seven_days" = "بؽ دونگ کردن سی 7 رۊ"; +"mute_forever" = "بؽ دونگ کردن سی همیشه"; +"share_location_for_5_minutes" = "سی 5 دؽقه"; +"share_location_for_30_minutes" = "سی 30 دؽقه"; +"share_location_for_one_hour" = "سی 1 ساعت"; +"share_location_for_two_hours" = "سی 2 ساعت"; +"share_location_for_six_hours" = "سی 6 ساعت"; +"file_saved_to" = "فایل من «%1$@» زفت وابی."; +"videochat" = "گوفت ۉ لوفت ویدیویی"; +"videochat_invitation" = "موکش و گوفت ۉ لوفت ویدیویی"; +"ask_start_chat_with" = "گوفت ۉ لوفت وا %1$@؟"; +"ask_delete_value" = "پاک کردن %1$@؟"; // %1$s is replaced by a comma-separated list of names -"ask_remove_members" = "pāk kerdên %1$@ zê jarga?"; +"ask_remove_members" = "پاک کردن %1$@ ز جرگه؟"; // %1$s is replaced by a comma-separated list of names -"ask_remove_from_broadcast" = "%1$@ zê nomga Broadcast pāk bu?"; -"open_url_confirmation" = "êxuy i lingê nê buguši?"; +"ask_remove_from_broadcast" = "%1$@ ز نومگه Broadcast پاک بۊ؟"; +"open_url_confirmation" = "اخۊی ای لینگن بۊگۊشی؟"; // contact list -"contacts_title" = "homdangal"; -"contacts_empty_hint" = "bî homdang."; -"chat_please_enter_message" = "ya payom bêza"; -"chat_record_slide_to_cancel" = "si raď kerdên bêkašês"; -"chat_share_with_title" = "yakrasuvi vā"; -"chat_input_placeholder" = "payom"; +"contacts_title" = "هومدنگۉݩ"; +"contacts_empty_hint" = "بؽ هومدنگ."; +"chat_please_enter_message" = "ی پیوم بزݩ."; +"chat_record_slide_to_cancel" = "سی رڌ کردن بکشس."; +"chat_share_with_title" = "یک رسۊوی وا"; +"chat_input_placeholder" = "پیوم"; "chat_request_label" = "darxāst"; -"chat_no_messages" = "payomi nî."; -"chat_self_talk_subtitle" = "payomāyî ke si xom fêšnāme."; +"chat_no_messages" = "پیومؽ نؽ."; +"chat_self_talk_subtitle" = "پیومایی ک سی خوم فشنام."; "saved_messages" = "payomā zaft biďa"; // Should match "Saved" from "Saved messages" "saved" = "zaft vâbi"; "save_as" = "zaft vê onvān"; -"retry_send" = "zênu si fêšnāďênê payom taqalā ko"; +"retry_send" = "ز نۊ سی فشناڌن پیوم تقلا کوݩ"; // mailing lists -"mailing_list" = "nomga posti"; +"mailing_list" = "نومگه پوستی"; // title shown above a list of chats where one should be selected (eg. when sharing files from a webxdc). the placeholder will be replaced by a file name -"send_file_to" = "fêšnāďên \"%1$@\" vê..."; +"send_file_to" = "فشناڌن \"%1$@\" و..."; // title shown above a list contacts where one should be selected (eg. when a webxdc attempts to send a message to a chat) -"send_message_to" = "fêšnāďên payom vê..."; -"show_location_traces" = "nêšow dāďên jā pā"; +"send_message_to" = "فشناڌن پیوم و..."; +"show_location_traces" = "نشۉ داڌن جا پا"; // search -"search" = "pitiniďên"; +"search" = "پیتینیڌن"; // create/edit groups, contact/group profile -"group_name" = "nomê jarga"; -"group_avatar" = "šîvātê jarga"; -"remove_group_image" = "pāk kerdên šîvātê jarga"; -"change_group_image" = "ālêštê šîvātê jarga"; -"group_create_button" = "vorkêlê jarga"; -"group_add_members" = "avordênê mêntor"; -"tab_contact" = "homdang"; -"tab_group" = "jarga"; -"tab_gallery" = "šîvātmāl"; -"tab_docs" = "sanadā"; -"tab_links" = "lingā"; -"tab_map" = "naqša"; -"media_preview" = "nêšow dāďên vārasgar"; -"send_message" = "fêšnāďên payom"; +"group_name" = "نوم جرگه"; +"group_avatar" = "شؽوات جرگه"; +"remove_group_image" = "پاک کردن شؽوات جرگه"; +"change_group_image" = "آلشت شؽوات جرگه"; +"group_create_button" = "وورکل جرگه"; +"group_add_members" = "ٱووردن منتور"; +"tab_contact" = "هومدنگ"; +"tab_group" = "جرگه"; +"tab_gallery" = "شؽوات مال"; +"tab_docs" = "سندا"; +"tab_links" = "لینگا"; +"tab_map" = "نقشه"; +"media_preview" = "نشۉ داڌن وارسگر"; +"send_message" = "فشناڌن پیوم"; // Headline in the "Connectivity" view. Placeholder will be replaced by the domain of the configured email-address. -"storage_on_domain" = "zaft kerdên mênê %1$@"; +"storage_on_domain" = "زفت کردن من %1$@"; // Shown in the title bar if the app is "Connecting"; prefer short strings. The ellipsis is a single character (…), not three (...) -"connectivity_connecting" = "honêy menpiz êbu..."; +"connectivity_connecting" = "هونی منپیز ابۊ..."; // Shown in the title bar if the app is "Updating" (eg. getting new/old message, sync things); prefer short strings. The ellipsis is a single character (…), not three (...) -"connectivity_updating" = "honêy vê ru êbu..."; +"connectivity_updating" = "هونی ورۊ ابۊ..."; // Shown in the setting if the app is "Connected" -"connectivity_connected" = "menpiz vâbi"; -"sending" = "honêy fêšnāďe êbu..."; +"connectivity_connected" = "منپیز وابی"; +"sending" = "هونی افشنه..."; // Subtitle in quota context of "Connetivity" view. Should be be plural always, no number is prefixed. -"messages" = "payomā"; -"sync_all" = "vê ru kerdên poy"; -"pref_your_name" = "nomê to"; -"pref_notifications_show" = "nêšow dāďên"; -"pref_language" = "zow"; -"pref_chats" = "goftoloftā"; +"messages" = "پیوما"; +"sync_all" = "ورۊ کردن پوی"; +"pref_your_name" = "نوم ایسا"; +"pref_notifications_show" = "نشۉ داڌن"; +"pref_language" = "زۉݩ"; +"pref_chats" = "گوفت ۉ لوفتا"; // deprecated -"pref_message_text_size" = "hêndā hîlê payom"; -"pref_log_header" = "gozārêš"; -"pref_background_btn_gallery" = "pêsand zê šîvātmāl"; -"pref_show_emails_all" = "poy"; +"pref_message_text_size" = "هندا هؽلا پیوم"; +"pref_log_header" = "گوزارش"; +"pref_background_btn_gallery" = "پسند ز شؽوات مال"; +"pref_show_emails_all" = "پوی"; // %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB -"up_to_x" = "tā saqfe %1$@"; +"up_to_x" = "تا سقف %1$@"; // %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB. Resulting string eg. "1 MiB message" -"n_bytes_message" = "payom %1$@"; -"profile_image_select" = "pêsandê šîvātê porofāyl"; -"profile_image_delete" = "pāk kerdên šîvātê porofāyl"; -"pref_edit_profile" = "ālêštê porofāyl"; +"n_bytes_message" = "پیوم %1$@"; +"profile_image_select" = "پسند شؽوات پوروفایل"; +"profile_image_delete" = "پاک کردن شؽوات پوروفایل"; +"pref_edit_profile" = "آلشت پوروفایل"; // Emoji picker and categories -"emoji_search_results" = "natijêyalê pitiniďên"; -"emoji_symbols" = "nemāďā"; -"emoji_flags" = "bêyraqā"; +"emoji_search_results" = "نتیجیل پیتینیڌن"; +"emoji_symbols" = "نمادا"; +"emoji_flags" = "بیرقا"; // automatically delete message -"delete_old_messages" = "pāk kerdên payomā qaďimi"; -"autodel_device_title" = "pāk kerdên payomā zê dasgā"; -"autodel_server_title" = "pāk kerdên payomā zê sêrvêr"; +"delete_old_messages" = "پاک کردن پیوما قڌیمی"; +"autodel_device_title" = "پاک کردن پیوما ز دسگا"; +"autodel_server_title" = "پاک کردن پیوما ز سرور"; // "At once" in the meaning of "Immediately", without any intervening time. -"autodel_at_once" = "ya kêrat"; -"after_30_seconds" = "ba\'ďê 30 sâniya"; -"after_1_minute" = "ba\'ďê 1 dîqa"; -"after_5_minutes" = "ba\'ďê 5 dîqa"; -"after_30_minutes" = "ba\'ďê 30 dîqa"; -"autodel_after_1_hour" = "ba\'ďê 1 sāat"; -"autodel_after_1_day" = "ba\'ďê 1 ru"; -"autodel_after_1_week" = "ba\'ďê 1 hafta"; -"after_5_weeks" = "ba\'ďê 5 hafta"; -"autodel_after_1_year" = "ba\'ďê 1 sāl"; +"autodel_at_once" = "ی کرت دیندا دانلود"; +"after_30_seconds" = "دیندا 30 سانیه"; +"after_1_minute" = "دیندا 1 دؽقه"; +"after_5_minutes" = "دیندا 5 دؽقه"; +"after_30_minutes" = "دیندا 30 دؽقه"; +"autodel_after_1_hour" = "دیندا 1 ساعت"; +"autodel_after_1_day" = "دیندا 1 رۊ"; +"autodel_after_1_week" = "دیندا 1 هفته"; +"after_5_weeks" = "دیندا 5 هفته"; +"autodel_after_1_year" = "دیندا 1 سال"; // "left" in the meaning of "exited"; %1$s will be replaced by name and address of the contact leaving the group -"group_left_by_other" = "%1$@ zê jarga ra\'ď."; -"qrshow_x_joining" = "%1$@ mêntor vâbi."; -"qrshow_x_verified" = "%1$@ taiď vâbi."; -"contact_verified" = "%1$@ taiď vâbi."; +"group_left_by_other" = "%1$@ ز جرگه رئڌ."; +"qrshow_x_joining" = "%1$@ ٱووڌ."; +"qrshow_x_verified" = "%1$@ تاییڌ وابی."; +"contact_verified" = "%1$@ تاییڌ وابی."; // Shown in contact profile. The placeholder will be replaced by the name of the contact that introduced the contact. -"verified_by" = "taiď vâbiďa vê das %1$@"; -"notify_reply_button" = "vėlom"; -"notify_new_message" = "payomė nu"; -"notify_name_and_message" = "nom vo payom"; -"notify_name_only" = "têynā nom"; -"notify_no_name_or_message" = "nom yā payomi nî"; -"global_menu_preferences_language_desktop" = "zow"; -"global_menu_file_desktop" = "fāyl"; -"global_menu_file_quit_desktop" = "vê dar zêyďên"; -"global_menu_edit_desktop" = "ālêšt"; -"global_menu_edit_cut_desktop" = "jā goro kerdên"; -"global_menu_edit_copy_desktop" = "lefgiri"; -"global_menu_edit_paste_desktop" = "jā vandên"; -"global_menu_file_open_desktop" = "gušiďênê dêltā čat"; -"delete_message_desktop" = "pāk kerdên payom"; -"remove_desktop" = "pāk kerdên"; -"save_desktop" = "zaft"; -"name_desktop" = "nom"; -"select_group_image_desktop" = "pêsandê šîvātê jarga"; -"message_detail_sent_desktop" = "fêšnāďe vâbi"; -"message_detail_received_desktop" = "gêrîďe vâbi"; -"show_window" = "nêšow dāďênê nimdari"; -"open_settings" = "gušiďênê sāmovā"; +"verified_by" = "تاییڌ وابیڌه و دس %1$@"; +"notify_reply_button" = "ولوم"; +"notify_new_message" = "پیوم نۊ"; +"notify_name_and_message" = "نوم وو پیوم"; +"notify_name_only" = "تینا نوم"; +"notify_no_name_or_message" = "بؽ نوم یا پیوم"; +"global_menu_preferences_language_desktop" = "زۉݩ"; +"global_menu_file_desktop" = "فایل"; +"global_menu_file_quit_desktop" = "و در زیڌن"; +"global_menu_edit_desktop" = "آلشت"; +"global_menu_edit_cut_desktop" = "جا گورو"; +"global_menu_edit_copy_desktop" = "لف گیری"; +"global_menu_edit_paste_desktop" = "جا وندن"; +"global_menu_file_open_desktop" = "گۊشیڌن دلتا چت"; +"delete_message_desktop" = "پاک کردن پیوم"; +"remove_desktop" = "پاک کردن"; +"save_desktop" = "زفت"; +"name_desktop" = "نوم"; +"select_group_image_desktop" = "پسند شؽوات جرگه"; +"message_detail_sent_desktop" = "فشناڌ"; +"message_detail_received_desktop" = "گرؽڌه وابی"; +"show_window" = "نشۉ داڌن نیمدری"; +"open_settings" = "گۊشیڌن سامووا"; diff --git a/deltachat-ios/bqi.lproj/Localizable.stringsdict b/deltachat-ios/bqi.lproj/Localizable.stringsdict index ecd0d9fea..b0427a3e6 100644 --- a/deltachat-ios/bqi.lproj/Localizable.stringsdict +++ b/deltachat-ios/bqi.lproj/Localizable.stringsdict @@ -13,9 +13,9 @@ NSStringFormatValueTypeKey d one - %d dîqa + %d دؽقه other - %d dîqa + %d دؽقه n_hours @@ -29,9 +29,9 @@ NSStringFormatValueTypeKey d one - %d sāat + %d ساعت other - %d sāat + %d ساعت n_chats @@ -45,9 +45,9 @@ NSStringFormatValueTypeKey d one - %d čat + %d چت other - %d čat + %d چت n_contacts @@ -61,9 +61,9 @@ NSStringFormatValueTypeKey d one - %d homdang + %d هومدنگ other - %d homdang + %d هومدنگ n_messages @@ -77,9 +77,9 @@ NSStringFormatValueTypeKey d one - %d payom + %d پیوم other - %d payom + %d پیوم n_members @@ -93,9 +93,9 @@ NSStringFormatValueTypeKey d one - %d mėntor + %d منتور other - %d mėntor + %d منتور n_selected @@ -109,9 +109,9 @@ NSStringFormatValueTypeKey d one - %d tā pêsand vâbi + %d تا پسند وابی other - %d tā pêsand vâbi + %d تا پسند وابی ask_delete_messages_simple @@ -127,7 +127,7 @@ one êxuy %d payom pāk bu? other - êxuy %d payom pāk bu? + اخۊی %d پیوم پاک بۊ؟ chat_n_new_messages @@ -141,9 +141,9 @@ NSStringFormatValueTypeKey d one - %d payomê nu + %d پیوم نۊ other - %d payomê nu + %d پیوم نۊ diff --git a/deltachat-ios/de.lproj/Localizable.strings b/deltachat-ios/de.lproj/Localizable.strings index a149baff8..161498b37 100644 --- a/deltachat-ios/de.lproj/Localizable.strings +++ b/deltachat-ios/de.lproj/Localizable.strings @@ -558,7 +558,7 @@ "switch_account" = "Profil wechseln"; "add_account" = "Profil hinzufügen"; "profile_tag" = "Private Bezeichnung"; -"profile_tag_hint" = "Familie, Arbeit, Freunde"; +"profile_tag_hint" = "z.B. Familie, Arbeit, Freunde"; "profile_tag_explain" = "Die Bezeichnung ist nur für Sie sichtbar um zwischen Profilen zu unterscheiden."; "delete_account" = "Profil löschen"; "delete_account_ask" = "Möchten Sie das Profil und alle Daten wirklich von diesem Gerät löschen?"; diff --git a/deltachat-ios/en.lproj/Localizable.strings b/deltachat-ios/en.lproj/Localizable.strings index 851566ad6..4903f6b59 100644 --- a/deltachat-ios/en.lproj/Localizable.strings +++ b/deltachat-ios/en.lproj/Localizable.strings @@ -1063,3 +1063,6 @@ "backup_successful_explain_ios" = "You can find the backup in the \"Delta Chat\" folder using the \"Files\" app.\n\nMove the backup out of this folder to keep it when deleting Delta Chat."; "location_denied" = "Location access denied"; "location_denied_explain_ios" = "In the system settings, enable \"Privacy/Location Services\" and set \"Delta Chat/Location\" to \"Always\" and \"Precise\"."; +"widget_no_apps" = "In-Chat Utilities or Games will be shown here"; +"widget_most_recent_apps_title" = "Shortcuts"; +"widget_most_recent_apps_description" = "Shows the most recent In-Chat Utilities and Games"; diff --git a/deltachat-ios/fa.lproj/Localizable.strings b/deltachat-ios/fa.lproj/Localizable.strings index b5094832e..b5aad1578 100644 --- a/deltachat-ios/fa.lproj/Localizable.strings +++ b/deltachat-ios/fa.lproj/Localizable.strings @@ -1074,4 +1074,5 @@ GNU GPL ورژن ۳ "perm_enable_bg_reminder_title" = "برای دریافت پیام‌ها به صورت پس زمینه در دلتاچت این جا را ضربه بزنید. "; "perm_enable_bg_already_done" = "قبلا اجازه دسترسی به فعالیت در پس زمینه را به دلتاچت داده‌اید. \n\n اگر پیام‌ها هنوز هم در شرایط پس زمینه نمی‌آمد لطفا تنظیم‌های سیستم را نیز بررسی نمایید. "; // device messages for updates -"update_1_50_android" = "چه چیز تازه است؟\n\n❤️‍🔥 منوی انتخاب ایموجی جدید با ایموجی‌های جدید\🎮n\n بهبود برنامه‌های چت: دریافت اعلان و بازکردن برنامه‌های پشتیبانی‌کننده در محتوا. مثلا یک خط جدید در تقویم را مستقیم باز کنید.\👍n\n زمانی که به پیام شما واکنشی داده می‌شود، خبردار می‌شوید.\n\n 🛠️ همچنین رفع اشکال‌ها و دیگر چیز‌ها در %1$@"; +"update_1_50_android" = "چه چیز تازه است؟\n\n❤️‍🔥 منوی انتخاب ایموجی جدید با ایموجی‌های جدید\🎮n\n بهبود برنامه‌های چت: دریافت اعلان و بازکردن برنامه‌های پشتیبانی‌کننده در محتوا. مثلا یک خط جدید در تقویم را مستقیم باز کنید.\👍n\n زمانی که به پیام شما واکنشی داده می‌شود، خبردار می‌شوید.\n\n 🛠️ همچنین رفع اشکال‌ها و دیگر چیز‌ها در +%1$@"; diff --git a/deltachat-ios/it.lproj/Localizable.strings b/deltachat-ios/it.lproj/Localizable.strings index b9873f555..0d3c328f7 100644 --- a/deltachat-ios/it.lproj/Localizable.strings +++ b/deltachat-ios/it.lproj/Localizable.strings @@ -548,7 +548,7 @@ "login_error_server" = "Per piacere inserisci un server / indirizzo IP valido"; "login_error_port" = "Per piacere inserisci una porta valida (1-65535)"; "login_error_required_fields" = "Per piacere inserisci un indirizzo e-mail e una password validi"; -"import_backup_title" = "Ripristina dal Backup"; +"import_backup_title" = "Ripristina da Backup"; "import_backup_ask" = "Backup trovato in \"%1$@\".\n\nVuoi importarlo e usare tutti i dati e le impostazioni salvate?"; "import_backup_no_backup_found" = "Nessun backup trovato\n\nCopia il backup in \"%1$@\" e riprova ancora. Altimenti premi \"Inizia a messaggiare\" per procedere con le impostazioni normali."; // Translators: %1$s will be replaced by the e-mail address diff --git a/scripts/untranslated.xml b/scripts/untranslated.xml index 46f445091..defebba59 100644 --- a/scripts/untranslated.xml +++ b/scripts/untranslated.xml @@ -7,4 +7,7 @@ You can find the backup in the \"Delta Chat\" folder using the \"Files\" app.\n\nMove the backup out of this folder to keep it when deleting Delta Chat. Location access denied In the system settings, enable \"Privacy/Location Services\" and set \"Delta Chat/Location\" to \"Always\" and \"Precise\". + In-Chat Utilities or Games will be shown here + Shortcuts + Shows the most recent In-Chat Utilities and Games