Skip to content

Commit

Permalink
Introduce string providing mechanism
Browse files Browse the repository at this point in the history
The proposal is that the Core SDK will return to us a dictionary with the keys and
values just as they come from the backend. This is also exactly how the strings
are saved in the `Localizable.strings` file, accessed through the `Localization`
enum. When the Core SDK is configured and is able to provide the dictionary, we
save it to a StringProviding struct. When a string is accessed through the
use of `Localization`, then `Localization` knows what is the key of the string,
and also specifies a fallback in case reading from the file fails. So with this
same key, we can go to the StringProviding, consult the dictionary, and if the
string is not there, use the file fallback, and if not use the string fallback.
This means that with this simple PR, once the Core SDK provides the dictionary,
we support custom locales for the whole Widgets SDK with minimal effort. The
alternative is to create a model which we need to keep in sync between Core and
Widgets, which seems like a big mess.

MOB-2282
  • Loading branch information
gersonnoboa authored and github-review-helper committed Sep 13, 2023
1 parent 47448ea commit 343d8cd
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 52 deletions.
20 changes: 20 additions & 0 deletions GliaWidgets.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@
311CAFCD29F8FAE20067B59F /* SecureConversations.TranscriptModel+CustomCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311CAFCC29F8FAE20067B59F /* SecureConversations.TranscriptModel+CustomCard.swift */; };
313EBD552943116E008E9597 /* SecureConversations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 313EBD542943116E008E9597 /* SecureConversations.swift */; };
3142696A29FFB712003DF62E /* Interactor.Failing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3142696929FFB712003DF62E /* Interactor.Failing.swift */; };
3146C9432AB1851C0047D8CC /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3146C9422AB1851C0047D8CC /* LocalizationTests.swift */; };
3146C9492AB18AC70047D8CC /* Localization+StringProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3146C9482AB18AC70047D8CC /* Localization+StringProviding.swift */; };
315BAB1A29ADFEBC00FF284B /* ConfirmationStyle+TitleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 315BAB1929ADFEBC00FF284B /* ConfirmationStyle+TitleStyle.swift */; };
315BAB1C29ADFEC800FF284B /* ConfirmationStyle+SubtitleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 315BAB1B29ADFEC800FF284B /* ConfirmationStyle+SubtitleStyle.swift */; };
315BAB1E29ADFED800FF284B /* ConfirmationStyle+CheckMessagesButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 315BAB1D29ADFED800FF284B /* ConfirmationStyle+CheckMessagesButtonStyle.swift */; };
Expand All @@ -195,6 +197,7 @@
3197F7B429F7C26A008EE9F7 /* SecureConversations.ChatWithTranscriptViewModel+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3197F7B329F7C26A008EE9F7 /* SecureConversations.ChatWithTranscriptViewModel+Hashable.swift */; };
3197F7B629F7C2E5008EE9F7 /* SecureConversations.SecureChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3197F7B529F7C2E5008EE9F7 /* SecureConversations.SecureChatModel.swift */; };
3197F7B829F7C318008EE9F7 /* SecureConversations.CommonEngagementModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3197F7B729F7C318008EE9F7 /* SecureConversations.CommonEngagementModel.swift */; };
31B1F8A92AB093ED009EC5AD /* StringProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31B1F8A82AB093ED009EC5AD /* StringProviding.swift */; };
31D286AD2A00DD2C009192A6 /* SecureConversations.ConfirmationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D286AC2A00DD2C009192A6 /* SecureConversations.ConfirmationViewModelTests.swift */; };
31D286AF2A00DE2B009192A6 /* SecureConversations.ConfirmationViewModel.Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D286AE2A00DE2B009192A6 /* SecureConversations.ConfirmationViewModel.Mock.swift */; };
31DB0C01287C2EFC00FB288E /* StaticValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31DB0C00287C2EFC00FB288E /* StaticValues.swift */; };
Expand Down Expand Up @@ -915,6 +918,8 @@
311CAFCC29F8FAE20067B59F /* SecureConversations.TranscriptModel+CustomCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SecureConversations.TranscriptModel+CustomCard.swift"; sourceTree = "<group>"; };
313EBD542943116E008E9597 /* SecureConversations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureConversations.swift; sourceTree = "<group>"; };
3142696929FFB712003DF62E /* Interactor.Failing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interactor.Failing.swift; sourceTree = "<group>"; };
3146C9422AB1851C0047D8CC /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
3146C9482AB18AC70047D8CC /* Localization+StringProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Localization+StringProviding.swift"; sourceTree = "<group>"; };
315BAB1929ADFEBC00FF284B /* ConfirmationStyle+TitleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfirmationStyle+TitleStyle.swift"; sourceTree = "<group>"; };
315BAB1B29ADFEC800FF284B /* ConfirmationStyle+SubtitleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfirmationStyle+SubtitleStyle.swift"; sourceTree = "<group>"; };
315BAB1D29ADFED800FF284B /* ConfirmationStyle+CheckMessagesButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfirmationStyle+CheckMessagesButtonStyle.swift"; sourceTree = "<group>"; };
Expand All @@ -927,6 +932,7 @@
3197F7B329F7C26A008EE9F7 /* SecureConversations.ChatWithTranscriptViewModel+Hashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SecureConversations.ChatWithTranscriptViewModel+Hashable.swift"; sourceTree = "<group>"; };
3197F7B529F7C2E5008EE9F7 /* SecureConversations.SecureChatModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureConversations.SecureChatModel.swift; sourceTree = "<group>"; };
3197F7B729F7C318008EE9F7 /* SecureConversations.CommonEngagementModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureConversations.CommonEngagementModel.swift; sourceTree = "<group>"; };
31B1F8A82AB093ED009EC5AD /* StringProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringProviding.swift; sourceTree = "<group>"; };
31D286AC2A00DD2C009192A6 /* SecureConversations.ConfirmationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureConversations.ConfirmationViewModelTests.swift; sourceTree = "<group>"; };
31D286AE2A00DE2B009192A6 /* SecureConversations.ConfirmationViewModel.Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureConversations.ConfirmationViewModel.Mock.swift; sourceTree = "<group>"; };
31DB0C00287C2EFC00FB288E /* StaticValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticValues.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1657,7 +1663,9 @@
1A205D5C25655CB1003AA3CD /* Info.plist */,
1A60AFAE256680EF00E53F53 /* L10n.swift */,
31E35AB92A8648E9006EC7FB /* Localization.swift */,
3146C9482AB18AC70047D8CC /* Localization+StringProviding.swift */,
31882C9A2AA21B71009DE4BD /* Localization+Templates.swift */,
31B1F8A82AB093ED009EC5AD /* StringProviding.swift */,
31DB0C00287C2EFC00FB288E /* StaticValues.swift */,
);
path = GliaWidgets;
Expand All @@ -1666,6 +1674,7 @@
1A205D6525655CB2003AA3CD /* GliaWidgetsTests */ = {
isa = PBXGroup;
children = (
3146C9412AB1850A0047D8CC /* Resources */,
7552DFB22A6FBC6E0093519B /* CoreSdk */,
7552DFAF2A6FB37E0093519B /* ChatMessage */,
846A5C3729D18D220049B29F /* ScreenShareHandler */,
Expand Down Expand Up @@ -2569,6 +2578,14 @@
path = ChatTranscript;
sourceTree = "<group>";
};
3146C9412AB1850A0047D8CC /* Resources */ = {
isa = PBXGroup;
children = (
3146C9422AB1851C0047D8CC /* LocalizationTests.swift */,
);
path = Resources;
sourceTree = "<group>";
};
315BAB1829ADFE9E00FF284B /* ConfirmationStyle */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4289,6 +4306,7 @@
846A5C4029ED83C50049B29F /* CallVisualizer.Coordinator.DelegateEvent.swift in Sources */,
845876B4282AA296007AC3DF /* ButtonView.Props.Accessibility.swift in Sources */,
1A0C9AE025C9624500815406 /* ObservableValue.swift in Sources */,
31B1F8A92AB093ED009EC5AD /* StringProviding.swift in Sources */,
75940962298D3889008B173A /* MessageMetadata.swift in Sources */,
7594094D298D37E8008B173A /* Glia.Environment.Mock.swift in Sources */,
1A60AFB9256682AF00E53F53 /* FlowCoordinator.swift in Sources */,
Expand Down Expand Up @@ -4555,6 +4573,7 @@
3100EEF2293E214B00D57F71 /* SecureConversations.Coordinator.swift in Sources */,
1AA738AE2578E0D500E1120F /* ConnectAnimationView.swift in Sources */,
754CC61627E2816F005676E9 /* Survey.InputQuestionView.swift in Sources */,
3146C9492AB18AC70047D8CC /* Localization+StringProviding.swift in Sources */,
9A1992DF27D62C2E00161AAE /* ImageView.Cache.Mock.swift in Sources */,
8491AF0D2A7A9CB900CC3E72 /* Theme.VisitorChatMessageStyle.swift in Sources */,
1A2DA72D25EF9DD900032611 /* FileUpload.swift in Sources */,
Expand Down Expand Up @@ -4788,6 +4807,7 @@
84681A952A61844000DD7406 /* ChatViewModelTests+Gva.swift in Sources */,
847A7643285A1914004044D1 /* FileUploadListViewModelTests.swift in Sources */,
9A1992E727D66C7400161AAE /* UIKitBased.Failing.swift in Sources */,
3146C9432AB1851C0047D8CC /* LocalizationTests.swift in Sources */,
846A5C3929D18D400049B29F /* ScreenShareHandlerTests.swift in Sources */,
9AE05CB62805D2CB00871321 /* Interactor.Environment.Failing.swift in Sources */,
846429862A45DB4100943BD6 /* AlertViewController.Kind+Mock.swift in Sources */,
Expand Down
22 changes: 22 additions & 0 deletions GliaWidgets/Localization+StringProviding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation

extension Localization {
static func tr(
_ table: String,
_ key: String,
_ args: CVarArg...,
fallback value: String,
stringProviding: StringProviding? = Glia.sharedInstance.stringProviding,
bundleManaging: BundleManaging = .live
) -> String {
guard
let stringProviding,
let remoteString = stringProviding.getRemoteString(key)
else {
let format = bundleManaging.current().localizedString(forKey: key, value: value, table: table)
return String(format: format, locale: Locale.current, arguments: args)
}

return remoteString
}
}
21 changes: 0 additions & 21 deletions GliaWidgets/Localization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -640,25 +640,4 @@ internal enum Localization {
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces

// MARK: - Implementation Details

extension Localization {
private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String {
let format = BundleToken.bundle.localizedString(forKey: key, value: value, table: table)
return String(format: format, locale: Locale.current, arguments: args)
}
}

// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type

// swiftlint:enable all
14 changes: 10 additions & 4 deletions GliaWidgets/Public/Glia/Glia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public class Glia {
/// Used to monitor engagement state changes.
public var onEvent: ((GliaEvent) -> Void)?

var stringProviding: StringProviding?

public lazy var callVisualizer = CallVisualizer(
environment: .init(
data: environment.data,
Expand Down Expand Up @@ -128,10 +130,14 @@ public class Glia {
if let callback = completion {
createdInteractor.withConfiguration { [weak createdInteractor] in
guard let interactor = createdInteractor else { return }
interactor.state = GliaCore.sharedInstance
.getCurrentEngagement()?.engagedOperator
.map(InteractorState.engaged) ?? interactor.state
callback()

// TODO: Configure string providing from Core SDK here.

interactor.state = GliaCore.sharedInstance
.getCurrentEngagement()?.engagedOperator
.map(InteractorState.engaged) ?? interactor.state

callback()
}
}

Expand Down
5 changes: 5 additions & 0 deletions GliaWidgets/StringProviding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct StringProviding {
var getRemoteString: ((String) -> String?)
}
66 changes: 66 additions & 0 deletions GliaWidgetsTests/Resources/LocalizationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Foundation
import XCTest
@testable import GliaWidgets

final class LocalizationTests: XCTestCase {
let testString = "Glia"

func test_stringProvider() {
let stringProviding = StringProviding(getRemoteString: { _ in self.testString })

let localizationString = Localization.tr(
"",
"",
fallback: "",
stringProviding: stringProviding
)

XCTAssertEqual(localizationString, testString)
}

func test_fallback() {
let localizationString = Localization.tr(
"",
"",
fallback: testString
)

XCTAssertEqual(localizationString, testString)
}

func test_fallbackWhenStringProvidingReturnsNil() {
let stringProviding = StringProviding(getRemoteString: { _ in nil })

let localizationString = Localization.tr(
"",
"",
fallback: testString,
stringProviding: stringProviding
)

XCTAssertEqual(localizationString, testString)
}

func test_fileFromString() {
let localizationString = Localization.tr(
"Localizable",
"alert.action.settings",
fallback: ""
)

XCTAssertEqual(localizationString, "Settings")
}

func test_fileFromStringWhenStringProvidingReturnsNil() {
let stringProviding = StringProviding(getRemoteString: { _ in nil })

let localizationString = Localization.tr(
"Localizable",
"alert.action.settings",
fallback: "",
stringProviding: stringProviding
)

XCTAssertEqual(localizationString, "Settings")
}
}
27 changes: 0 additions & 27 deletions swiftgen-strings.stencil
Original file line number Diff line number Diff line change
Expand Up @@ -79,33 +79,6 @@ import Foundation
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces

// MARK: - Implementation Details

extension {{enumName}} {
private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String {
{% if param.lookupFunction %}
let format = {{ param.lookupFunction }}(key, table, value)
{% else %}
let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: value, table: table)
{% endif %}
return String(format: format, locale: Locale.current, arguments: args)
}
}
{% if not param.bundle and not param.lookupFunction %}

// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type
{% endif %}
{% else %}
// No string found
{% endif %}
Expand Down

0 comments on commit 343d8cd

Please sign in to comment.