SwiftUI: Localization on the Fly

Anton Savinov
The Startup
Published in
5 min readJan 31, 2021

Hi everybody. Today I would like to share my experience with localization on the fly for iOS application developed on SwiftUI. The reason to write this article is my own application where I applied this approach. From my point of view, this approach seems concise and can be applied to other applications as well.

Prons:

  • The application controls language that user will see.
  • The language is saved between app launches.

Cons:

Let’s start.

Imagine we have to localize our application into 3 languages:

  • English (US)
  • Russian
  • Spanish

For that we will create the corresponding files for localization:

1. Go to localization menu: Project -> Info -> Localizations

Here we will see English (US) is the base localization. Let’s tap by “+” button to add Russian and Spanish.

2. Create Localizable.strings: Files -> New -> File -> Resource -> Strings file.

3. Find Inspector Panel and tap by “localize…” button to localize the current file.

At the moment, Xcode will create separate language folder for that file, i.e., “en.lproj” for english, “es.lproj” for spanish (“es”) and so on. The localized file is then put into these folders.

4. Let’s add two more localization files by checking the corresponding checkboxes in Inspector Panel. After checking the additional language, you can expand the Localizable.strings and see there’s multiple language version.

Ok. Our localizable files are ready. The next step is to create the business logic.

Let’s create “Language” enumeration that defines three languages for localization.

If we wanna localize text dynamically (here I mean text that defines in the source code), then we need to create the next String extension.

I guess you already saw something similar. Here we are interested in bundle argument that provides an opportunity to navigate through resources. Bundle is a mechanism for accessing application resources, that is, we can independently determine the source of resources, in our case, strings. I would like to notice Localizable.strings is the default string resource file. In other words, if we don’t specify the tableName parameter, then the application will take strings from Localizable.strings by default. But in our case, we define the bundle by ourselves using a path to localization strings that are kept in the corresponding lproj folders.

The next step is to implement LocalizationService that will do the next things:

  1. Provide the current language.
  2. Save the language in UserDefaults to keep chosen language between app launches.
  3. Send a notification in case the new language was selected.

Before creating our SettingsView, let’s put localized strings into the corresponding files:

  • Localizable.strings (English):
"settings_language" = "Language";
"settings_language_footer" = "Choose interface language."
  • Localizable.strings (Russian):
"settings_language" = "Язык";
"settings_language_footer" = "Выберите язык интерфейса.";
  • Localizable.strings (Spanish):
"settings_language" = "Idioma";
"settings_language_footer" = "Elija el idioma de la interfaz.";

My dear friends from Spain. I’m sorry but I used translate.google.com to localize strings on Spanish. If there is something wrong, please, let me know =)

Ok, we smoothly moved to make the user interface. Let’s draw SettingsView.

Step #1: We defined language variable as @AppStorage. It will reflect value from LocalizationService (i.e., from UserDefaults) and invalidate SettingsView on a change in value in that UserDefaults.

Step #2: For every Text view we would like to see localized we have to call method .localized() for a given string and pass there the current language.

Step #3. Every time we selected the new language from the menu, the new one will be saved into UserDefaults. Since the value in UserDefaults was updated by key for which @AppStorage effectively watches the last one will refresh body our SettingsView.

Now let’s put two more strings into our localizable files.

  • Localizable.strings (English):
"hello_world" = "Hello everyone!!!";
"this_text_localised" = "This text was localized on the fly as well.";
  • Localizable.strings (Russian):
"hello_world" = "Всем привет!!!";
"this_text_localised" = "Этот текст также локализован на лету";
  • Localizable.strings (Spanish):
"hello_world" = "Hola a todos !!!";
"this_text_localised" = "Este texto también se localizó sobre la marcha.";

And then create one more view. I called it as LocalizationResultView.

Here we did almost the same things that we did for SettingsView excluding we don’t change the language here. Since we defined the language variable as @AppStorage property wrapper, then this view will be re-drawn automatically if the language is changed.

Cool, right? Not so many steps to achieve the result.

The last thing I would like to describe briefly -> what will we need to do in case our view models depend on localization? Do you remember LocalizationService sends a notification every time the new language is available? Right, we will add an observer. I will put an example:

I guess the main idea for localization view models is clear here but it depends on what kind of tasks you, my friends, try to manage.

I suppose this article will help you localize your application developed on SwiftUI. I put the source code for this article in my repo.

Good luck!

--

--