Inclusivity with Voice & Language

May 30 2025 · Swift 5.9, iOS 17, Xcode 15.3

Lesson 03: Localization for a Global Audience

Demo: Localization for a Global Audience

Episode complete

Play next episode

Next
Transcript

In this demo, you’ll create a string catalog and use it to fix some user-facing text in an app. Then, you’ll create a French version of the app. If you’re following along, download the course materials, start Xcode and open the ClothesPicker app in the 03-localization-for-a-global-audience/Starter folder.

The preview uses an iPhone 15 simulator.

To keep it simple, this app contains only the item view for a larger app that lets users buy clothes. The item here is Shirt, and the user can tap one of the shirt images to select a color.

Tapping a button sets the colorChoice State property to that color. A stepper appears, to let the user buy more than one shirt.

“2 blue shirt” doesn’t sound right. The item name should be shirts when quantity is greater than 1. One way to fix this is to define a computed property that adds the “s”:

var itemName: String {
  if quantity > 1 {
    return "\(item.name.lowercased())" + "s"
  } else {
    return "\(item.name.lowercased())"
  }
}

Then, use itemName in the stepper label:

Text("\(quantity) \(colorChoice) \(itemName)")

This works: When you increase quantity, you get shirts.

And when you decrease quantity to 1, you get shirt.

You know this demo’s going to show you an easier way. Before that, notice this other wording that you might want to fix.

“Tap” is what users do on a device with a touch screen. If this app were running on a Mac or Apple TV, the user should be told to “click”. Again, you could write code to detect the device and change the instruction, but there’s an easier way — you knew I was going to say that.

First, get rid of the itemName code.

Localizable strings can help you even in your app’s base language. Start by creating a String Catalog: Create a new file, search for “string”, select “String Catalog”, and leave the file name as Localizable.

Now, take a look at your new file. You always get a string catalog for the base language, which is English for this app. There’s nothing in it yet — you need to build the app. But first, look at the app’s Build Settings.

To get the full benefits of automation, make sure all these are set to Yes, then return to Localizable and build the app.

To see what’s happened, hold the Option key while opening ContentView so it opens in an assistant editor alongside Localizable.

Xcode has picked out the literal strings in the Text views, with placeholders for item.name, quantity and colorChoice. The quantity placeholder %lld is the string format specifier for Int.

You can add a comment to a Text view, either to help the translator or as a reminder to yourself:

Text("This is a man's \(item.name.lowercased()) that is never worn as well as by a woman.",
     comment: "Description of item, same for all items")
Text("...tall. He wears ...", comment: "Description of model")
Text("Tap to select the color of your \(item.name.lowercased()):",
     comment: "Instruction to user")
Text("\(quantity) \(colorChoice) \(item.name.lowercased())",
     comment: "Displays [number] of [color] [item]")

Build the app.

And here are the comments you added.

Now, start by fixing the singular/plural problem in the stepper label. Right-click it and select Vary by Plural.

In English, a noun has at most one plural form — regular plurals add “s” or “es”, but some nouns change their ending, such as “woman” and “women”, and some nouns are their own plural, like “sheep”. For most items of clothing, adding an “s” works, and it’s as easy as this:

Because you’ve added a variation, this string has state NEEDS REVIEW. Right-click the badge on One and select Mark as Reviewed.

Now, build the project then refresh the preview to see if it works:

And, yes, now the label handles regular plurals correctly! String catalog plural variation works best with static nouns, where you can specify the correct plural form. One way to handle variable nouns is to add a namePlural property to Item, and then use namePlural in the computed property itemName:

var itemName: String {
  if quantity > 1 {
    return "\(item.namePlural)"
  } else {
    return "\(item.name.lowercased())"
  }
}

In the next lesson, you’ll learn yet another way to handle pluralization. Spoiler: You need to work with AttributedString and markdown.

Your next task is to fix the tap-or-click wording. Right-click the key and select Vary by Device. By the way, notice the Vary by Plural option isn’t available here because there’s no number argument in the string.

What you want is to specify “click” for Mac and leave “tap” for the touch-screen devices. But it’s easier to see the effect on an iPad simulator, so select iPad and change “Tap” to “Click”.

Now, build the project then build and run it in an iPad simulator to see if it works.

Yes, it says “Click” instead of “Tap”! Stop the app and close the assistant editor.

French Version

And finally, the main attraction! It’s time to add another language to the app!

Click the + button down here in the lower left corner. Select French.

You get a new string catalog with all the strings from the English string catalog. The “0%” means you haven’t created any French strings yet. Start with the stepper label, which has no static English text: Copy and paste the two variants as suggested.


Next, copy and paste the model description, so you don’t have to type all the placeholders, and edit the English into French:

🏃🏻‍➡️%1$@ mesure %2$lld cm. Il porte une taille %3$@.

Using your favorite translator, enter these French translations of the tap-or-click instructions:

Cliquez pour sélectionner la couleur de votre %@:

and

Appuyez pour sélectionner la couleur de votre %@:

Then translate the item description, which is actually the English translation of some text from a French clothing site. Enter this as the French text:

C'est un %@ d'homme qui n'est jamais aussi bien porté que par une femme.

French nouns are masculine or feminine, so “un” should sometimes be “une”, and “porté” should sometimes be “portée” with a second “e”. You’ll take care of this in the next lesson.

Notice these green check marks next to your completed translations and that the percentage here has gone up. Press Command-B to build the project.

Now, how to test this? You don’t need to run it on a device or even in a simulator. Simply edit the app’s scheme and, in the Options tab, set App Language to French. Then, open ContentView and refresh the preview.

There’s one minor problem and a few bigger ones. The tap instruction is longer in French, so move the multilineTextAlignment modifier to modify the whole VStack:

.multilineTextAlignment(.center)

It doesn’t have any effect in the iPhone 15 simulator, but it’ll help on smaller screens. Try it on an iPhone SE:

Now, you need to get “Shirt” and the colors translated into French. But they’re not even in the English string catalog. “Shirt” is in the Item initializer, and the colors are in the ColorButton initializers. No problem: String has a localized initializer! Edit “Shirt” and the colors to these String values:

String(localized: "Shirt", comment: "Item of clothing")
String(localized: "red", comment: "color")
String(localized: "blue", comment: "color")
String(localized: "green", comment: "color")
String(localized: "black", comment: "color")

You’re manually adding these strings to the string catalog and including comments to provide context to the translator.

In Localizable, select the English string catalog and build the app.

Good, the missing keys are now localizable and already listed in the French string catalog!

noir
bleu
vert
rouge
Chemise

French adjectives are also masculine or feminine to match the noun, and they also have a plural form. You’ll take care of that in the next lesson. If you don’t have a green tick next to French, mark the NEW flags as Reviewed.

Xcode thinks you’re done, but there’s actually one more problem. To see it, refresh the preview and tap a color.

In French, the noun comes before the adjective — the stepper label should be “1 chemise bleu”. Back to Localizable to fix this manually.

%1$lld %3$@ %2$@
%1$lld %3$@s %2$@

You swap the second and third arguments.

Now, check this in ContentView:

And it all works! You’ve succeeded in creating a French version of the app!

See forum comments
Cinema mode Download course materials from Github
Previous: Localization Techniques Next: Conclusion