Developing Perspective

#82: Localizing an App.


00:00:00   Hello, and welcome to Developing Perspective.

00:00:03   Developing Perspective is a podcast discussing news of note in Iowa's development, Apple,

00:00:07   and the like.

00:00:08   I'm your host, David Smith.

00:00:09   I'm an independent iOS developer based in Herne, Virginia.

00:00:12   This is show number 82, and today is Tuesday, September 25th.

00:00:16   Developing Perspective is never longer than 15 minutes, so let's get started.

00:00:19   All right, so this is going to be the next in the series of episodes that I'm going to

00:00:24   be talking about the new app I'm working on.

00:00:26   It's a weather app.

00:00:27   I think I've never mentioned its name before, and I'm going to do that now.

00:00:33   The app is called Check the Weather, so that's hopefully being a little bit playful, because

00:00:37   I know what I do many times a day is I'm always like, "Oh, let me check the weather.

00:00:41   Let me check the weather."

00:00:42   So that's what I named the app.

00:00:43   And so this is going to be the next thing in the series of posts I've been doing about

00:00:49   how I've been building it, some of the considerations that go into that.

00:00:53   And these are very developer-oriented, and hopefully I've gotten a lot of good feedback

00:00:56   from people who say it's really great to kind of see the inner workings of how, how you

00:00:59   actually go about building something like this.

00:01:02   And so I'm going to keep doing that.

00:01:03   And today I'm going to be talking about localization.

00:01:06   And this is something that I've never done before.

00:01:08   And if you're a long time listener of Developing Perspective, you'll know that about, I think

00:01:12   about two months ago, a month and a half ago, it was something that I talked about and I

00:01:15   did an episode about, talking about how I struggle with that.

00:01:19   And it's something that I've sort of always thought, oh man, I really wish I did, or I

00:01:23   really think I should, or those types of things.

00:01:25   But the reality is I never have.

00:01:27   And I've been doing this for four, four and a half years now.

00:01:30   I mean, it's a long time I've been making apps.

00:01:32   I've never done any localization work at all.

00:01:35   And so it was kind of like, well, why isn't that?

00:01:37   Why aren't I doing this?

00:01:38   It seems like if I look at my sales,

00:01:40   at least 25% of my users probably

00:01:45   don't speak English as their first language.

00:01:47   And that's just basically, you know, of app sales.

00:01:50   Certainly the United States is the largest market.

00:01:52   In my experience, it's at least 50 to 60% of app sales

00:01:55   probably in the United States, at least of my apps.

00:01:58   Obviously that's probably different for different types of apps.

00:02:02   Then you have the next big ones are Canada, the United Kingdom, Australia, New Zealand,

00:02:06   which all of course speak English.

00:02:08   There are sort of slight differences in terms of spelling or terms or things like that.

00:02:14   And then you start to get into all the rest of the world.

00:02:16   And so you start to talk about other big countries like Germany, France, Spain, Italy.

00:02:22   And so the list goes on from there.

00:02:23   Japan.

00:02:24   I mean, there are a lot of countries with a lot of phones and a lot of users who may

00:02:30   struggle to use your app if you only are providing it in English.

00:02:33   And so this is kind of a process of taking your application.

00:02:38   I mean, I'm sort of starting from a place of that many of the people who listen to this

00:02:43   are English speakers.

00:02:45   And obviously that's based on the fact that I only ever do episodes in English.

00:02:48   And so even if English is not your second language, in order for this to be useful to

00:02:52   you, you probably speak English.

00:02:57   But it's taking that sort of that basis of saying,

00:03:00   here's an app that's in English,

00:03:03   and I'm going to try and make it feel comfortable,

00:03:05   to feel natural, and to be appealing to a user

00:03:07   in another part of the world.

00:03:11   And so this is sort of that process,

00:03:12   and this is how it went.

00:03:14   And at first, on the outset, I'll say,

00:03:16   I was surprised, and pleasantly so,

00:03:17   about how easy, or maybe easy is the wrong word,

00:03:18   how straightforward it was to do a localization of this app.

00:03:22   The app is fairly simple, and a lot of it's graphical and number-based, so it's not too

00:03:27   hard.

00:03:28   But overall, the tools and the process that I had to go through to actually make this

00:03:31   happen was fairly straightforward.

00:03:33   And so I was pretty happy with that.

00:03:36   And so this is kind of how it worked.

00:03:38   So to start with, the way you do localization, primarily in sort of cocoa development, iPhone,

00:03:45   iPhone, Mac, I think it's the same,

00:03:50   is there's a concept of an NS localized string.

00:03:52   And this is basically just a key value store

00:03:56   that you associate with your application,

00:04:01   where rather than providing text in terms of a static string,

00:04:03   so say this is a button called refresh,

00:04:07   usually either in a NIMF file or in a source code,

00:04:10   you would say, you know, button dot, you know, set title,

00:04:14   you know, ampersand, quote, refresh, close quote,

00:04:19   and you would give it that literal string.

00:04:21   And all you do for localization is rather than

00:04:23   hard coding that value directly, you pass in that,

00:04:27   you can certainly do it this way,

00:04:28   or you can do it in a variety of different ways,

00:04:30   but the simplest way is you rather than passing

00:04:31   that string indirectly, you pass that token

00:04:35   to a function called NSLocalizeString.

00:04:37   And what that does is at runtime, rather than just displaying that token directly, it'll

00:04:43   run through sort of a key value store or a lookup table that you associate with your

00:04:49   application that has a bunch of translations.

00:04:52   So if you're in English, it will display whatever the value associated with that token is in

00:04:58   English.

00:04:59   And if you're in Japan, it will be Japanese.

00:05:00   And if you're in France, it will be French.

00:05:02   And it's actually not based on the country you're in.

00:05:04   It's based on the language that the device is set to.

00:05:07   So that's probably a subtle distinction,

00:05:09   but it's important that it's not about where the user is,

00:05:11   it's about what the user has told their phone

00:05:14   or their iPad to display.

00:05:17   And then it displays it, and that's really all you do.

00:05:20   I mean, the process I went through is probably sort of,

00:05:22   it's not great, but it works pretty well,

00:05:25   is I just went through my app, I did a global search,

00:05:28   and when I came to time to sort of go through

00:05:30   and do the localization, I just did a global search

00:05:33   for all my literal strings, and I just looked through them,

00:05:37   and all the ones that made sense to actually localize.

00:05:40   I just went in, changed the token.

00:05:42   I changed that from doing a direct string literal

00:05:45   to NSLocalizedString.

00:05:47   And I did the sort of lazy approach

00:05:48   where you make the token start off as the English string.

00:05:53   So a lot of my string, my tokens are just literals.

00:05:56   And then you give a comment.

00:05:58   And the comment is important.

00:05:59   It's the second parameter to the NSLocalizedString function.

00:06:02   But the importance about the comment

00:06:04   is that's an opportunity that you

00:06:05   you have to tell the translator, to tell somebody who's looking at this token, what it means

00:06:10   in context.

00:06:11   And that's important often for localization because this translator is trying to give

00:06:16   you not just like a raw translation, they're trying to give you a relevant translation.

00:06:21   So it's not, you know, a lot of words in English especially can have multiple meanings in multiple

00:06:26   contexts.

00:06:27   And so you want to give as much context.

00:06:28   So rather than necessarily it being refresh and then you just your comment is a refresh

00:06:33   which is completely useless, you can say,

00:06:38   "A button pressed when the user is trying to refresh

00:06:40   the displayed information."

00:06:44   And the translation for that may be different

00:06:46   in a different language than if it was a different kind

00:06:48   of refresh, than if it was, I don't even know.

00:06:51   But it's like you're trying to give as much context

00:06:54   to that as you can.

00:06:57   And then basically that's it.

00:06:59   And then you go into Xcode and you say,

00:07:00   "I'm going to generate some strings files."

00:07:01   And there's a little tool called Gen Strings that comes with Xcode or the developer tools.

00:07:06   I think it may be part of the command line tools for a set, but it's pretty straightforward

00:07:09   once you get Gen Strings, and that's G-E-N strings, that's one word.

00:07:13   And you just run that over your source code or your nib files or all kinds of things,

00:07:18   and it'll output what Xcode calls a strings file.

00:07:22   And that's just a dot strings file as the extension.

00:07:26   basically a look-up table of comment, token equals value.

00:07:30   And that's it.

00:07:33   And it's just in a very simple format

00:07:35   that's machine parsable and all that.

00:07:38   And basically that's it.

00:07:38   Once you've got that, you've generated your strings file,

00:07:41   you now have the basis of what you need

00:07:43   to do your localization.

00:07:44   That's what you're going to send to a translator.

00:07:46   That's what you're going to work through.

00:07:48   One thing I will say,

00:07:49   just before I get too far ahead of myself,

00:07:52   in an ideal world, you probably make your tokens

00:07:54   more meaningful and more generic.

00:07:59   I've run into this a little bit myself,

00:08:02   where I have these very funny things

00:08:03   because my tokens were just direct English words

00:08:05   rather than actually being tokens

00:08:08   in a more abstract sense.

00:08:10   So rather than saying refresh,

00:08:15   I could have said refresh button title as my token,

00:08:16   which isn't what would be displayed to the user.

00:08:22   And in the English localization,

00:08:20   need to go and set the value of that.

00:08:25   But the thing that's often what you'll end up with is,

00:08:27   the lazy approach is just to use your English words as the tokens.

00:08:30   But what that means is, of course,

00:08:33   if you want to change the copy in your app,

00:08:35   you're going to end up with these very strange things

00:08:38   where the token isn't abstract.

00:08:39   It's a very literal token, it's refresh,

00:08:43   but it's actually going to turn into update.

00:08:45   So you have this refresh equals update in your strings file,

00:08:46   which is a little weird.

00:08:51   So that's just in an ideal sense, in a true sense,

00:08:53   which would probably be wise to do,

00:08:56   is to make your tokens abstract.

00:08:58   So then the next thing I did is you have your strings file,

00:09:00   you just want to look at it, make sure it's good and clean

00:09:03   and parsed, and you're ready to go.

00:09:05   There's a great tool that I found for doing that.

00:09:08   This is by Oliver Drobnik.

00:09:10   You probably know him online as Coconetics,

00:09:12   and he has a tool called Linguin,

00:09:14   which I have a link to in the show notes.

00:09:12   And basically, this is a tool for managing strings files.

00:09:15   It doesn't do really much beyond that.

00:09:17   But what it's really great for doing is it can look at your

00:09:22   Xcode project file and look at the strings files that you

00:09:24   have, tell you what tokens you're missing, what tokens

00:09:27   you have, and kind of just lays them out in a nice grid

00:09:30   format, which I find really helpful.

00:09:32   So I highly recommend that if you're going to do any

00:09:33   localization work.

00:09:34   It's in the Mac App Store.

00:09:35   It's pretty inexpensive.

00:09:37   It's just a great little tool for kind of managing this.

00:09:40   So the next process, once you have your strings files,

00:09:45   is you're going to need to take those and get them translated.

00:09:48   And there's a variety of ways you could do that.

00:09:50   You could ask friends, customers, people you know.

00:09:52   If you're a linguist yourself, maybe you can just do them yourself.

00:09:55   But basically what you're going to be doing is trying to take all those tokens and translate them.

00:09:58   What I ended up doing is I was looking for a variety of sources for services for doing this.

00:10:02   And I posted on Twitter and I got a lot of recommendations and feedback.

00:10:06   And there seems like there's maybe four main vendors for doing this.

00:10:11   There's Applingua, WordCraft, iCanLocalize, and Tetris.

00:10:17   And I'm not sure if that's how you say it, the T-E-T-H-R-A-S,

00:10:21   which is actually the one I went with.

00:10:25   And basically all of these services take in a strings file.

00:10:27   They're very iOS oriented.

00:10:30   So they take a strings file directly, they parse it,

00:10:32   they manage it on their servers,

00:10:32   and they help kind of work you through that process.

00:10:34   They do different amounts of hand-holding, and what I ended up doing is I went with Tetris

00:10:40   because they were a nice managed approach.

00:10:45   And by that I mean I'm not interacting with my translators directly.

00:10:49   I'm not trying to do quality assurance or project management or any of those types of

00:10:53   things.

00:10:54   You know, they're taking care of that for you, and you pay a slight premium for that.

00:10:57   I think it's about 10 to 18 cents a word, which seemed pretty middle of the road.

00:11:02   You can go a little bit cheaper if it's something like I can localize, but I feel like you're

00:11:06   doing a bit more of that.

00:11:08   You're deeper in the process, which for me, I'd rather spend a little bit of extra money.

00:11:12   It's not a huge amount, and I'd rather spend that little bit of extra money to have someone

00:11:16   manage that for me who's done this before, who can interact with translators in a way

00:11:19   that makes sense.

00:11:20   So I'm going to talk about my experience with them and how that went.

00:11:25   And so basically, I created an account with them,

00:11:28   pretty straightforward.

00:11:29   I uploaded my strings file.

00:11:30   I had written an app description that I also submitted.

00:11:33   So they translated that, and I'll

00:11:34   include that in my iTunes Connect

00:11:36   when I submit it so that if you're in that country,

00:11:38   you can read a description in your language, which I hope

00:11:41   is kind of useful and nice to them.

00:11:43   I'm probably also going to actually do the English one

00:11:45   below it, just in case you also speak English

00:11:47   or in case there's something funny in the translation

00:11:50   that hopefully that'll help.

00:11:52   But I submitted both of those.

00:11:54   I had a pretty low word count, it wasn't too bad.

00:11:56   The actual app itself only has 122 words

00:11:59   for translation, which is not too bad.

00:12:02   And like I said, a lot of that's just,

00:12:04   the nature of the app isn't very word-based.

00:12:07   And most of those are actually in the settings

00:12:08   or tutorial areas of the app.

00:12:09   They're not actually part of the functional part,

00:12:12   because most of the functional part

00:12:14   is either graphics or displaying numbers.

00:12:16   It's 26 degrees and 18% chance of rain.

00:12:18   And so basically I just went through,

00:12:21   submitted them all, picked the languages I want.

00:12:23   I just kind of did, I guess you could sort of call the big five or the big six, depending

00:12:28   on how you want to look at it.

00:12:29   I did German, French, Italian, Japanese, and Chinese, and submitted those.

00:12:36   And Spanish, sorry.

00:12:37   So those were my six.

00:12:39   And just sort of submit them.

00:12:41   You sort of go through, you buy it.

00:12:44   You're paying by word.

00:12:45   It's all upfront pricing, which was great.

00:12:47   And you just kind of wait a day or two.

00:12:49   I mean, I was very impressed by the turnaround time.

00:12:51   It was only, I think the longest translation was my Japanese one and it took probably about

00:12:55   48 hours from when I hit submit.

00:12:58   A lot of them were coming in quicker than that and it was just great.

00:13:01   I mean, the way they do it, which I think a lot of them do, there's this nice little

00:13:05   kind of interactivity with the translators that's asynchronous and sort of low stress,

00:13:11   but a lot of times the translators will find little questions or sort of questions or hiccups

00:13:15   or things that they're not sure about.

00:13:17   And so rather than just kind of guessing,

00:13:20   they can create what they call discussion questions

00:13:23   in their system, and I think a bunch of people

00:13:25   do a similar kind of thing, where they'll ask you a question

00:13:27   and they'll say, "Hey, so I see I can't find,

00:13:31   "so if I'm translating this word,

00:13:32   "there's like two or three different ways

00:13:34   "I could translate it, which of these sort of meanings

00:13:36   "or contexts will, you know, would work best for you

00:13:39   "in this context?"

00:13:40   And you can insert and go back and forth,

00:13:42   or they'll sometimes be like, "Here's two options,

00:13:44   "this one's shorter, but this one's,

00:13:46   "this longer one's better."

00:13:47   So if you have space, use the longer one.

00:13:49   If it's, basically, a problem, use a shorter one.

00:13:51   And that was basically it.

00:13:52   Now I had my translated list of things,

00:13:56   some things that made that process go better,

00:13:58   really good comments, and submitting

00:14:00   screenshots of your app.

00:14:01   I think translators really like being able to see where

00:14:04   that word is going in context.

00:14:05   So two things you can really do to help them out,

00:14:08   and having nice clean strings.

00:14:11   So now I have my strings.

00:14:12   I pop them into Xcode, and that's it.

00:14:15   Now my app is localized into seven languages, I guess, six foreign and one English, and

00:14:20   it's seemed to work pretty great.

00:14:22   I can launch it in those apps and it looks great.

00:14:24   I don't really speak those languages, so it's hard for me to know necessarily of the quality

00:14:27   of the translation.

00:14:28   But going through one of these managed vendors, the goal is that a lot of that quality has

00:14:33   been taken care of because they've been QA'd internally by their linguists or by two linguists

00:14:38   or by someone who at least knows what's going on.

00:14:39   And so that's all.

00:14:41   That's all I did, and it's great.

00:14:42   and I look forward to seeing what that does for my sales abroad. Hopefully it increases them. That's certainly the goal.

00:14:47   Alright, that's it for today's show. As always, if you have questions, comments, concerns, or complaints,

00:14:52   I'm on Twitter @_DavidSmith. And otherwise, I hope you have a great week, happy coding, and I will talk to you soon. Bye.