PodSearch

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.