Skip to content

Commit 9e49e0f

Browse files
authored
Show translated text for participant messages (#776)
1 parent fd48ffb commit 9e49e0f

File tree

37 files changed

+398
-19
lines changed

37 files changed

+398
-19
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
77
- Feature rich markdown rendering with AttributedString [#757](https://github.com/GetStream/stream-chat-swiftui/pull/757)
88
- Add `Fonts.title2` for supporting markdown headers [#757](https://github.com/GetStream/stream-chat-swiftui/pull/757)
99
- Add `resignsFirstResponderOnScrollDown` to `MessageListConfig` [#769](https://github.com/GetStream/stream-chat-swiftui/pull/769)
10+
- Show auto-translated message translations ([learn more](https://getstream.io/chat/docs/ios-swift/translation/#enabling-automatic-translation)) [#776](https://github.com/GetStream/stream-chat-swiftui/pull/776)
1011
### 🔄 Changed
1112
- Uploading a HEIC photo from the library is now converted to JPEG for better compatibility [#767](https://github.com/GetStream/stream-chat-swiftui/pull/767)
1213
- Customizing the message avatar view is reflected in all views that use it [#772](https://github.com/GetStream/stream-chat-swiftui/pull/772)
1314
- Made the sendMessage method in MessageComposerViewModel open [#779](https://github.com/GetStream/stream-chat-swiftui/pull/779)
1415
- Move `ChangeBarsVisibilityModifier` into `ViewFactory` for better customization [#774](https://github.com/GetStream/stream-chat-swiftui/pull/774)
16+
### 🎭 New Localizations
17+
- `message.translatedTo` [#776](https://github.com/GetStream/stream-chat-swiftui/pull/776)
1518

1619
# [4.73.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.73.0)
1720
_February 28, 2025_
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// Copyright © 2025 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
import StreamChat
7+
import StreamChatSwiftUI
8+
9+
final class AppConfiguration {
10+
static let `default` = AppConfiguration()
11+
12+
/// The translation language to set on connect.
13+
var translationLanguage: TranslationLanguage?
14+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// Copyright © 2025 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import StreamChat
6+
import SwiftUI
7+
8+
struct AppConfigurationTranslationView: View {
9+
@Environment(\.dismiss) var dismiss
10+
11+
var selection: Binding<TranslationLanguage?> = Binding {
12+
AppConfiguration.default.translationLanguage
13+
} set: { newValue in
14+
AppConfiguration.default.translationLanguage = newValue
15+
}
16+
17+
var body: some View {
18+
List {
19+
ForEach(TranslationLanguage.all, id: \.languageCode) { language in
20+
Button(action: {
21+
selection.wrappedValue = language
22+
dismiss()
23+
}) {
24+
HStack {
25+
Text(language.languageCode)
26+
Spacer()
27+
if selection.wrappedValue == language {
28+
Image(systemName: "checkmark")
29+
}
30+
}
31+
}
32+
.foregroundStyle(.primary)
33+
}
34+
.navigationTitle("Translation Language")
35+
}
36+
}
37+
}
38+
39+
extension TranslationLanguage {
40+
static let all = allCases.sorted(by: { $0.languageCode < $1.languageCode })
41+
}
42+
43+
#Preview {
44+
NavigationView {
45+
AppConfigurationTranslationView()
46+
}
47+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// Copyright © 2025 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Combine
6+
import SwiftUI
7+
8+
struct AppConfigurationView: View {
9+
var body: some View {
10+
NavigationView {
11+
List {
12+
Section("Connect User Configuration") {
13+
NavigationLink("Translation") {
14+
AppConfigurationTranslationView()
15+
}
16+
}
17+
}
18+
.navigationBarTitleDisplayMode(.inline)
19+
.navigationTitle("App Configuration")
20+
}
21+
}
22+
}
23+
24+
#Preview {
25+
AppConfigurationView()
26+
}

DemoAppSwiftUI/AppDelegate.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
8181
userInfo: .init(
8282
id: credentials.id,
8383
name: credentials.name,
84-
imageURL: credentials.avatarURL
84+
imageURL: credentials.avatarURL,
85+
language: AppConfiguration.default.translationLanguage
8586
),
8687
token: token
8788
)

DemoAppSwiftUI/LoginView.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ struct LoginView: View {
2020
Text("Welcome to Stream Chat")
2121
.font(.title)
2222
.padding(.all, 8)
23+
24+
Button("Configuration") {
25+
viewModel.showsConfiguration = true
26+
}
27+
.buttonStyle(.borderedProminent)
2328

2429
Text("Select a user to try the iOS SDK:")
2530
.font(.body)
@@ -42,6 +47,9 @@ struct LoginView: View {
4247
.overlay(
4348
viewModel.loading ? ProgressView() : nil
4449
)
50+
.sheet(isPresented: $viewModel.showsConfiguration) {
51+
AppConfigurationView()
52+
}
4553
}
4654
}
4755

DemoAppSwiftUI/LoginViewModel.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class LoginViewModel: ObservableObject {
1010

1111
@Published var demoUsers = UserCredentials.builtInUsers
1212
@Published var loading = false
13+
@Published var showsConfiguration = false
1314

1415
@Injected(\.chatClient) var chatClient
1516

@@ -28,7 +29,12 @@ class LoginViewModel: ObservableObject {
2829
LogConfig.level = .warning
2930

3031
chatClient.connectUser(
31-
userInfo: .init(id: credentials.id, name: credentials.name, imageURL: credentials.avatarURL),
32+
userInfo: .init(
33+
id: credentials.id,
34+
name: credentials.name,
35+
imageURL: credentials.avatarURL,
36+
language: AppConfiguration.default.translationLanguage
37+
),
3238
token: token
3339
) { [weak self] error in
3440
if let error = error {

Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/PinnedMessagesView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,6 @@ struct PinnedMessageView<Factory: ViewFactory>: View {
101101
return "📊 \(L10n.Channel.Item.poll)"
102102
}
103103
let messageFormatter = InjectedValues[\.utils].messagePreviewFormatter
104-
return messageFormatter.formatAttachmentContent(for: message) ?? message.adjustedText
104+
return messageFormatter.formatAttachmentContent(for: message, in: channel) ?? message.adjustedText
105105
}
106106
}

Sources/StreamChatSwiftUI/ChatChannel/Composer/MessageComposerView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ public struct ComposerInputView<Factory: ViewFactory>: View, KeyboardReadable {
300300
isInComposer: true,
301301
scrolledId: .constant(nil)
302302
)
303+
.environment(\.channelTranslationLanguage, viewModel.channelController.channel?.membership?.language)
303304
}
304305

305306
if !addedAssets.isEmpty {

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import StreamChat
77
import SwiftUI
88

99
public struct MessageContainerView<Factory: ViewFactory>: View {
10-
10+
@Environment(\.channelTranslationLanguage) var translationLanguage
11+
1112
@Injected(\.fonts) private var fonts
1213
@Injected(\.colors) private var colors
1314
@Injected(\.images) private var images
@@ -225,6 +226,12 @@ public struct MessageContainerView<Factory: ViewFactory>: View {
225226
}
226227
}
227228

229+
if message.textContent(for: translationLanguage) != nil,
230+
let localizedName = translationLanguage?.localizedName {
231+
Text(L10n.Message.translatedTo(localizedName))
232+
.font(fonts.footnote)
233+
.foregroundColor(Color(colors.subtitleText))
234+
}
228235
if showsAllInfo && !message.isDeleted {
229236
if message.isSentByCurrentUser && channel.config.readEventsEnabled {
230237
HStack(spacing: 4) {

0 commit comments

Comments
 (0)