{"id":10891,"date":"2020-07-02T15:52:49","date_gmt":"2020-07-02T13:52:49","guid":{"rendered":"https:\/\/phrase.com\/blog\/?p=10891"},"modified":"2023-09-25T10:22:30","modified_gmt":"2023-09-25T08:22:30","slug":"ios-tutorial-internationalization-localization","status":"publish","type":"post","link":"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/","title":{"rendered":"The Ultimate Guide to iOS Localization"},"content":{"rendered":"\n<div id=\"acf\/text-block_984c5549fe93e60fb3e5b145c9353a32\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The iOS operating system keeps growing and becoming better each year. For developers, Apple&#8217;s increased attention to internationalization means that it&#8217;s easier to make apps that are ready for global markets.<\/p>\n<p>With the App Store <a href=\"https:\/\/www.apple.com\/newsroom\/2020\/04\/apple-services-now-available-in-more-countries-around-the-world\/\">available in 175 countries<\/a> and counting over half a billion visitors each week, making an application available in as many regions and languages as possible is one sure way to app success, and the ease of doing it now means there&#8217;s barely any excuse to skip it.<\/p>\n<p>This step-by-step guide will show you how much simpler it is to open your application to the world. We&#8217;ll go through localization in both UIKit and SwiftUI, which makes this suitable for all types of teams.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_69_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#installation-and-setup\" title=\"Installation and setup\">Installation and setup<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#creating-a-project\" title=\"Creating a project\">Creating a project<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-add-supported-languages-to-my-app\" title=\"How do I add supported languages to my app?\">How do I add supported languages to my app?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#adding-languages-for-localization\" title=\"Adding languages for localization\">Adding languages for localization<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#changes-in-the-project-after-adding-language-support\" title=\"Changes in the project after adding language support\">Changes in the project after adding language support<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-work-with-translation-files\" title=\"How do I work with translation files?\">How do I work with translation files?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-handle-language-fallback\" title=\"How do I handle language fallback?\">How do I handle language fallback?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#generic-dialects\" title=\"Generic Dialects\">Generic Dialects<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#programmatic-fallback\" title=\"Programmatic fallback\">Programmatic fallback<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#working-with-localizablestrings\" title=\"Working with Localizable.strings\">Working with Localizable.strings<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#exporting-localizable-files-for-translators\" title=\"Exporting Localizable files for Translators\">Exporting Localizable files for Translators<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-localize-my-storyboards\" title=\"How do I localize my storyboards?\">How do I localize my storyboards?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-localize-strings-in-my-swift-code\" title=\"How do I localize strings in my Swift code?\">How do I localize strings in my Swift code?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-work-with-the-users-preferred-system-locale\" title=\"How do I work with the user\u2019s preferred system locale?\">How do I work with the user\u2019s preferred system locale?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#in-app-locale-switching\" title=\"In-app locale switching\">In-app locale switching<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#programmatic-language-switching\" title=\"Programmatic Language Switching\">Programmatic Language Switching<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-work-with-dynamic-values-in-my-translation-strings\" title=\"How do I work with dynamic values in my translation strings?\">How do I work with dynamic values in my translation strings?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-work-with-localized-plural-strings\" title=\"How do I work with localized plural strings?\">How do I work with localized plural strings?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-localize-numbers\" title=\"How do I localize numbers?\">How do I localize numbers?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-localize-dates\" title=\"How do I localize dates?\">How do I localize dates?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#how-do-i-localize-my-swiftui-views\" title=\"How do I localize my SwiftUI views?\">How do I localize my SwiftUI views?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#swiftui-translated-previews\" title=\"SwiftUI Translated Previews\">SwiftUI Translated Previews<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#interpolation\" title=\"Interpolation\">Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#plurals\" title=\"Plurals\">Plurals<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-25\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#combining-plurals-and-interpolation\" title=\"Combining plurals and interpolation\">Combining plurals and interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-26\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#adding-comments\" title=\"Adding comments\">Adding comments<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-27\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#add-localization-strings-for-non-text-views\" title=\"Add localization strings for non-text views\">Add localization strings for non-text views<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-28\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#programmatic-language-switching-2\" title=\"Programmatic language switching\">Programmatic language switching<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-29\" href=\"https:\/\/phrase.com\/blog\/posts\/ios-tutorial-internationalization-localization\/#wrapping-up\" title=\"Wrapping up\">Wrapping up<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"installation-and-setup\"><\/span><span style=\"color: #181818; font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial, sans-serif; font-size: 40px; font-weight: bold;\">Installation and setup<\/span><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>In this tutorial, we&#8217;ll be working on &#8220;Sleepy,&#8221; an iOS app that sells albums that help you sleep. The demo app will allow us to showcase all key steps in iOS localization. Sleepy is inspired by <a href=\"https:\/\/www.behance.net\/uladluch\">Uladzislau Luchkouski<\/a>\u2019s case study on <a href=\"https:\/\/www.behance.net\/gallery\/124869259\/Sleep-Sounds-Android-iOS-Application\">Sleepy sounds<\/a>. While Sleepy uses Swift 5, we&#8217;ll use Xcode Version 13.3.1 with SwiftUI 3 on iOS 15.<\/p>\n<table style=\"border-collapse: collapse; width: 100%;\">\n<tbody>\n<tr>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-37835\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-app-1.jpg\" alt=\"Sleepy app screen 1 | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-app-1.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-app-1-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-app-1-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-app-1-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-app-1-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-app-1-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-37840\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleep-app-2.jpg\" alt=\"Sleepy app screen 2 | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleep-app-2.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleep-app-2-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleep-app-2-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleep-app-2-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleep-app-2-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleep-app-2-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now that our environment is ready, we\u2019re ready to build our dream application. The first step is to think about the languages you&#8217;d like to support. In this case, Sleepy will have English as the base language, and it will also support Spanish and Hebrew<span style=\"font-weight: 400;\">\u2014Hebrew will help in dealing with Right-To-Left (RTL) languages<\/span>. You&#8217;re free to add as many languages as it makes sense for your app<span style=\"font-weight: 400;\">\u2014and the same process can be repeated<\/span>\u00a0each time.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"creating-a-project\"><\/span>Creating a project<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The principles learned from localizing this simple application can be applied to any type of application, regardless of size. You can get the full project here in <a href=\"https:\/\/github.com\/PhraseApp-Blog\/sleepy-uikit\">UIKit<\/a> and <a href=\"https:\/\/github.com\/PhraseApp-Blog\/sleepy-swiftui\">SwiftUI<\/a>. When you successfully run the project, you should be able to see the list of sleep sounds. Clicking on any album will show more details.<\/p>\n<table style=\"border-collapse: collapse; width: 100%;\">\n<tbody>\n<tr>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-37947\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-home.jpg\" alt=\"Sleepy app UI kit home screen | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-home.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-home-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-home-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-home-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-home-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-home-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-37952\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-details.jpg\" alt=\"Sleepy app detail screen | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-details.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-details-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-details-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-details-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-details-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/sleepy-uikit-details-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-add-supported-languages-to-my-app\"><\/span>How do I add supported languages to my app?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>iOS can be localized into over 100 locales and regions. iOS uses the two-letter ISO 639-1 standard to represent languages e.g <code>en<\/code> for English, <code>fr<\/code> for French, etc.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> If an ISO 639-1 code is not available for a particular language, use the ISO 639-2 code instead. For example, there is no ISO 639-1 code for the Hawaiian language, so use the ISO 639-2 code, <code>haw<\/code>.<\/p>\n<p>Apple provides more information on language codes in its <a href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/MacOSX\/Conceptual\/BPInternational\/LanguageandLocaleIDs\/LanguageandLocaleIDs.html\">documentation<\/a>. These identifiers are what tell the iOS system which languages your application is localized in, and show the user that language.<\/p>\n<p>Now that our project is ready, let&#8217;s move to the fun part: localization.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-37957 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/localisation-setup.png\" alt=\"Localization setup screen | Phrase\" width=\"1401\" height=\"903\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/localisation-setup.png 1401w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/localisation-setup-300x193.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/localisation-setup-1024x660.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/localisation-setup-768x495.png 768w\" sizes=\"(max-width: 1401px) 100vw, 1401px\" \/><\/p>\n<p>From the <span class=\"notion-enable-hover\" data-token-index=\"1\" data-reactroot=\"\">Project Navigator, s<\/span><span class=\"notion-enable-hover\" data-token-index=\"3\" data-reactroot=\"\">elect <\/span>the project name. Then check<em><span class=\"notion-enable-hover\" data-token-index=\"5\" data-reactroot=\"\"> Use Base Internationalization<\/span><\/em> if it&#8217;s not checked.<\/p>\n<p>Base internationalization separates user-facing strings from <code>.storyboard<\/code> and <code>.xib<\/code> files. It relieves localizers of the need to modify\u00a0<code>.storyboard<\/code> and\u00a0<code>.xib<\/code> files in Interface Builder. When you check the <em>Use Base Internationalization<\/em> option, Xcode transfers the <code>Main.storyboard<\/code> and <code>LaunchScreen.storyboard<\/code> into the <code>Base.lproj<\/code> folder for the default language. You\u2019ll remember that our base language in this tutorial is English.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> You will have to manually manage all localization files and folder structure if you don\u2019t enable <em>Use Base Internationalization<\/em>. The <a href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/MacOSX\/Conceptual\/BPInternational\/MaintaingYourOwnStringsFiles\/MaintaingYourOwnStringsFiles.html#\/\/apple_ref\/doc\/uid\/10000171i-CH3-SW4\">official guide<\/a> is a handy read if you need to manage localization files yourself.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"adding-languages-for-localization\"><\/span>Adding languages for localization<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The next thing is to add the languages that you want to support. In our case, we&#8217;ll add Spanish and Hebrew. From the <em>Project Navigator<\/em> \u279e <em>Select<\/em> the project name. Under <em>Localizations<\/em>, click the plus (+) symbol to add the languages and regions you want to support. A list of languages will show for you to select your language of choice, let&#8217;s start with Spanish.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-37962 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/add-localization.png\" alt=\"Screen for adding languages for localization | Phrase\" width=\"307\" height=\"512\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/add-localization.png 307w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/add-localization-180x300.png 180w\" sizes=\"(max-width: 307px) 100vw, 307px\" \/><\/p>\n<p>After you select your language (Spanish in our case), a <em>choose files and reference language to create your desired language localization<\/em> sheet will show. Uncheck <code>LaunchScreen.storyboard<\/code>.\u00a0Apple allows only static assets in the <code>LaunchScreen<\/code> and does not support localization in the <code>LaunchScreen<\/code>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-37967\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/choose-files.png\" alt=\"Screen for choosing files for localization | Phrase\" width=\"731\" height=\"431\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/choose-files.png 731w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/choose-files-300x177.png 300w\" sizes=\"(max-width: 731px) 100vw, 731px\" \/><\/p>\n<p>In the screen above, 2 things require your attention:<\/p>\n<ol>\n<li>Reference Language\u2014the <em>Base<\/em> language in which we have designed our application, which is by-default\u00a0<em>English<\/em>. It\u2019s recommended to keep it in English (or your team\u2019s native\/fluent language). Changing this to another language will create all localization files using the keys from that language. Since the base language should be the guide for localization, it\u2019s advisable to leave keep it that way.<\/li>\n<li>File Types\u2014make sure that all files are set to <em>Localizable Strings<\/em>.<\/li>\n<li>Click <em>Finish<\/em> as shown in the screenshot above.<\/li>\n<\/ol>\n<p>Repeat these same steps for any number of languages the app should support.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"changes-in-the-project-after-adding-language-support\"><\/span>Changes in the project after adding language support<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>After adding all your supported languages, you&#8217;ll realize some changes in your project. Let&#8217;s go over these changes, and what it means for our goal of localization.<\/p>\n<ul>\n<li>Language folders: Xcode will create <code>es.lproj<\/code> and <code>he.lproj<\/code> that contains the <code>Main.strings<\/code> for Spanish and Hebrew respectively.<\/li>\n<li>Storyboard: <code>Main.storyboard<\/code> should now be a folder containing the base <code>Main.storyboard<\/code> and <code>Main.strings<\/code> of supported languages.<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-37972\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/added-languages-folder.png\" alt=\"Added languages folder | Phrase\" width=\"787\" height=\"357\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/added-languages-folder.png 787w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/added-languages-folder-300x136.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/added-languages-folder-768x348.png 768w\" sizes=\"(max-width: 787px) 100vw, 787px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-37977\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/project-changes-after-adding-new-language.png\" alt=\"Project changes after adding a new language | Phrase\" width=\"1400\" height=\"902\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/project-changes-after-adding-new-language.png 1400w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/project-changes-after-adding-new-language-300x193.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/project-changes-after-adding-new-language-1024x660.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/12\/project-changes-after-adding-new-language-768x495.png 768w\" sizes=\"(max-width: 1400px) 100vw, 1400px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-translation-files\"><\/span>How do I work with translation files?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>In our localization journey, we&#8217;ll come across different files that all help us achieve our goal.<\/p>\n<ul>\n<li>Strings Files: A strings file contains the translations of localized user-facing strings for one language with optional comments. The syntax for each string in the strings file is a key-value pair in which <code>key<\/code> is the identifier for looking up the <code>value<\/code> that contains the translation.<\/li>\n<li><code>lproj<\/code> folder: is a directory that stores language-specific resources e.g <code>es.lproj<\/code>, <code>he.lproj<\/code>.<\/li>\n<li>XLIFF: is an XML-based bitext format that standardizes how localizable data is passed between tools during a localization process.<\/li>\n<\/ul>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> <a href=\"https:\/\/developer.apple.com\/documentation\/xcode\/editing-xliff-and-strings-files\">Learn more about the XLIFF format<\/a> in Apple\u2019s documentation.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-handle-language-fallback\"><\/span>How do I handle language fallback?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I know you might be asking yourself, what happens if a user is using a language your app doesn&#8217;t support or your app supports the language but not every string for that particular language is localized?<\/p>\n<ul>\n<li>With the languages that aren&#8217;t supported at all, iOS will default to a base language or the development language.<\/li>\n<li>For partially translated languages, Apple uses an <a href=\"https:\/\/developer.apple.com\/library\/archive\/qa\/qa1828\/_index.html\">algorithm<\/a> to help determine which language the user should see. Below is the algorithm in pseudocode:<\/li>\n<\/ul>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\"><\/code><\/span><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">determineTheLanguageToUse<\/span><span class=\"hljs-params\">()<\/span><\/span> {\n    <span class=\"hljs-keyword\">for<\/span> each user's preferredLanguages\n      <span class=\"hljs-keyword\">if<\/span> app supports the language\n        <span class=\"hljs-keyword\">return<\/span> the language\n      <span class=\"hljs-keyword\">if<\/span> app supports a more generic dialect\n        <span class=\"hljs-keyword\">return<\/span> the generic language\n\n     <span class=\"hljs-comment\">\/\/ Exhausted preferredLanguages and still cannot determine..<\/span>\n     <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-type\">CFBundleDevelopmentRegion<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b4996ef72d9a09b05936631b5642d2c6\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><span role=\"img\" aria-label=\"\ud83d\uddd2\ufe0f\">\ud83d\uddd2\ufe0f<\/span> <em><span class=\"notion-enable-hover\" data-token-index=\"1\" data-reactroot=\"\">Note \u00bb <\/span><\/em>A user\u2019s <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\" data-reactroot=\"\">preferredLanguages<\/span><\/code> are those listed in iOS\u2019s <em><span class=\"notion-enable-hover\" data-token-index=\"5\" data-reactroot=\"\">Settings App \u279e General \u279e Language &amp;<\/span><span class=\"notion-enable-hover\" data-token-index=\"5\" data-reactroot=\"\"> Region<\/span>.<\/em><\/p>\n<h3><span class=\"ez-toc-section\" id=\"generic-dialects\"><\/span>Generic Dialects<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The pseudocode for language selection is quite straightforward, except it doesn\u2019t cover generic dialects. There are languages that iOS supports in more than one dialect. <code>en<\/code> for English can have different dialects, like <code>en-GB<\/code> for English (United Kingdom), <code>en-US<\/code> for English (United States), <code>en-IE<\/code> for English (Ireland), etc. In our current app, if the user prefers <code>en-GB<\/code>, <code>en<\/code> will be used because it&#8217;s the closest generic dialect to <code>en-GB<\/code>.<\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> The other way round is not true. If the app supports <code>en-GB<\/code> (and not <code>en<\/code>), then if a user prefers <code>en<\/code>, then <code>en-GB<\/code> will not be the fallback because <code>en-GB<\/code> is not more generic. In this case it defaults to <code>CFBundleDevelopmentRegion<\/code>, which is the default language and region for the application.<\/p>\n<p>\ud83e\udd3f <em>Go Deeper \u00bb<\/em> The official <a href=\"https:\/\/developer.apple.com\/library\/archive\/technotes\/tn2418\/_index.html\">documentation<\/a> has more details on how locale matching and fallback decisions are made.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"programmatic-fallback\"><\/span>Programmatic fallback<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>To prevent any unexpected behavior, we can create an extension function that encapsulates <code>NSLocalizedString<\/code> with the desired <code>defaultLanguage<\/code> to fall back on when the preferred language isn&#8217;t found. This will be used throughout the project.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nUtils.swift\n*\/<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">extension<\/span> <span class=\"hljs-title\">String<\/span> <\/span>{\n\n     <span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">localize<\/span><span class=\"hljs-params\">(comment: String = <span class=\"hljs-string\">\"\"<\/span>)<\/span><\/span> -&gt; <span class=\"hljs-type\">String<\/span> {\n         <span class=\"hljs-keyword\">let<\/span> defaultLanguage = <span class=\"hljs-string\">\"en\"<\/span>\n         <span class=\"hljs-keyword\">let<\/span> value = <span class=\"hljs-type\">NSLocalizedString<\/span>(<span class=\"hljs-keyword\">self<\/span>, comment: comment)\n         <span class=\"hljs-keyword\">if<\/span> value != <span class=\"hljs-keyword\">self<\/span> || <span class=\"hljs-type\">NSLocale<\/span>.preferredLanguages.first == defaultLanguage {\n             <span class=\"hljs-keyword\">return<\/span> value <span class=\"hljs-comment\">\/\/ String localization was found<\/span>\n         }\n\n\t\t\t\t <span class=\"hljs-comment\">\/\/ Load resource for default language to be used as<\/span>\n         <span class=\"hljs-comment\">\/\/ the fallback language<\/span>\n         <span class=\"hljs-keyword\">guard<\/span> <span class=\"hljs-keyword\">let<\/span> path = <span class=\"hljs-type\">Bundle<\/span>.main.path(forResource: defaultLanguage, ofType: <span class=\"hljs-string\">\"lproj\"<\/span>), <span class=\"hljs-keyword\">let<\/span> bundle = <span class=\"hljs-type\">Bundle<\/span>(path: path) <span class=\"hljs-keyword\">else<\/span> {\n             <span class=\"hljs-keyword\">return<\/span> value\n         }\n\n         <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-type\">NSLocalizedString<\/span>(<span class=\"hljs-keyword\">self<\/span>, bundle: bundle, comment: <span class=\"hljs-string\">\"\"<\/span>)\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_af531e28e1f18093009ff760ae749c63\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We can use <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\" data-reactroot=\"\">localize()<\/span><\/code> as follows.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/\/ Prints the translation of the key localised to the user's<\/span>\n<span class=\"hljs-comment\">\/\/ language or default to English<\/span>\n<span class=\"hljs-built_in\">print<\/span>(<span class=\"hljs-string\">\"translation.key\"<\/span>.localize()) <\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e7ce56ca1d7fdd4b14a46e888c631535\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"working-with-localizablestrings\"><\/span>Working with Localizable.strings<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><code>Localizable.strings<\/code> stores the strings to be used in the application as key-value pairs and will be used for localizing in the languages that you support.<\/p>\n<p>Let&#8217;s create <code>Localizable.strings<\/code> by going to <em>File<\/em> \u279e <em>New<\/em> \u279e <em>File<\/em> or just press <em>Cmd+N<\/em>. Search for \u201cstrings\u201d\u00a0 and you will see <em>Strings File<\/em> in the <em>Resource<\/em> pane.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38283 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/create-localizable-string.png\" alt=\"Create localizable strings | Phrase\" width=\"732\" height=\"523\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/create-localizable-string.png 732w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/create-localizable-string-300x214.png 300w\" sizes=\"(max-width: 732px) 100vw, 732px\" \/><\/p>\n<p>Select the <em>Strings File<\/em> as shown in the image above and click <em>Next<\/em>. Name the file <code>Localizable<\/code> and <em>Create<\/em> the file.<\/p>\n<p>Click on the newly created <code>Localizable.strings<\/code> file and click on <em>Localize<\/em> in the <em>Inspector.<\/em> On the alert that appears, click <em>Localize<\/em> to create the localization file for English.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38291 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localise-file.png\" alt=\"Localise file | Phrase\" width=\"1400\" height=\"901\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localise-file.png 1400w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localise-file-300x193.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localise-file-1024x659.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localise-file-768x494.png 768w\" sizes=\"(max-width: 1400px) 100vw, 1400px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-38296 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localize-alert.png\" alt=\"Localize alert | Phrase\" width=\"292\" height=\"258\" \/><\/p>\n<p>In the <em><span class=\"notion-enable-hover\" data-token-index=\"1\" data-reactroot=\"\">Inspector<\/span><\/em>, check all the other supported localizations for your application under the Localization section to create the localization strings for those languages.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-38301 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localize-check-languages.png\" alt=\"Localize check languages | Phrase\" width=\"257\" height=\"97\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"exporting-localizable-files-for-translators\"><\/span>Exporting Localizable files for Translators<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Localization can be a lot of work. Direct translations from Google aren&#8217;t good enough all the time. You might need experts like <a href=\"https:\/\/phrase.com\/roles\/translators\/\">Phrase<\/a> who can solve that headache for you so you can focus on building the product. You can export your strings for a translator. After the translation is completed by experts, simply import the translated strings back to use in your application.<\/p>\n<p>In this method, we shall see how to export localizable files for the localizers (translators). To do so, first create a separate folder for Localization (as shown below):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38306 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localization-folder.png\" alt=\"Localization folder | Phrase\" width=\"494\" height=\"176\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localization-folder.png 494w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localization-folder-300x107.png 300w\" sizes=\"(max-width: 494px) 100vw, 494px\" \/><\/p>\n<p>As you can see, there are two folders within the <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\" data-reactroot=\"\">Localization <\/span><\/code>folder. <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\" data-reactroot=\"\">To Localizers <\/span><\/code>is the folder where we, being the developers, export our localizable files for translators. <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\" data-reactroot=\"\">From Localizers<\/span><\/code> is the folder from where we would import the localized files from translators to our project. After creating the folders, go back to our Xcode project and open the root folder <em><span class=\"notion-enable-hover\" data-token-index=\"7\" data-reactroot=\"\">Info<\/span><\/em> settings again. Let&#8217;s export by going to <em><span class=\"notion-enable-hover\" data-token-index=\"9\" data-reactroot=\"\">Product Menu<\/span><\/em> \u279e <em><span class=\"notion-enable-hover\" data-token-index=\"11\" data-reactroot=\"\">Export Localizations<\/span><\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-38761\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-screens.jpeg\" alt=\"\" width=\"2560\" height=\"1080\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-screens.jpeg 2560w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-screens-300x127.jpeg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-screens-1024x432.jpeg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-screens-768x324.jpeg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-screens-1536x648.jpeg 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-screens-2048x864.jpeg 2048w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/p>\n<p>Once you click the <em><span class=\"notion-enable-hover\" data-token-index=\"1\" data-reactroot=\"\">Export Localizations<\/span><\/em> option, Xcode will ask you to save the localizable files to your specified folder as shown below:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38323 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-alert.png\" alt=\"Export localizations alert | Phrase\" width=\"808\" height=\"455\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-alert.png 808w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-alert-300x169.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/export-localizations-alert-768x432.png 768w\" sizes=\"(max-width: 808px) 100vw, 808px\" \/><\/p>\n<p>This will export all selected localizations in XLIFF format that localizers can use to localize your application. When translators are done they&#8217;ll send an XLIFF file back that you should save in <code>From Localizers<\/code> to be imported. To import the localization, go to <em>Product Menu<\/em> \u279e <em>Import Localizations<\/em> \u279e <em>Select file in<\/em> <code>From Localizers<\/code>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-my-storyboards\"><\/span>How do I localize my storyboards?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Localization can be done on mainly 2 levels in UIKit. Localize directly on the storyboard, mostly for static text, and programmatically. We will start with how to localize storyboards directly.<\/p>\n<p>We have already laid the groundwork for localizing storyboards in our setup steps. In the <em>Changes in the Project After Language Support<\/em> section, we saw that <code>Main.strings<\/code> was created for our non-base supported languages. These <code>Main.strings<\/code> contain ids for labels in the storyboards.<\/p>\n<p>When we select the <code>Main(Spanish)<\/code> file, we see the key-value pairs of object ids and their text, representing how these strings will display in Spanish. As you can see, it&#8217;s still in English so we&#8217;ll have to localize it to Spanish.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38328 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-before-storyboard-translation.png\" alt=\"Spanish before storyboard translation | Phrase\" width=\"1404\" height=\"904\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-before-storyboard-translation.png 1404w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-before-storyboard-translation-300x193.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-before-storyboard-translation-1024x659.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-before-storyboard-translation-768x494.png 768w\" sizes=\"(max-width: 1404px) 100vw, 1404px\" \/><\/p>\n<p>After translation.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38333 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-after-storyboard-translation.png\" alt=\"Spanish after storyboard translation | Phrase\" width=\"1404\" height=\"902\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-after-storyboard-translation.png 1404w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-after-storyboard-translation-300x193.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-after-storyboard-translation-1024x658.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-after-storyboard-translation-768x493.png 768w\" sizes=\"(max-width: 1404px) 100vw, 1404px\" \/><\/p>\n<p>Now let&#8217;s run the application in Spanish to see if our localization worked. By default, every time you run the application, it uses the base language. You can run your application using different locales by <em><span class=\"notion-enable-hover\" data-token-index=\"1\" data-reactroot=\"\">Option + Clicking<\/span><\/em> on the <em><span class=\"notion-enable-hover\" data-token-index=\"3\" data-reactroot=\"\">(Play)<\/span> <span class=\"discussion-id-cc5e45d7-8e1b-4659-b03a-d82544459bd1 notion-enable-hover\" data-token-index=\"5\" data-reactroot=\"\">Start active scheme button<\/span><\/em>, then going to <em><span class=\"notion-enable-hover\" data-token-index=\"7\" data-reactroot=\"\">Run<\/span><\/em> \u2192 <em><span class=\"notion-enable-hover\" data-token-index=\"9\" data-reactroot=\"\">Options<\/span><\/em> \u2192 <em><span class=\"notion-enable-hover\" data-token-index=\"11\" data-reactroot=\"\">App Language<\/span><\/em> \u2192<em> <span class=\"notion-enable-hover\" data-token-index=\"13\" data-reactroot=\"\">Change to a supported language <\/span><\/em>(Spanish in this case) \u2192 Click <em><span class=\"notion-enable-hover\" data-token-index=\"15\" data-reactroot=\"\">Run<\/span><\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-38766\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-storyboard-localization-run-screen.jpeg\" alt=\"\" width=\"1401\" height=\"901\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-storyboard-localization-run-screen.jpeg 1401w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-storyboard-localization-run-screen-300x193.jpeg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-storyboard-localization-run-screen-1024x659.jpeg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spannish-storyboard-localization-run-screen-768x494.jpeg 768w\" sizes=\"(max-width: 1401px) 100vw, 1401px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38343\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/detail_screen.jpg\" alt=\"Detail screen | Phrase\" width=\"349\" height=\"756\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/detail_screen.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/detail_screen-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/detail_screen-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/detail_screen-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/detail_screen-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/detail_screen-946x2048.jpg 946w\" sizes=\"(max-width: 349px) 100vw, 349px\" \/><\/p>\n<p>The <code>Main(Spanish)<\/code> file is translated. The rest of the app will gradually be translated as we move. Now that we&#8217;ve just learned how to localize storyboards, you can follow the same steps and localize the storyboard for Hebrew.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-strings-in-my-swift-code\"><\/span>How do I localize strings in my Swift code?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We can also localize our application programmatically using the <code>Localizable<\/code> files. <code>Localizable<\/code> files also use key-value pairs in the format <code>\"key\" = \"value\";<\/code> for localization. In the <code>Localizable(English)<\/code> file, we&#8217;ll add the rest of the strings representing the music data.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nLocalizable.strings\n*\/<\/span>\n\n<span class=\"hljs-comment\">\/* Titles of Sleep albums *\/<\/span>\n<span class=\"hljs-string\">\"slumber\"<\/span> = <span class=\"hljs-string\">\"Slumber\"<\/span>;\n<span class=\"hljs-string\">\"white.noise\"<\/span> = <span class=\"hljs-string\">\"White Noise\"<\/span>;\n<span class=\"hljs-string\">\"soothing\"<\/span> = <span class=\"hljs-string\">\"Soothing\"<\/span>;\n<span class=\"hljs-string\">\"paino\"<\/span> = <span class=\"hljs-string\">\"Piano\"<\/span>;\n<span class=\"hljs-string\">\"wind\"<\/span> = <span class=\"hljs-string\">\"Wind\"<\/span>;\n<span class=\"hljs-string\">\"cold.fusion\"<\/span> = <span class=\"hljs-string\">\"Cold Fusion\"<\/span>;\n\n<span class=\"hljs-comment\">\/* Categories of Sleep albums *\/<\/span>\n<span class=\"hljs-string\">\"zen\"<\/span> = <span class=\"hljs-string\">\"Zen\"<\/span>;\n<span class=\"hljs-string\">\"instrumental\"<\/span> = <span class=\"hljs-string\">\"Instrumental\"<\/span>;\n<span class=\"hljs-string\">\"nature\"<\/span> = <span class=\"hljs-string\">\"Nature\"<\/span>;\n\n<span class=\"hljs-comment\">\/* About of Sleep albums *\/<\/span>\n<span class=\"hljs-string\">\"about1\"<\/span> = <span class=\"hljs-string\">\"An acoustic mix has been specially selected for you. The camping atmosphere will help you improve your sleep and your body as a whole. Your dreams will be delightful and vivid.\"<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_11515ca2b7bcdfed402d8f21739f5fe3\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>iOS already gives us a hassle-free way to display these strings using their keys for easy localization. <code>NSLocalizedString<\/code> returns a localized version of a string from the default table, which Xcode autogenerates when exporting localizations.<\/p>\n<p>Referring back to the programmatic fallback section, the extension function we created makes use of <code>NSLocalizedString<\/code> using two of the most common implementations.<\/p>\n<ul>\n<li><code>NSLocalizedString(key, comment)<\/code>: <code>key<\/code> for a string in the default table, and <code>comment<\/code> helps give context to a translator as to how the string is used within the application.<\/li>\n<li><code>NSLocalizedString(key, tableName, bundle, value, comment)<\/code>: This signature becomes useful when you want to split your strings files into smaller, manageable sizes. This is especially important when you are working on a very large app and you don\u2019t want to have all your localizable strings in one. <code>Localizable.strings<\/code> ****file. <code>NSLocalizedString(key, comment)<\/code>only reads <code>Localizable.strings<\/code> file by default. In this signature:\n<ul>\n<li><code>tableName<\/code>\u00a0is the name of the table containing the key-value pairs.<\/li>\n<li><code>bundle<\/code> is a directory in the file system that groups executable code and related resources together in one place. We often just use <code>Bundle.main<\/code> here.<\/li>\n<li><code>value<\/code> is the string you want to default to in case no string was found for the <code>key<\/code> provided.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>With this newfound knowledge, let us localize for Spanish and Hebrew and use the <code>localize()<\/code> extension function we have already created in the programmatic fallback section.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nLocalizable.strings\n*\/<\/span>\n\n<span class=\"hljs-comment\">\/* Titles of Sleep albums *\/<\/span>\n<span class=\"hljs-string\">\"slumber\"<\/span> = <span class=\"hljs-string\">\"\u05ea\u05b0\u05e0\u05d5\u05bc\u05de\u05b8\u05d4\"<\/span>;\n<span class=\"hljs-string\">\"white.noise\"<\/span> = <span class=\"hljs-string\">\"\u05e8\u05e2\u05e9 \u05dc\u05d1\u05df\"<\/span>;\n<span class=\"hljs-string\">\"soothing\"<\/span> = <span class=\"hljs-string\">\"\u05d4\u05b7\u05e8\u05d2\u05b8\u05e2\u05b8\u05d4\"<\/span>;\n<span class=\"hljs-string\">\"paino\"<\/span> = <span class=\"hljs-string\">\"\u05e4\u05bc\u05b0\u05e1\u05b7\u05e0\u05b0\u05ea\u05b5\u05e8\"<\/span>;\n<span class=\"hljs-string\">\"wind\"<\/span> = <span class=\"hljs-string\">\"\u05e8\u05d5\u05bc\u05d7\u05b7\"<\/span>;\n\n<span class=\"hljs-comment\">\/* Categories of Sleep albums *\/<\/span>\n<span class=\"hljs-string\">\"cold.fusion\"<\/span> = <span class=\"hljs-string\">\"\u05d6\u05df\"<\/span>;\n<span class=\"hljs-string\">\"zen\"<\/span> = <span class=\"hljs-string\">\"\u05de\u05d5\u05b9\u05e2\u05b4\u05d9\u05dc\"<\/span>;\n<span class=\"hljs-string\">\"instrumental\"<\/span> = <span class=\"hljs-string\">\"\u05d8\u05b4\u05d1\u05e2\u05b4\u05d9\"<\/span>;\n\n<span class=\"hljs-comment\">\/* About Sleep albums *\/<\/span>\n<span class=\"hljs-string\">\"about.acoustic\"<\/span> = <span class=\"hljs-string\">\"\u05de\u05d9\u05e7\u05e1 \u05d0\u05e7\u05d5\u05e1\u05d8\u05d9 \u05e0\u05d1\u05d7\u05e8 \u05d1\u05de\u05d9\u05d5\u05d7\u05d3 \u05e2\u05d1\u05d5\u05e8\u05db\u05dd. \u05d0\u05d5\u05d5\u05d9\u05e8\u05ea \u05d4\u05e7\u05de\u05e4\u05d9\u05e0\u05d2 \u05ea\u05e2\u05d6\u05d5\u05e8 \u05dc\u05db\u05dd \u05dc\u05e9\u05e4\u05e8 \u05d0\u05ea \u05d4\u05e9\u05d9\u05e0\u05d4 \u05d5\u05d0\u05ea \u05d4\u05d2\u05d5\u05e3 \u05db\u05d5\u05dc\u05d5. \u05d4\u05d7\u05dc\u05d5\u05de\u05d5\u05ea \u05e9\u05dc\u05da \u05d9\u05d4\u05d9\u05d5 \u05de\u05e2\u05e0\u05d2\u05d9\u05dd \u05d5\u05d7\u05d9\u05d9\u05dd.\"<\/span>;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e070d40a121fc6e99213a0ea863226e5\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We&#8217;ll localize the songs data in <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\" data-reactroot=\"\">Utils.swift<\/span><\/code> using the extension function. This will localize the strings and fallback to a default language if no translation is found.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nUtils.swift\n*\/<\/span>\n\n<span class=\"hljs-type\">SleepData<\/span>(\n    image: <span class=\"hljs-string\">\"image1\"<\/span>,\n    title: <span class=\"hljs-string\">\"slumber\"<\/span>.localize(),\n    numberOfSongs: <span class=\"hljs-number\">4<\/span>,\n    category: <span class=\"hljs-string\">\"zen\"<\/span>.localize(),\n    songs: &#91;<span class=\"hljs-string\">\"Friday Night Lights\"<\/span>, <span class=\"hljs-string\">\"4 your eyes only\"<\/span>, <span class=\"hljs-string\">\"Love yours\"<\/span>],\n    about: <span class=\"hljs-string\">\"about.acoustic\"<\/span>.localize())<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_d4173136490416099b63a07703f2df98\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38351\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-programmatic-translation.jpg\" alt=\"Hebrew programmatic translation | Phrase\" width=\"349\" height=\"756\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-programmatic-translation.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-programmatic-translation-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-programmatic-translation-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-programmatic-translation-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-programmatic-translation-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-programmatic-translation-946x2048.jpg 946w\" sizes=\"(max-width: 349px) 100vw, 349px\" \/><\/p>\n<p><span role=\"img\" aria-label=\"\ud83d\uddd2\">\ud83d\uddd2<\/span> Note \u00bb There is no translation for <em><span class=\"notion-enable-hover\" data-token-index=\"1\" data-reactroot=\"\">title6<\/span><\/em> which is <em><span class=\"notion-enable-hover\" data-token-index=\"3\" data-reactroot=\"\">Cold Fusion<\/span><\/em> in English, so we fell back on english to translate that string.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-the-users-preferred-system-locale\"><\/span>How do I work with the user\u2019s preferred system locale?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The iOS system automatically handles switching locales of applications when the app supports the current locale, e.g. if the phone&#8217;s language setting is Hebrew, I&#8217;ll see the Hebrew version of the app. If the language is switched to Spanish, the iOS system will change the app version to Spanish. If the language is changed to one that doesn&#8217;t have a localization, the base language is used for the application.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"in-app-locale-switching\"><\/span>In-app locale switching<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>It\u2019s best to let the OS handle the user\u2019s preferred locale, and to have your apps adapt to it. In fact, Apple warns against apps switching the locale themselves: All programmatic approaches can lead to unforeseen changes and might not work after an iOS update. The preferred way to handle this is to programmatically open the <em>Preferred Language<\/em> settings for your app so users can change the settings themselves.<\/p>\n<table style=\"width: 100%; border-collapse: collapse; border-style: hidden;\">\n<tbody>\n<tr>\n<td style=\"width: 50%;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38362 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_263C903C-8BBB-4E38-8172-B92ACADEAED8.png\" alt=\"Simulator screenshot | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_263C903C-8BBB-4E38-8172-B92ACADEAED8.png 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_263C903C-8BBB-4E38-8172-B92ACADEAED8-139x300.png 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_263C903C-8BBB-4E38-8172-B92ACADEAED8-473x1024.png 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_263C903C-8BBB-4E38-8172-B92ACADEAED8-768x1662.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_263C903C-8BBB-4E38-8172-B92ACADEAED8-710x1536.png 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_263C903C-8BBB-4E38-8172-B92ACADEAED8-946x2048.png 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38367 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_4D6992AA-E48D-4462-A42D-9A0789D588F7.png\" alt=\"Simulator screenshot 2\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_4D6992AA-E48D-4462-A42D-9A0789D588F7.png 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_4D6992AA-E48D-4462-A42D-9A0789D588F7-139x300.png 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_4D6992AA-E48D-4462-A42D-9A0789D588F7-473x1024.png 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_4D6992AA-E48D-4462-A42D-9A0789D588F7-768x1662.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_4D6992AA-E48D-4462-A42D-9A0789D588F7-710x1536.png 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/simulator_screenshot_4D6992AA-E48D-4462-A42D-9A0789D588F7-946x2048.png 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This is the code snippet that opens your app\u2019s settings for the user to change the language. Use this at any appropriate part of your app, e.g in Sleepy we can use this in the menu on the home screen.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">let<\/span> settingsURL = <span class=\"hljs-type\">URL<\/span>(string: <span class=\"hljs-type\">UIApplication<\/span>.openSettingsURLString) {\n    <span class=\"hljs-type\">UIApplication<\/span>.shared.<span class=\"hljs-keyword\">open<\/span>(settingsURL, options: &#91;:], completionHandler: <span class=\"hljs-literal\">nil<\/span>)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_28261421923b7bcb9bb34c9464e30288\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"programmatic-language-switching\"><\/span>Programmatic Language Switching<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Even though it is not recommended by Apple, use cases differ and changing the locale via code might be what you need. iOS saves the locale in a user default with the key <code>AppleLanguages<\/code> and changing this will update the application&#8217;s locale. The catch is that the app has to be reopened to see the change. We&#8217;ll use a notification to prompt the user to reopen the application. You can choose to show an alert or sheet that explains why the user needs to reopen the application after changing the language. The best user experience to go around this can differ so we leave you to make your own decision. We will use notifications for our app, Sleepy.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/*\nViewController.swift\n*\/<\/span>\n\nprivate func setLangauge(languageCode: <span class=\"hljs-built_in\">String<\/span>, <span class=\"hljs-attr\">language<\/span>: <span class=\"hljs-built_in\">String<\/span>) {\n     <span class=\"hljs-keyword\">let<\/span> alert = UIAlertController(title: nil, <span class=\"hljs-attr\">message<\/span>: <span class=\"hljs-string\">\"changing.language\"<\/span>.localize(), <span class=\"hljs-attr\">preferredStyle<\/span>: .alert)\n\n      alert.addAction(UIAlertAction(title: <span class=\"hljs-string\">\"exit.sleepy\"<\/span>.localize() , <span class=\"hljs-attr\">style<\/span>: .default, <span class=\"hljs-attr\">handler<\/span>: { &#91;weak self](_) <span class=\"hljs-keyword\">in<\/span>\n\t\t\t\t\t\t<span class=\"hljs-comment\">\/\/ Update app's language with the language code<\/span>\n            UserDefaults.standard.set(&#91;languageCode], <span class=\"hljs-attr\">forKey<\/span>: <span class=\"hljs-string\">\"AppleLanguages\"<\/span>)\n            UserDefaults.standard.synchronize()\n\n            self?.closeApplicationWithNotification(language: language)\n    }))\n\n\t\t <span class=\"hljs-comment\">\/\/ Show change language alert to user<\/span>\n     self.present(alert, <span class=\"hljs-attr\">animated<\/span>: <span class=\"hljs-literal\">true<\/span>, <span class=\"hljs-attr\">completion<\/span>: nil)\n}\n\n private func closeApplicationWithNotification(language: <span class=\"hljs-built_in\">String<\/span>) {\n     <span class=\"hljs-keyword\">let<\/span> content = UNMutableNotificationContent()\n     content.title = <span class=\"hljs-string\">\"Language changed to \\(language)\"<\/span>\n     content.body = <span class=\"hljs-string\">\"Tap to reopen the application\"<\/span>\n     content.sound = UNNotificationSound.default\n\n     <span class=\"hljs-keyword\">let<\/span> trigger = UNTimeIntervalNotificationTrigger(timeInterval: <span class=\"hljs-number\">0.5<\/span>, <span class=\"hljs-attr\">repeats<\/span>: <span class=\"hljs-literal\">false<\/span>)\n     <span class=\"hljs-keyword\">let<\/span> identifier = <span class=\"hljs-string\">\"sleepy\"<\/span>\n     <span class=\"hljs-keyword\">let<\/span> request = UNNotificationRequest.init(identifier: identifier, <span class=\"hljs-attr\">content<\/span>: content, <span class=\"hljs-attr\">trigger<\/span>: trigger)\n     <span class=\"hljs-keyword\">let<\/span> center = UNUserNotificationCenter.current()\n     center.add(request)\n\n     exit(EXIT_SUCCESS)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9186dac69a1156b4b126b978969ae5fd\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>These two functions will help us change the locale of our application and send a notification to the user to tap on it to open the application. The function <code>closeApplicationWithNotification<\/code> is optional and the user experience of reopening the application should be handled as you see fit. We use <code>exit(EXIT_SUCCESS)<\/code> to close the application to relaunch for localization changes to fully apply to the application e.g Hebrew switches to Right to Left.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> Check out <a href=\"https:\/\/developer.apple.com\/library\/archive\/qa\/qa1561\/_index.html#\/\/apple_ref\/doc\/uid\/DTS40007952\">How do I programmatically quit my iOS application?<\/a> in the official Apple docs.<\/p>\n<p>Let&#8217;s look at how we use our functions can be used in our application. We&#8217;ll have a menu display the supported languages and selecting one will change the app language.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nViewController.swift\n*\/<\/span>\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">configureActionItemMenu<\/span><span class=\"hljs-params\">()<\/span><\/span> {\n    <span class=\"hljs-keyword\">let<\/span> menuItems = &#91;\n         <span class=\"hljs-type\">UIAction<\/span>(title: <span class=\"hljs-string\">\"English\"<\/span>, handler: { &#91;<span class=\"hljs-keyword\">weak<\/span>   <span class=\"hljs-keyword\">self<\/span>] (<span class=\"hljs-number\">_<\/span>) <span class=\"hljs-keyword\">in<\/span>\n             <span class=\"hljs-keyword\">self<\/span>?.setLangauge(languageCode: <span class=\"hljs-string\">\"en\"<\/span>, language: <span class=\"hljs-string\">\"English\"<\/span>)\n        }),\n         <span class=\"hljs-type\">UIAction<\/span>(title: <span class=\"hljs-string\">\"Hebrew\"<\/span>,handler: { &#91;<span class=\"hljs-keyword\">weak<\/span>   <span class=\"hljs-keyword\">self<\/span>] (<span class=\"hljs-number\">_<\/span>) <span class=\"hljs-keyword\">in<\/span>\n             <span class=\"hljs-keyword\">self<\/span>?.setLangauge(languageCode: <span class=\"hljs-string\">\"he\"<\/span>, language: <span class=\"hljs-string\">\"Hebrew\"<\/span>)\n        }),\n         <span class=\"hljs-type\">UIAction<\/span>(title: <span class=\"hljs-string\">\"Spanish\"<\/span>, handler: { &#91;<span class=\"hljs-keyword\">weak<\/span>   <span class=\"hljs-keyword\">self<\/span>] (<span class=\"hljs-number\">_<\/span>) <span class=\"hljs-keyword\">in<\/span>\n         <span class=\"hljs-keyword\">self<\/span>?.setLangauge(languageCode: <span class=\"hljs-string\">\"es\"<\/span>, language: <span class=\"hljs-string\">\"Spanish\"<\/span>)\n        })\n    ]\n\n    <span class=\"hljs-keyword\">let<\/span> languageMenu = <span class=\"hljs-type\">UIMenu<\/span>(\n\t\t\ttitle: <span class=\"hljs-string\">\"choose.language\"<\/span>.localize(), \n\t\t\timage: <span class=\"hljs-literal\">nil<\/span>, \n\t\t\tidentifier: <span class=\"hljs-literal\">nil<\/span>, \n\t\t\toptions: &#91;], \n\t\t\tchildren: menuItems\n\t\t)\n\n    navigationItem.rightBarButtonItem = <span class=\"hljs-type\">UIBarButtonItem<\/span>(\n\t\t\ttitle: <span class=\"hljs-string\">\"choose.language\"<\/span>.localize(), \n\t\t\timage: <span class=\"hljs-type\">UIImage<\/span>(systemName: <span class=\"hljs-string\">\"gear\"<\/span>), \n\t\t\tprimaryAction: <span class=\"hljs-literal\">nil<\/span>, \n\t\t\tmenu: languageMenu\n\t\t)\n    navigationItem.rightBarButtonItem?.tintColor = .white\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_99405c0157077643f2d4acc19f6c3e22\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<div style=\"width: 435px;\" class=\"wp-video\"><!--[if lt IE 9]><script>document.createElement('video');<\/script><![endif]-->\n<video class=\"wp-video-shortcode\" id=\"video-10891-1\" width=\"435\" height=\"887\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/sleepy-ios-app-uikit-language-change.mp4?_=1\" \/><a href=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/sleepy-ios-app-uikit-language-change.mp4\">https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/sleepy-ios-app-uikit-language-change.mp4<\/a><\/video><\/div>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-dynamic-values-in-my-translation-strings\"><\/span>How do I work with dynamic values in my translation strings?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Interpolation is one of the most common use cases for strings, so what happens when you want to use it with localized strings? Once again, we&#8217;ll build on our extensions to make interpolation easy and clean throughout our project.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nUtils.swift\nSleepyUIKit\n*\/<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">extension<\/span> <span class=\"hljs-title\">String<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">localizeFormat<\/span><span class=\"hljs-params\">(args: &#91;CVarArg], comment: String = <span class=\"hljs-string\">\"\"<\/span>)<\/span><\/span> -&gt; <span class=\"hljs-type\">String<\/span> {\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">self<\/span>.localizeStringFormat(key: <span class=\"hljs-keyword\">self<\/span>, comment: comment, args: args)\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">func<\/span> <span class=\"hljs-title\">localizeStringFormat<\/span><span class=\"hljs-params\">(key: String, comment: String = <span class=\"hljs-string\">\"\"<\/span>, args: CVarArg... )<\/span><\/span> -&gt; <span class=\"hljs-type\">String<\/span> {\n        <span class=\"hljs-keyword\">let<\/span> format = <span class=\"hljs-type\">NSLocalizedString<\/span>(key, comment: comment)\n        <span class=\"hljs-keyword\">let<\/span> result = <span class=\"hljs-built_in\">withVaList<\/span>(args) {\n            (<span class=\"hljs-type\">NSString<\/span>(format: format, locale: <span class=\"hljs-type\">NSLocale<\/span>.current, arguments: $<span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">as<\/span> <span class=\"hljs-type\">String<\/span>)\n        }\n        <span class=\"hljs-keyword\">return<\/span> result\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_558bd6d2bbd79c8cd639350d7a2106aa\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Add the following localizations in <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\" data-reactroot=\"\">Localizable.strings<\/span><\/code> to be used in interpolation. String specifiers are special characters that indicate the type an interpolation parameter should be. You can add a single or multiple specifiers to the same string interpolation. A full list of all the specifiers available to us can be found <a class=\"notion-link-token notion-enable-hover\" href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/Strings\/Articles\/formatSpecifiers.html\" target=\"_blank\" rel=\"noopener noreferrer\" data-token-index=\"3\" data-reactroot=\"\"><span class=\"link-annotation-unknown-block-id-2423406\">here<\/span><\/a>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-string\">\"sleep.points\"<\/span> = <span class=\"hljs-string\">\"%1lu Sleep Points\"<\/span>; <span class=\"hljs-comment\">\/\/ Number interpolation<\/span>\n<span class=\"hljs-string\">\"hello.user\"<\/span> = <span class=\"hljs-string\">\"Hello %1$@\"<\/span>; <span class=\"hljs-comment\">\/\/ String interpolation<\/span>\n<span class=\"hljs-string\">\"share\"<\/span> = <span class=\"hljs-string\">\"Share %1lu song to %2$@\"<\/span>; <span class=\"hljs-comment\">\/\/ Multiple interpolations<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c6a84952a124137a6ab548897c8a04ac\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now in our code we can use these strings together with the extension functions. Number formatting will be tackled more in-depth later in the article.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/\/ Output assumes English (en-US) is the active locale<\/span>\n\nsomeLabel.text = <span class=\"hljs-string\">\"sleep.points\"<\/span>.localizeFormat(args: &#91;<span class=\"hljs-number\">3000<\/span>])\n<span class=\"hljs-comment\">\/\/ =&gt; 3,000 Sleep Points<\/span>\n\nsomeLabel.text = <span class=\"hljs-string\">\"hello.user\"<\/span>.localizeFormat(args: &#91;<span class=\"hljs-string\">\"John\"<\/span>])\n<span class=\"hljs-comment\">\/\/ =&gt; Hello John<\/span>\n\nsomeLabel.text = <span class=\"hljs-string\">\"share\"<\/span>.localizeFormat(args: &#91;<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-string\">\"Norris\"<\/span>])\n<span class=\"hljs-comment\">\/\/ =&gt; Share 1 song to Norris<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5a2f924c797ce2d5a6842ac5411ae2bd\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-localized-plural-strings\"><\/span>How do I work with localized plural strings?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Currently, with our app, we display &#8220;Songs&#8221; even if the Sleep Album has just 1 &#8220;Song&#8221;. Plurals help to manage situations like this to make sure your app is following the right grammatical rules for every language. Xcode uses <code>.stringsdict<\/code> file to generate localizations for plurals, let&#8217;s create one so we can fix the pluralization issues in the app.<\/p>\n<p>Let&#8217;s create <code>.stringsdict<\/code> by going to <em>File \u279e New \u279e File<\/em> or just tap <code>Ctrl+N<\/code> or <code>Cmd+N<\/code>. Search for \u201cstringsdict\u201d\u00a0 and you will see <em>Strings File<\/em> in the <em>Resource<\/em> pane. Click on <em>Next<\/em> and follow the prompts to create a file.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38404 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/stringsdict-resource-window.png\" alt=\"Stringsdict resources window | Phrase\" width=\"729\" height=\"518\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/stringsdict-resource-window.png 729w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/stringsdict-resource-window-300x213.png 300w\" sizes=\"(max-width: 729px) 100vw, 729px\" \/><\/p>\n<p>Click on the newly created <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\" data-reactroot=\"\">Localizable.stringsdict<\/span> <\/code>file and click on <em><span class=\"notion-enable-hover\" data-token-index=\"3\" data-reactroot=\"\">Localize<\/span><\/em> in the <span class=\"notion-enable-hover\" data-token-index=\"5\" data-reactroot=\"\"><em>Inspector<\/em>. <\/span>On the alert that appears, click <em><span class=\"notion-enable-hover\" data-token-index=\"7\" data-reactroot=\"\">Localize<\/span><\/em> to create the localization file for English. In the <em><span class=\"notion-enable-hover\" data-token-index=\"9\" data-reactroot=\"\">Inspector<\/span><\/em>, check all the other supported localizations for your application under the <em><span class=\"notion-enable-hover\" data-token-index=\"11\" data-reactroot=\"\">Localization<\/span><\/em> section to create localization strings for those languages.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\">&lt;!-- <span class=\"hljs-type\">Localizable<\/span>(<span class=\"hljs-type\">English<\/span>).stringsdict --&gt;\n\n&lt;?xml version=<span class=\"hljs-string\">\"1.0\"<\/span> encoding=<span class=\"hljs-string\">\"UTF-8\"<\/span>?&gt;\n&lt;!<span class=\"hljs-type\">DOCTYPE<\/span> plist <span class=\"hljs-type\">PUBLIC<\/span> <span class=\"hljs-string\">\"-\/\/Apple\/\/DTD PLIST 1.0\/\/EN\"<\/span> <span class=\"hljs-string\">\"http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd\"<\/span>&gt;\n&lt;plist version=<span class=\"hljs-string\">\"1.0\"<\/span>&gt;\n&lt;dict&gt;\n\t&lt;key&gt;songs&lt;\/key&gt; &lt;!-- <span class=\"hljs-type\">Localized<\/span> <span class=\"hljs-type\">String<\/span> <span class=\"hljs-type\">Key<\/span> --&gt;\n\t&lt;dict&gt;\n\t\t&lt;key&gt;<span class=\"hljs-type\">NSStringLocalizedFormatKey<\/span>&lt;\/key&gt;\n\t\t&lt;string&gt;%#@songsCount@&lt;\/string&gt;\n\t\t&lt;key&gt;songsCount&lt;\/key&gt; &lt;!-- <span class=\"hljs-type\">Variable<\/span> --&gt;\n\t\t&lt;dict&gt;\n\t\t\t&lt;key&gt;<span class=\"hljs-type\">NSStringFormatSpecTypeKey<\/span>&lt;\/key&gt;\n\t\t\t&lt;string&gt;<span class=\"hljs-type\">NSStringPluralRuleType<\/span>&lt;\/string&gt;\n\t\t\t&lt;key&gt;<span class=\"hljs-type\">NSStringFormatValueTypeKey<\/span>&lt;\/key&gt;\n\t\t\t&lt;string&gt;d&lt;\/string&gt;\n\t\t\t&lt;key&gt;zero&lt;\/key&gt;\n\t\t\t&lt;string&gt;<span class=\"hljs-type\">No<\/span> <span class=\"hljs-type\">Songs<\/span>&lt;\/string&gt;\n\t\t\t&lt;key&gt;one&lt;\/key&gt;\n\t\t\t&lt;string&gt;%d <span class=\"hljs-type\">Song<\/span>&lt;\/string&gt;\n\t\t\t&lt;key&gt;other&lt;\/key&gt;\n\t\t\t&lt;string&gt;%d <span class=\"hljs-type\">Songs<\/span>&lt;\/string&gt;\n\t\t&lt;\/dict&gt;\n\t&lt;\/dict&gt;\n&lt;\/dict&gt;\n&lt;\/plist&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_0716bf66846e1158199390686eff5e2a\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Let&#8217;s go through it step by step.<\/p>\n<ul>\n<li><strong>Localized String Key<\/strong> : the key to be used in NSLocalizedString.<\/li>\n<li><code>NSStringLocalizedFormatKey<\/code>\n<ul>\n<li>The text to be localized ie. <code>%#@songsCount@<\/code>.<\/li>\n<li>Variables should be defined in the format, <code>%#@<\/code> and followed by <code>@<\/code>.<\/li>\n<li>Only define variables for parameters where there are plurals.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Variable<\/strong> : the rules that apply to a specific variable. You create one for every variable you defined in <code>NSStringLocalizedFormatKey<\/code> ie. <code>songsCount<\/code>.<\/li>\n<li><code>NSStringFormatSpecTypeKey<\/code> : the rule for processing the parameter. It&#8217;s already set to <code>NSStringPluralRuleType<\/code> so we can leave it as is.<\/li>\n<li><code>NSStringFormatValueTypeKey<\/code> : the format of the parameter like <em>d<\/em> for integer or <em>f<\/em> for double. For the full list, you can look into <a href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/CoreFoundation\/Conceptual\/CFStrings\/formatSpecifiers.html#\/\/apple_ref\/doc\/uid\/TP40004265\">Apple&#8217;s string format specifiers<\/a>.<\/li>\n<li><strong>CLDR Language Plural Rules<\/strong> : You can choose different plural rules like <code>zero<\/code>, <code>one<\/code>, <code>two<\/code>, <code>few<\/code>, <code>many<\/code>, and <code>other<\/code>. For some languages like English, you only need to configure two rules, <code>one<\/code> and <code>other<\/code>. Other languages have only a single plural rule and some languages have more than two. A detailed guide is provided by <a href=\"https:\/\/unicode-org.github.io\/cldr-staging\/charts\/latest\/supplemental\/language_plural_rules.html\">here<\/a>.<\/li>\n<\/ul>\n<p>Let&#8217;s make changes to our code to properly format plurals. Great news: We don&#8217;t need to create a new extension function to handle this, <code>localizeFormat<\/code> already handles plurals.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\">someLabel.text = <span class=\"hljs-string\">\"songs\"<\/span>.localizeFormat(args: &#91;<span class=\"hljs-number\">1<\/span>])<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5cf44a6e7e8bccbd5d527103178b08d6\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38409\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/before-pluralization.jpg\" alt=\"Before pluralization | Phrase\" width=\"349\" height=\"756\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/before-pluralization.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/before-pluralization-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/before-pluralization-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/before-pluralization-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/before-pluralization-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/before-pluralization-946x2048.jpg 946w\" sizes=\"(max-width: 349px) 100vw, 349px\" \/><\/p>\n<table style=\"width: 100%; border-collapse: collapse; border-style: hidden;\">\n<tbody>\n<tr>\n<td style=\"width: 50%;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38414 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/english-pluralization.jpg\" alt=\"English pluralization | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/english-pluralization.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/english-pluralization-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/english-pluralization-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/english-pluralization-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/english-pluralization-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/english-pluralization-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38419 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-pluralization.jpg\" alt=\"Hebrew pluralization | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-pluralization.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-pluralization-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-pluralization-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-pluralization-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-pluralization-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-pluralization-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p style=\"text-align: center;\"><em>The song text before and after pluralization<\/em><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-numbers\"><\/span>How do I localize numbers?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Formatting numbers is one aspect of localization that is often forgotten, as not all languages format numbers the same way. iOS provides a <code>NumberFormatter<\/code> that takes care of handling formatting for different locales. What has been missing from our app is displaying the price of the albums and we&#8217;ll use this as an opportunity to utilize <code>NumberFormatter<\/code>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nSleepDetailViewController.swift\n*\/<\/span>\n<span class=\"hljs-keyword\">let<\/span> numberFormatter = <span class=\"hljs-type\">NumberFormatter<\/span>()\nnumberFormatter.numberStyle = .currency\npriceButton.setTitle(numberFormatter.string(from: <span class=\"hljs-number\">99.99<\/span>), <span class=\"hljs-keyword\">for<\/span>: .normal)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_fd24d1f5185ced0812a56c13df07d760\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Run the application with App Language as Hebrew and App Region as Israel.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38442 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/number-hebrew-formatting.png\" alt=\"Number hebrew formatting | Phrase\" width=\"450\" height=\"66\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/number-hebrew-formatting.png 450w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/number-hebrew-formatting-300x44.png 300w\" sizes=\"(max-width: 450px) 100vw, 450px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38447\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-price.jpg\" alt=\"Hebrew price | Phrase\" width=\"349\" height=\"756\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-price.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-price-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-price-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-price-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-price-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-price-946x2048.jpg 946w\" sizes=\"(max-width: 349px) 100vw, 349px\" \/><\/p>\n<p>Another common use case for number formatting is for decimals. The <code>numberFormatter.numberStyle = .currency<\/code> format style correctly displays numbers to fit the locale of the user, e.g. 123456789 will be $123,456,789.00 for English, US (<code>en-US<\/code>) and 1.234.567.899,00 for Spanish, Spain (<code>es-ES<\/code>).<\/p>\n<p>\u270b<em>Heads up \u00bb<\/em> Number formats are region specific. The user\u2019s locale is used by default but you can specify the locale of a number with <code>numberFormatter.locale = Locale(identifier: \"en-US\")<\/code> to avoid any surprises, e.g. using 123456789 will be $123,456,789.00 for <code>en-US<\/code> and \u20b91,234,567,899.00 for <code>en-IN<\/code>.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> <code>NumberFormatter<\/code> can do a lot more than just currency localization, check the full list of styles from the <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/numberformatter\/style\">documentation<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-dates\"><\/span>How do I localize dates?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It is good practice to show dates in the locale of your users. The <code>DateFormatter<\/code> class makes this easy. Let&#8217;s show the current date whenever an album is opened.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> <code>DateFormatter<\/code> has a lot more to offer, go through the <a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/data_formatting\">documentation<\/a> to delve deeper.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\n\tSleepDetailViewController.swift\n*\/<\/span>\n\n<span class=\"hljs-keyword\">let<\/span> dateformatter = <span class=\"hljs-type\">DateFormatter<\/span>()\ndateformatter.dateStyle = .long\ndateLabel.text = dateformatter.string(from: <span class=\"hljs-type\">Date<\/span>())<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_718eca06c64e27d95917a4d89d671ccf\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<table style=\"width: 100%; border-collapse: collapse; border-style: hidden;\">\n<tbody>\n<tr>\n<td style=\"width: 50%;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38457 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-date-format.jpg\" alt=\"Hebrew date format | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-date-format.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-date-format-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-date-format-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-date-format-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-date-format-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/hebrew-date-format-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38463 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spanish-date-format.jpg\" alt=\"Spanish date format | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spanish-date-format.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spanish-date-format-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spanish-date-format-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spanish-date-format-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spanish-date-format-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/spanish-date-format-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We&#8217;ve learned how to localize our application while addressing the most common issues with localization. Let&#8217;s now shift our attention to how to do localization with SwiftUI.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-my-swiftui-views\"><\/span>How do I localize my SwiftUI views?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>SwiftUI is a declarative modern framework that helps build applications on all Apple platforms. SwiftUI has great localization support and requires less effort. <code>Text<\/code>, the most common SwiftUI view already comes pre-built with support for <code>LocalizedStringKey<\/code>, which means passing the key of your localized strings will just work. The setup for localization is the same as in UIKit, so go ahead and follow the same steps to set up.<\/p>\n<p>Let&#8217;s use a common view in SwiftUI, <code>Text<\/code>, to demonstrate this. Let&#8217;s copy the storyboard localization strings into <code>Localizable.strings<\/code> since there is no storyboard in SwiftUI.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nLocalizable.strings\n*\/<\/span>\n<span class=\"hljs-string\">\"list.of.songs\"<\/span> = <span class=\"hljs-string\">\"LIST OF SONGS\"<\/span>;\n<span class=\"hljs-string\">\"about.this.pack\"<\/span> = <span class=\"hljs-string\">\"About this pack\"<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nSleepDetailView.swift\n*\/<\/span>\n\n<span class=\"hljs-type\">Text<\/span>(<span class=\"hljs-string\">\"about.this.pack\"<\/span>)\n    .font(.title3)\n    .fontWeight(.semibold)\n    .foregroundColor(.white)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_903ada02aea655d640135900d4304747\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<table style=\"width: 100%; border-collapse: collapse; border-style: hidden;\">\n<tbody>\n<tr>\n<td style=\"width: 50%;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38474 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Hebrew-about-pack.jpg\" alt=\"SwiftUI Hebrew about pack | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Hebrew-about-pack.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Hebrew-about-pack-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Hebrew-about-pack-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Hebrew-about-pack-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Hebrew-about-pack-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Hebrew-about-pack-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<td style=\"width: 50%; border-style: hidden;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-38479 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Spannish-about-pack.jpg\" alt=\"SwiftUI Spanish about pack | Phrase\" width=\"1170\" height=\"2532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Spannish-about-pack.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Spannish-about-pack-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Spannish-about-pack-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Spannish-about-pack-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Spannish-about-pack-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-Spannish-about-pack-946x2048.jpg 946w\" sizes=\"(max-width: 1170px) 100vw, 1170px\" \/><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>About pack is localized in both Hebrew and Spanish by just passing the key of the localized string.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"swiftui-translated-previews\"><\/span>SwiftUI Translated Previews<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>With the power of SwiftUI previews, you can preview localization by indicating the locale as an environment. Previews show by default in Xcode but if yours isn\u2019t showing, the shortcut, <em>Option + Command + Return<\/em> can be used to toggle it on and off.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nSleepDetailView.swift\n*\/<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">struct<\/span> <span class=\"hljs-title\">SleepDetailView_Previews<\/span>: <span class=\"hljs-title\">PreviewProvider<\/span> <\/span>{\n    <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">var<\/span> previews: some <span class=\"hljs-type\">View<\/span> {\n        <span class=\"hljs-type\">Group<\/span> {\n         <span class=\"hljs-type\">SleepDetailView<\/span>(sleeper: <span class=\"hljs-type\">SleepData<\/span>.list.first!)\n            .environment(\\.locale, .<span class=\"hljs-keyword\">init<\/span>(identifier: <span class=\"hljs-string\">\"en\"<\/span>))\n\n         <span class=\"hljs-type\">SleepDetailView<\/span>(sleeper: <span class=\"hljs-type\">SleepData<\/span>.list.first!)\n            .environment(\\.locale, .<span class=\"hljs-keyword\">init<\/span>(identifier: <span class=\"hljs-string\">\"he\"<\/span>))\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3d1dd50010ea19893be9729691162efa\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38484 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/localized-previews.png\" alt=\"Localized previews | Phrase\" width=\"271\" height=\"929\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Interpolation can be achieved by making a few changes to our <code>Localizable.strings<\/code>, the key of our strings needs to contain specifiers when we are localizing for SwiftUI.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/\/ UIKit<\/span>\n<span class=\"hljs-string\">\"hello.user\"<\/span> = <span class=\"hljs-string\">\"Hello %@\"<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9e8d201338087fa15e82dbf7864e40d0\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u2026becomes\u2026<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/\/ SwiftUI<\/span>\n<span class=\"hljs-string\">\"hello.user %@\"<\/span> = <span class=\"hljs-string\">\"Hello %@\"<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_4056ccd569e32f1af98e7873afa26cd7\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We can then use this in our views.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-keyword\">let<\/span> user = <span class=\"hljs-string\">\"Conan\"<\/span>\n\n<span class=\"hljs-type\">Text<\/span>(<span class=\"hljs-string\">\"hello.user \\(user)\"<\/span>) <span class=\"hljs-comment\">\/\/ Hello Conan<\/span>\n.foregroundColor(.white.opacity(<span class=\"hljs-number\">0.7<\/span>))\n.font(.subheadline)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9d5f38d7b0a5f4a10ee5ac5d1315512e\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"plurals\"><\/span>Plurals<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Plurals in SwiftUI follow the same pattern as it was in UIKit, by using <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\" data-reactroot=\"\">Localizable.stringsdict<\/span><\/code>. The change with SwiftUI is that we have to add the specifiers to the <strong><span class=\"discussion-id-2bd6a849-7716-4633-8710-b2865868aab5 notion-enable-hover\" data-token-index=\"3\" data-reactroot=\"\">Localized String Key<\/span><\/strong>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\">&lt;?xml version=<span class=\"hljs-string\">\"1.0\"<\/span> encoding=<span class=\"hljs-string\">\"UTF-8\"<\/span>?&gt;\n&lt;!<span class=\"hljs-type\">DOCTYPE<\/span> plist <span class=\"hljs-type\">PUBLIC<\/span> <span class=\"hljs-string\">\"-\/\/Apple\/\/DTD PLIST 1.0\/\/EN\"<\/span> <span class=\"hljs-string\">\"http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd\"<\/span>&gt;\n&lt;plist version=<span class=\"hljs-string\">\"1.0\"<\/span>&gt;\n&lt;dict&gt;\n\t&lt;key&gt;songs %d&lt;\/key&gt; &lt;!-- <span class=\"hljs-type\">Localized<\/span> <span class=\"hljs-type\">String<\/span> <span class=\"hljs-type\">Key<\/span> --&gt;\n\t&lt;dict&gt;\n\t\t&lt;key&gt;<span class=\"hljs-type\">NSStringLocalizedFormatKey<\/span>&lt;\/key&gt;\n\t\t&lt;string&gt;%#@songsCount@&lt;\/string&gt;\n\t\t&lt;key&gt;songsCount&lt;\/key&gt;\n\t\t&lt;dict&gt;\n\t\t\t&lt;key&gt;<span class=\"hljs-type\">NSStringFormatSpecTypeKey<\/span>&lt;\/key&gt;\n\t\t\t&lt;string&gt;<span class=\"hljs-type\">NSStringPluralRuleType<\/span>&lt;\/string&gt;\n\t\t\t&lt;key&gt;<span class=\"hljs-type\">NSStringFormatValueTypeKey<\/span>&lt;\/key&gt;\n\t\t\t&lt;string&gt;d&lt;\/string&gt;\n\t\t\t&lt;key&gt;zero&lt;\/key&gt;\n\t\t\t&lt;string&gt;<span class=\"hljs-type\">No<\/span> <span class=\"hljs-type\">Songs<\/span>&lt;\/string&gt;\n\t\t\t&lt;key&gt;one&lt;\/key&gt;\n\t\t\t&lt;string&gt;%d <span class=\"hljs-type\">Song<\/span>&lt;\/string&gt;\n\t\t\t&lt;key&gt;other&lt;\/key&gt;\n\t\t\t&lt;string&gt;%d <span class=\"hljs-type\">Songs<\/span>&lt;\/string&gt;\n\t\t&lt;\/dict&gt;\n\t&lt;\/dict&gt;\n&lt;\/dict&gt;\n&lt;\/plist&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nSleeperView.swift\n*\/<\/span>\n\n<span class=\"hljs-type\">Text<\/span>(<span class=\"hljs-string\">\"\\(5) songs\"<\/span>)\n    .foregroundColor(.white.opacity(<span class=\"hljs-number\">0.7<\/span>))\n    .font(.subheadline)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_1844058dab2d258200c9a2a312d180ad\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-38492\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-plurals.jpg\" alt=\"SwiftUI plurals | Phrase\" width=\"349\" height=\"756\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-plurals.jpg 1170w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-plurals-139x300.jpg 139w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-plurals-473x1024.jpg 473w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-plurals-768x1662.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-plurals-710x1536.jpg 710w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/SwiftUI-plurals-946x2048.jpg 946w\" sizes=\"(max-width: 349px) 100vw, 349px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"combining-plurals-and-interpolation\"><\/span>Combining plurals and interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Things might not always be straight forward when building your applications. There can be various combinations of plurals, interpolation, etc. all mixed. Let&#8217;s take a look at combining plurals and interpolation.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\">&lt;!-- <span class=\"hljs-type\">Localizable<\/span>(<span class=\"hljs-type\">English<\/span>).stringsdict --&gt;\n\n&lt;?xml version=<span class=\"hljs-string\">\"1.0\"<\/span> encoding=<span class=\"hljs-string\">\"UTF-8\"<\/span>?&gt;\n&lt;!<span class=\"hljs-type\">DOCTYPE<\/span> plist <span class=\"hljs-type\">PUBLIC<\/span> <span class=\"hljs-string\">\"-\/\/Apple\/\/DTD PLIST 1.0\/\/EN\"<\/span> <span class=\"hljs-string\">\"http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd\"<\/span>&gt;\n&lt;plist version=<span class=\"hljs-string\">\"1.0\"<\/span>&gt;\n&lt;dict&gt;\n    &lt;key&gt;%lld songs %@&lt;\/key&gt;\n    &lt;dict&gt;\n        &lt;key&gt;<span class=\"hljs-type\">NSStringLocalizedFormatKey<\/span>&lt;\/key&gt;\n        &lt;string&gt;%#@songsCount@&lt;\/string&gt;\n        &lt;key&gt;songsCount&lt;\/key&gt;\n        &lt;dict&gt;\n            &lt;key&gt;<span class=\"hljs-type\">NSStringFormatSpecTypeKey<\/span>&lt;\/key&gt;\n            &lt;string&gt;<span class=\"hljs-type\">NSStringPluralRuleType<\/span>&lt;\/string&gt;\n            &lt;key&gt;<span class=\"hljs-type\">NSStringFormatValueTypeKey<\/span>&lt;\/key&gt;\n            &lt;string&gt;d&lt;\/string&gt;\n            &lt;key&gt;zero&lt;\/key&gt;\n            &lt;string&gt;<span class=\"hljs-type\">No<\/span> <span class=\"hljs-type\">Songs<\/span> \u2022 %@&lt;\/string&gt;\n            &lt;key&gt;one&lt;\/key&gt;\n            &lt;string&gt;%d <span class=\"hljs-type\">Song<\/span> \u2022 %@&lt;\/string&gt;\n            &lt;key&gt;other&lt;\/key&gt;\n            &lt;string&gt;%d <span class=\"hljs-type\">Songs<\/span> \u2022 %@&lt;\/string&gt;\n        &lt;\/dict&gt;\n    &lt;\/dict&gt;\n&lt;\/dict&gt;\n&lt;\/plist&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nSleepDetailView.swift\n*\/<\/span>\n\n<span class=\"hljs-type\">Text<\/span>(<span class=\"hljs-string\">\"\\(sleeper.numberOfSongs) songs \u2022 \\(sleeper.category)\"<\/span>)\n    .foregroundColor(.white.opacity(<span class=\"hljs-number\">0.7<\/span>))\n    .font(.subheadline)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_0047846265936a62044766f4bd2dfa17\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"adding-comments\"><\/span>Adding comments<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>We&#8217;ve already seen the importance of adding comments when localizing strings. Let\u2019s continue with our best practices here in SwiftUI.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-type\">Text<\/span>(<span class=\"hljs-string\">\"sleepy\"<\/span>, comment: <span class=\"hljs-string\">\"Navigation title\"<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_d8f609b32dce9418a94218a7420cc99a\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"add-localization-strings-for-non-text-views\"><\/span>Add localization strings for non-text views<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>For anything other than a text view, initialize the view with text views or add strings for your app to provide localized views. You can localize other views, such as <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/label\">Label<\/a> or <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/picker\">Picker<\/a>, as they accept a <code>LocalizedStringKey<\/code>. The easiest way is to pass <code>Text<\/code> into views that accept it or use the <code>localize()<\/code> extension function we&#8217;ve been using in UIKit.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-string\">\"sleep.points\"<\/span> = <span class=\"hljs-string\">\"%1lu Sleep Points\"<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\">navigationTitle(<span class=\"hljs-type\">Text<\/span>(<span class=\"hljs-string\">\"\\(3000) sleep.points\"<\/span>))\n<span class=\"hljs-comment\">\/\/ =&gt; 3000 Sleep Points<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\">navigationTitle(<span class=\"hljs-string\">\"sleep.points\"<\/span>.localizeFormat(args: &#91;<span class=\"hljs-number\">3000<\/span>]))\n<span class=\"hljs-comment\">\/\/ =&gt; 3000 Sleep Points<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8d615a2be2674b414238f294cee3c90b\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"programmatic-language-switching-2\"><\/span>Programmatic language switching<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>We will utilize the same functions from our UIKit example to achieve this in SwiftUI.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"Swift\" data-shcb-language-slug=\"swift\"><span><code class=\"hljs language-swift\"><span class=\"hljs-comment\">\/*\nContentView.swift\n*\/<\/span>\n...\n.alert(<span class=\"hljs-string\">\"changing.language\"<\/span>, isPresented: $showRestartApplication, actions: {\n  <span class=\"hljs-type\">Button<\/span>(<span class=\"hljs-string\">\"exit.sleepy\"<\/span>) {\n      <span class=\"hljs-type\">UserDefaults<\/span>.standard.<span class=\"hljs-keyword\">set<\/span>(&#91;selectedLanguage.code], forKey: <span class=\"hljs-string\">\"AppleLanguages\"<\/span>)\n      <span class=\"hljs-type\">UserDefaults<\/span>.standard.synchronize()\n      <span class=\"hljs-keyword\">self<\/span>.closeApplicationWithNotification(language: selectedLanguage.name)\n  }\n})<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Swift<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">swift<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8c35553050e11f472b7447afc1b7e732\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<div style=\"width: 435px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-10891-2\" width=\"435\" height=\"887\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/sleepy-ios-app-swiftui-language-change.mp4?_=2\" \/><a href=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/sleepy-ios-app-swiftui-language-change.mp4\">https:\/\/phrase.com\/wp-content\/uploads\/2023\/01\/sleepy-ios-app-swiftui-language-change.mp4<\/a><\/video><\/div>\n<h2><span class=\"ez-toc-section\" id=\"wrapping-up\"><\/span>Wrapping up<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Now we are ready to share our wonderful sleep albums with the world. We have learned how to achieve industry-standard levels of localization with both UIKit and SwiftUI. We&#8217;ve gone through regular scenarios and complex ones. With this, your applications are ready to conquer international markets.<\/p>\n<p>As easy as this is, <a href=\"https:\/\/phrase.com\/platform\/strings\/\">Phrase Strings<\/a> can even make it easier. A specialized software localization platform, Phrase Strings has a powerful web admin console that helps both translators and developers collaborate in the cloud. When you <a href=\"https:\/\/phrase.com\/blog\/posts\/ios-over-the-air-translation-with-phrase\/\">localize iOS apps with Phrase Strings<\/a>, you can connect to a robust API and a growing number of native integrations with your favorite tools to stay focused on your code.<\/p>\n<p>Check out all of <a href=\"https:\/\/phrase.com\/solutions\/\">Phrase&#8217;s features<\/a>, and sign up for a <a href=\"https:\/\/eu.phrase.com\/idm-ui\/signup\">free 14-day trial<\/a>.<\/p>\n<p>If you want to learn even more about iOS internationalization, feel free to check out the following articles:<\/p>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/mobile-app-localization-why-and-how\/\">How to Make the Most of Mobile App Localization<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/ios-automate-ios-storyboard-localization\/\">Automating iOS Storyboard Localization<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/app-localization-the-ultimate-guide-to-app-store-optimization\/\">Implementing App Store Localization<\/a><\/li>\n<\/ul>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Get to know the ins and outs of iOS localization and how to make your app ready for a global user base step by step, including a demo app example.<\/p>\n","protected":false},"author":6,"featured_media":38849,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":false,"_modified_date":"","_searchwp_excluded":"","footnotes":""},"categories":[40],"class_list":["post-10891","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-localization"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/10891"}],"collection":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/comments?post=10891"}],"version-history":[{"count":17,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/10891\/revisions"}],"predecessor-version":[{"id":65200,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/10891\/revisions\/65200"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media\/38849"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media?parent=10891"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/categories?post=10891"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}