Google Translate Tutorial for iOS: How To Translate Text With Google Translate and JSON on the iPhone

Ray Wenderlich
Funny Translation App We Will Build

Funny Translation App We Will Build

Google Translate provides a really neat API that you can use to quickly and easily use in your apps to translate text between tons of different languages.

In this Google Translate tutorial, we’re going to show you how you can use this API from your iPhone app to translate text a user enters in, back and forth from Japanese to English, until a “final match” is found, similar to the amusing Translation Party web page created by Will Carlough and Richard Boenigk.

This Google Translate tutorial will provide an introduction to JSON as well as the Google Translate APIs specifically. It is based upon an excellent tutorial on using JSON on the iPhone by Dan Grigsby at MobileOrchard (we miss you Dan!), but has more details on JSON for those who are new to the concept and information about the Google Translate API specifically.


What is JSON?

JSON is a simple human readable format that is often used to send data over a network connection.

For example, if you have an array of three strings, the JSON representation would simply be:

["test1", "test2", "test3"]

If you have a Pet object with member variables name, breed, and age, the JSON representation would simply be:

{"name" : "Dusty", "breed": "Poodle", "age": 7}

It’s that simple, which is why it’s so easy and popular to use. For the full spec, which can be read in just a couple minutes, check out the JSON format home page.

What is a JSON web service?

Many third parties such as Google or Yahoo! provide web services that return JSON formatted data when you visit a URL with a specified query string.

For the case of Google Translate’s web service, you can translate some text by issuing a GET request to the following URL:

http://ajax.googleapis.com/ajax/services/language/translate

When you issue the GET request, you need to pass some parameters to the web service. You do this by appending a query string to the end of the URL. In short, you begin the query string with a “?” and then add a sequence of “name=value” pairs, separated by the “&” symbol.

The web service defines which arguments need to be passed to the web service. For Google Translate, they have a nice definition of the Google Translate parameters that you can check out.

But the synopsis is to translate some text with Google Translate, you need to pass three arguments:

  • v: Short for “version”, and should always be set to 1.0
  • q: Short for “query”, and should be set to the text to translate.
  • langpair: Short for “language pair”, and should be set to a string containing the pair of two-letter language codes for the languages to translate, separated by a “|” sign.

Note that the values for each of these arguments needs to be escaped by adding percent escapes, according to the rules of URL encoding. For example, the “|” sign is escaped to “%7C”. Luckily, we don’t have to worry about this on the iPhone because there’s there’s a function in NSString that we can use to easily perform the required escaping.

So, the full URL with the query string to translate some text would look something like this:

http://ajax.googleapis.com/ajax/services/language/translate?q=where's%20
    the%20bathroom?&v=1.0&langpair=en%7Cja

Once you perform a GET request with that URL, Google will respond with a string that looks something like this:

{"responseData": {"translatedText":"\u3053\u3053\u3067\u3001\u30d0\u30b9\
    u30eb\u30fc\u30e0\u3068\u306f\uff1f"}, 
    "responseDetails": null, "responseStatus": 200}

Based on what we know about JSON, this means that there is an object with three member variables: “responseData”, “responseDetails”, and “responseStatus”. In the case of “responseData”, that is a sub-object with a member variable of “translatedText”. The value of “translatedText” is just our escaped (Japanese in this case) string.

Ok – enough background, let’s get started!

Adding a JSON Framework to our project

Although you could write the code to parse this string yourself, it’s much easier and less error prone if you use one of the excellent libraries available to do so. For this Google Translate tutorial, we are going to use the Open Source Objective-C JSON framework developed by Stig Brautaset. So go ahead and visit site now and download the latest version of the framework.

Once you have it downloaded, make a new View-based Application in XCode and name the project GoogleTranslate. Right click on “GoogleTranslate” under “Groups & Files”, and click “Add\New Group” and name the group “JSON”. Then open the disk image of the JSON framework that you downloaded, open the JSON folder inside, and drag all of the files in that folder to the new “JSON” group you created in XCode. When the dialog pops up, verify that “Copy items into destination group’s folder (if needed)” is checked.

Next, open up GoogleTranslateViewController.m under Classes and add the following import to the top of the file:

#import "JSON.h"

Compile your project – if it compiles OK you’ve successfully integrated the JSON framework!

How the JSON Framework Works

The main class in the JSON Framework is named SBJsonParser. Once you get the JSON results from the web server, you pass it to the SBJsonParser via the objectWithString method. The parser will do all of the work to parse the string and convert the values into the most appropriate Objective-C type.

The JSON datatypes are converted to Objective-C types as follows (from SBJsonParser.h):

  • Null -> NSNull
  • String -> NSMutableString
  • Array -> NSMutableArray
  • Object -> NSMutableDictionary
  • Boolean -> NSNumber (initialised with -initWithBool:)
  • Number -> NSDecimalNumber

So in our case, the response from Google Translate would be converted into an NSMutableDictionary with three keys: responseData, responseDetails, and responseStatus.

The one we care about the most is responseData. It’s actually another NSMutableDictionary with just one key: translatedText. And translatedText is a simple NSString – the result we’re looking for!

Ok we’re really done with background information for real now! Let’s set up our interface.

Creating the Interface

Before we begin, let’s set up some member variables in our GoogleTranslateViewController class so we can hook them up with Interface Builder shortly. Open up GoogleTranslateViewController.h and edit the file to look as follows:

#import <UIKit/UIKit.h>
 
@interface GoogleTranslateViewController : UIViewController {
    IBOutlet UITextView *textView;
    IBOutlet UITextField *textField;
    IBOutlet UIButton *button;
}
 
- (IBAction)doTranslation;
 
@end

Now expand the Resources tab and open up the GoogleTranslateViewController.xib. Open up the main view, and use the library to add a text field view, button, text view, and label to look like the following:

Layout in IB for our View

In the properties, remove all of the text in the Text View, and unclick “Editable”. Then, control-drag from the File’s Owner to the text view, text field, and button in the XIB window and connect them to their outlets. Finally, control drag from the button back to the File’s Owner to connect the doTranslation callback.

Save your XIB and compile and run the project to make sure that everything shows up OK so far. But don’t click the button yet, we haven’t actually written the callback! :]

Performing the Translation

We are going to retrieve the JSON response from the web server asynchronously. This means that rather than using initWithContentsOfURL – which is a no-no because it makes the interface unresponsive while the download is taking place – we will register ourselves as a delegate so we get notifications as the data is being downloaded in the background.

So to accomplish this, add three more member variables to your class in GoogleTranslateViewController.h:

NSMutableData *responseData;
NSMutableArray *translations;
NSString *_lastText;

And one property:

@property (nonatomic, copy) NSString * lastText;

Then go to GoogleTranslateViewController.m and add the following header:

#import "JSON.h"

And the following line right under the @implementation:

@synthesize lastText = _lastText;

And modify the dealloc method to include our cleanup code before we forget:

- (void)dealloc {
    [translations release];
    [_lastText release];
    [super dealloc];
}

Last piece of grunt work – create the translations array in the viewDidLoad method as follows:

- (void)viewDidLoad {
    [super viewDidLoad];
    translations = [[NSMutableArray alloc] init]; 
}

Ok – now time to perform the translation. Add the following methods to the bottom of the file:

- (void)performTranslation {
 
    responseData = [[NSMutableData data] retain];
 
    NSString *langString = @"en|ja";
    NSString *textEscaped = [_lastText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSString *langStringEscaped = [langString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSString *url = [NSString stringWithFormat:@"http://ajax.googleapis.com/ajax/services/language/translate?q=%@&v=1.0&langpair=%@",
                     textEscaped, langStringEscaped];
 
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];
 
}
 
- (IBAction)doTranslation {
 
    [translations removeAllObjects];
    [textField resignFirstResponder];
 
    button.enabled = NO;
    self.lastText = textField.text;
    [translations addObject:_lastText];
    textView.text = _lastText;    
 
    [self performTranslation];
 
}

Let’s look at doTranslation first. In this method, we clear out our member variable that we are going to use to store the translations. We also remove the keyboard from the display by calling resignFirstResponder on the text field. We set the button to disabled, store away the text that the user wants to translate in lastText and set the initial value of the text view to that value, and then call the performTranslation method.

In performTranslation, we first initialize an NSMutableData object that will be used to store the JSON response as it comes back from the server.

We then construct the URL to retrieve the translation from the Google server, using stringByAddingPercentEscapesUsingEncoding in order to escape our string properly.

Finally, we create an NSURLConnection with a NSURLRequest in order to start the process of downloading the data asynchronously. We set ourselves as the delegate so we will receive callbacks when the data arrives.

So let’s write those callbacks now! Add the following to the bottom of the file:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [responseData setLength:0];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [responseData appendData:data];
}
 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    textView.text = [NSString stringWithFormat:@"Connection failed: %@", [error description]];
    button.enabled = YES;
}
 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [connection release];
 
    NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    [responseData release];
 
    NSMutableDictionary *luckyNumbers = [responseString JSONValue];
    [responseString release];
    if (luckyNumbers != nil) {
 
        NSDecimalNumber * responseStatus = [luckyNumbers objectForKey:@"responseStatus"];
        if ([responseStatus intValue] != 200) {
            button.enabled = YES;
            return;
        }
 
        NSMutableDictionary *responseDataDict = [luckyNumbers objectForKey:@"responseData"];
        if (responseDataDict != nil) {
            NSString *translatedText = [responseDataDict objectForKey:@"translatedText"];
            [translations addObject:translatedText];
            self.lastText = translatedText;            
            textView.text = [textView.text stringByAppendingFormat:@"\n%@", translatedText];
            button.enabled = YES;
        }
    }
 
}

Most of this is the pretty standard method way to collect the data as it comes in our NSMutableArray (responseData).

However the real fun occurs in connectionDidFinishLoading. We convert our NSData to a string, and then call a shortcut extension on NSString to invoke the JSON parser and convert the JSON string into the appropriate object (we know in this case that it will be a NSMutableDictionary). Note that we could have done this same thing by instantiating a SBJsonParser object and calling objectWithString – but this is just a bit simpler.

We then look inside the response, check the status to make sure we were successful, and then get the sub-dictionary of “responseData”. We then pull our the translated text, and add the results to the text view.

Compile and run your project, and if all works well you should see something like the following:

Our first translation

Finishing Touches

Now let’s add a little fun to the project and keep performing the translations back and forth until we arrive at a final result. Modify the beginning of your performTranslation method to look as follows:

- (void)performTranslation {
 
    // Break if we exceed limit
    if (translations.count > 50) {
        button.enabled = YES;
        return;
    }
 
    responseData = [[NSMutableData data] retain];
    BOOL translateToEnglish = ([translations count] % 2 == 0);
 
    // Bail if we have a match
    if (!translateToEnglish && [translations count] >= 3) {
        NSString *oldString = [translations objectAtIndex:[translations count] - 3];
        if ([oldString caseInsensitiveCompare:_lastText] == NSOrderedSame) {
            button.enabled = YES;
            return;
        }
    }
 
    NSString *langString;
    if (translateToEnglish) {
        langString = @"ja|en";        
    } else {
        langString = @"en|ja";
    }
 
    NSString *textEscaped = [_lastText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    ///...

Here we just add a little fun logic into the mix. We bail out if we’ve performed over 50 back and forth translations. We figure out if we’re translating to English or not by checking the size of our translation array – we alternate between English and Japanese, so every time the count is divisible by 2, we must be trying to translate some Japanese to English.

We check to see if the last English result matches the current English result, and we know we’re done if it matches. Lastly, we set the language string to alternate based on what we’re trying to do.

Now navigate to the connectionDidFinishLoading method. Add the following line inside at the end of the if (responseDataDict != nil) block:

[self performTranslation];

Compile and run the project, and you now should see some amusing results! Funny how much is lost in translation!

Screenshot of A Full and Amusing Translation

Can I Use Google Translate in My App?

If your app is free, definitely.

But if your app is paid, it’s a good question. According to the Google AJAX API Terms of Service, generally the implementation of Google Translate in your application must be available free of charge. So it’s pretty clear that Google wouldn’t allow you to whip up an app that is just a wrapper over Google Translate and sell it as-is.

However it’s a bit more unclear what is allowed when you have significant other features in your app, and Google translate is just one part of that. Some people on the Google AJAX forums believe that as long as you aren’t charging for the translation results specifically and give attribution to Google, you’ll be OK. I’ve also seen people doing this already on the App Store and haven’t heard of any issues.

But neither I nor the other folks are lawyers, so you might want to consult with one if you want to use Google APIs in your app!

And that’s a wrap!

That’s all we’re going to cover for now with the Google Translate API and JSON. Hopefully, this was a good introduction to JSON for those of you who are new to it.

Here’s a sample project with all of the code we’ve developed in the above Google Translate tutorial.

Have you been using JSON in your apps in any cool or interesting ways? Please share any tips or advice you may have below!

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

User Comments

28 Comments

[ 1 , 2 ]
  • Thanks Hollance,

    That helps. I'm curious to see how iOS 5 will handle everything.
    Even though I have already some apps published, I still get sometimes confused with the way properties retain the memory. Especially in this app where the instance variable is named _x, and the property is x, and the synchronization is @synchronize x= _x; - I was not fully sure whether _x was the property or instance variable...
    Just looking at Ray's properties tutorial too, I think I understand it better now.

    Christina
    cabhara
  • One more question: why is the property done with copy? what is the difference to retain?

    @property (nonatomic, copy) NSString * lastText;
    cabhara
  • NSString and NSArray properties are often done with copy instead of retain. That is so you cannot do this:

    Code: Select all

    NSMutableString *s = @"Hi!";
    someObject.someProperty = s;
    [s appendString:@" Now I have changed"];


    If someProperty is retain, then changing s will change the property. But if it is copy, then changing s will leave the property alone because they are now separate objects.
    Hollance
  • Hi All!

    why i translate, i have issues:

    2011-12-02 09:53:22.233 ScavengerHunt[292:e903] -[NSNull objectForKey:]: unrecognized selector sent to instance 0x157a5e8
    2011-12-02 09:53:22.288 ScavengerHunt[292:e903] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull objectForKey:]: unrecognized selector sent to instance 0x157a5e8'
    *** Call stack at first throw:
    (
    0 CoreFoundation 0x0150d5a9 __exceptionPreprocess + 185
    1 libobjc.A.dylib 0x01661313 objc_exception_throw + 44
    2 CoreFoundation 0x0150f0bb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3 CoreFoundation 0x0147e966 ___forwarding___ + 966
    4 CoreFoundation 0x0147e522 _CF_forwarding_prep_0 + 50
    5 ScavengerHunt 0x000515c6 +[GoogleTranslate googleTranslate:from:to:] + 582
    6 ScavengerHunt 0x0004b4a3 -[AppChatViewController tableView:cellForRowAtIndexPath:] + 1075
    7 UIKit 0x006bbb98 -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] + 634
    8 UIKit 0x006b14cc -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:] + 75
    9 UIKit 0x006c68cc -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow:] + 1561
    10 UIKit 0x006be90c -[UITableView layoutSubviews] + 242
    11 QuartzCore 0x012e0a5a -[CALayer layoutSublayers] + 181
    12 QuartzCore 0x012e2ddc CALayerLayoutIfNeeded + 220
    13 QuartzCore 0x012880b4 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 310
    14 QuartzCore 0x01289294 _ZN2CA11Transaction6commitEv + 292
    15 QuartzCore 0x0128946d _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 99
    16 CoreFoundation 0x014ee89b __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 27
    17 CoreFoundation 0x014836e7 __CFRunLoopDoObservers + 295
    18 CoreFoundation 0x0144c1d7 __CFRunLoopRun + 1575
    19 CoreFoundation 0x0144b840 CFRunLoopRunSpecific + 208
    20 CoreFoundation 0x0144b761 CFRunLoopRunInMode + 97
    21 GraphicsServices 0x01e241c4 GSEventRunModal + 217
    22 GraphicsServices 0x01e24289 GSEventRun + 115
    23 UIKit 0x00654c93 UIApplicationMain + 1160
    24 ScavengerHunt 0x0000231d main + 125
    25 ScavengerHunt 0x00002295 start + 53

    Please help me!!!

    thanks,
    chiataytuday
  • chiataytuday wrote:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull objectForKey:]: unrecognized selector sent to instance 0x157a5e8'

    You are calling the objectForKey method on an object of class NSNull. You probably expected this object to be an NSDictionary but it isn't. Most likely the JSON you are receiving is invalid or contains other data than your code is expecting.
    Hollance
  • This was a great tutorial and I had no problem building and running it in iOS 5.1 but unfortunately it doesn't work anymore.
    I mean the program still works but you won't get any translated text back. Google has turned off the v1.0 translate API and now has v2 but it is no longer free. Shame.
    js33
  • Google Translate api is now depricated :(
    gourav.vaidya
  • Thanks for great tutorial. I have downloaded your sample project , but its not working.
    It returns me HTTP 403 error code. I think this API is deprecated & new API v2 is only paid service.
    https://developers.google.com/translate/v2/faq

    Let me know , if I am wrong. Thanks
    appDev
  • it is not working.what is the alternative for this?
    murali
  • i used your sample source and i run on xcode but i didn't hear any speech when i wrote something on textbox and pressed GO button ?

    Is there any means i can use google translator in paid apps and how you are sure google permit on paid apps for iphoen this app https://itunes.apple.com/us/app/60+-lan ... 80733?mt=8
    is using google translator and its paid application.
    emysa341
  • I downloaded your example, but its not working. I can only see English text...

    Could you please upload working example?
    parkarfahim22
  • hi there is any way to use google translator free or alternative method .......
    ranahamadkhan
  • Microsoft has a translator API. Check out http://www.microsoft.com/en-us/translator/developers.aspx
    The API is available for FREE for usage up to 2 million characters per month.


    Check out https://github.com/bitmapdata/MSTranslateVendor for an IOS API to talk to the Microsoft Translator service.

    Have fun!
    Richard Caseyrcasey
[ 1 , 2 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Non-Gaming!

    Loading ... Loading ...

Last week's winner: How To Make a Tower Defense Game with Swift.

Suggest a Tutorial - Past Results

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in December: The Great CALayer Tour

Sign Up - December

Our Books

Our Team

Tutorial Team

... 57 total!

Update Team

  • Zouhair Mahieddine

... 14 total!

Editorial Team

... 22 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

... 4 total!