Localizing your App Store marketing content with Localazy and Fastlane

Localizing your App Store marketing content with Localazy and Fastlane

I briefly talked about my setup in https://paraside.in/translating-your-ios-app-with-localazy/, but I think it deserves its own article given how annyoing dealing with this is (to me, at least).

With a combination of fastlane.tools and localazy.com, you can simplify your life a lot already.

Let's see how with a couple of tips.

🚧 Fair warning: it's still very much a work in process, I haven't gone through the whole process of adding a new language and uploading it to production. But the "downloading from App Store", "translating in Localazy" and "updating the metadata handled by fastlane" parts are all working.

What needs to be translated?

Your app page is one of the first point of contact with your (hopefully future) users, and even though you may have localized your whole app, if you don't spend the extra time localizing your marketing content, there's a chance you will scare away a good number of users.

Here are the different contents:

  • App name: you may or may not want to translate the name of your app. No Meat Today is my brand, but maybe I should consider changing it for some countries like China or Japan
  • Subtitle: this is really important since it's how people get to understand your app
  • Screenshots: these are a bit of a hassle to make in my case, I'll want to automate the creation at some point probably. But there is text, and even if I have to do some copy/pasting, centralizing it in Localazy is still helpful
  • Release notes: I try to actually explain what I do and avoid generic things like "Fixed bugs and made improvements". This changes with every version and is one of the reasons why the subscription including virtual translator credits start to make sense.
  • Promotional text: another kind of text that is likely to change a bit more often then the rest
  • Keywords: these will require instructions, at the minimum that one shouldn't use spaces (to optimize the number of characters)

fastlane setup

fastlane is an amazing tool that does many things, but I currently don't use it to handle my certificates, sign and ship my app, which is probably what it's most well-known for.

And you don't have to setup the whole thing either. In our case, what we care about is what's called Deliver.

It's pretty easy to setup fastlane, you can find:

I did try fastlane init swift but I've used good old ruby in the past and I was a bit lost so I deleted everything and ran a simple fastlane init.

You should end up with a fastlane subfolder, with an AppFile and a DeliverFile in it. For some reason, it didn't pick the proper app_identifier in my AppFile, so I had to change it manually.

Once this is done, you should end up with a new folder in your project that looks like this. I added it to Xcode with no Target membership so that I can handle everything from there.

Downloading from the App Store

If you need to redownload your data at some point, check the docs but basically you can call fastlane deliver download_metadata or fastlane deliver download_screenshots.

Uploading to the App Store

All this data can be edited and uploaded back to App Store connect by running fastlane deliver from the terminal.

As mentionned in the docs:

To get a list of available options run
fastlane action deliver

Xcode setup

Foreword: when I wrote the version version of this article, txt support wasn't available. Two days later, it was, and Václav Hodek (Localazy's Founder) helped me a configuration that made sense for me. Here I'm sharing it with you with some additional tips from my initial setup (the AppStore.strings).

OK, now that you have an easy way to download/upload your metadata from the App Store, how do we get it translated?

I use two things:

  • the txt files from fastlane
  • an AppStore.strings that isn't associated with any Xcode target (because we don't want it to be compiled in the app's binary)

The AppStore.strings

Initially, all content was inside this file, which means I would manually copy/paste the subtitle, description, release_notes, etc. As I explained in the intro, Localazy now supports txt files, but I left the initial .strings example file to

Here's what that AppStore.strings looks like:

/* Max 30 characters */
"AppStore.subtitle" = "Eat less meat: track, decide!";
/* Max 4000 characters */
"AppStore.description" = "# Exclusive to Apple #

You believe that eating less meat might be good for you, animals and our home planet.
But when you begin to reduce meat consumption, it’s hard to measure your effort and balance your diet.
I know, I’ve been there.

No Meat Today makes tracking your meatless meals in a fun & simple way, whether you want to progressively adopt a vegan diet or find a flexitarian balance that suits you.

--- A LOT MORE TEXT ---

*** Wait no more, become Naomist, adopt the milky way! ***";

/* 2 lines. Use ** to surround the words that should be highlighted */
"AppStore.screenshots.1.headline" = "Your companion to
*eat less meat*";
/* 2 lines */
"AppStore.screenshots.1.subheadline" = "Size your efforts by attracting
cows to your meatless planet";
/* 2 lines. Use ** to surround the words that should be highlighted */
"AppStore.screenshots.2.headline" = "What should your
*next meal* be?";
/* 2 lines */
"AppStore.screenshots.2.subheadline" = "Use your history to decide if
it's time to go meatless";
/* 2 lines */
"AppStore.screenshots.2.notes" = "I call that
\"Asking Naomi\"";
/* 2 lines. Use ** to surround the words that should be highlighted */
"AppStore.screenshots.3.headline" = "Find the balance
*that suits you*";
/* 2 lines */
"AppStore.screenshots.3.subheadline" = "Adjust your target meatless days,
your cows will leave if you go astray";
/* ⚠️ Do not translate "No Milk Today", it's the title of a song. 2 lines. Use ** to surround the words that should be highlighted */
"AppStore.screenshots.4.headline" = "No Milk Today. The vegan journey.";
/* 2 lines */
"AppStore.screenshots.4.subheadline" = "For those of you who want to give
up animal products entirely";
"AppStore.screenshots.4.notes" = "Vegetarians often have a hard time cutting out dairy. Instead of tracking your meatless days, use No Meat Today to track your dairyless days.";

A couple notes about this file:

  • all keys are prefixed with "AppStore" to identify them easily in Localazy
  • I copied the subtitle and description from the metadata (txt files) that fastlane downloaded. ⚠️ Note: these are no longer part of this file since I'm now using the fastlane configuration explained in the next section
  • for the subtitle and description, I added a comment about the max number of characters that will show up as translation notes. This is especially important for the subtitle since 30 characters comes pretty quickly.
  • I use the fact that .strings file support multiline for the description and for the screenshots
  • If you look at my first screenshot in the app page, you can see that the headline has 2 colors. This may not work in all languages, and since I need to manually copy/paste the texts anyway, I added some extra instructions about how to indicate which part of the text should be highlighted.

Of course, you need to change your Localazy configuration (the one I tell you all about here) to sync that AppStore.strings.

"upload": {
        "files": [
            {
                "type": "ios-strings",
                "pattern": "No Meat Today/Base.lproj/AppStore.strings",
                "path": "No Meat Today"
            },
            {
                "type": "ios-strings",
                "pattern": "No Meat Today/fr.lproj/AppStore.strings",
                "path": "No Meat Today",
                "lang": "fr"
            },
            …
        ]
 }

Uploading/Download text files to Localazy

First, some things that are not entirely obvious.

On the one hand, when you add a language to Xcode, the default choice is the first of the 3 panels below, which in most cases only offers ISO 639-1 2 letter codes without any notion of region.

You can still access the full catalog if you need to, but if you're like me, you only ever used the first panel.

As it turns out, when you localize your App Store marketing content, only the equivalent of that first panel is available, and there's no way to access the other two.

But what happens when you download your metadata with fastlane? It adds the default region code to the folder as you can see below.

And now we have a problem. 😣

Indeed, in my case, Localazy knows about my "en" and "fr" languages, but when I will try to download the files back, it won't have a way to know that the files should be written using "en-US" and "fr-FR" in the path, and will simply use "en" and "fr".

The configuration

Fortunately, Václav Hodek (Localazy's Founder) came to my rescue and saved me a lot of doc-reading time by suggesting the following configuration.

"download": {
        "files": [
            {
              "conditions": [["!startsWith: fastlane, ${path}"]],
              "output": "${path}/${iosLprojFolder}/${file}"
            },
            {
              "conditions": [["startsWith: fastlane, ${path}", "equals: fr, ${lang}"]],
              "output": "fastlane/metadata/fr-FR/${file}"
            }
        ]
    }

Let's quickly unwrap this (check-out the download reference for more details):

  • the first "conditions/output" pair handles all non-fastlane files, with the special iosLprojFolder that knows how to turn a language code into a language folder
  • the second one handles the French metadata alone

This means that if you need to handle other languages, you'll have to copy/paste and adjust that 2nd "conditions/output" pair. This is how we coded the logic that "fr" = "fr-FR" into the configuration.

It's a bit unsatisfying because it's not universal, but it's still manageable.

Other configurations

When we talked with Václav, he suggested two other options. Here there are, with the reasons why I didn't favor them (but you might, so I'm sharing them):

  • have en_US, fr_FR on Localazy and use transformations in localazy.json to strip the information out for lproj folder: this would mean changing the language settings in Xcode, with the possibility that it messes things for other regions (would fr_CA still get French?), plus I'd rather use what seems to be the default for both Xcode and the App Sore
  • separate project for app store descriptions and use en_US, fr_FR as locales there instead: having two projects in Localazy adds some complexity (to manage translators, glossaries, etc.)

💡Random Tips

  • Don't forget to add the maximum number of characters in the translation notes (especially for things like the subtitle)
  • All keys are named "content", so you can use that to find them, but for now you can't search by filename

Conclusion

With the support of txt files, the combo Localazy+Fastlane made localazing your App Store marketing content really easy:

  • download your metadata from App Store Connect with fastlane
  • upload to Localazy
  • translate (& review, don't forget to review!)
  • download from Localazy
  • upload your metadata to App Store Connect with fastlane

Now you can spend time crafting words instead of copy/pasting text, and your app will both be better and more international. Magic ✨

Before you leave 😇

Consider doing or or more of these:

  • Follow me on twitter @sowenjub or maybe even subscribe to this blog's newsletter (sporadic)
  • Use my referral code to create your localazy account
  • Download No Meat Today, a companion app for people who want to eat less meat, whatever you put behind "less" and "meat" (and ping me if you want to help translate it)