Skip to content

Commit

Permalink
Merge pull request #48 from stelabouras/fix/current-locale
Browse files Browse the repository at this point in the history
Refactor TXPreferredLocaleProvider logic
  • Loading branch information
Nikos Vasileiou authored Dec 27, 2022
2 parents e983af3 + 1a35520 commit 249d851
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 22 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ on:
jobs:
build:

runs-on: macos-latest
runs-on: macos-12

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Build
run: swift build -v
- name: Run tests
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,11 @@ key if the entry is not found.

- Adds method to activate SDK from a Swift Package.
- Adds reference to SwiftUI limitation in README.

## Transifex iOS SDK 1.0.3

*December 27, 2022*

- Fixes TXPreferredLocaleProvider so that it uses the correct language candidate
based on user's preference and supported languages by the app developer.
- Fixes deprecation warnings on Github action.
2 changes: 1 addition & 1 deletion Sources/Transifex/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ render '\(stringToRender)' locale code: \(localeCode) params: \(params). Error:
/// A static class that is the main point of entry for all the functionality of Transifex Native throughout the SDK.
public final class TXNative : NSObject {
/// The SDK version
internal static let version = "1.0.2"
internal static let version = "1.0.3"

/// The filename of the file that holds the translated strings and it's bundled inside the app.
public static let STRINGS_FILENAME = "txstrings.json"
Expand Down
57 changes: 43 additions & 14 deletions Sources/Transifex/Locales.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,23 @@ public protocol TXCurrentLocaleProvider {
func currentLocale() -> String
}

/// Class that returns the language code of the current user's locale and falls back to "en" if the language
/// code cannot be found.
/// Class that returns the active language code that can be used for localization, based on the current user's
/// language preferences and the locales that the application supports. The class falls back to "en" if the
/// language code cannot be found.
public final class TXPreferredLocaleProvider : NSObject {
private static let FALLBACK_LOCALE = "en"
private var _appLocales : [String]
private var _currentLocale : String

override init() {
/// Designated initializer.
///
/// - Parameter appLocales: The locales the the application supports.
init(_ appLocales: [String]) {
_appLocales = appLocales

// Fetch the current locale on initialization and return it when it's
// requested by the `currentLocale()` method.
_currentLocale = TXPreferredLocaleProvider.getCurrentLocale()
_currentLocale = Self.getCurrentLocale(_appLocales)

super.init()

Expand All @@ -38,18 +46,33 @@ public final class TXPreferredLocaleProvider : NSObject {

@objc
private func currentLocaleDidChange() {
_currentLocale = TXPreferredLocaleProvider.getCurrentLocale()
_currentLocale = Self.getCurrentLocale(_appLocales)
}

private static func getPreferredLocale() -> Locale {
guard let preferredIdentifier = Locale.preferredLanguages.first else {
if let preferredLanguage = Locale.preferredLanguages.first {
return Locale(identifier: preferredLanguage)
}
else {
return Locale.autoupdatingCurrent
}
return Locale(identifier: preferredIdentifier)
}

private static func getCurrentLocale() -> String {
return getPreferredLocale().languageCode ?? "en"
private static func getCurrentLocale(_ appLocales: [String]) -> String {
// Attempt to get the most preferred locale based on:
// * the supported app locales provided by the developer when
// initializing the TXNative instance.
// * the preferred languages set by the user in the device Settings.
if let preferredLocalization = Bundle.preferredLocalizations(from: appLocales,
forPreferences: Locale.preferredLanguages).first {
return preferredLocalization
}
// Although it's highly unlikely that a preferred localization will not
// be extracted using the preferredLocalizations() call, we have a
// number of fallbacks in place to locate the best candidate.
else {
return getPreferredLocale().languageCode ?? FALLBACK_LOCALE
}
}
}

Expand Down Expand Up @@ -113,13 +136,19 @@ public final class TXLocaleState : NSObject {
// Make sure we filter all duplicate values
// by converting the array to a Set.
var distinctAppLocales = Set(appLocales)
// Insert the source locale in case it hasn't been added.
distinctAppLocales.insert(sourceLocale)
self.appLocales = Array(distinctAppLocales)
// Remove the source locale (if it has been already added by the
// developer).
distinctAppLocales.remove(sourceLocale)

self.translatedLocales = self.appLocales.filter { $0 != sourceLocale }
self.translatedLocales = Array(distinctAppLocales)

self.appLocales = Array(distinctAppLocales)
// Add the source locale as the first element of the array, as the
// `TXPreferredLocaleProvider` (and `Bundle.preferredLocalizations` more
// specifically) uses the first element as a fallback.
self.appLocales.insert(sourceLocale, at: 0)

self.currentLocaleProvider = currentLocaleProvider ?? TXPreferredLocaleProvider()
self.currentLocaleProvider = currentLocaleProvider ?? TXPreferredLocaleProvider(self.appLocales)
}

/// Returns true if the given locale is the source locale, false otherwise.
Expand Down
37 changes: 32 additions & 5 deletions Tests/TransifexTests/TransifexTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -572,22 +572,47 @@ final class TransifexTests: XCTestCase {
XCTAssertEqual(result, "ERROR")
}

func testCurrentLocale() {
func testCurrentLocaleNotFirstPreference() {
let appleLanguagesKey = "AppleLanguages"
let storedLanguages = UserDefaults.standard.value(forKey: appleLanguagesKey)

UserDefaults.standard.set([ "el" ],
UserDefaults.standard.set([ "nl", "fr" ],
forKey: appleLanguagesKey)

let locale = TXLocaleState(appLocales: [])
let locale = TXLocaleState(sourceLocale: "en",
appLocales: [ "fr", "de", "es", "it"])

XCTAssertEqual(locale.currentLocale,
"el")
"fr")

UserDefaults.standard.set(storedLanguages,
forKey: appleLanguagesKey)
}

func testCurrentLocaleNotAnyPreference() {
let appleLanguagesKey = "AppleLanguages"
let storedLanguages = UserDefaults.standard.value(forKey: appleLanguagesKey)

UserDefaults.standard.set([ "nl", "fr" ],
forKey: appleLanguagesKey)

let locale = TXLocaleState(sourceLocale: "en",
appLocales: [ "de", "es", "it"])

XCTAssertEqual(locale.currentLocale,
"en")

UserDefaults.standard.set(storedLanguages,
forKey: appleLanguagesKey)
}

func testSourceLocalePosition() {
let locale = TXLocaleState(sourceLocale: "en",
appLocales: [ "fr", "el" ])

XCTAssertTrue(locale.appLocales.first == "en")
}

func testTranslateWithSourceStringsInCache() {
let sourceLocale = "en"
let localeState = TXLocaleState(sourceLocale: sourceLocale,
Expand Down Expand Up @@ -661,7 +686,9 @@ final class TransifexTests: XCTestCase {
("testPlatformStrategyWithInvalidSourceString", testPlatformStrategyWithInvalidSourceString),
("testErrorPolicy", testErrorPolicy),
("testErrorPolicyException", testErrorPolicyException),
("testCurrentLocale", testCurrentLocale),
("testCurrentLocaleNotFirstPreference", testCurrentLocaleNotFirstPreference),
("testCurrentLocaleNotAnyPreference", testCurrentLocaleNotAnyPreference),
("testSourceLocalePosition", testSourceLocalePosition),
("testTranslateWithSourceStringsInCache", testTranslateWithSourceStringsInCache),
]
}

0 comments on commit 249d851

Please sign in to comment.