From 2834eed2b3d72806b63179147df939c6391c9109 Mon Sep 17 00:00:00 2001
From: Mel <78050250+mludowise-stripe@users.noreply.github.com>
Date: Mon, 14 Oct 2024 16:53:08 -0700
Subject: [PATCH 1/2] [Connect] Locale fixes (#4120)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
1. Moves `toLanguageTag()` out of `StripePaymentSheet` and
`StripeFinancialConnections` (they were duplicated) and into
`StripeCore` so it can be used across PaymentSheet,
FinancialConnections, and Connect.
2. Fixes a bug in `toLanguageTag()` where extended locales were
including `@`, which is not a valid BCP 47 locale identifier.
Ex: Using a different calendar -> `en-US@calendar=japanese`
Ex: On iOS 16+ when specifying a different language region from device
region (e.g. Language=English (UK) and Region=United States) ->
`en-GB@rg=uszzzz`
3. Fixes a bug in `toLanguageTag()` where Language=Chinese, Traditional
and Region=Hong Kong (China) was dropping the script from the locale
(`zh-HK` instead of `zh-Hant-HK`)
4. Updates Connect to use `toLanguageTag()` instead of our custom
`webIdentifier` locale helper when passing the locale identifier to the
web view.
## Motivation
https://jira.corp.stripe.com/browse/MXMOBILE-2840
Fixes formatting the locale when sending it to the Connect web view for
languages that specify a script or when the device region is different
from the language region.
Examples:
* If the selected language was "English (UK)" but the device region was
"United States", we were previously sending "en-US" when the correct
language would have been "en-UK"
* If the selected language was "Chinese (Traditional)" with region
"Taiwan (China)", we were previously sending "zh_TW" when it should have
been "zh-Hant" or "zh-Hant-TW". By not including the script ("Hant"),
the web view was defaulting to simplified Chinese.
## Testing
- [x] Unit tests (Locale+StripeTests.swift)
- [x] Manual testing (see below)
### iOS 17
| Scenario | Screenshot |
| ------- | ---------- |
| Region=US, Language=English → US english: |
|
| Region=US, Language=English (UK) → British English: |
|
| Region=Taiwan (China), Language=Chinese, Traditional → Traditional
Chinese: |
|
| Region=Hong Kong (China), Language=Chinese, Traditional → Traditional
Chinese: |
|
| Region=Taiwan (China), Language=Chinese, Simplified → Simplified
Chinese: |
|
| Region=China mainland, Language=Chinese, Simplified → Simplified
Chinese: |
|
| Region=China mainland, Language=Chinese, Traditional → Traditional
Chinese: |
|
### iOS 15:
| Scenario | Screenshot |
| ------- | ---------- |
| Region=US, Language=English → US english: |
|
| Region=United Kingdom, Language=English → British English: |
|
| Region=Taiwan, Language=Chinese, Traditional → Traditional Chinese: |
|
| Region=Hong Kong, Language=Chinese, Traditional → Traditional Chinese:
|
|
| Region=Taiwan, Language=Chinese, Simplified → Simplified Chinese: |
|
| Region=China mainland, Language=Chinese, Simplified → Simplified
Chinese: |
|
| Region=China mainland, Language=Chinese, Traditional → Traditional
Chinese: |
|
---------
Co-authored-by: Yuki
---
.../StripeConnectExample/Info.plist | 94 +++++++++----------
.../StripeConnect.xcodeproj/project.pbxproj | 10 +-
.../Extensions/Locale+extension.swift | 20 ----
.../Webview/ConnectComponentWebView.swift | 6 +-
.../StripeCore.xcodeproj/project.pbxproj | 4 +
.../Source/Categories/Locale+StripeCore.swift | 50 ++++++++++
.../Categories/Locale+StripeTests.swift | 74 +++++++++++++++
.../project.pbxproj | 4 -
.../Source/Helpers/Locale+Extensions.swift | 33 -------
.../project.pbxproj | 4 -
.../Internal/Link/Utils/Locale+Link.swift | 35 -------
11 files changed, 181 insertions(+), 153 deletions(-)
delete mode 100644 StripeConnect/StripeConnect/Source/Internal/Extensions/Locale+extension.swift
create mode 100644 StripeCore/StripeCoreTests/Categories/Locale+StripeTests.swift
delete mode 100644 StripeFinancialConnections/StripeFinancialConnections/Source/Helpers/Locale+Extensions.swift
delete mode 100644 StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Utils/Locale+Link.swift
diff --git a/Example/StripeConnectExample/StripeConnectExample/Info.plist b/Example/StripeConnectExample/StripeConnectExample/Info.plist
index ba2d7071d64..55fec2539f3 100644
--- a/Example/StripeConnectExample/StripeConnectExample/Info.plist
+++ b/Example/StripeConnectExample/StripeConnectExample/Info.plist
@@ -8,53 +8,53 @@
CFBundleLocalizations
- bg-BG
- zh-Hans
- zh-Hant-HK
- zh-Hant-TW
- hr-HR
- cs-CZ
- da-DK
- nl-NL
- en-AU
- en-IN
- en-IE
- en-NZ
- en-SG
- en-GB
- en-US
- et-EE
- fil-PH
- fi-FI
- fr-CA
- fr-FR
- de-DE
- el-GR
- hu-HU
- id-ID
- it-IT
- ja-JP
- ko-KR
- lv-LV
- lt-LT
- ms-MY
- mt-MT
- nb-NO
- pl-PL
- pt-BR
- pt-PT
- ro-RO
- sk-SK
- sl-SI
- es-AR
- es-BR
- es-419
- es-MX
- es-ES
- sv-SE
- th-TH
- tr-TR
- vi-VN
+ bg_BG
+ zh_Hans
+ zh-Hant_HK
+ zh-Hant_TW
+ hr_HR
+ cs_CZ
+ da_DK
+ nl_NL
+ en_AU
+ en_IN
+ en_IE
+ en_NZ
+ en_SG
+ en_GB
+ en_US
+ et_EE
+ fil_PH
+ fi_FI
+ fr_CA
+ fr_FR
+ de_DE
+ el_GR
+ hu_HU
+ id_ID
+ it_IT
+ ja_JP
+ ko_KR
+ lv_LV
+ lt_LT
+ ms_MY
+ mt_MT
+ nb_NO
+ pl_PL
+ pt_BR
+ pt_PT
+ ro_RO
+ sk_SK
+ sl_SI
+ es_AR
+ es_BR
+ es_419
+ es_MX
+ es_ES
+ sv_SE
+ th_TH
+ tr_TR
+ vi_VN
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
diff --git a/StripeConnect/StripeConnect.xcodeproj/project.pbxproj b/StripeConnect/StripeConnect.xcodeproj/project.pbxproj
index 6ccc3902278..68af8a0abc0 100644
--- a/StripeConnect/StripeConnect.xcodeproj/project.pbxproj
+++ b/StripeConnect/StripeConnect.xcodeproj/project.pbxproj
@@ -51,7 +51,6 @@
416E9E862C76B35E00A0B917 /* PayoutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416E9E852C76B35E00A0B917 /* PayoutsViewController.swift */; };
416E9E892C76B36F00A0B917 /* PayoutsViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416E9E872C76B36F00A0B917 /* PayoutsViewControllerTests.swift */; };
416E9ECF2C77EAA400A0B917 /* EmbeddedComponentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416E9ECE2C77EAA400A0B917 /* EmbeddedComponentError.swift */; };
- 416E9ED22C77F6E000A0B917 /* Locale+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416E9ED12C77F6E000A0B917 /* Locale+extension.swift */; };
416E9ED42C77F90600A0B917 /* WKScriptMessage+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416E9ED32C77F90600A0B917 /* WKScriptMessage+extension.swift */; };
4171B1592C9A5EEC00547F7D /* AccountOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4171B1582C9A5EEC00547F7D /* AccountOnboardingViewController.swift */; };
41810D692C88C4B100F10EB7 /* AppearanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41810D682C88C4B100F10EB7 /* AppearanceTests.swift */; };
@@ -89,8 +88,8 @@
41D17A6E2C5A7429007C6EE6 /* Version.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 41D17A632C5A7429007C6EE6 /* Version.xcconfig */; };
E6165CBF2CA7BF2200B76DA5 /* FetchInitComponentPropsMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6165CBE2CA7BF2200B76DA5 /* FetchInitComponentPropsMessageHandler.swift */; };
E6165CC12CA7D09900B76DA5 /* FetchInitComponentPropsMessageHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6165CC02CA7D09900B76DA5 /* FetchInitComponentPropsMessageHandlerTests.swift */; };
- E65691222CA52D5900E0DB00 /* StripeConnect+Exports.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65691212CA52D5900E0DB00 /* StripeConnect+Exports.swift */; };
E65691202CA5248300E0DB00 /* AccountManagementViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E656911F2CA5248300E0DB00 /* AccountManagementViewControllerTests.swift */; };
+ E65691222CA52D5900E0DB00 /* StripeConnect+Exports.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65691212CA52D5900E0DB00 /* StripeConnect+Exports.swift */; };
E65691252CA52F9D00E0DB00 /* NotificationBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65691232CA52F8600E0DB00 /* NotificationBannerViewController.swift */; };
E65691272CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65691262CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift */; };
E688AE002CADD8C400951D97 /* NotificationBannerViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E688ADFF2CADD8C400951D97 /* NotificationBannerViewControllerTests.swift */; };
@@ -157,7 +156,6 @@
416E9E852C76B35E00A0B917 /* PayoutsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayoutsViewController.swift; sourceTree = ""; };
416E9E872C76B36F00A0B917 /* PayoutsViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayoutsViewControllerTests.swift; sourceTree = ""; };
416E9ECE2C77EAA400A0B917 /* EmbeddedComponentError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmbeddedComponentError.swift; sourceTree = ""; };
- 416E9ED12C77F6E000A0B917 /* Locale+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Locale+extension.swift"; sourceTree = ""; };
416E9ED32C77F90600A0B917 /* WKScriptMessage+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKScriptMessage+extension.swift"; sourceTree = ""; };
4171B1582C9A5EEC00547F7D /* AccountOnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountOnboardingViewController.swift; sourceTree = ""; };
41810D682C88C4B100F10EB7 /* AppearanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceTests.swift; sourceTree = ""; };
@@ -195,14 +193,14 @@
41D17A622C5A7429007C6EE6 /* StripeiOS-Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "StripeiOS-Shared.xcconfig"; sourceTree = ""; };
41D17A632C5A7429007C6EE6 /* Version.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; };
E6165CBE2CA7BF2200B76DA5 /* FetchInitComponentPropsMessageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchInitComponentPropsMessageHandler.swift; sourceTree = ""; };
+ E6165CC02CA7D09900B76DA5 /* FetchInitComponentPropsMessageHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchInitComponentPropsMessageHandlerTests.swift; sourceTree = ""; };
E656911F2CA5248300E0DB00 /* AccountManagementViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagementViewControllerTests.swift; sourceTree = ""; };
+ E65691212CA52D5900E0DB00 /* StripeConnect+Exports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StripeConnect+Exports.swift"; sourceTree = ""; };
E65691232CA52F8600E0DB00 /* NotificationBannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationBannerViewController.swift; sourceTree = ""; };
E65691262CA533CD00E0DB00 /* OnNotificationsChangeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnNotificationsChangeHandler.swift; sourceTree = ""; };
E688ADFF2CADD8C400951D97 /* NotificationBannerViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationBannerViewControllerTests.swift; sourceTree = ""; };
E688AE022CADE36900951D97 /* OnNotificationsChangeHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnNotificationsChangeHandlerTests.swift; sourceTree = ""; };
E6C5F5F52C9FEE0200861709 /* AccountManagementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagementViewController.swift; sourceTree = ""; };
- E6165CC02CA7D09900B76DA5 /* FetchInitComponentPropsMessageHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchInitComponentPropsMessageHandlerTests.swift; sourceTree = ""; };
- E65691212CA52D5900E0DB00 /* StripeConnect+Exports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StripeConnect+Exports.swift"; sourceTree = ""; };
E6F485F72C9E35A5000D914F /* PaymentDetailsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentDetailsViewController.swift; sourceTree = ""; };
E6F485FB2C9E360A000D914F /* ConnectJSURLParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectJSURLParams.swift; sourceTree = ""; };
E6F485FD2C9E36B2000D914F /* PaymentDetailsViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentDetailsViewControllerTests.swift; sourceTree = ""; };
@@ -351,7 +349,6 @@
416E9ED02C77F6C100A0B917 /* Extensions */ = {
isa = PBXGroup;
children = (
- 416E9ED12C77F6E000A0B917 /* Locale+extension.swift */,
416E9ED32C77F90600A0B917 /* WKScriptMessage+extension.swift */,
41542A682C88B6F2004E728E /* JSONEncoder+extension.swift */,
41542A6A2C88B79E004E728E /* JSONSerialization+extension.swift */,
@@ -703,7 +700,6 @@
E6C5F5F62C9FEE0200861709 /* AccountManagementViewController.swift in Sources */,
413987C82C63F34B001D375E /* DebugMessageHandler.swift in Sources */,
410D0FCC2C6CFFDB009B0E26 /* AccountSessionClaimedMessageHandler.swift in Sources */,
- 416E9ED22C77F6E000A0B917 /* Locale+extension.swift in Sources */,
41BCCFED2C8B34F600797E01 /* StringCodingKey.swift in Sources */,
4186664E2C66ACB3003DB62E /* OnLoadErrorMessageHandler.swift in Sources */,
410D0FE52C6D32F0009B0E26 /* ApplicationURLOpener.swift in Sources */,
diff --git a/StripeConnect/StripeConnect/Source/Internal/Extensions/Locale+extension.swift b/StripeConnect/StripeConnect/Source/Internal/Extensions/Locale+extension.swift
deleted file mode 100644
index f16d3842af8..00000000000
--- a/StripeConnect/StripeConnect/Source/Internal/Extensions/Locale+extension.swift
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// Locale+extension.swift
-// StripeConnect
-//
-// Created by Chris Mays on 8/22/24.
-//
-
-import Foundation
-@_spi(STP) import StripeCore
-
-extension Locale {
- /// iOS uses underscores for locale (`en_US`) but web uses hyphens (`en-US`)
- var webIdentifier: String {
- guard let region = stp_regionCode,
- let language = stp_languageCode else {
- return ""
- }
- return "\(language)-\(region)"
- }
-}
diff --git a/StripeConnect/StripeConnect/Source/Internal/Webview/ConnectComponentWebView.swift b/StripeConnect/StripeConnect/Source/Internal/Webview/ConnectComponentWebView.swift
index 8908cea5129..a881d306a7f 100644
--- a/StripeConnect/StripeConnect/Source/Internal/Webview/ConnectComponentWebView.swift
+++ b/StripeConnect/StripeConnect/Source/Internal/Webview/ConnectComponentWebView.swift
@@ -101,7 +101,7 @@ class ConnectComponentWebView: ConnectWebView {
loadContent: loadContent)
}
func updateAppearance(appearance: Appearance) {
- sendMessage(UpdateConnectInstanceSender.init(payload: .init(locale: webLocale.webIdentifier, appearance: .init(appearance: appearance, traitCollection: traitCollection))))
+ sendMessage(UpdateConnectInstanceSender.init(payload: .init(locale: webLocale.toLanguageTag(), appearance: .init(appearance: appearance, traitCollection: traitCollection))))
updateColors(appearance: appearance)
}
@@ -162,7 +162,7 @@ private extension ConnectComponentWebView {
// If self no longer exists give default values
return .init(locale: "", appearance: .init(appearance: .default, traitCollection: .init()))
}
- return .init(locale: webLocale.webIdentifier,
+ return .init(locale: webLocale.toLanguageTag(),
appearance: .init(appearance: componentManager.appearance, traitCollection: self.traitCollection),
fonts: componentManager.fonts.map({ .init(customFontSource: $0) }))
}))
@@ -188,7 +188,7 @@ private extension ConnectComponentWebView {
) { [weak self] _ in
// swiftlint:disable:previous unused_capture_list
guard let self else { return }
- sendMessage(UpdateConnectInstanceSender(payload: .init(locale: webLocale.webIdentifier, appearance: .init(appearance: componentManager.appearance, traitCollection: traitCollection))))
+ sendMessage(UpdateConnectInstanceSender(payload: .init(locale: webLocale.toLanguageTag(), appearance: .init(appearance: componentManager.appearance, traitCollection: traitCollection))))
}
}
diff --git a/StripeCore/StripeCore.xcodeproj/project.pbxproj b/StripeCore/StripeCore.xcodeproj/project.pbxproj
index c15c74a4036..54acec977d2 100644
--- a/StripeCore/StripeCore.xcodeproj/project.pbxproj
+++ b/StripeCore/StripeCore.xcodeproj/project.pbxproj
@@ -117,6 +117,7 @@
DFF3092E51B6C3ED81AB1448 /* String+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF688F66CD047D08B3AE0CB /* String+Localized.swift */; };
E2B25D45D457A76A782D9089 /* STPAnalyticEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6C28853EF0316366FB8DC4 /* STPAnalyticEvent.swift */; };
E344C20A07D8B8F33B530974 /* TestConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B2A342F16484705840F1B5 /* TestConstants.swift */; };
+ E6EF91C32CB9DC410082DD1B /* Locale+StripeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6EF91C22CB9DC3C0082DD1B /* Locale+StripeTests.swift */; };
EFE476BA387E91BE1D5D3E1D /* URLRequest+StripeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B75708617FB765AB211FD9A /* URLRequest+StripeTest.swift */; };
EFF90360C85642F7F2898186 /* URLEncoderTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2E1D80D0342CF09CB05415 /* URLEncoderTest.swift */; };
F5DB5D52E2668136FF6D70D6 /* NSMutableURLRequest+StripeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66CC52EF207F05E0EFAEACD8 /* NSMutableURLRequest+StripeTest.swift */; };
@@ -332,6 +333,7 @@
E1C72BA9C44FF60A0E7BEF76 /* STPMultipartFormDataPart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPMultipartFormDataPart.swift; sourceTree = ""; };
E4B2A342F16484705840F1B5 /* TestConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConstants.swift; sourceTree = ""; };
E60F4A38EEF5EA11568B3A64 /* StripeCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StripeCore.h; sourceTree = ""; };
+ E6EF91C22CB9DC3C0082DD1B /* Locale+StripeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Locale+StripeTests.swift"; sourceTree = ""; };
E919FBEB852CFEA9517FCBDC /* FraudDetectionData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FraudDetectionData.swift; sourceTree = ""; };
EA55726A0FE74A4D90A10C01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
ECF3D265DCDD0D64F6D7E6B2 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; };
@@ -427,6 +429,7 @@
3A3F265C90373C3EF2F61523 /* Categories */ = {
isa = PBXGroup;
children = (
+ E6EF91C22CB9DC3C0082DD1B /* Locale+StripeTests.swift */,
34895253739089BC125D2625 /* Dictionary+StripeTests.swift */,
8F2949BE5129DC4BBCD69B05 /* NSArray+StripeCoreTest.swift */,
66CC52EF207F05E0EFAEACD8 /* NSMutableURLRequest+StripeTest.swift */,
@@ -950,6 +953,7 @@
84487D8E9B08106C89753536 /* Error_SerializeForLoggingTest.swift in Sources */,
0A78AD04075C43A4059C344E /* STPAnalyticsClientTest.swift in Sources */,
934CCB00769674F13192A126 /* Dictionary+StripeTests.swift in Sources */,
+ E6EF91C32CB9DC410082DD1B /* Locale+StripeTests.swift in Sources */,
49ECDA412CA340E100F647F0 /* AsyncTests.swift in Sources */,
A50CB2ACAC1DCF9539D76F25 /* NSArray+StripeCoreTest.swift in Sources */,
F5DB5D52E2668136FF6D70D6 /* NSMutableURLRequest+StripeTest.swift in Sources */,
diff --git a/StripeCore/StripeCore/Source/Categories/Locale+StripeCore.swift b/StripeCore/StripeCore/Source/Categories/Locale+StripeCore.swift
index 5adf8f1f2be..3b064f2b29b 100644
--- a/StripeCore/StripeCore/Source/Categories/Locale+StripeCore.swift
+++ b/StripeCore/StripeCore/Source/Categories/Locale+StripeCore.swift
@@ -41,4 +41,54 @@ import Foundation
return self.isoRegionCodes
#endif
}
+
+ /// Returns the BCP 47(-ish) language tag representing the locale.
+ ///
+ /// The language tag is expected to be well-formed as long as the locale identifier contains a
+ /// valid language code. For example:
+ ///
+ /// ```
+ /// let locale = Locale(identifier: "fr_CA")
+ /// locale.toLanguageTag() // -> "fr-CA"
+ /// ```
+ ///
+ /// The following example returns `"-ES"`, even though `"und-ES"` will be the appropriate BCP 47 tag:
+ ///
+ /// ```
+ /// let locale = Locale(identifier: "_ES")
+ /// locale.toLanguageTag() // -> "-ES"
+ /// ```
+ /// All system iOS and macOS locales are expected to contain valid language codes.
+ ///
+ /// On iOS 16+, the device region may be different from the language region. When these are different,
+ /// the device region is encoded at the end. The example below corresponds to:
+ /// Language=English (UK) and Region=United States:
+ ///
+ /// ```
+ /// let locale = Locale(identifier: "en_GB@rg=uszzzz")
+ /// locale.toLanguageTag() // -> "en-GB"
+ /// ```
+ ///
+ func toLanguageTag() -> String {
+ var tag = Locale.canonicalLanguageIdentifier(from: self.identifier)
+
+ // Drop sub-tags or extended variants like `en-US@calendar=gregorian`
+ // or `en-GB@rg=uszzzz`
+ if let unextended = tag.split(separator: "@").first {
+ tag = String(unextended)
+ }
+
+ /*
+ iOS omits the language script when specifying:
+ language=Chinese, Traditional and region=Hong Kong (China)
+
+ Stripe's web and backend localization will default to Simplified Chinese
+ (zh-Hans) if no script is specified, so insert the `Hant` script to
+ ensure Traditional Chinese is returned.
+ */
+ if tag == "zh-HK" {
+ tag = "zh-Hant-HK"
+ }
+ return tag
+ }
}
diff --git a/StripeCore/StripeCoreTests/Categories/Locale+StripeTests.swift b/StripeCore/StripeCoreTests/Categories/Locale+StripeTests.swift
new file mode 100644
index 00000000000..8e8d855df91
--- /dev/null
+++ b/StripeCore/StripeCoreTests/Categories/Locale+StripeTests.swift
@@ -0,0 +1,74 @@
+//
+// Locale+StripeTests.swift
+// StripeCore
+//
+// Created by Mel Ludowise on 10/11/24.
+//
+
+@_spi(STP) import StripeCore
+import XCTest
+
+class Locale_StripeTests: XCTestCase {
+ func testLanguageTag() {
+ // Language=English, region not specified
+ XCTAssertEqual(Locale(identifier: "en").toLanguageTag(), "en")
+ XCTAssertEqual(Locale(identifier: "en_GB").toLanguageTag(), "en-GB")
+
+ // Language=English, region=US, calendar=Japanese
+ XCTAssertEqual(Locale(identifier: "en_US@calendar=japanese").toLanguageTag(), "en-US")
+
+ // Chinese languages, region not specified
+ XCTAssertEqual(Locale(identifier: "zh").toLanguageTag(), "zh")
+ XCTAssertEqual(Locale(identifier: "zh-Hans").toLanguageTag(), "zh-Hans")
+ XCTAssertEqual(Locale(identifier: "zh-Hant").toLanguageTag(), "zh-Hant")
+
+ // Language=Simplified Chinese, region=China mainland
+ XCTAssertEqual(Locale(identifier: "zh_CN").toLanguageTag(), "zh-Hans")
+ XCTAssertEqual(Locale(identifier: "zh-Hans_CN").toLanguageTag(), "zh-Hans")
+
+ // Language=Simplified Chinese, region=Taiwan (China)
+ XCTAssertEqual(Locale(identifier: "zh-Hans_TW").toLanguageTag(), "zh-Hans-TW")
+
+ // Language=Simplified Chinese, region=Hong Kong (China)
+ XCTAssertEqual(Locale(identifier: "zh-Hans_HK").toLanguageTag(), "zh-Hans-HK")
+
+ // Language=Traditional Chinese, region=China Mainland
+ XCTAssertEqual(Locale(identifier: "zh-Hant_CN").toLanguageTag(), "zh-Hant-CN")
+
+ // Language=Traditional Chinese, region=Taiwan (China)
+ XCTAssertEqual(Locale(identifier: "zh_TW").toLanguageTag(), "zh-Hant")
+ XCTAssertEqual(Locale(identifier: "zh-Hant_TW").toLanguageTag(), "zh-Hant")
+
+ // Language=Traditional Chinese, region=Hong Kong (China)
+ XCTAssertEqual(Locale(identifier: "zh_HK").toLanguageTag(), "zh-Hant-HK")
+ XCTAssertEqual(Locale(identifier: "zh-Hant_HK").toLanguageTag(), "zh-Hant-HK")
+ }
+
+ /// On iOS 16+, the device region may be different from the language region
+ /// The `@rg={region-code}zzzz` indicates the device region when it's different from the language region
+ func testLanguageTag_languageRegionDifferentFromDevice() {
+ // Language=English (UK), region=United States
+ XCTAssertEqual(Locale(identifier: "en_GB@rg=uszzzz").toLanguageTag(), "en-GB")
+
+ // Language=Portuguese (Brazil), region=Portugal
+ XCTAssertEqual(Locale(identifier: "pt_BR@rg=ptzzzz").toLanguageTag(), "pt-BR")
+
+ // Language=Simplified Chinese, region=Hong Kong (China)
+ XCTAssertEqual(Locale(identifier: "zh-Hans@rg=hkzzzz").toLanguageTag(), "zh-Hans")
+
+ // Language=Simplified Chinese, region=Taiwan (China)
+ XCTAssertEqual(Locale(identifier: "zh-Hans@rg=twzzzz").toLanguageTag(), "zh-Hans")
+
+ // Language=Traditional Chinese (Taiwan), region=China mainland
+ XCTAssertEqual(Locale(identifier: "zh-Hant_TW@rg=cnzzzz").toLanguageTag(), "zh-Hant")
+
+ // Language=Traditional Chinese (Taiwan), region=Hong Kong (China)
+ XCTAssertEqual(Locale(identifier: "zh-Hant_TW@rg=hkzzzz").toLanguageTag(), "zh-Hant")
+
+ // Language=Traditional Chinese (Hong Kong), region=Taiwan (China)
+ XCTAssertEqual(Locale(identifier: "zh-Hant_HK@rg=twzzzz").toLanguageTag(), "zh-Hant-HK")
+
+ // Language=Traditional Chinese (Hong Kong), region=China mainland
+ XCTAssertEqual(Locale(identifier: "zh-Hant_HK@rg=cnzzzz").toLanguageTag(), "zh-Hant-HK")
+ }
+}
diff --git a/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj b/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj
index 5aa2eeddfab..d4d00ff12a8 100644
--- a/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj
+++ b/StripeFinancialConnections/StripeFinancialConnections.xcodeproj/project.pbxproj
@@ -135,7 +135,6 @@
77D3B375B9DBF80BA209BC99 /* FinancialConnectionsSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E05C2C5CDAA55CE700662040 /* FinancialConnectionsSessionTests.swift */; };
7AE7474B7AFF416B6072721C /* StripeCore+Import.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CF67A1F497E6CC73029CF0 /* StripeCore+Import.swift */; };
7DEC399FFE0BAAAB2026E684 /* PaymentAccount+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A72263B4842E5E30FF91AD /* PaymentAccount+Extensions.swift */; };
- 825C2182D13D7AC2DF67BB5E /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AE2036F58EE5C098C1B25B /* Locale+Extensions.swift */; };
82FD3CEE526DE8B6519F666E /* FinancialConnectionsSessionFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = A872C2B500306F775622F904 /* FinancialConnectionsSessionFetcher.swift */; };
846D1D7429B9E414744DEC99 /* FinancialConnectionsSheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E7725EF317C3BD62ADF845 /* FinancialConnectionsSheetTests.swift */; };
864C5159C62C562C655B53F7 /* StripeFinancialConnections.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E37D8CE9CD73443A9AAF2AE8 /* StripeFinancialConnections.framework */; };
@@ -422,7 +421,6 @@
742D94AC4B2D17F8282A6788 /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = fil.lproj/Localizable.strings; sourceTree = ""; };
75B23D2BD3CD4071A40D9AE9 /* FinancialConnectionsEvent+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FinancialConnectionsEvent+Extensions.swift"; sourceTree = ""; };
77A71EBB1B98CD285DD17D5F /* AccountFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFetcherTests.swift; sourceTree = ""; };
- 77AE2036F58EE5C098C1B25B /* Locale+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Locale+Extensions.swift"; sourceTree = ""; };
780BC432329228B042DA97D8 /* NativeFlowDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeFlowDataManager.swift; sourceTree = ""; };
782A419DCF59BE6AB6439D04 /* add@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "add@3x.png"; sourceTree = ""; };
7AFC0D3ED86914DC4216CCCA /* AuthFlowHelpersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthFlowHelpersTests.swift; sourceTree = ""; };
@@ -1052,7 +1050,6 @@
064D0E3A3AC71FAA60B54FC5 /* Helpers.swift */,
31AD3BE82B0C2F000080C800 /* ScreenNativeScale.swift */,
65BCC4356AE3295B4A2F4A28 /* Image.swift */,
- 77AE2036F58EE5C098C1B25B /* Locale+Extensions.swift */,
267B3586136203186882F5CE /* NSAttributedString+Extensions.swift */,
B2A72263B4842E5E30FF91AD /* PaymentAccount+Extensions.swift */,
6E2D765AC793D89D26B74FC4 /* STPLocalizedString.swift */,
@@ -1348,7 +1345,6 @@
34E12CB27B60F6A53D030765 /* FinancialConnectionsFont.swift in Sources */,
C3338FA5019EC8E99E2BA62F /* Helpers.swift in Sources */,
A573468B2800DABF384CAB43 /* Image.swift in Sources */,
- 825C2182D13D7AC2DF67BB5E /* Locale+Extensions.swift in Sources */,
6A43202A2B7E8C5400A67A70 /* Constants.swift in Sources */,
A79D6A26EE9FF96D24F4AC5C /* NSAttributedString+Extensions.swift in Sources */,
7DEC399FFE0BAAAB2026E684 /* PaymentAccount+Extensions.swift in Sources */,
diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Helpers/Locale+Extensions.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Helpers/Locale+Extensions.swift
deleted file mode 100644
index 2c8c7051e40..00000000000
--- a/StripeFinancialConnections/StripeFinancialConnections/Source/Helpers/Locale+Extensions.swift
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// Locale+Extensions.swift
-// StripeFinancialConnections
-//
-// Created by Krisjanis Gaidis on 6/20/23.
-//
-
-import Foundation
-
-extension Locale {
-
- /// Returns the BCP 47(-ish) language tag representing the locale.
- ///
- /// The language tag is expected to be well-formed as log as the locale identifier contains a
- /// valid language code. For example:
- ///
- /// ```
- /// let locale = Locale(identifier: "fr_CA")
- /// locale.toLanguageTag() // -> "fr-CA"
- /// ```
- ///
- /// The following example returns `"-ES"`, even though `"und-ES"` will be the appropriate BCP 47 tag:
- ///
- /// ```
- /// let locale = Locale(identifier: "_ES")
- /// locale.toLanguageTag() // -> "-ES"
- /// ```
- ///
- /// All system iOS and macOS locales are expected to contain valid language codes.
- func toLanguageTag() -> String {
- return Locale.canonicalLanguageIdentifier(from: self.identifier)
- }
-}
diff --git a/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj b/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj
index d997b6fd4e8..6c55f3a5f5c 100644
--- a/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj
+++ b/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj
@@ -51,7 +51,6 @@
2CE83364A23B4E3BAFD447CA /* WalletHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E32B3AC4CC1C4F2DEEC5F292 /* WalletHeaderView.swift */; };
2E4C37C73AD202C8A3DD2E4E /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FF291A25EA43D4D100983B /* LoadingViewController.swift */; };
2EC9C94DD8D62E4F4EFC8AB8 /* IntentStatusPollerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990304EF35A0EE37DCE20D5B /* IntentStatusPollerTest.swift */; };
- 2EDF4115FDC40A5B0672CCFD /* Locale+Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5DCF73BEC0CC35D4CE30361 /* Locale+Link.swift */; };
311AC53D6C76953E9B70148A /* ConsumerSession+PublishableKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2D61B52BFA201D25E8F6428 /* ConsumerSession+PublishableKey.swift */; };
313F5F832B0BE5FD00BD98A9 /* Docs.docc in Sources */ = {isa = PBXBuildFile; fileRef = 313F5F822B0BE5FD00BD98A9 /* Docs.docc */; };
31699A812BE183B30048677F /* DownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31699A802BE183B30048677F /* DownloadManager.swift */; };
@@ -630,7 +629,6 @@
C3C1A5F36075EAEA5A413DC5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
C46CB5AB992F8EEFE4E5460A /* PaymentSheetConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSheetConfiguration.swift; sourceTree = ""; };
C47D00B9DE812D0801B4E33F /* PaymentSheetLPMConfirmFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSheetLPMConfirmFlowTests.swift; sourceTree = ""; };
- C5DCF73BEC0CC35D4CE30361 /* Locale+Link.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Locale+Link.swift"; sourceTree = ""; };
C684CBDA487CC3E78676F52E /* LinkEmailElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkEmailElement.swift; sourceTree = ""; };
C784153FF2052C4CF240441C /* SeparatorLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorLabel.swift; sourceTree = ""; };
C830FEC205E7162FF4D414BE /* PaymentMethodMessagingViewFunctionalTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodMessagingViewFunctionalTest.swift; sourceTree = ""; };
@@ -1037,7 +1035,6 @@
children = (
790527588C37B2E2CE0DE3CF /* LinkPopupURLParser.swift */,
F69E543E007B4361C7929FF5 /* LinkURLGenerator.swift */,
- C5DCF73BEC0CC35D4CE30361 /* Locale+Link.swift */,
274491B25447ABB37EF29D93 /* OperationDebouncer.swift */,
);
path = Utils;
@@ -1840,7 +1837,6 @@
B626EE932BF2872200B05B05 /* PaymentMethodTypeImageView.swift in Sources */,
FA32A3732505CC037CDE64D7 /* LinkURLGenerator.swift in Sources */,
61D8688E2C06553E001FAD84 /* RightAccessoryButton.swift in Sources */,
- 2EDF4115FDC40A5B0672CCFD /* Locale+Link.swift in Sources */,
1ECC1086460E57AE75F18FBF /* OperationDebouncer.swift in Sources */,
E236FE31A51D130F93F9299B /* LinkAccountContext.swift in Sources */,
D0B9FBCB359A7D774B98D19E /* LinkCookieKey.swift in Sources */,
diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Utils/Locale+Link.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Utils/Locale+Link.swift
deleted file mode 100644
index 34f995f4a53..00000000000
--- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Utils/Locale+Link.swift
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// Locale+Link.swift
-// StripePaymentSheet
-//
-// Created by Ramon Torres on 8/3/22.
-// Copyright © 2022 Stripe, Inc. All rights reserved.
-//
-
-import Foundation
-
-extension Locale {
-
- /// Returns the BCP 47(-ish) language tag representing the locale.
- ///
- /// The language tag is expected to be well-formed as log as the locale identifier contains a
- /// valid language code. For example:
- ///
- /// ```
- /// let locale = Locale(identifier: "fr_CA")
- /// locale.toLanguageTag() // -> "fr-CA"
- /// ```
- ///
- /// The following example returns `"-ES"`, even though `"und-ES"` will be the appropriate BCP 47 tag:
- ///
- /// ```
- /// let locale = Locale(identifier: "_ES")
- /// locale.toLanguageTag() // -> "-ES"
- /// ```
- ///
- /// All system iOS and macOS locales are expected to contain valid language codes.
- func toLanguageTag() -> String {
- return Locale.canonicalLanguageIdentifier(from: self.identifier)
- }
-
-}
From 90f95a6dc4e2a08ff692d50ff6a77091d9254de9 Mon Sep 17 00:00:00 2001
From: Mat Schmid
Date: Tue, 15 Oct 2024 10:11:42 -0400
Subject: [PATCH 2/2] Move FC SDK build failures to Customer UX bots slack
channel (#4127)
## Summary
This moves the Financial Connections build failures to the
#connections-customer-ux-bots slack channel, as well as Kris's test
channel.
## Motivation
Better visibility on test failures.
## Testing
N/a
## Changelog
N/a
---
bitrise.yml | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/bitrise.yml b/bitrise.yml
index 7259abaeeea..0875938bc05 100644
--- a/bitrise.yml
+++ b/bitrise.yml
@@ -202,6 +202,17 @@ workflows:
inputs:
- event_description: iOS E2E tests failing! $BITRISE_BUILD_URL.
- integration_key: $AUX_PAGERDUTY_INTEGRATION_KEY
+ - slack@3:
+ is_always_run: true
+ run_if: .IsBuildFailed
+ inputs:
+ - webhook_url: $WEBHOOK_SLACK_CUX_BOTS
+ - webhook_url_on_error: $WEBHOOK_SLACK_CUX_BOTS
+ - slack@3:
+ is_always_run: true
+ inputs:
+ - webhook_url: $WEBHOOK_SLACK_CONNECTIONS_MOBILE
+ - webhook_url_on_error: $WEBHOOK_SLACK_CONNECTIONS_MOBILE
- slack@3:
is_always_run: true
run_if: .IsBuildFailed