diff --git a/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/OnfidoBuilderSerializer.kt b/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/OnfidoBuilderSerializer.kt index 11bd4c5..56b1f03 100644 --- a/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/OnfidoBuilderSerializer.kt +++ b/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/OnfidoBuilderSerializer.kt @@ -10,6 +10,7 @@ import com.onfido.android.sdk.capture.ui.options.stepbuilder.DocumentCaptureStep import com.onfido.sdk.flutter.helpers.CustomMediaCallback import io.flutter.embedding.engine.plugins.FlutterPlugin import com.onfido.android.sdk.capture.model.NFCOptions +import java.util.Locale internal fun Any?.deserializeOnfidoBuilder( context: Context @@ -22,6 +23,10 @@ internal fun Any?.deserializeOnfidoBuilder( builder.withSDKToken(it) } + (this["locale"] as? String)?.takeIf { it.isNotBlank() }?.let { localeTag -> + builder.withLocale(Locale.forLanguageTag(localeTag)) + } + val flowSteps = this["flowSteps"] as? Map<*, *> ?: throw Exception("Invalid arguments for start method (flow steps)") diff --git a/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/WorkflowSerializer.kt b/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/WorkflowSerializer.kt index e8d1c7f..696ee49 100644 --- a/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/WorkflowSerializer.kt +++ b/android/src/main/kotlin/com/onfido/sdk/flutter/serializer/WorkflowSerializer.kt @@ -4,6 +4,7 @@ import com.onfido.workflow.WorkflowConfig import com.onfido.android.sdk.capture.EnterpriseFeatures import com.onfido.sdk.flutter.helpers.BiometricTokenCallbackBridge import com.onfido.sdk.flutter.helpers.CustomMediaCallback +import java.util.Locale fun Any.deserializeWorkflowConfig(): WorkflowConfig { if (this !is Map<*, *>) throw Exception("Invalid arguments for start method") @@ -16,6 +17,10 @@ fun Any.deserializeWorkflowConfig(): WorkflowConfig { val builder = WorkflowConfig.Builder(sdkToken, workflowRunId) + (this["locale"] as? String)?.takeIf { it.isNotBlank() }?.let { localeTag -> + builder.withLocale(Locale.forLanguageTag(localeTag)) + } + val withMediaCallback = this["shouldUseMediaCallback"] as? Boolean ?: false if (withMediaCallback) { builder.withMediaCallback(mediaCallback = CustomMediaCallback()) diff --git a/ios/Classes/Helpers/OnfidoLocalizationHelper.swift b/ios/Classes/Helpers/OnfidoLocalizationHelper.swift new file mode 100644 index 0000000..d960751 --- /dev/null +++ b/ios/Classes/Helpers/OnfidoLocalizationHelper.swift @@ -0,0 +1,81 @@ +import Foundation +import Onfido + +enum OnfidoLocalizationHelper { + private static var onfidoBundle: Bundle { + Bundle(for: WorkflowConfiguration.self) + } + + static func localizedBundle(for locale: String) -> Bundle? { + for candidate in localizationCandidates(for: locale) { + guard let path = onfidoBundle.path(forResource: candidate, ofType: "lproj"), + let bundle = Bundle(path: path) else { + continue + } + return bundle + } + return nil + } + + static func applyLocalization( + to builder: OnfidoConfigBuilder, + locale: String?, + customLocalizationFileName: String? + ) { + if let customLocalizationFileName { + builder.withCustomLocalization(andTableName: customLocalizationFileName) + return + } + + guard let locale, !locale.isEmpty, let localizedBundle = localizedBundle(for: locale) else { + return + } + + builder.withCustomLocalization(andTableName: "Localizable", in: localizedBundle) + } + + static func applyLocalization( + to configuration: WorkflowConfiguration, + locale: String?, + customLocalizationFileName: String? + ) { + if let customLocalizationFileName { + configuration.withCustomLocalization(withTableName: customLocalizationFileName, in: .main) + return + } + + guard let locale, !locale.isEmpty, let localizedBundle = localizedBundle(for: locale) else { + return + } + + configuration.withCustomLocalization(withTableName: "Localizable", in: localizedBundle) + } + + private static func localizationCandidates(for locale: String) -> [String] { + let normalized = locale.replacingOccurrences(of: "_", with: "-") + let components = normalized.split(separator: "-", omittingEmptySubsequences: true) + + guard let language = components.first else { + return [normalized] + } + + var candidates = [String]() + + if components.count >= 2 { + let region = String(components[1]) + candidates.append("\(language)-\(region)") + candidates.append("\(language)-\(region.uppercased())") + candidates.append("\(language)-\(region.lowercased())") + } + + candidates.append(String(language)) + candidates.append(normalized) + + var uniqueCandidates: [String] = [] + for candidate in candidates where !uniqueCandidates.contains(candidate) { + uniqueCandidates.append(candidate) + } + + return uniqueCandidates + } +} \ No newline at end of file diff --git a/ios/Classes/Serializer/OnfidoBuilderSerializer.swift b/ios/Classes/Serializer/OnfidoBuilderSerializer.swift index 6e488e3..5179197 100644 --- a/ios/Classes/Serializer/OnfidoBuilderSerializer.swift +++ b/ios/Classes/Serializer/OnfidoBuilderSerializer.swift @@ -17,9 +17,11 @@ extension OnfidoConfig { onfidoBuilder.withSDKToken(token) } - if let fileName = dictionary["iosLocalizationFileName"] as? String { - onfidoBuilder.withCustomLocalization(andTableName: fileName) - } + OnfidoLocalizationHelper.applyLocalization( + to: onfidoBuilder, + locale: dictionary["locale"] as? String, + customLocalizationFileName: dictionary["iosLocalizationFileName"] as? String + ) var appearance = Appearance() if let iosAppearance = dictionary["iosAppearance"] as? NSDictionary { diff --git a/ios/Classes/Serializer/WorkflowConfigurationSerializer.swift b/ios/Classes/Serializer/WorkflowConfigurationSerializer.swift index 3807fb8..3f9066e 100644 --- a/ios/Classes/Serializer/WorkflowConfigurationSerializer.swift +++ b/ios/Classes/Serializer/WorkflowConfigurationSerializer.swift @@ -20,9 +20,11 @@ extension WorkflowConfiguration { if let enterpriseFeatures = dictionary["enterpriseFeatures"] as? NSDictionary { withEnterpriseFeatures(EnterpriseFeatures.builder(with: enterpriseFeatures)) } - if let fileName = dictionary["iosLocalizationFileName"] as? String { - withCustomLocalization(withTableName: fileName, in: Bundle.self.main) - } + OnfidoLocalizationHelper.applyLocalization( + to: self, + locale: dictionary["locale"] as? String, + customLocalizationFileName: dictionary["iosLocalizationFileName"] as? String + ) var appearance = Appearance() if let iosAppearance = dictionary["iosAppearance"] as? NSDictionary { diff --git a/lib/src/onfido.dart b/lib/src/onfido.dart index 2297848..95e2cc6 100644 --- a/lib/src/onfido.dart +++ b/lib/src/onfido.dart @@ -14,6 +14,7 @@ class Onfido { Onfido( {required String sdkToken, String? iosLocalizationFileName, + String? locale, IOSAppearance? iosAppearance, EnterpriseFeatures? enterpriseFeatures, NFCOptions? nfcOption, @@ -22,6 +23,7 @@ class Onfido { OnfidoTheme? onfidoTheme}) : _sdkToken = sdkToken, _iOSLocalizationFileName = iosLocalizationFileName, + _locale = locale, _enterpriseFeatures = enterpriseFeatures, _iosAppearance = iosAppearance, _nfcOption = nfcOption, @@ -31,6 +33,7 @@ class Onfido { final String _sdkToken; final String? _iOSLocalizationFileName; + final String? _locale; final EnterpriseFeatures? _enterpriseFeatures; final IOSAppearance? _iosAppearance; final NFCOptions? _nfcOption; @@ -46,6 +49,7 @@ class Onfido { flowSteps: flowSteps, iosAppearance: _iosAppearance, iosLocalizationFileName: _iOSLocalizationFileName, + locale: _locale, enterpriseFeatures: _enterpriseFeatures, nfcOption: _nfcOption, mediaCallback: _mediaCallback, @@ -61,6 +65,7 @@ class Onfido { mediaCallback: _mediaCallback, biometricTokenCallback: _biometricTokenCallback, iosLocalizationFileName: _iOSLocalizationFileName, + locale: _locale, enterpriseFeatures: _enterpriseFeatures, onfidoTheme: _onfidoTheme); } diff --git a/lib/src/platform/onfido_method_channel.dart b/lib/src/platform/onfido_method_channel.dart index 005e984..b370af6 100644 --- a/lib/src/platform/onfido_method_channel.dart +++ b/lib/src/platform/onfido_method_channel.dart @@ -26,6 +26,7 @@ class MethodChannelOnfido extends OnfidoPlatform { {required String sdkToken, required FlowSteps flowSteps, String? iosLocalizationFileName, + String? locale, IOSAppearance? iosAppearance, OnfidoMediaCallback? mediaCallback, EnterpriseFeatures? enterpriseFeatures, @@ -37,6 +38,7 @@ class MethodChannelOnfido extends OnfidoPlatform { iosAppearance: iosAppearance, shouldUseMediaCallback: mediaCallback != null, iosLocalizationFileName: iosLocalizationFileName, + locale: locale, enterpriseFeatures: enterpriseFeatures, nfcOption: nfcOption, onfidoTheme: onfidoTheme); @@ -55,6 +57,7 @@ class MethodChannelOnfido extends OnfidoPlatform { OnfidoMediaCallback? mediaCallback, BiometricTokenCallback? biometricTokenCallback, String? iosLocalizationFileName, + String? locale, EnterpriseFeatures? enterpriseFeatures, OnfidoTheme? onfidoTheme}) async { final arguments = StartStudioSerializer.serialize( @@ -64,6 +67,7 @@ class MethodChannelOnfido extends OnfidoPlatform { shouldUseMediaCallback: mediaCallback != null, shouldUseBiometricTokenCallback: biometricTokenCallback != null, iosLocalizationFileName: iosLocalizationFileName, + locale: locale, enterpriseFeatures: enterpriseFeatures, onfidoTheme: onfidoTheme); diff --git a/lib/src/platform/onfido_platform.dart b/lib/src/platform/onfido_platform.dart index c1d9ba6..d1fbdbe 100644 --- a/lib/src/platform/onfido_platform.dart +++ b/lib/src/platform/onfido_platform.dart @@ -9,6 +9,7 @@ abstract class OnfidoPlatform { {required String sdkToken, required FlowSteps flowSteps, String? iosLocalizationFileName, + String? locale, IOSAppearance? iosAppearance, EnterpriseFeatures? enterpriseFeatures, NFCOptions? nfcOption, @@ -22,6 +23,7 @@ abstract class OnfidoPlatform { OnfidoMediaCallback? mediaCallback, BiometricTokenCallback? biometricTokenCallback, String? iosLocalizationFileName, + String? locale, EnterpriseFeatures? enterpriseFeatures, OnfidoTheme? onfidoTheme}); } diff --git a/lib/src/serializer/start_options_serializer.dart b/lib/src/serializer/start_options_serializer.dart index d72cb3e..ca9c4b2 100644 --- a/lib/src/serializer/start_options_serializer.dart +++ b/lib/src/serializer/start_options_serializer.dart @@ -8,6 +8,7 @@ class StartOptionsSerializer { bool? shouldUseMediaCallback, EnterpriseFeatures? enterpriseFeatures, String? iosLocalizationFileName, + String? locale, bool? disableNFC, NFCOptions? nfcOption, OnfidoTheme? onfidoTheme}) { @@ -16,6 +17,7 @@ class StartOptionsSerializer { 'flowSteps': flowSteps.toJson(), 'iosAppearance': iosAppearance?.toJson(), 'iosLocalizationFileName': iosLocalizationFileName, + 'locale': locale, 'nfcOption': nfcOption?.name, 'shouldUseMediaCallback': shouldUseMediaCallback, 'enterpriseFeatures': enterpriseFeatures?.toJson(), diff --git a/lib/src/serializer/start_studio_serializer.dart b/lib/src/serializer/start_studio_serializer.dart index 9773a62..132bc45 100644 --- a/lib/src/serializer/start_studio_serializer.dart +++ b/lib/src/serializer/start_studio_serializer.dart @@ -7,6 +7,7 @@ class StartStudioSerializer { required String workflowRunId, IOSAppearance? iosAppearance, String? iosLocalizationFileName, + String? locale, bool? shouldUseMediaCallback, bool? shouldUseBiometricTokenCallback, EnterpriseFeatures? enterpriseFeatures, @@ -16,6 +17,7 @@ class StartStudioSerializer { 'workflowRunId': workflowRunId, 'iosAppearance': iosAppearance?.toJson(), 'iosLocalizationFileName': iosLocalizationFileName, + 'locale': locale, 'shouldUseMediaCallback': shouldUseMediaCallback, 'shouldUseBiometricTokenCallback': shouldUseBiometricTokenCallback, 'enterpriseFeatures': enterpriseFeatures?.toJson(),