{"id":15038737,"url":"https://github.com/applanga/sdk-ios","last_synced_at":"2025-04-10T00:02:47.503Z","repository":{"id":26848636,"uuid":"30308357","full_name":"applanga/sdk-ios","owner":"applanga","description":"With the Applanga iOS Localization SDK you can automate the iOS app translation process. You do not need to convert .string files to excel or xliff. Once the sdk is integrated you can translate your iOS app over the air and manage all the strings in the dashboard. iOS app localization has never been easier! https://www.applanga.com","archived":false,"fork":false,"pushed_at":"2025-02-27T14:57:01.000Z","size":176453,"stargazers_count":17,"open_issues_count":0,"forks_count":5,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-07T12:05:45.837Z","etag":null,"topics":["app","app-localization","applocale","applocalization","capture-screenshots","default-language","draft-mode","ios-app","ios-localization","localization","objective-c","sdk-ios","swift","swift-3","testing","translated-strings","translation","upload-screenshots","xcode","xcode9"],"latest_commit_sha":null,"homepage":"https://www.applanga.com","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/applanga.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-02-04T16:24:43.000Z","updated_at":"2025-03-01T04:01:15.000Z","dependencies_parsed_at":"2024-10-12T15:01:13.830Z","dependency_job_id":"62a01dfe-280a-48f9-a585-0a5dd658d59f","html_url":"https://github.com/applanga/sdk-ios","commit_stats":{"total_commits":156,"total_committers":9,"mean_commits":"17.333333333333332","dds":0.3782051282051282,"last_synced_commit":"9bc535561d7e083215388b2e818df777d4075dd0"},"previous_names":[],"tags_count":166,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/applanga%2Fsdk-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/applanga%2Fsdk-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/applanga%2Fsdk-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/applanga%2Fsdk-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/applanga","download_url":"https://codeload.github.com/applanga/sdk-ios/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247957880,"owners_count":21024774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["app","app-localization","applocale","applocalization","capture-screenshots","default-language","draft-mode","ios-app","ios-localization","localization","objective-c","sdk-ios","swift","swift-3","testing","translated-strings","translation","upload-screenshots","xcode","xcode9"],"created_at":"2024-09-24T20:39:57.527Z","updated_at":"2025-04-10T00:02:47.483Z","avatar_url":"https://github.com/applanga.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Applanga SDK for iOS Localization\n***\n*Version:* 2.0.212\n\n*Website:* \u003chttps://www.applanga.com\u003e \n\n*Changelog:* \u003chttps://www.applanga.com/changelog/ios\u003e\n***\n\n\n## Table of Contents\n\n  1. [Installation](#installation)\n  2. [Configuration](#configuration)\n  3. [Usage](#usage)\n  4. [Optional settings](#optional-settings)\n  5. [Localize Push Notifications \u0026 Info.plist](#automatic-push-notification-localization-and-infoplist-strings)\n  6. [SwiftUI](#swiftui)\n  7. [WatchOS](#watchos)\n  8. [MacOS](#macos)\n  9. [Branching](#branching)\n\n\nAutomatic Push Notification Localization and InfoPlist.strings\n\n## Installation\n#### CocoaPods [[?](http://cocoapods.org)]\n\n1. Refer to CocoaPod’s [Getting Started Guide](http://cocoapods.org/#getstarted) for detailed instructions about CocoaPods.\n\n2. After you have created your Podfile, insert this line of code: `pod 'Applanga'`. To be able to create screenshots during UITests, insert pod 'ApplangaUITest' for your UITest Target.\n\n3. Once you have done so, re-run `pod install` from the command line.\n\n#### Swift Package Manager [[?](https://swift.org/package-manager/)]\n\n##### NOTE: This is only supported in Xcode versions 12+\n\n1. Simply add the repository URL (https://github.com/applanga/sdk-ios) as a Swift package dependency, and select the latest release tag. \n\n#### Manual (zero-code)\n\n1. If you want to translate your iOS app, download the latest release of the Applanga iOS SDK from [Github](https://github.com/applanga/sdk-ios/releases). Unzip it, drag and drop Applanga.framework into the `Embedded Binaries` section of your target, and check the \"Copy items into destination group’s folder (if needed)\" option.\n\n2. Under the ***Build Settings*** tab, you need to change ***Basic*** to ***All*** and search for ***Other Linker Flags***. Double-click on the white space to the right of Other Linker Flags and a pop-up will open. Click the plus (+), and add ***-ObjC, -lsqlite3, -lz***.\n\n3. To be able to properly upload your app to iTunesConnect, you need to work around an App Store submission bug triggered by universal binaries. To do that, add a new `Run Script Phase` to your target’s `Build Phases`. **IMPORTANT:** make sure this `Run Script Phase` is below the `Embed Frameworks` build phase.\nYou can drag and drop build phases to rearrange them.\nPaste the following line in this `Run Script Phase`'s script text field:\n\n\t```bash\n\tbash \"$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH/Applanga.framework/strip-framework.sh\"\n\t```\n \n## Configuration\n1. To start iOS Localization with Applanga, download the Applanga Settings File for your app from the Project dashboard by clicking the ***[Prepare Release]*** button and then clicking ***[Get Settings File]***.\n \n2. Add the Applanga Settings File to your app's resources. It will be automatically loaded.\n \n3. Now, if you start your app you should see a log message that confirms that Applanga was initialized or a warning in case of a missing configuration.\n\n4. To make sure your Settings File is always up to date for every build, see the documentation section about **Automatic Applanga Settings File update** under **Optional settings**\n\n---\n\n###### *NOTE: to have native iOS dialogs properly translated and to show your supported languages on the Appstore, you need to have at least one .strings file bundled with your app for every language (the file can be empty).*\n\n---\n\n## Usage\n### Basic:\n\n- Once Applanga is integrated and configured, it synchronizes your local strings with the Applanga dashboard every time you start your app in [Debug Mode](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/debugging_with_xcode/chapters/debugging_tools.html) or [Draft Mode](https://www.applanga.com/docs/applanga-mobile-sdks/draft_on-device-testing) if new missing strings are found. Translations that you have stored in your *\"Localizable.strings\"* file or in *\".strings\"\"* that belong to storyboard or xib files of your app will be sent to the dashboard immediately. Applanga also automatically detects your strings in storyboards and in the code once they are used.\nStoryboards should be enabled for [Base Localization](https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/InternationalizingYourUserInterface/InternationalizingYourUserInterface.html#//apple_ref/doc/uid/10000171i-CH3-SW4). If you have additional *\".strings\"* files that should be automatically uploaded, you can add them in your `Info.plist` with the key `ApplangaAdditionalStringFiles`. If you have parts of your code in additional *\".framework\"* bundles, you can add them with the key `ApplangaAdditionalFrameworks` as a comma-separated list. You don’t need to use any special code. \n\t- With ***Objective-C*** use the native method ***[NSLocalizedStringWithDefaultValue(@\"APPLANGA_ID\", nil, NSBundle.mainBundle, @\"default value\", @\"\")](https://developer.apple.com/reference/foundation/nslocalizedstringwithdefaultvalue?language=objc)*** \n\t\n\t- With ***Swift*** use ***[NSLocalizedString(\"APPLANGA_ID\", value: \"default value\", comment: \"\")](https://developer.apple.com/reference/foundation/1418095-nslocalizedstring)*** as usual.\n\n\n---\n###### *NOTE: if you do not specify a default value, the string will not be created on the Applanga dashboard.*\n---\n\n### Extended:\n\nBesides the Basic usage, Applanga offers support for ***named arguments*** in your strings, ***pluralisation***, ***partial updates*** (to save space and bandwidth), as well as translation of HTML and JavaScript content in ```UIWebView``` instances.\n\n1. **Code Localization**\n \n\t1.1 **Strings** \n\t\n\t```objc\n\t//objc\n\t// get translated string for the current device locale\n\t[Applanga localizedStringForKey:@\"APPLANGA_ID\" withDefaultValue:@\"default value\"];\n\t```\n\t\n\t```swift\n\t//swift\n\tApplanga.localizedString(forKey: \"APPLANGA_ID\", withDefaultValue: \"default value\")\n\t```\n\n\t1.2 **Named Arguments**\n\t\n\t```objc\n\t//objc\n\t// if you pass a string:string dictionary you can get translated string\n\t// with named arguments. %{someArg} %{anotherArg} etc.\n\tNSDictionary* args = @{@\"someArg\": @\"awesome\",@\"anotherArg\": @\"crazy\"};\n\t[Applanga localizedStringForKey:@\"APPLANGA_ID\" withDefaultValue:@\"default value\" andArguments:args]\n\t```\n\t\n\t```swift\n\t//swift\n\tvar args: [String: String] = [\"someArg\": \"awesome\", \"anotherArg\": \"crazy\"];\n\tApplanga.localizedString(forKey: \"APPLANGA_ID\", withDefaultValue: \"default\", andArguments: args)\n\t```\n\tExample:\n\t\n\t*APPLANGA_ID* = *\"This value of the argument called someArg is %{someArg} and the value of anotherArg is **%{anotherArg}**. You can reuse arguments multiple times in your text which is **%{someArg}**, **%{anotherArg}** and **%{someArg}.**\"*\n\t\n\tgets converted to:\n\t\n\t*\"This value of the argument called someArg is awesome and the value of anotherArg is crazy. You can reuse arguments multiple times in your text which is awesome, crazy and awesome.\"*\t\n\t\t\n\t1.3 **Pluralisation**\n\t\n\t```objc\n\t//objc\n\t// get translated string in given pluralisation rule (one)\n\t[Applanga localizedStringForKey:@\"APPLANGA_ID\" withDefaultValue:@\"default value\" andArguments:nil andPluralRule:ALPluralRuleOne]\n\t```\n\t\n\t```swift\n\t//swift\n\tApplanga.localizedString(forKey: \"no default\", withDefaultValue: \"default\", andArguments: nil, andPluralRule: ALPluralRule.one)\n\t```\n\t\n\tAvailable pluralisation rules:\n\t\n\t```objc\n\t//objc\n\tALPluralRuleZero,\n\tALPluralRuleOne,\n\tALPluralRuleTwo,\n\tALPluralRuleFew,\n\tALPluralRuleMany,\n\tALPluralRuleOther\n\t```\n\t```swift\n\t//swift\n\tALPluralRule.zero,\n\tALPluralRule.one,\n\tALPluralRule.two,\n\tALPluralRule.few,\n\tALPluralRule.many,\n\tALPluralRule.other\n\t```\n\tYou can also specify a quantity and Applanga will pick the best pluralisation rule based on: [http://unicode.org/.../language_plural_rules.html\t](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html)\n\t\n\t```objc\n\t//objc\n\t// get a string in the given quantity\n\t[Applanga localizedStringForKey:@\"APPLANGA_ID\" withDefaultValue:@\"default value\" andArguments:nil andPluralRule:ALPluralRuleForQuantity(quantity)]\n\n\t// or get a formatted string with the given quantity\n\t[NSString localizedStringWithFormat:[Applanga localizedStringForKey:@\"APPLANGA_ID\" withDefaultValue:@\"default value\" andArguments:nil andPluralRule:ALPluralRuleForQuantity(quantity)], quantity]\n\t```\n\t```swift\n\t//swift\n\t// get a string in the given quantity\n\tApplanga.localizedString(forKey: \"APPLANGA_ID\", withDefaultValue: \"default value\", andArguments: nil, andPluralRule: ALPluralRuleForQuantity(quantity))\n\t\n\t//or get a formatted string with the given quantity\n\tNSString.localizedStringWithFormat(NSString(string:(Applanga.localizedString(forKey: \"APPLANGA_ID\", withDefaultValue: \"default\", andArguments: nil, andPluralRule: ALPluralRuleForQuantity(quantity)))), quantity)\n\t\n\t```\n\tOn the dashboard, you create a **puralized ID** by appending the pluralisation rule to your **ID** in the following format: `[zero]`, `[one]`,`[two]`,`[few]`,`[many]`, `[other]`.\n\t\n\tSo the ***zero*** pluralized ID for ***\"APPLANGA_ID\"*** is ***\"APPLANGA_ID[zero]\"***\n\t\t\n2. **Update Content**\n\t\n\tTo trigger an update call:\n\t\n\t```objc\n\t//objc\n\t[Applanga updateWithCompletionHandler:^(BOOL success) {\n\t\t//called if update is complete\n\t}];\n\t```\n\t\n\t```swift\n\t//swift\n\tApplanga.update { (success: Bool) in\n\t\t//called if update is complete\n\t}\n\t```\n\n\tThis will request the baselanguage, the development language and the long and short versions of the device's current language. If you are using groups, be aware that this will only update the **main** group.\n\t\t\n\tTo trigger an update for a specific set of groups and languages call:\n\t\n\t```objc\n\t//objc\n\tNSArray* groups = @[@\"GroupA\", @\"GroupB\"];\n\tNSArray* languages = @[@\"en\", @\"de\", @\"fr\"];\n\t \t\n\t[Applanga updateGroups:groups andLanguages:languages withCompletionHandler:^(BOOL success) {\n\t\t//called if update is complete\n\t}];\n\t```\n\t```swift\n\t//swift\n\tvar groups: [String] = [\"GroupA\", \"GroupB\"]\n\tvar languages: [String] = [\"en\", \"de\", \"fr\"]\n\t\n\tApplanga.updateGroups(groups, andLanguages: languages, withCompletionHandler:  {(success: Bool) in\n\t\t//called if update is complete\n\t})\n\t```\n\n3. **Change Language**\n \n  \tYou can change your app's language at runtime using the following call:\n  \t\n\t```objc\n\t//objc\n\tBOOL success = [Applanga setLanguage: language];\n\t```\n\t```swift\n\t//swift\n\tvar success: Bool = Applanga.setLanguage(language)\n\t```\n\t*language* must be the iso string of a language that has been added in \tthe dashboard. \n  \tThe return value will be `YES` if the language could be set, or if it already was the \tcurrent language, otherwise it will be `NO`. \n  \tThe set language will be saved, to reset to the \tdevice language call:\n  \t\n\t```objc\n\t//objc\n\tApplanga.setLanguage(nil); \n\t```\n\t```swift\n\t//swift\n\tApplanga.setLanguage(nil);\n\t```\n  \tAfter a successful call, you need to reinitialize your UI for the changes to \ttake effect. For example, you might recreate the root Storyboard controller and present it.\n  \t\n  \tThe language parameter should be in the [language]-[region] or \t[language]_[region] format, with [region] being optional. Examples: \"fr_CA\", \"en-us\", \"de\".\n\n  \tIf you have problems switching to a specific language, you can update your Settings File \tor specifically request that language within an update content call (see **2. Update Content**). You can also set a default language to have it requested on each update call (see **Optional settings**).\n  \t\n\t```objc\n\t+ (void) changeAppLanguage:(NSString *)language {\n\t\t\t[Applanga updateGroups:nil andLanguages:@[language] withCompletionHandler:^( BOOL updateSuccess ){\n\t\t\t\n\t\t\tif(updateSuccess){\n\t\t\t\t\tBOOL languageChangedSuccess = [Applanga setLanguage:language];\n\t\t\t\t\n\t\t\t\t\tif(languageChangedSuccess) {\n\t\t\t\t\t\t\t//recreate ui\n\t\t\t\t\t} \n\t\t\t} \n\t}\n\t```\n\t\n4. **WebViews**\n\t\n\tApplanga can also translate content in your WebViews if it is enabled.\n\t\n\tAdd `ApplangaTranslateWebViews` to your `Info.plist` and set it to `YES`  to enable translation support for all WebViews.\n\t\n\tBe aware that direct Applanga calls in the deprecated `UIWebView` must be synchronous, whereas in the newer `WKWebView`, they must be asynchronous.\n\n\tTo initalize Applanga for your webcontent in a `UIWebView`, you need to initialize Applanga from JavaScript like this:\n\t\n\t```javascript\n\t\u003cscript type=\"text/javascript\"\u003e\n\t\twindow.initApplanga = function() {\n\t\t\tif(typeof window.ApplangaNative !== 'undefined') { window.ApplangaNative.loadScript();\n\t  \t\t} else { setTimeout(window.initApplanga, 180); } \n\t  \t}; window.initApplanga();\n\t\u003c/script\u003e\n\t```\n\t\n\tThis is not needed if you use a `WKWebView`.\n\n\t4.1 **Strings**\n\t\t\n\tThe inner text and HTML of tags that have a `applanga-text=\"APPLANGA_ID\"` attribute will be replaced with the translated value of ***APPLANGA_ID***\n\t\n\t```html\n\t\u003cdiv applanga-text=\"APPLANGA_ID\"\u003e\n\t\t\t***This will be replaced with the value of APPLANGA_ID***\n\t\u003c/div\u003e\n\t```\n\t\n\tAlternatively, you can call `Applanga.getString` directly like this:\n\t\n\t```javascript\n\t//UIWebView\n\ttranslation = Applanga.getString('APPLANGA_ID')\n\t//WKWebView\n\tApplanga.getString('APPLANGA_ID', undefined, undefined,  undefined, undefined, undefined, \n\t function(translation) {\n\t})\n\t```\n\t\n\t4.2 **Arguments**\n\t\n\tYou can pass arguments with the ```applanga-args``` attribute.\n\tBy default the arguments are parsed as a comma-separated list that will replace fields as %{arrayIndex}. \n\t\n\t```html\n\t\u003cdiv applanga-text=\"APPLANGA_ID\" applanga-args=\"arg1,arg2,etc\"\u003e\n\t\t***This will be replaced with the value of APPLANGA_ID***\n\t\t***and formatted with arguments***\n\t\u003c/div\u003e\n\t```\n\tDirect call: \n\t\n\t```javascript\n\t//UIWebView\n\ttranslation = Applanga.getString('APPLANGA_ID', 'arg1,arg2,etc')\n\t//WKWebView\n\tApplanga.getString('APPLANGA_ID', 'arg1,arg2,etc', undefined,  undefined, undefined, undefined, \n\t function(translation) {\n\t})\n\t```\n\t\n\tTo define a different separator instead of a comma (e.g. in case your arguments contain commas) use ```applanga-args-separator```.\n\t\n\t```html\n\t\u003cdiv applanga-text=\"APPLANGA_ID\" \n\t\tapplanga-args=\"arg1;arg2;etc\"\n\t\tapplanga-args-separator=\";\"\u003e\n\t\t\t***This will be replaced with the value of APPLANGA_ID***\n\t\t\t***and formatted with arguments***\n\t\u003c/div\u003e \n\t```\n\n\tDirect call: \n\t\n\t```javascript\n\t//UIWebView\n\ttranslation = Applanga.getString('APPLANGA_ID', 'arg1,arg2,etc', ';')\n\t//WKWebView\n\tApplanga.getString('APPLANGA_ID', 'arg1,arg2,etc', ';',  undefined,  undefined,  undefined, \n\t function(translation) {\n\t})\n\t```\n\t\t\n\tOne Dimensional **JSON** Objects can also be used as ***Named Arguments*** if you add `applanga-args-separator=\"json\"`\n\t\n\t```html\n\t\u003cdiv applanga-text=\"APPLANGA_ID\" \n\t\t applanga-args=\"{'arg1':'value1', 'arg2':'value2', 'arg3':'etc'}\"\n\t\t applanga-args-separator=\"json\"\u003e\n\t\t\t***This will be replaced with the value of APPLANGA_ID***\n\t\t\t***and formatted with json arguments***\n\t\u003c/div\u003e \n\t```\n\t Direct call: \n\t \n\t ```javascript\n\t //UIWebView\n\t translation = Applanga.getString('APPLANGA_ID', \"{'arg1':'value1', 'arg2':'value2', 'arg3':'etc'}\", 'json')\n\t //WKWebView\n\t Applanga.getString('APPLANGA_ID', \"{'arg1':'value1', 'arg2':'value2', 'arg3':'etc'}\", 'json',  undefined,  undefined,  undefined, \n\t  function(translation) {\n\t})\n\t ```\n\t\n\t4.3 **Pluralisation**\n\t\t\n\tTo pluralize a HTML tag, you can pass the ```applanga-plural-rule``` attribute with the value `zero`, `one`, `two`, `few`, `many` and `other`.\n\t\n\t```html\n\t\u003cdiv applanga-text=\"APPLANGA_ID\" applanga-plural-rule=\"one\"\u003e\n\t\t***This will be replaced with the pluralized value of APPLANGA_ID***\n\t\u003c/div\u003e \n\t```\n\n\tDirect call: \n\t\n\t```javascript\n\t//UIWebView\n\ttranslation = Applanga.getPluralString('APPLANGA_ID', 'one')\n\t//WKWebView\n\tApplanga.getPluralString('APPLANGA_ID', 'one', undefined, undefined,\n\t function(translation) {\n\t})\n\t``` \n\t\n\tor with arguments: \t\n\t\n\t```javascript\n\t//UIWebView\n\ttranslation = Applanga.getPluralString('APPLANGA_ID', 'one', 'arg1;arg2;etc', ';')\n\t//WKWebView\n\tApplanga.getPluralString('APPLANGA_ID', 'one', 'arg1;arg2;etc', ';', \n\t function(translation) {\n\t})\n\t```\n\t\t\n\tYou can also pluralize by quantity via `applanga-plural-quantity`\n\n\t```html\n\t\u003cdiv applanga-text=\"APPLANGA_ID\" applanga-plural-quantity=42\u003e\n\t\t***This will be replaced with the pluralized value of APPLANGA_ID***\n\t\u003c/div\u003e \n\t```\n\n\tDirect call: \n\t\n\t```javascript\n\t//UIWebView\n\ttranslation = Applanga.getQuantityString('APPLANGA_ID', 42)\n\t//WKWebView\n\tApplanga.getQuantityString('APPLANGA_ID', 42,  undefined, undefined, \n\t function(translation) {\n\t})\n\t```\n\t\n\tor with arguments:\n\t\n\t```javascript\n\t//UIWebView\n\ttranslation = Applanga.getQuantityString('APPLANGA_ID', 42, 'arg1;arg2;etc', ';')\n\t//WKWebView\n\tApplanga.getQuantityString('APPLANGA_ID', 42, 'arg1;arg2;etc', ';', \n\t function(translation) {\n\t})\n\t```\n\n\t4.4 **Update Content**\n\t\n\tTo trigger a content update from a WebView use JavaScript:\n\t\n\t```javascript\n\tApplanga.updateGroups(\"GroupA, GroupB\", \"de, en, fr\", function(success){\n\t\t//called if update is complete\n\t});\t\t\n\t```\n \n    4.5 **Enable Show ID Mode**\n\n    ```javascript\n    Applanga.setShowIdModeEnabled(true);\n    ```\n\n\tIf `showIdMode` is enabled, Applanga will return your string IDs instead of your translations. This is important in the case of screenshots (see the SwiftUI Screenshots section below). For instance, if you have an argument string or any string that changes at runtime, it's possible that it won't be collected on a screenshot. If `showIdMode` is enabled, Applanga can make an exact match of the string ID so that the screenshot string collection is accurate. \n\n\tDon't use this flag in Production. To be able to the see changes, you have to reload your UI after changing this flag.\n\n5. **Screenshot Capturing**\n \t\n \tTo provide context for translation, the Applanga SDK offers functionality to upload screenshots of your app combined with meta-data such as the current language, resolution and the strings that are visible, including their positions.\n \tEach screenshot will be assigned to a tag. A tag may have multiple screenshots with \tdiffering core meta-data: language, app version, device, platform, OS and resolution. \n \tYou can read more here on the [Working with Screenshots](https://www.applanga.com/docs/applanga-screenshots/screenshots-on-applanga) and the [Uploading Screenshots](https://www.applanga.com/docs/applanga-screenshots/uploading-screenshots) articles.\n \t\n \t5.1 **Manual Screenshot Capture \u0026 Upload**\n \t\n \tTo manually capture a screenshot, you first have to set your app into [Draft Mode](https://www.applanga.com/docs/translation-management-dashboard/draft_on-device-testing).\n \t \n \tWith your app in Draft Mode, all you have to do is to make a two finger swipe downwards.\n \tThis will show the screenshot menu and load a list of [tags](https://applanga.com/docs#manage_tags).\n \t\n \tYou can now choose a tag and press *capture screenshot* to capture and upload a screenshot including all meta-data for the currently visible screen and assign it to the selected tag.\n \tTags have to be created in the dashboard before they are available in the screenshot menu.\n\n \t5.2 **Automatic Screenshot Capture \u0026 Upload via UITests**\n \t\n \tScreenshotting can be automated by either extending your existing UITests and capturing a screenshot on every View, or by creating a dedicated script that traverses your app an calls the Applanga screenshot capture method on each view.\n\n    Please refer to [Installation](#Installation) to add the `ApplangaUITest` package to your project.\n\n \tTo capture screenshots from UITests running in Xcode, you first have to initialize Applanga with the current app instance so it can set specific launch arguments before starting the tests:\n\n\t```objc\n\t//objc\n\tXCUIApplication* app = [[XCUIApplication alloc] init];\n\tApplangaUITest* applangaUITest = [[ApplangaUITest alloc] initWithApp:app enableShowIdMode:false];\n\t```\n\t```swift\n\t//swift \n\tlet app = XCUIApplication()\n\tlet applangaUITest = ApplangaUITest(app: app)\n\t```\n\t\n   To take a screenshot specify a tag/screen name and wait for the method to finish:\n    \n   ```objc\n   //objc\n   NSArray* expectations = [NSArray arrayWithObject:[self.applangaUITest takeScreenshotWithTag:@\"ScreenName\"]];\n   [self waitForExpectations:expectations timeout:10];\n   ```\n\n   ```swift\n\t//swift \n\twait(for: [applangaUITest!.takeScreenshot(tag: \"ScreenName\")], timeout: 10.0)\n\t```\n\t\n\tFull example:\n\t\n\t```objc\n\t#import \u003cApplangaUITest-Swift.h\u003e\n\t\n\t@interface MyUITestCase : XCTestCase\n\t@property ApplangaUITest *applangaUITest;\n\t@end\n\t\n\t@implementation MyUITestCase\n\t- (void)setUp {\n\t\t...\n\t\tXCUIApplication* app = [[XCUIApplication alloc] init];\n\t\tself.applangaUITest = [[ApplangaUITest alloc] initWithApp:app enableShowIdMode:false];\n\t\t[app launch];\n\t}\n\t\n\t- (void)testScreenshot {\n\t\tXCUIApplication *app = [[XCUIApplication alloc] init];\n\t\t\n\t\tNSArray* expectations = [NSArray arrayWithObject:[self.applangaUITest takeScreenshotWithTag:@\"ScreenName1\"]];\n\t\t[self waitForExpectations:expectations timeout:10];\n\t\t//navigate to next view\n\t\t...\n\t\tNSArray* expectations = [NSArray arrayWithObject:[self.applangaUITest takeScreenshotWithTag:@\"ScreenName2\"]];\n\t\t[self waitForExpectations:expectations timeout:10];\n\t}\n\t@end\n\t```\n    ```swift\n    import ApplangaUITest\n    class AutomatedScreenshotsTest: XCTestCase {\n        let app = XCUIApplication()\n        var applangaUITest: ApplangaUITest?\n\n        func testScreenshot() {\n            // enable show id mode if you are using swift ui so the string id will be linked to the tag name correctly\n            // after that repeat the screenshot without show id mode\n            applangaUITest = ApplangaUITest(app: app, enableShowIdMode: false) \n            app.launch()\n            wait(for: [applangaUITest!.takeScreenshot(tag: \"ScreenName\")], timeout: 10.0)\n        }\n    }\n    ```\n\n6. **Get available languages**\n\n  \tIt is possible to get the list of languages that are currently added to a project:\n  \t\n\t```objc\n\t//objc\n\tNSArray *languages = [Applanga availableLanguages];\n\t```\n\t```swift\n\t//swift\n\tlet languages = Applanga.availableLanguages()\n\t```\n\t\n\tThe result will contain the ISO language code of each language that has been added to the Applanga project. These values are synced during the `Applanaga.update()` response. To retrieve any changes to the available languages on the dashboard, an `Applanaga.update()` should be performed manually before using this list. \n\t\t\n## Optional settings\n\nYou can specify a set of default groups and languages in your plist, which will be updated on every `Applanga.update()` or `Applanga.updateGroups()` call. These groups and languages will be added to any that are specified in the call itself, they will *always* be requested.\n\tThe parameter value must be a string, with a comma-separated list of groups or languages.\n\n1. **Specify default groups**\n\n\t```xml\n\t...\n   \u003ckey\u003eApplangaUpdateGroups\u003c/key\u003e\n\t\u003cstring\u003eturorial,chapter1,chapter2\u003c/string\u003e\n\t...\n\t```\n\n2. **Specify default languages**\n\n\t```xml\n\t...\n\t\u003ckey\u003eApplangaUpdateLanguages\u003c/key\u003e\n\t\u003cstring\u003een,de-at,fr\u003c/string\u003e\n\t...\n\t```\n\n3. **Disable upload of storyboard strings**\n\tYou have the option to disable the collection of storyboard strings by setting this value \tto false. This will not prevent the upload of localized .strings files that you may have \tcreated for your storyboard, but it will stop the default upload of the cryptic string IDs \tthat are created for text UI elements in the storyboard.\n\t\n\t```xml\n\t...\n\t\u003ckey\u003eApplangaCollectStoryBoardStrings\u003c/key\u003e\n\t\u003cfalse/\u003e\n\t...\n\t```\n\t\n\n4. **Automatic Applanga Settings File update**\n\n\tIn case your app's user has no Internet connection, new translation updates can't be fetched, so the Applanga SDK falls back to the last locally cached version. If the app was started for the first time, there are no strings locally cached yet, so the Applanga SDK falls back to the Applanga Settings File that contains all strings from the moment it was generated, downloaded and integrated into your app before release. \n\n\tThe Applanga SDK comes with a python script called `settingsfile_update.py` that makes sure your app always has the latest Settings File version. The script searches recursively for `*.applanga` files in your project and checks if a newer version is available. If so, it replaces the old file with the newer Applanga Settings File from the Applanga backend. \n\t\n\tIn XCode you go to `Build Phases` and `New Run Script Phase` and add the following line (if you are using CocoaPods):\n\n\t```\n\tbash \"$SOURCE_ROOT/Pods/Applanga/Applanga.xcframework/update-settingsfile.sh\" \"$SOURCE_ROOT/$TARGET_NAME\"\n\t```\n\t\n\tor if you are using Swift Package Manager:\n\t\n\t```\n\tbash \"${BUILD_DIR%Build/*}/SourcePackages/checkouts/sdk-ios/Applanga.xcframework/update-settingsfile.sh\" \"$SOURCE_ROOT/$TARGET_NAME\"\n\t```\n\t\n\tor if you integrate the Applanga SDK manually:\n\t\n\t```\n\tbash \"$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH/Applanga.xcframework/update-settingsfile.sh\" \"$SOURCE_ROOT/$TARGET_NAME\"\n\t```\n\t--\n\t\n\tOptionally, you can also run the update script manually from the command line. Navigate to the `Applanga.framework` directory, where `settingsfile_update.py` is located, and run:\n\n\t```\n\tbash update-settingsfile.sh ${YOUR TARGET DIRECTORY PATH}\n\t```\n\n\tTo make sure that the script is running and to see when it does or doesn't update, check the build report in the report navigator window in XCode. There you will find logs for each update step.\n\n    If the file is updated successfully, you should see the log \"Settingsfile updated!\". If it is already up to date you will see the log \"Settingsfile up-to-date\".\n\n\n5. **Disable automatic string update on init**\n\t\n\tIf you wish to stop the SDK from automatically updating your strings on app launch, you can set the following:\n\n\t```xml\n\t...\n   \u003ckey\u003eApplangaInitialUpdate\u003c/key\u003e\n\t\u003cfalse/\u003e\n\t...\n\t```\n\tYou will still be able to call `Applanga.Update()` at any time to update your strings.\n\n6. **Disable Draft Mode**\n\n    If you wish to create a build that cannot enable Draft Mode at any time, you can include the following setting to your plist:\n\n\t```xml\n\t...\n   \u003ckey\u003eApplangaDraftModeEnabled\u003c/key\u003e\n\t\u003cfalse/\u003e\n\t...\n\t```\n\tYou can also use the following method at runtime:\n\n\t```\n\tApplanga.setDraftModeEnabled(bool);\n   ```\n    This will override the setting in the plist, but it will not override the [Draft Mode setting on the Applanga dashboard](https://www.applanga.com/docs/applanga-mobile-sdks/draft_on-device-testing#how-to-enable-or-disable-draft-mode-from-the-dashboard).\n\n7. **Convert Placeholders**\n\n    To convert placeholders between iOS and Android style, you need to enable the following in your plist: \n\t\n\t```xml\n\t...\n   \u003ckey\u003eApplangaConvertPlaceholders\u003c/key\u003e\n\t\u003ctrue/\u003e\n\t...\n   ```\n\n    ***Common placeholders***\n\n    These placeholder will not be converted as they are supported on iOS and Android:\n\n    - Scientific notation `%e` and `%E`\n    - `%c` and `%C` Unicode Characters\n    - `%f` floating point number\n    - `%g` and `%G` computerized scientific notation\n    - `%a` and `%A` Floating point numbers\n    - Octal integer `%o` (for `%O` see Android to iOS conversion)\n    - `%x` and `%X` hexadecimal presentation using lowercase letters (`%x`) or uppercase letters (`%X`)\n    - `%d` will remain `%d`\n    - Positional placeholder as `%1$s` are converted to `%1$@` and vice-versa\n\n    ***Android placeholders***\n    \n    - All instances of `%s` and `%S` will be converted to `%@`\n    - Unsupported conversion types such as `%h` and `%tY` will convert to default `%@` type.\n    - Boolean types `%b` and `%B` will be converted to `%@`\n    - `%h` and `%H` are converted to `%@`\n    - Positional strings using '\u003c' are supported. \"Duke's Birthday: `%1$tm` `%\u003cte`,`%\u003ctY`\" results in \"Duke's Birthday: `%1$@` `%1$@`,`%1$@`\"\n\n8. **Language Mapping**\n\n    You can map a locale to another locale. For example, if you don't have `es-CL` added to your dashboard it usually has a fallback to `es`. But if you want to treat `es-CL` as `es-MX` then you can add it to the map. Watch out for the log:\n    \n    `ApplangaLanguageMap: es-CL is mapped to es-MX`\n\n    Example:\n\n    ```xml\n    ...\n\t\u003ckey\u003eApplangaLanguageMap\u003c/key\u003e\n\t\u003cstring\u003ezh-Hant-HK=zh-HK,es-CL=es-MX\u003c/string\u003e\n    ...\n    ```\n\n\tThe locale is mapped in all places in the SDK, except when used in combination with [custom language fallback](#enable-custom-language-fallback). In this case, the custom fallback is performed as set in the plist, no additional mapping occurs for the entries in that plist.\n    If you have a locale that maps to another locale that has a custom fallback, this will also work, and the custom fallback will be performed after the mapping.\n\n9. **Enable system language fallback**\n\t\n\tBy default, Applanga uses a custom device locale order.\n\tThis plist value makes the SDK iterate over the languages by using the priority set by the system.\n\tThis order is used when translating strings and performing a fallback when the string isn’t found for a given language.\n\tIn this case, the SDK will try to translate the string with the next language in the list.\n\tThe available possibilities are:\n\n\t`applanga`: The default SDK order  \n\t`system`: Use the device system order\n\n\t```xml\n\t...\n\t\u003ckey\u003eApplangaLanguageFallback\u003c/key\u003e\n\t\u003cstring\u003esystem\u003c/string\u003e\n\t...\n\t```\n\n10. **Enable custom language fallback**\u003ca name=\"enable-custom-language-fallback\"\u003e\u003c/a\u003e\n\t\n\tThis plist value is a dictionary that allows to set a custom fallback per language. When the SDK needs to translate a key with a specified language, it uses the order as provided. This overrides any other system or default fallbacks only for those languages. Other languages work according to the fallback specified using the `ApplangaLanguageFallback` value (or default if it's not set). The fallback is only overridden for the top level language, so it's not possible to \"nest\" the custom fallbacks.\n\n\tExample:\n\t```xml\n\t...\n\t\u003ckey\u003eApplangaCustomLanguageFallback\u003c/key\u003e\n\t\u003cdict\u003e\n\t\t\u003ckey\u003ees-MX\u003c/key\u003e\n\t\t\u003carray\u003e\n\t\t\t\u003cstring\u003ees-MX\u003c/string\u003e\n\t\t\t\u003cstring\u003ees-US\u003c/string\u003e\n\t\t\t\u003cstring\u003ees\u003c/string\u003e\n\t\t\u003c/array\u003e\n\t\t\u003ckey\u003een-US\u003c/key\u003e\n\t\t\u003carray\u003e\n\t\t\t\u003cstring\u003een-GB\u003c/string\u003e\n\t\t\t\u003cstring\u003ede-DE\u003c/string\u003e\n\t\t\u003c/array\u003e\n\t\u003c/dict\u003e\n\t...\n\t```\n\n11. **Wait on App Start**\n\n    In order to receive the latest strings from your Applanga dashboard into your app, the SDK  will wait until the initial update has finished. This causes up to a 10-second delay (typically faster) when starting.\n\tIf you want this to happen in the background, you can add the `ApplangaWaitOnAppStart` key to your `Info.plist` and set it to false (example below). The Applanga SDK will not delay the app start, but this comes with the downside that initial screens may not have the latest over-the-air translated strings. In this case, you should make sure that your Settings File is up to date before a release. Be aware that some outdated strings may be displayed.\n\n\t Example:\n\n    ```xml\n    ...\n\t\u003ckey\u003eApplangaWaitOnAppStart\u003c/key\u003e\n\t\u003cfalse/\u003e\n    ...\n    ```\n\n12. **Automatic upload of a tag with current app strings**\n\n\tIf you want to upload a tag with all the local strings in your app, add the following key to the `Info.plist`:\n    \n\t```xml\n    ...\n\t\u003ckey\u003eApplangaTagLocalStringsPrefix\u003c/key\u003e\n\t\u003cstring\u003esome_tag\u003c/string\u003e\n    ...\n    ```\n\n\tThis plist value will be used as the tag prefix combined with the bundle version (e.g. some_tag1). When you start or run the app on debug mode, an automatic tag upload will be performed after `Applanga.update()`. The tag will be created on the dashboard if it doesn't exist yet.\n\n\tIt's possible to combine this option with the `ApplangaAdditionalStringFiles` and the `ApplangaAdditionalFrameworks` keys to include strings from any additional string files or frameworks.\n\n13. **Use supported language as screenshot language value**\n\n\tBy default, when uploading a screenshot using the SDK,\n\tit sets the language of the screenshot as the current preferred device locale \n\t(if it was mapped, or overridden with `Applanga.setLanguage` then that value will be used).\n\tIt is possible to make the SDK set the actual language used for localizing the strings by adding the following key to the plist:\n    \n\t```xml\n    ...\n\t\u003ckey\u003eApplangaScreenshotUseSupportedLanguageFallback\u003c/key\u003e\n\t\u003ctrue/\u003e\n    ...\n    ```\n\n\tWhen set to `true`, the value will be the first supported language in the fallback list. \n\n\n## Automatic Push Notification Localization and InfoPlist strings\n\nThe Applanga SDK can only localize local notifications. In the case of remote notifications, the app display name (`CFBundleDisplayName`) as well as the several other `NS*UsageDescription` strings defined in your `Info.plist` are not using the app runtime and therefore can't be translated. For these kind of strings, you can use the [Applanga Command Line Interface](https://www.applanga.com/docs-integration/cli) to manage the strings on the [Applanga Dashboard](https://dashboard.applanga.com) and update the InfoPlist.strings files whenever you create a new build.\n\nFor more details on that, please have a look at our blogpost on [Translating Push Notifications and Info.plist localization](https://www.applanga.com/blog/infoplist-strings-and-ios-push-notification-localization-automation/).\n\n## SwiftUI\n\nTo use Applanga with SwiftUI, please include the ApplanaSwiftUI framework into your project. To do so, follow the integration instructions in the [Readme](https://github.com/applanga/sdk-swiftui/blob/main/README.md) file.\n\nIf you want to keep using only the base Applanga framework, you can localize text components by using this extension:\n\n```swift\n\t//First add this extension to your project:\n\textension Text {\n\t    init(applangaKey : String){\n\t        self.init(NSLocalizedString(applangaKey, tableName: nil, bundle: Bundle.main, value: \"\", comment:\"\"))\n\t    }\n\t    init(applangaKey : String, defaultValue : String){\n\t        self.init(NSLocalizedString(applangaKey, tableName: nil, bundle: Bundle.main, value: defaultValue, comment:\"\"))\n    \t}\n\t}\n\t\n\t//Then localise a text like so:\n\t\n\tText(applangaKey: \"hello_world\")\n\t\n\t//or\n\t\n   \tText(applangaKey: \"hello_world\", defaultValue: \"Hello World\")\n\n```\n### SwiftUI Screenshots\n\nThe best method to take screenshots for your translations with SwiftUI is to capture your screenshots within UITests as described in [Automated during UITests](#Automated-during-UITests).\n\nTo enable the collection of string positions on your screen with SwiftUI, you need to enable the `showIdMode`, which returns the string ID instead of the translated string.\n\nThis is the only method that accurately links the string IDs with their position on the screenshot.\n\nTo enable the Applanga `showIdMode`, pass the parameter to your `ApplangaUITest` instance:\n\n```swift\n    let app = XCUIApplication()\n    let applangaUITest = ApplangaUITest(app: app, enableShowIdMode: true)\n    app.launch()\n```\n\nWe recommend that you capture all your screenshots with `showIdMode` enabled, and then capture all screenshots again with the `showIdMode` disabled. This way, all screenshots will be linked to the correct string IDs, and the actual translations will be shown on them.\n\n## WatchOS \n\nWhile screenshots and the Draft Mode menu are not available, string upload and automatic storyboard translation work in WatchOS targets. Simply follow these extra steps to get it to work:\n\n**a.** When installing with cocoa pods or SPM, you must also apply the Applanga SDK to the watch target that ends with the word \"extension\".  \n\n**b.** Make sure that any storyboard or string file you want to localise is also a member of the extension target.\n\n**c.** Add the name of the string file or storyboard that you want to localise to the `Info.plist` of the extension target. For example, if you are localising the `Interface.storyboard`, then add it like so:  \n`key=\"ApplangaAdditionalStringFiles\"\nvalue=\"Interface\".`\n\n**d.** In the `Info.plist` of your watch extension target, add the following entry: `key=\"ApplangaAdditionalFrameworks\" value=\"NAME OF YOUR PROJECT WatchKit App\".`\n\n## MacOS \n\nWhile screenshots and the Draft Mode menu are not available, string upload and automatic storyboard translation work in MacOS targets. Just install as you would the iOS SDK and use as normal.\n\t\n## TV OS\n\nAutomatic translations will work on TV OS without requiring any special changes. \n\nTo present the Draft Mode, use the following code:\n\n```swift\n\tApplanga.showDraftModeDialog()\n```\n\nafter the Draft Mode has been enabled, you can present the test menu overlay:\n```swift\n\tApplanga.setScreenShotMenuVisible(true)\n```\n\n## Branching\n\nIf your project is a branching project, use at least the SDK version 2.0.212 and update your Settings File.\nThe Settings File defines the default branch for your current app.\nThis branch is used on app start and for update calls.\nTo be sure branching is working look for the log line: `Branching is enabled.`\n\nTo learn more about branching, read our article on [branching](www.applanga.com/docs/advanced-features/branching).\n\n### Draft Mode\n\nWhen enabling the Draft Mode, you can switch your branch at runtime - an app restart is required.\nYou also can use our Draft Mode menu to switch to switch to another branch.\nEvery screenshot you take is linked to the current branch.\n\n### Production Apps\n\nAlready published apps that still use Settings Files without branching and older SDKs will still work. They will use the default branch defined on the Applanga dashboard.\n\n \n## Privacy Manifest\n\nThe SDK includes a privacy manifest that declares these items, all of which marked as `tracking`=`NO`:\n\n### API Usage\n - `NSPrivacyAccessedAPICategoryUserDefaults` - UserDefaults is only used to save SDK internal state, for example if the draft mode was enabled.\n\n ### Collected Data\n - `NSPrivacyCollectedDataTypeOtherDiagnosticData` - The SDK sends data when it detects runtime exceptions. The data contains SDK internal values and stack traces.\n - `NSPrivacyCollectedDataTypeDeviceID` - The `identifierForVendor` is used to count MAU\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapplanga%2Fsdk-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapplanga%2Fsdk-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapplanga%2Fsdk-ios/lists"}