diff --git a/Demo Project/ResponsiveTextFieldDemo.xcodeproj/project.pbxproj b/Demo Project/ResponsiveTextFieldDemo.xcodeproj/project.pbxproj index 161bf53..9adee40 100644 --- a/Demo Project/ResponsiveTextFieldDemo.xcodeproj/project.pbxproj +++ b/Demo Project/ResponsiveTextFieldDemo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 6ABA3EEE2C52A3580052571D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 6ABA3EED2C52A3580052571D /* Localizable.xcstrings */; }; A328FAB125FD8A5300B9CE72 /* ResponsiveTextFieldApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A328FAB025FD8A5300B9CE72 /* ResponsiveTextFieldApp.swift */; }; A328FAB325FD8A5300B9CE72 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A328FAB225FD8A5300B9CE72 /* ContentView.swift */; }; A328FAB525FD8A5400B9CE72 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A328FAB425FD8A5400B9CE72 /* Assets.xcassets */; }; @@ -15,6 +16,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 6ABA3EED2C52A3580052571D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; A328FAAD25FD8A5300B9CE72 /* ResponsiveTextFieldDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ResponsiveTextFieldDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; A328FAB025FD8A5300B9CE72 /* ResponsiveTextFieldApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponsiveTextFieldApp.swift; sourceTree = ""; }; A328FAB225FD8A5300B9CE72 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -60,6 +62,7 @@ A328FAB425FD8A5400B9CE72 /* Assets.xcassets */, A328FAB925FD8A5400B9CE72 /* Info.plist */, A328FAB625FD8A5400B9CE72 /* Preview Content */, + 6ABA3EED2C52A3580052571D /* Localizable.xcstrings */, ); path = ResponsiveTextFieldDemo; sourceTree = ""; @@ -124,6 +127,7 @@ knownRegions = ( en, Base, + ar, ); mainGroup = A328FAA425FD8A5300B9CE72; productRefGroup = A328FAAE25FD8A5300B9CE72 /* Products */; @@ -142,6 +146,7 @@ files = ( A328FAB825FD8A5400B9CE72 /* Preview Assets.xcassets in Resources */, A328FAB525FD8A5400B9CE72 /* Assets.xcassets in Resources */, + 6ABA3EEE2C52A3580052571D /* Localizable.xcstrings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Demo Project/ResponsiveTextFieldDemo/ContentView.swift b/Demo Project/ResponsiveTextFieldDemo/ContentView.swift index 65a9e3c..da5c31a 100644 --- a/Demo Project/ResponsiveTextFieldDemo/ContentView.swift +++ b/Demo Project/ResponsiveTextFieldDemo/ContentView.swift @@ -10,6 +10,9 @@ import SwiftUI import Combine struct ContentView: View { + @State + var fullName: String = "" + @State var email: String = "" @@ -60,7 +63,7 @@ struct ContentView: View { NavigationView { VStack { ResponsiveTextField( - placeholder: "Email address", + placeholder: "email_address_label", text: $email, firstResponderDemand: $emailResponderDemand.animation(), configuration: .email, @@ -86,7 +89,7 @@ struct ContentView: View { HStack(alignment: .center) { ResponsiveTextField( - placeholder: "Password", + placeholder: "password_label", text: $password, isSecure: hidePassword, firstResponderDemand: $passwordResponderDemand.animation(), @@ -113,24 +116,32 @@ struct ContentView: View { } } .padding(.bottom) + + ResponsiveTextField( + placeholder: "full_name_label", + text: $fullName + ) + .responsiveKeyboardReturnType(.next) + .fixedSize(horizontal: false, vertical: true) + .padding(.bottom) - Toggle("Editing Email?", isOn: isEditingEmail) + Toggle("editing_email_label", isOn: isEditingEmail) .padding(.bottom) - Toggle("Editing Password?", isOn: isEditingPassword) + Toggle("editing_password_label", isOn: isEditingPassword) .padding(.bottom) - Toggle("Hide Password?", isOn: $hidePassword) + Toggle("hide_password_label", isOn: $hidePassword) .padding(.bottom) - Toggle("Enabled?", isOn: $isEnabled) + Toggle("enable_label", isOn: $isEnabled) .padding(.bottom) - Button("Random password") { + Button("random_password_label") { password = UUID().uuidString } - Text("You typed the following email:") + Text("typed_email_label") .padding(.bottom) Text(email).font(.caption) diff --git a/Demo Project/ResponsiveTextFieldDemo/Localizable.xcstrings b/Demo Project/ResponsiveTextFieldDemo/Localizable.xcstrings new file mode 100644 index 0000000..8178a78 --- /dev/null +++ b/Demo Project/ResponsiveTextFieldDemo/Localizable.xcstrings @@ -0,0 +1,159 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "editing_email_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تحرير البريد الإلكتروني؟" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editing Email?" + } + } + } + }, + "editing_password_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعديل كلمة المرور؟" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editing Password?" + } + } + } + }, + "email_address_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عنوان البريد الإلكتروني" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email address" + } + } + } + }, + "enable_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ممكن؟" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enabled?" + } + } + } + }, + "full_name_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الاسم الكامل" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Full Name" + } + } + } + }, + "hide_password_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اخفاء كلمة المرور؟" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hide Password?" + } + } + } + }, + "password_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "كلمة المرور" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Password" + } + } + } + }, + "random_password_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "كلمة مرور عشوائية" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Random password" + } + } + } + }, + "typed_email_label" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لقد كتبت البريد الإلكتروني التالي:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You typed the following email:" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Sources/ResponsiveTextField/ResponsiveTextField.swift b/Sources/ResponsiveTextField/ResponsiveTextField.swift index f4af275..47e5eee 100644 --- a/Sources/ResponsiveTextField/ResponsiveTextField.swift +++ b/Sources/ResponsiveTextField/ResponsiveTextField.swift @@ -321,7 +321,7 @@ extension ResponsiveTextField: UIViewRepresentable { textField.handleDelete = handleDelete textField.supportedStandardEditActions = supportedStandardEditActions textField.standardEditActionHandler = standardEditActionHandler - textField.placeholder = placeholder + textField.placeholder = NSLocalizedString(placeholder, comment: "Responsive TextField placeholder") textField.text = text.wrappedValue textField.isEnabled = isEnabled textField.isSecureTextEntry = isSecure diff --git a/Tests/ResponsiveTextFieldTests/ResponsiveTextFieldTests.swift b/Tests/ResponsiveTextFieldTests/ResponsiveTextFieldTests.swift index 8d6cfa2..25aa679 100644 --- a/Tests/ResponsiveTextFieldTests/ResponsiveTextFieldTests.swift +++ b/Tests/ResponsiveTextFieldTests/ResponsiveTextFieldTests.swift @@ -104,4 +104,66 @@ final class ResponsiveTextFieldTests: XCTestCase { named: "Right" ) } + + @MainActor + func testTextFieldInArabic() { + // Set the locale to Arabic + setLanguage("ar") + + assertSnapshot( + of: ResponsiveTextField( + placeholder: "نص العنصر النائب", // Arabic for "Placeholder Text" + text: .constant(""), + isSecure: false, + firstResponderDemand: nil, + configuration: .empty + ).padding(), + as: .fixedSizeTextFieldImage, + named: "ArabicEmpty" + ) + + assertSnapshot( + of: ResponsiveTextField( + placeholder: "نص العنصر النائب", + text: .constant("نص في حقل الإدخال"), + isSecure: false, + firstResponderDemand: nil, + configuration: .empty + ).padding(), + as: .fixedSizeTextFieldImage, + named: "ArabicText" + ) + + // Reset to default language + setLanguage("en") + } + + @MainActor + func testTextFieldWithDifferentConfigurations() { + let configurations: [(ResponsiveTextField.Configuration, String)] = [ + (.empty, "Empty Configuration"), + (.password, "Password"), + (.autoclears, "Auto clear"), + (.email, "Email") + ] + + for (config, name) in configurations { + assertSnapshot( + of: ResponsiveTextField( + placeholder: "Placeholder Text", + text: .constant("Sample text"), + isSecure: false, + firstResponderDemand: nil, + configuration: config + ).padding(), + as: .fixedSizeTextFieldImage, + named: name + ) + } + } + + private func setLanguage(_ language: String) { + UserDefaults.standard.set([language], forKey: "AppleLanguages") + UserDefaults.standard.synchronize() + } } diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testSecureTextEntry.1.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testSecureTextEntry.1.png index d3256aa..e6584c0 100644 Binary files a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testSecureTextEntry.1.png and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testSecureTextEntry.1.png differ diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldInArabic.ArabicEmpty.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldInArabic.ArabicEmpty.png new file mode 100644 index 0000000..3439840 Binary files /dev/null and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldInArabic.ArabicEmpty.png differ diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldInArabic.ArabicText.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldInArabic.ArabicText.png new file mode 100644 index 0000000..9ca45af Binary files /dev/null and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldInArabic.ArabicText.png differ diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Auto-clear.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Auto-clear.png new file mode 100644 index 0000000..0da7269 Binary files /dev/null and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Auto-clear.png differ diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Email.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Email.png new file mode 100644 index 0000000..0da7269 Binary files /dev/null and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Email.png differ diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Empty-Configuration.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Empty-Configuration.png new file mode 100644 index 0000000..0da7269 Binary files /dev/null and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Empty-Configuration.png differ diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Password.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Password.png new file mode 100644 index 0000000..0da7269 Binary files /dev/null and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithDifferentConfigurations.Password.png differ diff --git a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithText.1.png b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithText.1.png index edb2ea0..0a376a0 100644 Binary files a/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithText.1.png and b/Tests/ResponsiveTextFieldTests/__Snapshots__/ResponsiveTextFieldTests/testTextFieldWithText.1.png differ