Crowdin iOS SDK delivers all new translations from Crowdin project to the application immediately

Overview

Crowdin iOS SDK

Crowdin iOS SDK delivers all new translations from Crowdin project to the application immediately. So there is no need to update this application via App Store to get the new version with the localization.

The SDK provides:

  • Over-The-Air Content Delivery – the localized content can be sent to the application from the project whenever needed.
  • Real-Time Preview – all the translations that are done in the Editor can be shown in your version of the application in real-time. View the translations already made and the ones you're currently typing in.
  • Screenshots – all the screenshots made in the application may be automatically sent to your Crowdin project with tagged source strings.

Status

Cocoapods Cocoapods platforms GitHub Release Date GitHub contributors GitHub issues GitHub License

Azure DevOps builds (branch) codecov Azure DevOps tests (branch)

Table of Contents

Requirements

  • Xcode 10.2
  • Swift 4.2
  • iOS 9.0

Dependencies

  • Starscream - Websockets in swift for iOS and OSX.

Installation

  1. Cocoapods

    To install Crowdin iOS SDK via cocoapods, make sure you have cocoapods installed locally. If not, install it with following command: sudo gem install cocoapods.

    Detailed instruction can be found here.

    Add the following line to your Podfile:

    pod 'CrowdinSDK'
  2. Cocoapods spec repository

    target 'MyApp' do
      pod 'CrowdinSDK'
    end
  3. Working with App Extensions

    Upon pod install result, you might experience some building issues in case your application embeds target extensions.

    Example error:

    'shared' (Swift) / 'sharedApplication' (Objective-C) is unavailable: not available on iOS (App Extension) - Use view controller based solutions where appropriate instead.

    In this scenario you'll need to add a post_install script to your Podfile

    post_install do |installer|
    
      extension_api_exclude_pods = ['CrowdinSDK']
    
      installer.pods_project.targets.each do |target|
    
          # the Pods contained into the `extension_api_exclude_pods` array
          # have to get the value (APPLICATION_EXTENSION_API_ONLY) set to NO
          # in order to work with service extensions
    
          if extension_api_exclude_pods.include? target.name
            target.build_configurations.each do |config|
              config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
            end
          end
        end
    end

    Then run pod install again to fix it.

After you've added CrowdinSDK to your Podfile, run pod install in your project directory, open App.xcworkspace and build it.

Wiki

Visit the Crowdin iOS SDK Wiki to see additional project documentation. Here you can find information about the Example project, SDK Controls, and more.

Setup

To configure iOS SDK integration you need to:

  • Upload your strings/stringsdict localization files to Crowdin. If you have ready translations, you can also upload them.
  • Set up Distribution in Crowdin.
  • Set up SDK and enable Over-The-Air Content Delivery feature.

Distribution is a CDN vault that mirrors the translated content of your project and is required for integration with iOS app.

To manage distributions open the needed project and go to Over-The-Air Content Delivery. You can create as many distributions as you need and choose different files for each. You’ll need to click the Release button next to the necessary distribution every time you want to send new translations to the app.

  1. Enable Over-The-Air Content Delivery in your Crowdin project so that application can pull translations from CDN vault.

  2. In order to start using CrowdinSDK you need to initialize it in AppDelegate or in Info.plist


Notes:

  • The CDN feature does not update the localization files. if you want to add new translations to the localization files you need to do it yourself.
  • Once SDK receives the translations, it's stored on the device as application files for further sessions to minimize requests the next time the app starts. Storage time can be configured using intervalUpdatesEnabled option.
  • CDN caches all the translation in release for up to 15 minutes and even when new translations are released in Crowdin, CDN may return it with a delay.

Setup with AppDelegate

Open AppDelegate.swift file and add:

import CrowdinSDK

In application method add:

let crowdinProviderConfig = CrowdinProviderConfig(hashString: "{your_distribution_hash}",
  sourceLanguage: "{source_language}")

CrowdinSDK.startWithConfig(crowdinSDKConfig, completion: {
    // SDK is ready to use, put code to change language, etc. here
})
Config option Description Example
hashString Distribution Hash hashString: "7a0c1ee2622bc85a4030297uo3b"
sourceLanguage Source language code in your Crowdin project. ISO 639-1 sourceLanguage: "en"
Objective-C

In AppDelegate.m add:

@import CrowdinSDK

or

#import<CrowdinSDK/CrowdinSDK.h>

In application method add:

CrowdinProviderConfig *crowdinProviderConfig = [[CrowdinProviderConfig alloc] initWithHashString:@"" sourceLanguage:@""];
CrowdinSDKConfig *config = [[[CrowdinSDKConfig config] withCrowdinProviderConfig:crowdinProviderConfig]];

[CrowdinSDK startWithConfig:config completion:^{
    // SDK is ready to use, put code to change language, etc. here
}];

If you have pure Objective-C project, then you will need to do some additional steps:

Add the following code to your Library Search Paths:

$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)

Add use_frameworks! to your Podfile.

Setup with Info.plist

Open Info.plist file and add:

CrowdinDistributionHash - Crowdin CDN hash value for current project (String value).

CrowdinSourceLanguage - Source language code (ISO 639-1) for current project on crowdin server (String value).

In AppDelegate you should call start method: CrowdinSDK.start() for Swift, and [CrowdinSDK start] for Objective-C.

Note! Using this setup method you will unable to set up additional Screenshots and Real-Time Preview project features.

Advanced Features

Real-Time Preview

All the translations that are done in the Editor can be shown in the application in real-time. View the translations already made and the ones you're currently typing in.

Add the code below to your Podfile:

use_frameworks!
target 'your-app' do
  pod 'CrowdinSDK'
  pod 'CrowdinSDK/LoginFeature' // Required for Real-Time Preview
  pod 'CrowdinSDK/RealtimeUpdate' // Required for Real-Time Preview
  pod 'CrowdinSDK/Settings' // Optional. To add 'settings' floating button
end

Open AppDelegate.swift file and in application method add:

let crowdinProviderConfig = CrowdinProviderConfig(hashString: "{your_distribution_hash}",
    sourceLanguage: "{source_language}")

var loginConfig: CrowdinLoginConfig
do {
	loginConfig = try CrowdinLoginConfig(clientId: "{client_id}",
		clientSecret: "{client_secret}",
		scope: "project",
		redirectURI: "{redirectURI}",
		organizationName: "{organization_name}")
} catch {
	print(error)
	// CrowdinLoginConfig initialization error handling, typically for empty values and for wrong redirect URI value.
}

let crowdinSDKConfig = CrowdinSDKConfig.config().with(crowdinProviderConfig: crowdinProviderConfig)
    .with(loginConfig: loginConfig)
    .with(settingsEnabled: true)
    .with(realtimeUpdatesEnabled: true)

CrowdinSDK.startWithConfig(crowdinSDKConfig, completion: {
	// SDK is ready to use, put code to change language, etc. here
})
Objective-C
CrowdinProviderConfig *crowdinProviderConfig = [[CrowdinProviderConfig alloc] initWithHashString:@"" sourceLanguage:@""];

NSError *error;
CrowdinLoginConfig *loginConfig = [[CrowdinLoginConfig alloc] initWithClientId:@"{client_id}" clientSecret:@"{client_secter}" scope:@"project" organizationName:@"{organization_name}" error:&error];

if (!error) {
	CrowdinSDKConfig *config = [[[CrowdinSDKConfig config] withCrowdinProviderConfig:crowdinProviderConfig] withLoginConfig:loginConfig];

	[CrowdinSDK startWithConfig:config completion:^{
		// SDK is ready to use, put code to change language, etc. here
	}];
} else {
	NSLog(@"%@", error.localizedDescription);
	// CrowdinLoginConfig initialization error handling, typically for empty values and for wrong redirect URI value.
}
Config option Description Example
hashString Distribution Hash hashString: "7a0c1ee2622bc85a4030297uo3b"
sourceLanguage Source language code in your Crowdin project. ISO 639-1 sourceLanguage: "en"
clientId, clientSecret Crowdin OAuth Client ID and Client Secret clientId: "gpY2yTbCVGEelrcx3TYB", clientSecret: "Xz95t0ASVgbvKaZbFB4SMHQzdUl1MSgSTabEDx9T"
scope Define the access scope for personal tokens scope: "project"
redirectURI A custom URL for your app. Read more in the article. It's an optional value. You should set it in case you want to use a specific URL scheme. In case you set a scheme which is not supported by your application init method will throw an exception. redirectURI: "crowdintest://"
organizationName An Organization domain name (for Crowdin Enterprise users only) organizationName: "mycompany"
settingsEnabled Enable floating settings view with a list of all active features and its statuses settingsEnabled: true
realtimeUpdatesEnabled Enable Real-Time Preview feature realtimeUpdatesEnabled: true

The last step is to handle authorization callback in your application:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
	guard let url = URLContexts.first?.url else { return }
	CrowdinSDK.handle(url: url)
}
Objective-C
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    return [CrowdinSDK handleWithUrl:url];
}

If you are using SceneDelegate, you need to handle callback in the SceneDelegate class implement method:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
	guard let url = URLContexts.first?.url else { return }
	CrowdinSDK.handle(url: url)
}
Objective-C
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
    return [CrowdinSDK handleWithUrl:url];
}

Screenshots

Enable if you want all the screenshots made in the application to be automatically sent to your Crowdin project with tagged strings. This will provide additional context for translators.

Add the code below to your Podfile:

use_frameworks!
target 'your-app' do
  pod 'CrowdinSDK'
  pod 'CrowdinSDK/LoginFeature' // Required for Screenshots
  pod 'CrowdinSDK/Screenshots' // Required for Screenshots
  pod 'CrowdinSDK/Settings' // Optional. To add 'settings' button
end

Open AppDelegate.swift file and in application method add:

let crowdinProviderConfig = CrowdinProviderConfig(hashString: "{your_distribution_hash}",
    sourceLanguage: "{source_language}")

var loginConfig: CrowdinLoginConfig
do {
	loginConfig = try CrowdinLoginConfig(clientId: "{client_id}",
		clientSecret: "{client_secret}",
		scope: "project.screenshot",
		redirectURI: "{redirectURI}",
		organizationName: "{organization_name}")
} catch {
	print(error)
	// CrowdinLoginConfig initialization error handling, typically for empty values and for wrong redirect URI value.
}

let crowdinSDKConfig = CrowdinSDKConfig.config().with(crowdinProviderConfig: crowdinProviderConfig)
    .with(screenshotsEnabled: true)
    .with(loginConfig: loginConfig)
    .with(settingsEnabled: true)

CrowdinSDK.startWithConfig(crowdinSDKConfig, completion: {
	// SDK is ready to use, put code to change language, etc. here
})
Objective-C
CrowdinProviderConfig *crowdinProviderConfig = [[CrowdinProviderConfig alloc] initWithHashString:@"" sourceLanguage:@""];

NSError *error;
CrowdinLoginConfig *loginConfig = [[CrowdinLoginConfig alloc] initWithClientId:@"{client_id}" clientSecret:@"{client_secter}" scope:@"project.screenshot" organizationName:@"{organization_name}" error:&error];

if (!error) {
	CrowdinSDKConfig *config = [[[CrowdinSDKConfig config] withCrowdinProviderConfig:crowdinProviderConfig] withLoginConfig:loginConfig];

	[CrowdinSDK startWithConfig:config completion:^{
		// SDK is ready to use, put code to change language, etc. here
	}];
} else {
	NSLog(@"%@", error.localizedDescription);
	// CrowdinLoginConfig initialization error handling, typically for empty values and for wrong redirect URI value.
}
Config option Description Example
hashString Distribution Hash hashString: "7a0c1ee2622bc85a4030297uo3b"
sourceLanguage Source language code in your Crowdin project. ISO 639-1 sourceLanguage: "en"
clientId, clientSecret Crowdin OAuth Client ID and Client Secret clientId: "gpY2yTbCVGEelrcx3TYB", clientSecret: "Xz95t0ASVgbvKaZbFB4SMHQzdUl1MSgSTabEDx9T"
scope Define the access scope for personal tokens scope: "project.screenshot"
redirectURI A custom URL for your app. Read more in the article. It's an optional value. You should set it in case you want to use a specific URL scheme. In case you set a scheme which is not supported by your application init method will throw an exception. redirectURI: "crowdintest://"
organizationName An Organization domain name (for Crowdin Enterprise users only) organizationName: "mycompany"
settingsEnabled Enable floating settings view with a list of all active features and its statuses settingsEnabled: true
screenshotsEnabled Enable floating button to send screenshots to Crowdin screenshotsEnabled: true

The last step is to handle authorization callback in your application:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
	guard let url = URLContexts.first?.url else { return }
	CrowdinSDK.handle(url: url)
}
Objective-C
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    return [CrowdinSDK handleWithUrl:url];
}

If you are using SceneDelegate, you need to handle callback in the SceneDelegate class implement method:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
	guard let url = URLContexts.first?.url else { return }
	CrowdinSDK.handle(url: url)
}
Objective-C
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
    return [CrowdinSDK handleWithUrl:url];
}

Notes

  1. Configuring translation update interval

    By default SDK is looking for new translation once per application load every 15 minutes. You can update translations in application every defined time interval. To enable this feature add pod CrowdinSDK/IntervalUpdate to your pod file:

    pod 'CrowdinSDK/IntervalUpdate'

    Then enable this option in CrowdinSDKConfig:

    .with(intervalUpdatesEnabled: true, interval: {interval})

    interval - defines translations update time interval in seconds. Minimum allowed interval is 15 minutes.

  2. R-Swift applications are also supported by Crowdin iOS SDK.

  3. To change SDK target language on the fly regardless of device locale use the following method:

    CrowdinSDK.enableSDKLocalization(true, localization: “<language_code>”)

    <language_code> - target language code in ISO 639-1 format.

  4. Currently, Custom Languages and Language Mapping are not supported for iOS SDK.

  5. Crowdin iOS SDK provides detailed debug mode - "Logs" tab in the Settings floating button module and logging into XCode console. To enable console logging, add the following option to your CrowdinSDKConfig

    .with(debugEnabled: true)
  6. Log callback. Crowdin SDK collects log messages for all actions that made by SDK (login/logout, download languages, API calls). This callback returns Log text each time a new Log is created. To subscribe on receiving Log messages just add a new callback like this:

    CrowdinSDK.setOnLogCallback { logMessage in
       print("LOG MESSAGE - \(logMessage)")
    }

File Export Patterns

You can set file export patterns and check existing ones using File Settings. The following placeholders are supported for iOS integration:

Name Description
%language% Language name (e.g. Ukrainian)
%locale% Locale (e.g. uk-UA)
%two_letters_code% Language code ISO 639-1 (i.e. uk)
%locale_with_underscore% Locale (e.g. uk_UA)
%osx_code% OS X locale identifier used to name ".lproj" directories
%osx_locale% OS X locale used to name translation resources (e.g. uk, zh-Hans, zh_HK)

Contributing

If you want to contribute please read the Contributing guidelines.

Seeking Assistance

If you find any problems or would like to suggest a feature, please feel free to file an issue on Github at Issues Page.

Need help working with Crowdin iOS SDK or have any questions? Contact Customer Success Service.

Security

Crowdin iOS SDK CDN feature is built with security in mind, which means minimal access possible from the end-user is required. When you decide to use Crowdin iOS SDK, please make sure you’ve made the following information accessible to your end-users.

  • We use the advantages of Amazon Web Services (AWS) for our computing infrastructure. AWS has ISO 27001 certification and has completed multiple SSAE 16 audits. All the translations are stored at AWS servers.
  • When you use Crowdin iOS SDK CDN – translations are uploaded to Amazon CloudFront to be delivered to the app and speed up the download. Keep in mind that your users download translations without any additional authentication.
  • We use encryption to keep your data private while in transit.
  • We do not store any Personally Identifiable Information (PII) about the end-user, but you can decide to develop the opt-out option inside your application to make sure your users have full control.
  • The Automatic Screenshots and Real-Time Preview features are supposed to be used by the development team and translators team. Those features should not be compiled to the production version of your app. Therefore, should not affect end-users privacy in any way.

Authors

License

The Crowdin iOS SDK is licensed under the MIT License. 
See the LICENSE file distributed with this work for additional 
information regarding copyright ownership.

Except as contained in the LICENSE file, the name(s) of the above copyright 
holders shall not be used in advertising or otherwise to promote the sale, 
use or other dealings in this Software without prior written authorization.
Comments
  • Support for Swift Package Manager

    Support for Swift Package Manager

    I saw that only Cocoapods is supported at the moment and it would be nice to have a support for Carthage and Swift Package Manager. Currently we use Carthage, but planning to move to Swift Package Manager (having it in xCode) in the future.

    enhancement 
    opened by vcs-jenkins 20
  • Crash on Apple TV device

    Crash on Apple TV device

    Describe the bug I am getting an app crash when using the SDK on a physical Apple TV device. I get the following error when the SDK is attempting to create the CrowdinFolder for the translation files:

    Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=513 "You don't have permission to save the file "Crowdin" in the folder ".Crowdin"."

    Note that this does not happen on the Apple TV simulator in Xcode, only a physical device.

    To Reproduce Steps to reproduce the behavior:

    1. Build to a physical Apple TV device
    2. The app crashes after starting the CrowdinSDK

    Expected behavior My understanding is that storing data on an Apple TV is not available outside of the cache directory, and when the SDK attempts to create a folder in the documents directory, the FileManager throws an exception.

    Screenshots Screen Shot 2022-02-22 at 6 49 22 PM

    Smartphone (please complete the following information):

    • Device: Apple TV 4K (2nd generation)
    • OS: tvOS 15.3

    Additional context I was able to avoid the crash by changing DocumentsFolder.documentsPath from: static let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] to: static let documentsPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0] but I am not aware if this will have any performance implications.

    bug help wanted 
    opened by dannyhuggins 13
  • Update SocketAPI to use 4.0.4 Starscream

    Update SocketAPI to use 4.0.4 Starscream

    This MR updates crowdin dependency of Starscream usages to version 4.0.4.

    This should resolve the issue: https://github.com/crowdin/mobile-sdk-ios/issues/167 (4.0.4 also removes the extra dependency added from Starscream: swift-nio-zlib-support)

    Feel free to ask something about my changes if you like.

    opened by alexookah 10
  • watchOS comparability issue

    watchOS comparability issue

    Describe the bug A clear and concise description of what the bug is.

    To Reproduce Steps to reproduce the behavior:

    1. Create watchOS project
    2. Add CrowdinSDK with Swift Package: https://github.com/crowdin/mobile-sdk-ios.git
    3. In the WatchKit Extension module open: Build Phases -> Link Binary With Libraries and add CrowdinSDK
    4. Open ExtensionDelegate.swift and add import CrowdinSDK
    5. Try build project for launch on Apple Watch or Apple Watch Simulator
    6. You will see 2 errors in LoginFeature.swift (from CrowdinSDK) on line: import SafariServices Error: No such module 'SafariServices' and: SafariServices is not available when building for watchOS Simulator. Consider using #if !os(watchOS) to conditionally import this framework.

    Screenshots Screen Shot 2022-04-18 at 13 08 27

    Smartphone (please complete the following information):

    • Device: Simulator Apple Watch Series 7
    • OS: watchOS 8.3
    bug help wanted 
    opened by Radzievskyi 9
  • Screenshot broken on 1.1.7

    Screenshot broken on 1.1.7

    After upgrading Crowdin to 1.1.7, I found the screenshot function is broken. And after some debugging, I found several issues.

    Precondition:

    My project has multiple localized string files.

    Issues

    1. Distribution only contains 1 file:

    • This download [CrowdinMappingDownloader download(with hash:...] succeed.
    • But [ScreenshotUploader uploadScreenshot(screenshot:...] gets early return. Because it hits line 75 in ScreenshotUploader(Newly added in 1.1.7) since the string files do not exist.
    • The screenshot function is only working on the specific screen (the file contains in the distribution)

    2, Distribution contains multiple files, such as a folder

    • This download [CrowdinMappingDownloader download(with hash:...] returns an error.
    opened by dabaicaifen 9
  • Access Markdown files via Over-the-air content delivery.

    Access Markdown files via Over-the-air content delivery.

    Hello

    I noticed that via the Tools -> Over-the-air(OTA) content delivery -> Distributions, we can select and release files that are not only Localizable.strings, but also .mdown files. Reading through the documentation, I saw that the the OTA content delivery does not change existing files or add new files, which means that they don't exist in the file system of the project when running the app. I am wondering how we can access the content of these .mdown files in the iOS project that have been released via OTA?

    Many thanks!

    question 
    opened by stevi-clue 8
  • Reduce unnecessary computation when not using realtime update feature

    Reduce unnecessary computation when not using realtime update feature

    This change shortcircuits processing that is unnecessary when the realtime-update feature is not used.

    Background Our app is very UI-rich, and we found that CrowdIn was using a significant amount of compute time.

    After tracing through the code, it appears that the CrowdInSDK was doing a significant amount of string searching and string matching (it was taking about 3-5% of cpu cycles on the main thread).

    The results of this string processing was not used anywhere except for the real-time update feature, a dev-only feature!

    To ensure that we don't do any of this processing when the real-time update feature is not even compiled (which is every prod build), this PR adds a short-circuit to avoid all that work within the library.

    This is a pretty strong and effective band-aid. We should never do processing on data that doesn't get used at all.

    Future work If a feature gets added down the road that do require the localization keys and values, it would be important to figure out how to optimize the findKey(for string: String) function for classes conforming to LocalizationDataSourceProtocol. That is extremely time-intensive on an app with a lot of text.

    opened by winstondu 8
  • Example can't authorize success.

    Example can't authorize success.

    I create a project in crowdin, and replace distributionHash, sourceLanguage, clientId, clientSecret in SceneDelegate.swift file. After I run the Example, and click "Log in" but authorize failed. the status code is 401, and the error message is "Client authentication failed". I use the same configuration in android sample, but success.

    bug 
    opened by weberlisper 8
  • Crash when accessing `currentLocalization` on startup in 1.1.4

    Crash when accessing `currentLocalization` on startup in 1.1.4

    Describe the bug In applicationDidFinishLaunching:

    CrowdinSDK.startWithConfig(crowdinSDKConfig) {
        let localization = CountrySelectionUserDefaults.selectedCountry.languageCode
        CrowdinSDK.enableSDKLocalization(true, localization: localization)
    }
    

    calling enableSDKLocalization will crash on a device (when running the app in Release Mode). When running with DEBUG configuration it works fine. I think it only crashes in Release mode because the build setting SWIFT_ENFORCE_EXCLUSIVE_ACCESS only crashes the app in release mode and it seems like this is the root cause of the crash.

    Attached the stack trace:

    Simultaneous accesses to 0x280a62770, but modification requires exclusive access.
    Previous access (a modification) started at meindm`Localization.provider.modify + 60 (0x102e0c7a8).
    Current access (a read) started at:
    0    libswiftCore.dylib                 0x00000001aa9cd248 swift_beginAccess + 560
    1    meindm                             0x0000000102e0c5ec Localization.provider.getter + 68
    2    meindm                             0x0000000102e0e984 Localization.localizedString(for:) + 100
    3    meindm                             0x0000000102d86940 NSBundle.swizzled_LocalizedString(forKey:value:table:) + 476
    4    meindm                             0x0000000102d86ef0 @objc NSBundle.swizzled_LocalizedString(forKey:value:table:) + 368
    5    Foundation                         0x000000019d5810a4 <redacted> + 1056
    6    Foundation                         0x000000019d653794 <redacted> + 184
    7    Foundation                         0x000000019d653e10 <redacted> + 196
    8    Foundation                         0x000000019d5a0770 <redacted> + 164
    9    meindm                             0x0000000102e3f7f0 Dictionary.write(to:) + 364
    10   meindm                             0x0000000102e3fba4 protocol witness for ReadWriteProtocol.write(to:) in conformance [A : B] + 32
    11   meindm                             0x0000000102df4f54 ReadWriteFile.save() + 392
    12   meindm                             0x0000000102e23594 LocalLocalizationStorage.saveLocalization() + 1336
    13   meindm                             0x0000000102e22138 LocalLocalizationStorage.strings.setter + 308
    14   meindm                             0x0000000102e22a3c LocalLocalizationStorage.fetchData() + 1092
    15   meindm                             0x0000000102e216f0 LocalLocalizationStorage.localization.didset + 152
    16   meindm                             0x0000000102e21428 LocalLocalizationStorage.localization.setter + 324
    17   meindm                             0x0000000102e213b8 @objc LocalLocalizationStorage.localization.setter + 80
    18   meindm                             0x0000000102e12858 LocalizationProvider.localization.didset + 328
    19   meindm                             0x0000000102e12b48 LocalizationProvider.localization.setter + 368
    20   meindm                             0x0000000102e194c0 protocol witness for LocalizationProviderProtocol.localization.setter in conformance LocalizationProvider + 48
    21   meindm                             0x0000000102e0d1f4 Localization.currentLocalization.setter + 628
    22   meindm                             0x0000000102dc8d58 static CrowdinSDK.currentLocalization.setter + 388
    23   meindm                             0x0000000102dc9dd0 static CrowdinSDK.enableSDKLocalization(_:localization:) + 528
    24   meindm                             0x0000000102529594 closure #1 in CrowdinAppService.configure() + 180
    25   meindm                             0x0000000102464a14 thunk for @escaping @callee_guaranteed () -> () + 56
    26   meindm                             0x000000010278ec94 thunk for @escaping @callee_unowned @convention(block) () -> () + 16
    27   meindm                             0x0000000102dc9944 closure #1 in static CrowdinSDK.startWithRemoteStorage(_:completion:) + 244
    28   meindm                             0x0000000102464a14 thunk for @escaping @callee_guaranteed () -> () + 56
    29   meindm                             0x000000010278ec94 thunk for @escaping @callee_unowned @convention(block) () -> () + 16
    30   meindm                             0x0000000102dc4388 CrowdinRemoteLocalizationStorage.prepare(with:) + 432
    31   meindm                             0x0000000102dc46e4 @objc CrowdinRemoteLocalizationStorage.prepare(with:) + 120
    32   meindm                             0x0000000102dc97a4 static CrowdinSDK.startWithRemoteStorage(_:completion:) + 396
    33   meindm                             0x0000000102dc6068 static CrowdinSDK.startWithConfig(_:completion:) + 836
    34   meindm                             0x0000000102dc63fc @objc static CrowdinSDK.startWithConfig(_:completion:) + 152
    35   meindm                             0x00000001025293a4 CrowdinAppService.configure() + 448
    36   meindm                             0x0000000102475d6c AppDelegate.application(_:didFinishLaunchingWithOptions:) + 536
    37   meindm                             0x00000001024762d4 @objc AppDelegate.application(_:didFinishLaunchingWithOptions:) + 232
    38   UIKitCore                          0x00000001a1322b10 <redacted> + 356
    39   UIKitCore                          0x00000001a1323648 <redacted> + 5048
    40   UIKitCore                          0x00000001a1329dc0 <redacted> + 1244
    41   UIKitCore                          0x00000001a0aceadc <redacted> + 148
    42   UIKitCore                          0x00000001a0f7f278 _UIScenePerformActionsWithLifecycleActionMask + 100
    43   UIKitCore                          0x00000001a0acf590 <redacted> + 196
    44   UIKitCore                          0x00000001a0acef88 <redacted> + 288
    45   UIKitCore                          0x00000001a0acf19c <redacted> + 740
    46   UIKitCore                          0x00000001a0acebf0 <redacted> + 336
    47   UIKitCore                          0x00000001a0ad3120 <redacted> + 188
    48   UIKitCore                          0x00000001a0ea3074 <redacted> + 812
    49   UIKitCore                          0x00000001a0f9862c _UISceneSettingsDiffActionPerformChangesWithTransitionContext + 244
    50   UIKitCore                          0x00000001a0ad2eac <redacted> + 140
    51   UIKitCore                          0x00000001a0f985b8 _UISceneSettingsDiffActionPerformActionsWithDelayForTransitionContext + 100
    52   UIKitCore                          0x00000001a0ad2c28 <redacted> + 376
    53   UIKitCore                          0x00000001a0942cf8 <redacted> + 636
    54   UIKitCore                          0x00000001a0941a1c <redacted> + 248
    55   UIKitCore                          0x00000001a0942bd0 <redacted> + 220
    56   UIKitCore                          0x00000001a13282cc <redacted> + 540
    57   UIKitCore                          0x00000001a0ec8524 <redacted> + 360
    58   FrontBoardServices                 0x00000001a23fc474 <redacted> + 424
    59   FrontBoardServices                 0x00000001a2421130 <redacted> + 100
    60   FrontBoardServices                 0x00000001a2406b14 <redacted> + 232
    61   FrontBoardServices                 0x00000001a2420d18 <redacted> + 312
    62   libdispatch.dylib                  0x0000000108b67720 _dispatch_client_callout + 16
    63   libdispatch.dylib                  0x0000000108b6aac8 _dispatch_block_invoke_direct + 232
    64   FrontBoardServices                 0x00000001a2445828 <redacted> + 40
    65   FrontBoardServices                 0x00000001a2445388 <redacted> + 404
    66   FrontBoardServices                 0x00000001a2445a28 <redacted> + 28
    67   CoreFoundation                     0x000000019d203c00 <redacted> + 24
    68   CoreFoundation                     0x000000019d203b20 <redacted> + 80
    69   CoreFoundation                     0x000000019d203240 <redacted> + 184
    70   CoreFoundation                     0x000000019d1fe014 <redacted> + 788
    71   CoreFoundation                     0x000000019d1fdb40 CFRunLoopRunSpecific + 424
    72   GraphicsServices                   0x00000001a73482ec GSEventRunModal + 160
    73   UIKitCore                          0x00000001a132bcb8 UIApplicationMain + 1932
    74   meindm                             0x0000000102851dac main + 676
    75   libdyld.dylib                      0x000000019d0858ec <redacted> + 4
    (lldb) 
    

    I also added a screenshot of the crash in the Xcode Debugger.

    To Reproduce Steps to reproduce the behavior:

    1. Call in applicationDidFinishLaunching:
    CrowdinSDK.startWithConfig(crowdinSDKConfig) {
        let localization = CountrySelectionUserDefaults.selectedCountry.languageCode
        CrowdinSDK.enableSDKLocalization(true, localization: localization)
    }
    

    Expected behavior The app does not crash

    Screenshots CleanShot 2020-09-14 at 15 13 49@2x

    Smartphone (please complete the following information):

    • Device: All devices
    • OS: iOS 13, iOS 14

    Additional context None

    bug help wanted 
    opened by dehlen 7
  • Fix warnings, Add hideSettings

    Fix warnings, Add hideSettings

    Creating this PL that will remove XCode warnings.

    • Migrate class to AnyObject in example project
    • Fix no method declared with Objective-C selector
    • Add hideSettings method
    opened by alexookah 6
  • Auto-tagging not working

    Auto-tagging not working

    Describe the bug The uploaded screenshots without tagging.

    To Reproduce 1, Only 1 localized string file, and related screen. 2, Integrate SDK, and capture screenshots.

    Expected behavior The uploaded screenshots have auto-tagging.

    Smartphone (please complete the following information):

    • Device: simulator, iPhone 12
    • OS: iOS 14.3
    • Crowdin: 1.1.7

    Additional context After debugging the code, the error returns at line 101 in ScreenshotUploader.swift, from screenshotsAPI.createScreenshotTags(projectId:... The error is:

    ▿ DecodingError
      ▿ keyNotFound : 2 elements
        - .0 : CodingKeys(stringValue: "data", intValue: nil)
        ▿ .1 : Context
          - codingPath : 0 elements
          - debugDescription : "No value associated with key CodingKeys(stringValue: \"data\", intValue: nil) (\"data\")."
          - underlyingError : nil
    
    bug 
    opened by dabaicaifen 5
  • SDK with SwiftUI does not seem to do much?

    SDK with SwiftUI does not seem to do much?

    Describe the bug I am not sure if this is a bug or you just don't support SwiftUI. I was under assumption if you make screenshot all the strings should be taged in them automatically, but only got can see string from UIKit context.

    Additional context Is this a bug? If not are you planning to add SwiftUI support any time soon? Thanks!

    bug 
    opened by edvinass 1
  • Copies/translations not fetched inside the completion block of CrowdinSDK.startWithConfig on a fresh install

    Copies/translations not fetched inside the completion block of CrowdinSDK.startWithConfig on a fresh install

    Hi there!

    I successfully integrated the CrowdinSDK in my app using Swift Package Manager and I am using OTA content delivery. On the first app launch with internet enabled, when I enter the completion block of CrowdinSDK.startWithConfig, and fetch the copy for a specific key using NSLocalizedString(key, comment: ""), I am only getting the key and not the copy. If I kill and relaunch the app, the copy for the same key is fetched successfully.

    Do you know what might be causing this and do you have any suggestion on its mitigation?

    Many thanks!

    bug help wanted hacktoberfest 
    opened by stevi-clue 6
  • Setting an unsupported locale code doesn't fall back to a language code

    Setting an unsupported locale code doesn't fall back to a language code

    Describe the bug Currently, our server tells the clients which locale code to use. (We recognize this is backwards.) This means we call CrowdinSDK.currentLocalization to override the target language. The concern is that the Crowdin supported language codes doesn't list "Italian Italy" (it-IT) just Italy (it). Same story with fr-FR. Meanwhile, Apple's Locale.current.identifer will return it_IT and fr_FR. So there's some mismatching between locale codes between the Apple, our server, and Crowdin.

    Over-the-air also doesn't appear to support falling back from it-IT to just it. Instead it fails to retrieve any language over-the-air at all. I spoke with our Android representative who said their SDK integration doesn't have this issue.

    Furthermore, additionally, I can't add "it-IT" as a custom language (see screenshot).

    To Reproduce After the SDK initializes, set the CrowdinSDK.currentLocalization to a locale code that isn't officially supported by Crowdin. e.g. "it-IT". Ensure there is a localization for the language code e.g. just "it".

    Actual behavior No calls to retrieve over-the-air translations are made.

    Expected behavior I see two possible outcomes:

    1. Crowdin could provide "it-IT" as opposed to just "Italian" on the website?
    2. Crowdin SDK could handle either "it-IT" or "it"?

    Screenshots Screen Shot 2022-03-18 at 4 13 36 PM

    Smartphone (please complete the following information): Doesn't seem to matter? I am using an iPhone SE sim 15.2

    Additional context

    bug help wanted hacktoberfest 
    opened by chrispomeroyhale 1
  • Option to disable method swizzling of NSLocalizedString

    Option to disable method swizzling of NSLocalizedString

    Is your feature request related to a problem? My teammates have communicated concern about the use of Objective-C method swizzling of NSLocalizedString. The SDK unexpected changes functionality of something it doesn't own (an Apple API), and we may unknowingly be integrating with another SDK that also use swizzling.

    Because our app is only interested in Over-the-Air for the time being, we'd like the option to disable swizzling. As I understand, swizzling is only a requirement of the optional Real-Time Preview feature, and for improved functionality of the also optional IntervalUpdates feature. Perhaps we will be interested in enabling Real-Time Preview for non-production builds with swizzling at a later date.

    Describe the solution you'd like If the SDK replicates Apple's, can we make Localization.localizedString(for:) public so that developers can call it directly? And then include a config option that conditionally disables CrowdinSDK.swizzle() and CrowdinSDK.swizzleControlMethods().

    enhancement help wanted 
    opened by chrispomeroyhale 2
  • Content Delivery from two Crowdin projects at the same time on iOS

    Content Delivery from two Crowdin projects at the same time on iOS

    Hi! I am wondering if it is currently possible to have content delivery and real-time preview from two Crowdin projects at the same time? I saw the example project, but that one only describes how to fetch data from only one project.

    Thank you beforehand!

    enhancement 
    opened by stevi-clue 2
Releases(1.5.1)
  • 1.5.1(Dec 17, 2022)

    Fixed

    • Fix issue with race condition after downloading localizations in #211 by @serhii-londar
    • Fix description of screenshots feature in #209 by @serhii-londar
    Source code(tar.gz)
    Source code(zip)
  • 1.5.0(Nov 1, 2022)

    Updated

    • SPM fixes in #202 by @serhii-londar
    • Fix issue with screenshots uploading in #205 by @serhii-londar
    • Changed the API base URL in #204 by @manish-singh-bisht
    • Delete .swift_version in #206 by @SabinaHuseinova
    Source code(tar.gz)
    Source code(zip)
  • 1.4.3(Jul 16, 2022)

    Fixed

    • Trimming whitespaces in the middle of a string (#195) @sandorgyulai
    • Fix crash during folder creation for plurals bundle (#196) @serhii-londar
    Source code(tar.gz)
    Source code(zip)
  • 1.4.2(Jun 6, 2022)

    Added

    • macOS, watchOS, tvOS support (#193)
    • Add a new button for stoping SDK in the floating menu (#190)
    • Add SwiftUI description (#184)

    Updated

    • Localization Provider Tests (#181)
    • Update README Appdelegate openUrl (#172)

    Fixed

    • Fix issue with sync manifest downloading (#174)
    • Fix issue with crash on Apple TV devices (#180)
    • Fix issue with duplicated requests for manifest and supported languages (#183)
    • Fix build errors for watchOS (#189)
    Source code(tar.gz)
    Source code(zip)
  • 1.4.1(Feb 9, 2022)

    Updated

    • Move Starscream dependency to RealTimePreview feature (#166) @serhii-londar
    • Update SocketAPI to use 4.0.4 Starscream (#168) @alexookah
    • Remove Pods files for Tests project (#170) @serhii-londar
    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Dec 30, 2021)

    Added

    • Swift Package Manager support (#148)
    • tvOS support (#157)

    Fixed

    • Fix issue with xliff localization parsing for xib and storyboard files (#156)
    Source code(tar.gz)
    Source code(zip)
  • 1.3.1(Dec 2, 2021)

  • 1.3.0(Dec 2, 2021)

  • 1.2.4(Sep 17, 2021)

  • 1.2.2(Aug 11, 2021)

  • 1.2.1(Apr 9, 2021)

    Added

    • Added debug mode and improved logging functionality (#117)

    Updated

    • Move manifest error notification to CrowdinProvider/Core subspec (#119)
    • Example project improvements (#120, #122)
    • Changed error messages text (#123)
    • Code structure improvements (#129, #130)

    Fixed

    • Fixed encoding issue of URL during activating real-time preview (#124)
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Mar 2, 2021)

    Added

    • Added %two_letters_code% support for file export pattern (#114)
    • SDK Controls: added improvement for logout flow - show confirmation alert (#115)

    Updated

    • Improve localization usage (#113)

      Use bundle localization if specific target languages are missing in Crowdin or not downloaded yet from the distribution.

    Fixed

    • Example project: fixed gear button visibility for ios version less than 14 (#116)
    Source code(tar.gz)
    Source code(zip)
  • 1.1.8(Jan 17, 2021)

    Added

    • New Example project (#108)

    Crowdin Example is a simple reminders app designed to illustrate how you can use Crowdin SDK features with a real iOS app. This app's primary purpose is to show the Crowdin SDK integration process in action and test the possibilities it provides.

    For more details read the Wiki article.

    Fixed

    • Issues with screenshots for projects uses SceneDelegate (#108)
    • Issues with real-time preview after the first start (#108)
    Source code(tar.gz)
    Source code(zip)
  • 1.1.7(Dec 4, 2020)

  • 1.1.6(Oct 9, 2020)

    Added

    • Add status for login state
    • Add status for real-time preview feature
    • Add Logs screen

    Updated

    • Update floating button design

    Fixed

    • Fix issues with a floating button statuses update
    • Fix issues with realtime preview feature
    • Fix issue with downloading XLIFF files
    Source code(tar.gz)
    Source code(zip)
  • 1.1.2(Jul 7, 2020)

  • 1.1.0(Jun 9, 2020)

    Added

    • Support iOS XLIFF files.
    • browser presentation in the application without opening Safari.

    Updated

    • Remove localizations parameter for CrowdinProviderConfig.
    • Localization downloading based on release timestamp (CDN cache issues).
    Source code(tar.gz)
    Source code(zip)
  • 1.0.7(Mar 25, 2020)

  • 1.0.6(Feb 27, 2020)

    Added

    • View all actual translations in Real-Time preview feature

    Updated

    • Update example project
    • Update content delivery API

    Fixed

    • Fix issue with downloading localization strings for organizations
    • Fix pod validation error
    Source code(tar.gz)
    Source code(zip)
  • 1.0.5(Feb 14, 2020)

  • 1.0.4(Jan 21, 2020)

    Update all API calls with new API updates. Fix typo in RealtimeUpdates extensions. Set minimum time interval for localization update to 15 minutes. Fix issues with plurals realtime updates. Fix issues for Info.plist SDK setup.

    Source code(tar.gz)
    Source code(zip)
Owner
Crowdin
Crowdin is a cloud-based solution that streamlines localization management for your team
Crowdin
iOS localization swift code generation project

code-gen-library - localization-swift module code-gen-library - localization-swift module with Python bash script execute to localization swift files(

umut boz 0 Oct 26, 2021
Demo project of Swift language crash using Release build on iOS 14

Demo project of Swift language crash using Release build on iOS 14 Repro steps O

Daohan Chong 0 Mar 24, 2022
A Kotlin multiplatform base project

PraxisKMM Minimal Kotlin Multiplatform project with SwiftUI, Jetpack Compose, Compose for Wear OS,. Currently running on Android (Jetpack Compose) ??

Mutual Mobile 45 Nov 15, 2022
Localizations is an OS X app that manages your Xcode project localization files (.strings)

Localizations 0.2 Localizations is an OS X app that manages your Xcode project localization files (.strings). It focuses on keeping .strings files in

Arnaud Thiercelin 129 Jul 19, 2022
Localization of the application with ability to change language "on the fly" and support for plural form in any language.

L10n-swift is a simple framework that improves localization in swift app, providing cleaner syntax and in-app language switching. Overview ?? Features

Adrian Bobrowski 287 Dec 24, 2022
Setting up application specific localized string within xib file.

LocalizedView ##What is this? LocalizedView is a helper class for setting up application specific localized string within Xib file. Here is a video de

darkcl 8 Oct 2, 2017
An application to convert strings to diffirent formats

Localized An application to convert strings to diffirent formats This app will help developers to convert strings from application in one platform to

null 16 Feb 12, 2021
Android/iOS Apps created to practice with different iOS/Android Tech. These apps were built to have similar feature sets using native Android/iOS.

AgilityFitTodayApp Android/iOS Apps created to practice with different iOS/Android Tech. These apps were built to have similar feature sets using nati

Lauren Yew 1 Feb 25, 2022
Will Powell 1.2k Dec 29, 2022
Localize iOS apps in a smarter way using JSON files. Swift framework.

Swifternalization Swift library that helps in localizing apps in a different, better, simpler, more powerful way than system localization does. It use

Tomasz Szulc 575 Nov 3, 2022
Simple solution to localize your iOS App.

Hodor is a simple solution to localize your iOS App quickly, allow you to change language of project in-app without quiting the app, Just like WeChat.

Aufree 545 Dec 24, 2022
Check Localizable.strings files of iOS Apps

Rubustrings Check the format and consistency of the Localizable.strings files of iOS Apps with multi-language support Rubustrings is also available fo

David Cordero 110 Nov 12, 2022
transai is a localization tool on Android and iOS.

transai transai is a command line tool to help you do Android and iOS translation management. You can extract string files to csv format, or generate

Jintin 56 Nov 12, 2022
iOS implementation of the Catrobat language

Catty Catty, also known as Pocket Code for iOS, is an on-device visual programming system for iPhones. Catrobat is a visual programming language and s

null 74 Dec 25, 2022
Save development time! Respresso automatically transforms and delivers your digital assets into your projects

Introduction Respresso is a centralized resource manager for shared Android, iOS and Web frontend projects. It allows you to simply import the latest

Respresso 10 Nov 8, 2022
Save development time! Respresso automatically transforms and delivers your digital assets into your projects

Respresso Android client Respresso is a centralized resource manager for shared Android, iOS and Web frontend projects. It allows you to simply import

Respresso 11 May 27, 2021
Save development time! Respresso automatically transforms and delivers your digital assets into your projects

Respresso iOS client Respresso is a centralized resource manager for shared Android, iOS and Web frontend projects. It allows you to simply import the

Respresso 50 May 1, 2021
All new design. Inspect your iOS application at runtime.

Peek: All new design Peek 5 with an all new design and all new features. Whether you're a developer, designer or QA/tester, Peek can help you at all s

Shaps 2.6k Dec 17, 2022
Don't start from scratch, start from Here! This is a starter project for iOS projects. It contains all the basic configurations and common libraries for your project.

Starter-iOS Don't start from scratch, start from Here! This is a starter project for iOS projects. It contains all the basic configurations and common

Shaban Kamel 6 May 24, 2022
📱The all-new iOS app for ownCloud

ownCloud iOS App ?? The new iOS App for your ownCloud! ?? https://ownCloud.com Account List File List File Actions Preview Files Quick Access Settings

ownCloud 163 Dec 29, 2022